diff --git a/app/Mage.php b/app/Mage.php index f48d1fe5b7..a298754e90 100644 --- a/app/Mage.php +++ b/app/Mage.php @@ -82,7 +82,7 @@ final class Mage { public static function getVersion() { - return '1.2.0.3'; + return '1.2.1'; } /** @@ -416,15 +416,22 @@ public static function throwException($message, $messageStorage=null) public static function app($code = '', $type = 'store', $options=array()) { if (null === self::$_app) { - Varien_Profiler::start('app/init'); - + Varien_Profiler::start('mage::app::construct'); self::$_app = new Mage_Core_Model_App(); + Varien_Profiler::stop('mage::app::construct'); Mage::setRoot(); Mage::register('events', new Varien_Event_Collection()); + + + Varien_Profiler::start('mage::app::register_config'); Mage::register('config', new Mage_Core_Model_Config()); + Varien_Profiler::stop('mage::app::register_config'); + Varien_Profiler::start('mage::app::init'); self::$_app->init($code, $type, $options); + Varien_Profiler::stop('mage::app::init'); + self::$_app->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS); } return self::$_app; @@ -440,17 +447,17 @@ public static function app($code = '', $type = 'store', $options=array()) public static function run($code = '', $type = 'store', $options=array()) { try { - Varien_Profiler::start('app'); + Varien_Profiler::start('mage'); - Varien_Profiler::start('app::init'); + Varien_Profiler::start('mage::app'); self::app($code, $type, $options); - Varien_Profiler::stop('app::init'); + Varien_Profiler::stop('mage::app'); - Varien_Profiler::start('app::dispatch'); + Varien_Profiler::start('mage::dispatch'); self::app()->getFrontController()->dispatch(); - Varien_Profiler::stop('app::dispatch'); + Varien_Profiler::stop('mage::dispatch'); - Varien_Profiler::stop('app'); + Varien_Profiler::stop('mage'); } catch (Mage_Core_Model_Session_Exception $e) { header('Location: ' . Mage::getBaseUrl()); diff --git a/app/code/core/Mage/Admin/Model/Mysql4/User.php b/app/code/core/Mage/Admin/Model/Mysql4/User.php index 6f75d6089b..4fb8586563 100644 --- a/app/code/core/Mage/Admin/Model/Mysql4/User.php +++ b/app/code/core/Mage/Admin/Model/Mysql4/User.php @@ -37,10 +37,26 @@ class Mage_Admin_Model_Mysql4_User extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('admin/user', 'user_id'); - $this->_uniqueFields = array( - array('field' => 'email', 'title' => Mage::helper('adminhtml')->__('Email')), - array('field' => 'username', 'title' => Mage::helper('adminhtml')->__('User Name')), - ); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array( + array( + 'field' => 'email', + 'title' => Mage::helper('adminhtml')->__('Email') + ), + array( + 'field' => 'username', + 'title' => Mage::helper('adminhtml')->__('User Name') + ), + ); + return $this; } /** @@ -144,6 +160,9 @@ public function delete(Mage_Core_Model_Abstract $user) return true; } + /** + * TODO: unify _saveRelations() and add() methods, they make same things + */ public function _saveRelations(Mage_Core_Model_Abstract $user) { $rolesIds = $user->getRoleIds(); @@ -159,11 +178,10 @@ public function _saveRelations(Mage_Core_Model_Abstract $user) foreach ($rolesIds as $rid) { $rid = intval($rid); if ($rid > 0) { - //$row = $this->load($user, $rid); + $row = Mage::getModel('admin/role')->load($rid)->getData(); } else { $row = array('tree_level' => 0); } - $row = array('tree_level' => 0); $data = array( 'parent_id' => $rid, @@ -197,8 +215,8 @@ public function _getRoles(Mage_Core_Model_Abstract $user) return (($roles = $read->fetchCol($select)) ? $roles : array()); } - public function add(Mage_Core_Model_Abstract $user) { - + public function add(Mage_Core_Model_Abstract $user) + { $dbh = $this->_getWriteAdapter(); $aRoles = $this->hasAssigned2Role($user); @@ -226,7 +244,8 @@ public function add(Mage_Core_Model_Abstract $user) { return $this; } - public function deleteFromRole(Mage_Core_Model_Abstract $user) { + public function deleteFromRole(Mage_Core_Model_Abstract $user) + { if ( $user->getUserId() <= 0 ) { return $this; } diff --git a/app/code/core/Mage/Admin/Model/Roles.php b/app/code/core/Mage/Admin/Model/Roles.php index 736f2d45fa..365df4c486 100644 --- a/app/code/core/Mage/Admin/Model/Roles.php +++ b/app/code/core/Mage/Admin/Model/Roles.php @@ -88,10 +88,10 @@ protected function _buildResourcesArray(Varien_Simplexml_Element $resource=null, // return array(); //} - $resource->title = Mage::helper($module)->__((string)$resource->title); + //$resource->title = Mage::helper($module)->__((string)$resource->title); if ( is_null($represent2Darray) ) { - $result[$resourceName]['name'] = (string)$resource->title; + $result[$resourceName]['name'] = Mage::helper($module)->__((string)$resource->title); $result[$resourceName]['level'] = $level; } else { $result[] = $resourceName; diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit.php index 341620b00f..889119cc5c 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit.php @@ -45,8 +45,9 @@ public function __construct() protected function _prepareLayout() { + $category = Mage::registry('current_category'); if (Mage::app()->getConfig()->getModuleConfig('Mage_GoogleOptimizer')->is('active', true) - && Mage::helper('googleoptimizer')->isOptimizerActive()) { + && Mage::helper('googleoptimizer')->isOptimizerActive($category->getStoreId())) { $this->setChild('googleoptimizer_js', $this->getLayout()->createBlock('googleoptimizer/js')->setTemplate('googleoptimizer/js.phtml') ); diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tabs.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tabs.php index 48f17f51e5..bb04589528 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tabs.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tabs.php @@ -61,8 +61,10 @@ protected function _prepareLayout() 'label' => Mage::helper('catalog')->__('Custom Design'), 'content' => $this->getLayout()->createBlock('adminhtml/catalog_category_tab_design')->toHtml(), )); + + $category = Mage::registry('current_category'); if (Mage::app()->getConfig()->getModuleConfig('Mage_GoogleOptimizer')->is('active', true) - && Mage::helper('googleoptimizer')->isOptimizerActive()) { + && Mage::helper('googleoptimizer')->isOptimizerActive($category->getStoreId())) { $this->addTab('googleoptimizer', array( 'label' => Mage::helper('googleoptimizer')->__('Category View Optimization'), 'content' => $this->getLayout()->createBlock('googleoptimizer/adminhtml_catalog_category_edit_tab_googleoptimizer')->toHtml(), diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit/Tab/Main.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit/Tab/Main.php index 7909647d26..99cb916ca8 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit/Tab/Main.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit/Tab/Main.php @@ -339,6 +339,16 @@ protected function _prepareForm() )); // } + $htmlAllowed = $fieldset->addField('is_html_allowed_on_front', 'select', array( + 'name' => 'is_html_allowed_on_front', + 'label' => Mage::helper('catalog')->__('Allow HTML-tags on Front-end'), + 'title' => Mage::helper('catalog')->__('Allow HTML-tags on Front-end'), + 'values' => $yesno, + )); + if (!$model->getId()) { + $htmlAllowed->setValue(1); + } + if ($model->getId()) { $form->getElement('attribute_code')->setDisabled(1); diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Inventory.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Inventory.php index 06a091184b..a44fc92de3 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Inventory.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Inventory.php @@ -42,6 +42,16 @@ public function getBackordersOption() return Mage::getSingleton('cataloginventory/source_backorders')->toOptionArray(); } + /** + * Retrieve stock option array + * + * @return array + */ + public function getStockOption() + { + return Mage::getSingleton('cataloginventory/source_stock')->toOptionArray(); + } + public function getProduct() { return Mage::registry('product'); diff --git a/app/code/core/Mage/Adminhtml/Block/Promo/Widget/Chooser/Sku.php b/app/code/core/Mage/Adminhtml/Block/Promo/Widget/Chooser/Sku.php index 288e512338..91175c4fb6 100644 --- a/app/code/core/Mage/Adminhtml/Block/Promo/Widget/Chooser/Sku.php +++ b/app/code/core/Mage/Adminhtml/Block/Promo/Widget/Chooser/Sku.php @@ -37,7 +37,13 @@ class Mage_Adminhtml_Block_Promo_Widget_Chooser_Sku extends Mage_Adminhtml_Block public function __construct($arguments=array()) { parent::__construct($arguments); - $this->setId('skuChooserGrid_'.$this->getId()); + + if ($this->getRequest()->getParam('current_grid_id')) { + $this->setId($this->getRequest()->getParam('current_grid_id')); + } else { + $this->setId('skuChooserGrid_'.$this->getId()); + } + $form = $this->getJsFormObject(); $this->setRowClickCallback("$form.chooserGridRowClick.bind($form)"); $this->setCheckboxCheckCallback("$form.chooserGridCheckboxCheck.bind($form)"); @@ -129,8 +135,9 @@ protected function _prepareColumns() public function getGridUrl() { return $this->getUrl('*/*/chooser', array( - '_current' => true, - 'collapse' => null + '_current' => true, + 'current_grid_id' => $this->getId(), + 'collapse' => null )); } diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Items/Abstract.php b/app/code/core/Mage/Adminhtml/Block/Sales/Items/Abstract.php index bc1110b026..e19c85b9b7 100644 --- a/app/code/core/Mage/Adminhtml/Block/Sales/Items/Abstract.php +++ b/app/code/core/Mage/Adminhtml/Block/Sales/Items/Abstract.php @@ -91,8 +91,11 @@ public function addItemRender($type, $block, $template) * @param string $template * @return Mage_Adminhtml_Block_Sales_Items_Abstract */ - public function addColumnRender($column, $block, $template) + public function addColumnRender($column, $block, $template, $type=null) { + if (!is_null($type)) { + $column .= '_' . $type; + } $this->_columnRenders[$column] = array( 'block' => $block, 'template' => $template, @@ -127,17 +130,22 @@ public function getItemRenderer($type) * Retrieve column renderer block * * @param string $column + * @param string $compositePart * @return Mage_Core_Block_Abstract */ - public function getColumnRenderer($column) + public function getColumnRenderer($column, $compositePart='') { + if (isset($this->_columnRenders[$column . '_' . $compositePart])) { + $column .= '_' . $compositePart; + } if (!isset($this->_columnRenders[$column])) { return false; } if (is_null($this->_columnRenders[$column]['renderer'])) { $this->_columnRenders[$column]['renderer'] = $this->getLayout() ->createBlock($this->_columnRenders[$column]['block']) - ->setTemplate($this->_columnRenders[$column]['template']); + ->setTemplate($this->_columnRenders[$column]['template']) + ->setRenderedBlock($this); } return $this->_columnRenders[$column]['renderer']; } @@ -171,7 +179,13 @@ public function getItemHtml(Varien_Object $item) */ public function getColumnHtml(Varien_Object $item, $column, $field = null) { - if ($block = $this->getColumnRenderer($column)) { + if ($item->getOrderItem()) { + $block = $this->getColumnRenderer($column, $item->getOrderItem()->getProductType()); + } else { + $block = $this->getColumnRenderer($column, $item->getProductType()); + } + + if ($block) { $block->setItem($item); if (!is_null($field)) { $block->setField($field); diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Items/Column/Name/Grouped.php b/app/code/core/Mage/Adminhtml/Block/Sales/Items/Column/Name/Grouped.php new file mode 100644 index 0000000000..a887b4adcf --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Block/Sales/Items/Column/Name/Grouped.php @@ -0,0 +1,58 @@ + + */ +class Mage_Adminhtml_Block_Sales_Items_Column_Name_Grouped extends Mage_Adminhtml_Block_Sales_Items_Column_Name +{ + /** + * Prepare item html + * + * This method uses renderer for real product type + * + * @return string + */ + protected function _toHtml() + { + if ($this->getItem()->getOrderItem()) { + $item = $this->getItem()->getOrderItem(); + } else { + $item = $this->getItem(); + } + if ($productType = $item->getRealProductType()) { + $renderer = $this->getRenderedBlock()->getColumnHtml($this->getItem(), $productType); + return $renderer; + } + return parent::_toHtml(); + } +} +?> \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Form/Account.php b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Form/Account.php index 1ba106fb98..d2e3787315 100644 --- a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Form/Account.php +++ b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Create/Form/Account.php @@ -89,8 +89,10 @@ protected function _prepareForm() if ($inputType = $attribute->getFrontend()->getInputType()) { $element = $fieldset->addField($attribute->getAttributeCode(), $inputType, array( - 'name' => $attribute->getAttributeCode(), - 'label' => $attribute->getFrontend()->getLabel(), + 'name' => $attribute->getAttributeCode(), + 'label' => $attribute->getFrontend()->getLabel(), + 'class' => $attribute->getFrontend()->getClass(), + 'required' => $attribute->getIsRequired(), ) ) ->setEntityAttribute($attribute) diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Creditmemo/View.php b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Creditmemo/View.php index b60de99864..179e6e4a97 100644 --- a/app/code/core/Mage/Adminhtml/Block/Sales/Order/Creditmemo/View.php +++ b/app/code/core/Mage/Adminhtml/Block/Sales/Order/Creditmemo/View.php @@ -104,7 +104,7 @@ public function getHeaderText() $header = Mage::helper('sales')->__('Credit Memo #%s | Date %s | Status %s (%s)', $this->getCreditmemo()->getIncrementId(), - $this->formatDate($this->getCreditmemo()->getCreatedAt(), 'medium', true), + $this->formatDate($this->getCreditmemo()->getCreatedAtDate(), 'medium', true), $this->getCreditmemo()->getStateName(), $emailSent ); diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Order/View.php b/app/code/core/Mage/Adminhtml/Block/Sales/Order/View.php index b6c4279c33..9bf627035f 100644 --- a/app/code/core/Mage/Adminhtml/Block/Sales/Order/View.php +++ b/app/code/core/Mage/Adminhtml/Block/Sales/Order/View.php @@ -144,20 +144,8 @@ public function getHeaderText() { $text = Mage::helper('sales')->__('Order # %s | Order Date %s', $this->getOrder()->getRealOrderId(), - $this->formatDate($this->getOrder()->getCreatedAt(), 'medium', true) + $this->formatDate($this->getOrder()->getCreatedAtDate(), 'medium', true) ); - /*if ($this->getOrder()->getRelationParentRealId()) { - $text = Mage::helper('sales')->__('Order # %s | Order Date %s', - $this->getOrder()->getRealOrderId(), - $this->formatDate($this->getOrder()->getCreatedAt(), 'medium', true) - ); - } - else { - $text = Mage::helper('sales')->__('Order # %s | Order Date %s', - $this->getOrder()->getRealOrderId(), - $this->formatDate($this->getOrder()->getCreatedAt(), 'medium', true) - ); - }*/ return $text; } diff --git a/app/code/core/Mage/Adminhtml/Block/Sales/Order/View/Tab/History.php b/app/code/core/Mage/Adminhtml/Block/Sales/Order/View/Tab/History.php index 577eaa359e..c9ac8a48f7 100644 --- a/app/code/core/Mage/Adminhtml/Block/Sales/Order/View/Tab/History.php +++ b/app/code/core/Mage/Adminhtml/Block/Sales/Order/View/Tab/History.php @@ -64,49 +64,49 @@ public function getFullHistory(){ $this->_prepareHistoryItem( $_history->getStatusLabel(), $_history->getIsCustomerNotified(), - $_history->getCreatedAt()); + $_history->getCreatedAtDate()); } foreach ($order->getCreditmemosCollection() as $_memo){ $_fullHistory[$_memo->getEntityId()] = $this->_prepareHistoryItem($this->__('Credit Memo #%s created', $_memo->getIncrementId()), - $_memo->getEmailSent(), $_memo->getCreatedAt()); + $_memo->getEmailSent(), $_memo->getCreatedAtDate()); foreach ($_memo->getCommentsCollection() as $_comment){ $_fullHistory[$_comment->getEntityId()] = $this->_prepareHistoryItem($this->__('Credit Memo #%s comment added', $_memo->getIncrementId()), - $_comment->getIsCustomerNotified(), $_comment->getCreatedAt(), $_comment->getComment()); + $_comment->getIsCustomerNotified(), $_comment->getCreatedAtDate(), $_comment->getComment()); } } foreach ($order->getShipmentsCollection() as $_shipment){ $_fullHistory[$_shipment->getEntityId()] = $this->_prepareHistoryItem($this->__('Shipment #%s created', $_shipment->getIncrementId()), - $_shipment->getEmailSent(), $_shipment->getCreatedAt()); + $_shipment->getEmailSent(), $_shipment->getCreatedAtDate()); foreach ($_shipment->getCommentsCollection() as $_comment){ $_fullHistory[$_comment->getEntityId()] = $this->_prepareHistoryItem($this->__('Shipment #%s comment added', $_shipment->getIncrementId()), - $_comment->getIsCustomerNotified(), $_comment->getCreatedAt(), $_comment->getComment()); + $_comment->getIsCustomerNotified(), $_comment->getCreatedAtDate(), $_comment->getComment()); } } foreach ($order->getInvoiceCollection() as $_invoice){ $_fullHistory[$_invoice->getEntityId()] = $this->_prepareHistoryItem($this->__('Invoice #%s created', $_invoice->getIncrementId()), - $_invoice->getEmailSent(), $_invoice->getCreatedAt()); + $_invoice->getEmailSent(), $_invoice->getCreatedAtDate()); foreach ($_invoice->getCommentsCollection() as $_comment){ $_fullHistory[$_comment->getEntityId()] = $this->_prepareHistoryItem($this->__('Invoice #%s comment added', $_invoice->getIncrementId()), - $_comment->getIsCustomerNotified(), $_comment->getCreatedAt(), $_comment->getComment()); + $_comment->getIsCustomerNotified(), $_comment->getCreatedAtDate(), $_comment->getComment()); } } foreach ($order->getTracksCollection() as $_track){ $_fullHistory[$_track->getEntityId()] = $this->_prepareHistoryItem($this->__('Tracking number %s for %s assigned', $_track->getNumber(), $_track->getTitle()), - false, $_track->getCreatedAt()); + false, $_track->getCreatedAtDate()); } krsort($_fullHistory); diff --git a/app/code/core/Mage/Adminhtml/Block/System/Cache/Edit.php b/app/code/core/Mage/Adminhtml/Block/System/Cache/Edit.php index f55afeb4e4..e428fd6879 100644 --- a/app/code/core/Mage/Adminhtml/Block/System/Cache/Edit.php +++ b/app/code/core/Mage/Adminhtml/Block/System/Cache/Edit.php @@ -144,6 +144,15 @@ public function getCatalogData() ) ), ), + 'rebuild_inventory_stock_status' => array( + 'label' => Mage::helper('adminhtml')->__('Inventory Stock Status'), + 'buttons' => array( + array( + 'name' => 'rebuild_inventory_stock_status', + 'action' => Mage::helper('adminhtml')->__('Refresh'), + ) + ), + ), ); } } \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/Block/System/Design/Grid.php b/app/code/core/Mage/Adminhtml/Block/System/Design/Grid.php index a73fff8826..6ca67b6dc2 100644 --- a/app/code/core/Mage/Adminhtml/Block/System/Design/Grid.php +++ b/app/code/core/Mage/Adminhtml/Block/System/Design/Grid.php @@ -18,14 +18,25 @@ * versions in the future. If you wish to customize Magento for your * needs please refer to http://www.magentocommerce.com for more information. * - * @category Mage - * @package Mage_Adminhtml - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) - * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + * @category Mage + * @package Mage_Adminhtml + * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + */ + +/** + * Design changes grid + * + * @category Mage + * @package Mage_Adminhtml + * @author Magento Core Team */ class Mage_Adminhtml_Block_System_Design_Grid extends Mage_Adminhtml_Block_Widget_Grid { + /** + * Class constructor + */ public function __construct() { parent::__construct(); @@ -34,6 +45,11 @@ public function __construct() $this->setUseAjax(true); } + /** + * Prepare grid data collection + * + * @return Mage_Adminhtml_Block_System_Design_Grid + */ protected function _prepareCollection() { $storeId = (int) $this->getRequest()->getParam('store', 0); @@ -45,21 +61,27 @@ protected function _prepareCollection() return $this; } + /** + * Define grid columns + * + * @return Mage_Adminhtml_Block_System_Design_Grid + */ protected function _prepareColumns() { if (!Mage::app()->isSingleStoreMode()) { $this->addColumn('store_id', array( - 'header' => Mage::helper('catalog')->__('Store'), - 'width' => '100px', - 'type' => 'store', - 'index' => 'store_id', + 'header' => Mage::helper('catalog')->__('Store'), + 'width' => '100px', + 'type' => 'store', + 'store_view' => true, + 'sortable' => false, + 'index' => 'store_id', )); } - $this->addColumn('package', - array( - 'header'=> Mage::helper('catalog')->__('Design'), - 'width' => '150px', + $this->addColumn('package', array( + 'header' => Mage::helper('catalog')->__('Design'), + 'width' => '150px', 'index' => 'design', )); $this->addColumn('date_from', array( @@ -81,14 +103,25 @@ protected function _prepareColumns() return parent::_prepareColumns(); } + /** + * Prepare row click url + * + * @param Varien_Object $row + * @return string + */ public function getRowUrl($row) { - return $this->getUrl('*/*/edit', array('id'=>$row->getId())); + return $this->getUrl('*/*/edit', array('id' => $row->getId())); } + /** + * Prepare grid url + * + * @return string + */ public function getGridUrl() { - return $this->getUrl('*/*/grid', array('_current'=>true)); + return $this->getUrl('*/*/grid', array('_current' => true)); } } diff --git a/app/code/core/Mage/Adminhtml/Controller/Action.php b/app/code/core/Mage/Adminhtml/Controller/Action.php index 7b10248141..52a859f543 100644 --- a/app/code/core/Mage/Adminhtml/Controller/Action.php +++ b/app/code/core/Mage/Adminhtml/Controller/Action.php @@ -1,352 +1,354 @@ - - */ -class Mage_Adminhtml_Controller_Action extends Mage_Core_Controller_Varien_Action -{ - - const FLAG_IS_URLS_CHECKED = 'check_url_settings'; - - /** - * Used module name in current adminhtml controller - */ - protected $_usedModuleName = 'adminhtml'; - - protected function _isAllowed() - { - return true; - } - - /** - * Retrieve adminhtml session model object - * - * @return Mage_Adminhtml_Model_Session - */ - protected function _getSession() - { - return Mage::getSingleton('adminhtml/session'); - } - - /** - * Retrieve base admihtml helper - * - * @return Mage_Adminhtml_Helper_Data - */ - protected function _getHelper() - { - return Mage::helper('adminhtml'); - } - - /** - * Define active menu item in menu block - * - * @return Mage_Adminhtml_Controller_Action - */ - protected function _setActiveMenu($menuPath) - { - $this->getLayout()->getBlock('menu')->setActive($menuPath); - return $this; - } - - /** - * @return Mage_Adminhtml_Controller_Action - */ - protected function _addBreadcrumb($label, $title, $link=null) - { - $this->getLayout()->getBlock('breadcrumbs')->addLink($label, $title, $link); - return $this; - } - - /** - * @return Mage_Adminhtml_Controller_Action - */ - protected function _addContent(Mage_Core_Block_Abstract $block) - { - $this->getLayout()->getBlock('content')->append($block); - return $this; - } - - protected function _addLeft(Mage_Core_Block_Abstract $block) - { - $this->getLayout()->getBlock('left')->append($block); - return $this; - } - - protected function _addJs(Mage_Core_Block_Abstract $block) - { - $this->getLayout()->getBlock('js')->append($block); - return $this; - } - - public function hasAction($action) - { - return true; - } - - /** - * Controller predispatch method - * - * @return Mage_Adminhtml_Controller_Action - */ - public function preDispatch() - { - Mage::getDesign()->setArea('adminhtml') - // [bug] this value will be overriden by defaults, how can it be set in adminhtml/etc/config.xml? - ->setPackageName((string)Mage::getConfig()->getNode('stores/admin/design/package/name')) - ->setTheme((string)Mage::getConfig()->getNode('stores/admin/design/theme/default')); - - $this->getLayout()->setArea('adminhtml'); - - Mage::dispatchEvent('adminhtml_controller_action_predispatch_start', array()); - - parent::preDispatch(); - - if ($this->getRequest()->isPost() && !$this->_validateFormKey()) { - $this->setFlag('', self::FLAG_NO_DISPATCH, true); - $this->setFlag('', self::FLAG_NO_POST_DISPATCH, true); - if ($this->getRequest()->getQuery('isAjax', false) || $this->getRequest()->getQuery('ajax', false)) { - $this->getResponse()->setBody(Zend_Json::encode(array( - 'error' => true, - 'error_msg' => Mage::helper('adminhtml')->__('Invalid Form Key') - ))); - } else { - $this->_redirectReferer(); - } - return $this; - } - - if ($this->getRequest()->isDispatched() - && $this->getRequest()->getActionName() !== 'denied' - && !$this->_isAllowed()) { - $this->_forward('denied'); - $this->setFlag('', self::FLAG_NO_DISPATCH, true); - return $this; - } - - if (!$this->getFlag('', self::FLAG_IS_URLS_CHECKED) - && !$this->getRequest()->getParam('forwarded') - && !$this->_getSession()->getIsUrlNotice(true) - && !Mage::getConfig()->getNode('global/can_use_base_url')) { - $this->_checkUrlSettings(); - $this->setFlag('', self::FLAG_IS_URLS_CHECKED, true); - } - if (is_null(Mage::getSingleton('adminhtml/session')->getLocale())) { - Mage::getSingleton('adminhtml/session')->setLocale(Mage::app()->getLocale()->getLocaleCode()); - } - - return $this; - } - - protected function _checkUrlSettings() - { - /** - * Don't check for data saving actions - */ - if ($this->getRequest()->getPost() || $this->getRequest()->getQuery('isAjax')) { - return $this; - } - - $configData = Mage::getModel('core/config_data'); - - $defaultUnsecure= (string) Mage::getConfig()->getNode('default/'.Mage_Core_Model_Store::XML_PATH_UNSECURE_BASE_URL); - $defaultSecure = (string) Mage::getConfig()->getNode('default/'.Mage_Core_Model_Store::XML_PATH_SECURE_BASE_URL); - - if ($defaultSecure == '{{base_url}}' || $defaultUnsecure == '{{base_url}}') { - $this->_getSession()->addNotice( - $this->__('{{base_url}} is not recommended to use in a production environment to declare the Base Unsecure Url / Base Secure Url. It is highly recommended to change this value in your Magento configuration.', $this->getUrl('adminhtml/system_config/edit', array('section'=>'web'))) - ); - return $this; - } - - $dataCollection = $configData->getCollection() - ->addValueFilter('{{base_url}}'); - - $url = false; - foreach ($dataCollection as $data) { - if ($data->getScope() == 'stores') { - $code = Mage::app()->getStore($data->getScopeId())->getCode(); - $url = $this->getUrl('adminhtml/system_config/edit', array('section'=>'web', 'store'=>$code)); - } - if ($data->getScope() == 'websites') { - $code = Mage::app()->getWebsite($data->getScopeId())->getCode(); - $url = $this->getUrl('adminhtml/system_config/edit', array('section'=>'web', 'website'=>$code)); - } - - if ($url) { - $this->_getSession()->addNotice( - $this->__('{{base_url}} is not recommended to use in a production environment to declare the Base Unsecure Url / Base Secure Url. It is highly recommended to change this value in your Magento configuration.', $url) - ); - return $this; - } - } - return $this; - } - - public function deniedAction() - { - $this->getResponse()->setHeader('HTTP/1.1','403 Forbidden'); - if (!Mage::getSingleton('admin/session')->isLoggedIn()) { - $this->_redirect('*/index/login'); - return; - } - $this->loadLayout(array('default', 'adminhtml_denied')); - $this->renderLayout(); - } - - public function loadLayout($ids=null, $generateBlocks=true, $generateXml=true) - { - parent::loadLayout($ids, $generateBlocks, $generateXml); - $this->_initLayoutMessages('adminhtml/session'); - return $this; - } - - public function norouteAction($coreRoute = null) - { - $this->getResponse()->setHeader('HTTP/1.1','404 Not Found'); - $this->getResponse()->setHeader('Status','404 File not found'); - $this->loadLayout(array('default', 'adminhtml_noroute')); - $this->renderLayout(); - } - - - /** - * Retrieve currently used module name - * - * @return string - */ - public function getUsedModuleName() - { - return $this->_usedModuleName; - } - - /** - * Set currently used module name - * - * @param string $moduleName - * @return Mage_Adminhtml_Controller_Action - */ - public function setUsedModuleName($moduleName) - { - $this->_usedModuleName = $moduleName; - return $this; - } - - /** - * Translate a phrase - * - * @return string - */ - public function __() - { - $args = func_get_args(); - $expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->getUsedModuleName()); - array_unshift($args, $expr); - return Mage::app()->getTranslator()->translate($args); - } - - /** - * Set referer url for redirect in responce - * - * Is overriden here to set defaultUrl to admin url - * - * @param string $defaultUrl - * @return Mage_Adminhtml_Controller_Action - */ - protected function _redirectReferer($defaultUrl=null) - { - $defaultUrl = empty($defaultUrl) ? $this->getUrl('*') : $defaultUrl; - parent::_redirectReferer($defaultUrl); - return $this; - } - - /** - * Declare headers and content file in responce for file download - * - * @param string $fileName - * @param string $content set to null to avoid starting output, $contentLength should be set explicitly in that case - * @param string $contentType - * @param int $contentLength explicit content length, if strlen($content) isn't applicable - * @return Mage_Adminhtml_Controller_Action - */ - protected function _prepareDownloadResponse($fileName, $content, $contentType = 'application/octet-stream', $contentLength = null) - { - $session = Mage::getSingleton('admin/session'); - if ($session->isFirstPageAfterLogin()) { - $this->_redirect($session->getUser()->getStartupPageUrl()); - return $this; - } - $this->getResponse() - ->setHttpResponseCode(200) - ->setHeader('Pragma', 'public', true) - ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) - ->setHeader('Content-type', $contentType, true) - ->setHeader('Content-Length', is_null($contentLength) ? strlen($content) : $contentLength) - ->setHeader('Content-Disposition', 'attachment; filename=' . $fileName) - ->setHeader('Last-Modified', date('r')); - if (!is_null($content)) { - $this->getResponse()->setBody($content); - } - return $this; - } - - /** - * Set redirect into responce - * - * @param string $path - * @param array $arguments - */ - protected function _redirect($path, $arguments=array()) - { - $this->_getSession()->setIsUrlNotice($this->getFlag('', self::FLAG_IS_URLS_CHECKED)); - $this->getResponse()->setRedirect($this->getUrl($path, $arguments)); - return $this; - } - - protected function _forward($action, $controller = null, $module = null, array $params = null) - { - $this->_getSession()->setIsUrlNotice($this->getFlag('', self::FLAG_IS_URLS_CHECKED)); - return parent::_forward($action, $controller, $module, $params); - } - - /** - * Generate url by route and parameters - * - * @param string $route - * @param array $params - * @return string - */ - public function getUrl($route='', $params=array()) - { - return Mage::helper('adminhtml')->getUrl($route, $params); - } - -} + + */ +class Mage_Adminhtml_Controller_Action extends Mage_Core_Controller_Varien_Action +{ + + const FLAG_IS_URLS_CHECKED = 'check_url_settings'; + + /** + * Used module name in current adminhtml controller + */ + protected $_usedModuleName = 'adminhtml'; + + protected function _isAllowed() + { + return true; + } + + /** + * Retrieve adminhtml session model object + * + * @return Mage_Adminhtml_Model_Session + */ + protected function _getSession() + { + return Mage::getSingleton('adminhtml/session'); + } + + /** + * Retrieve base admihtml helper + * + * @return Mage_Adminhtml_Helper_Data + */ + protected function _getHelper() + { + return Mage::helper('adminhtml'); + } + + /** + * Define active menu item in menu block + * + * @return Mage_Adminhtml_Controller_Action + */ + protected function _setActiveMenu($menuPath) + { + $this->getLayout()->getBlock('menu')->setActive($menuPath); + return $this; + } + + /** + * @return Mage_Adminhtml_Controller_Action + */ + protected function _addBreadcrumb($label, $title, $link=null) + { + $this->getLayout()->getBlock('breadcrumbs')->addLink($label, $title, $link); + return $this; + } + + /** + * @return Mage_Adminhtml_Controller_Action + */ + protected function _addContent(Mage_Core_Block_Abstract $block) + { + $this->getLayout()->getBlock('content')->append($block); + return $this; + } + + protected function _addLeft(Mage_Core_Block_Abstract $block) + { + $this->getLayout()->getBlock('left')->append($block); + return $this; + } + + protected function _addJs(Mage_Core_Block_Abstract $block) + { + $this->getLayout()->getBlock('js')->append($block); + return $this; + } + + public function hasAction($action) + { + return true; + } + + /** + * Controller predispatch method + * + * @return Mage_Adminhtml_Controller_Action + */ + public function preDispatch() + { + Mage::getDesign()->setArea('adminhtml') + // [bug] this value will be overriden by defaults, how can it be set in adminhtml/etc/config.xml? + ->setPackageName((string)Mage::getConfig()->getNode('stores/admin/design/package/name')) + ->setTheme((string)Mage::getConfig()->getNode('stores/admin/design/theme/default')); + + $this->getLayout()->setArea('adminhtml'); + + Mage::dispatchEvent('adminhtml_controller_action_predispatch_start', array()); + + parent::preDispatch(); + + if ($this->getRequest()->isPost() + && !$this->_validateFormKey() + && Mage::getSingleton('admin/session')->isLoggedIn()) { + + $this->setFlag('', self::FLAG_NO_DISPATCH, true); + $this->setFlag('', self::FLAG_NO_POST_DISPATCH, true); + if ($this->getRequest()->getQuery('isAjax', false) || $this->getRequest()->getQuery('ajax', false)) { + $this->getResponse()->setBody(Zend_Json::encode(array( + 'error' => true, + 'error_msg' => Mage::helper('adminhtml')->__('Invalid Form Key') + ))); + } else { + $this->_redirectReferer(); + } + return $this; + } + + if ($this->getRequest()->isDispatched() + && $this->getRequest()->getActionName() !== 'denied' + && !$this->_isAllowed()) { + $this->_forward('denied'); + $this->setFlag('', self::FLAG_NO_DISPATCH, true); + return $this; + } + + if (!$this->getFlag('', self::FLAG_IS_URLS_CHECKED) + && !$this->getRequest()->getParam('forwarded') + && !$this->_getSession()->getIsUrlNotice(true) + && !Mage::getConfig()->getNode('global/can_use_base_url')) { + $this->_checkUrlSettings(); + $this->setFlag('', self::FLAG_IS_URLS_CHECKED, true); + } + if (is_null(Mage::getSingleton('adminhtml/session')->getLocale())) { + Mage::getSingleton('adminhtml/session')->setLocale(Mage::app()->getLocale()->getLocaleCode()); + } + + return $this; + } + + protected function _checkUrlSettings() + { + /** + * Don't check for data saving actions + */ + if ($this->getRequest()->getPost() || $this->getRequest()->getQuery('isAjax')) { + return $this; + } + + $configData = Mage::getModel('core/config_data'); + + $defaultUnsecure= (string) Mage::getConfig()->getNode('default/'.Mage_Core_Model_Store::XML_PATH_UNSECURE_BASE_URL); + $defaultSecure = (string) Mage::getConfig()->getNode('default/'.Mage_Core_Model_Store::XML_PATH_SECURE_BASE_URL); + + if ($defaultSecure == '{{base_url}}' || $defaultUnsecure == '{{base_url}}') { + $this->_getSession()->addNotice( + $this->__('{{base_url}} is not recommended to use in a production environment to declare the Base Unsecure Url / Base Secure Url. It is highly recommended to change this value in your Magento configuration.', $this->getUrl('adminhtml/system_config/edit', array('section'=>'web'))) + ); + return $this; + } + + $dataCollection = $configData->getCollection() + ->addValueFilter('{{base_url}}'); + + $url = false; + foreach ($dataCollection as $data) { + if ($data->getScope() == 'stores') { + $code = Mage::app()->getStore($data->getScopeId())->getCode(); + $url = $this->getUrl('adminhtml/system_config/edit', array('section'=>'web', 'store'=>$code)); + } + if ($data->getScope() == 'websites') { + $code = Mage::app()->getWebsite($data->getScopeId())->getCode(); + $url = $this->getUrl('adminhtml/system_config/edit', array('section'=>'web', 'website'=>$code)); + } + + if ($url) { + $this->_getSession()->addNotice( + $this->__('{{base_url}} is not recommended to use in a production environment to declare the Base Unsecure Url / Base Secure Url. It is highly recommended to change this value in your Magento configuration.', $url) + ); + return $this; + } + } + return $this; + } + + public function deniedAction() + { + $this->getResponse()->setHeader('HTTP/1.1','403 Forbidden'); + if (!Mage::getSingleton('admin/session')->isLoggedIn()) { + $this->_redirect('*/index/login'); + return; + } + $this->loadLayout(array('default', 'adminhtml_denied')); + $this->renderLayout(); + } + + public function loadLayout($ids=null, $generateBlocks=true, $generateXml=true) + { + parent::loadLayout($ids, $generateBlocks, $generateXml); + $this->_initLayoutMessages('adminhtml/session'); + return $this; + } + + public function norouteAction($coreRoute = null) + { + $this->getResponse()->setHeader('HTTP/1.1','404 Not Found'); + $this->getResponse()->setHeader('Status','404 File not found'); + $this->loadLayout(array('default', 'adminhtml_noroute')); + $this->renderLayout(); + } + + + /** + * Retrieve currently used module name + * + * @return string + */ + public function getUsedModuleName() + { + return $this->_usedModuleName; + } + + /** + * Set currently used module name + * + * @param string $moduleName + * @return Mage_Adminhtml_Controller_Action + */ + public function setUsedModuleName($moduleName) + { + $this->_usedModuleName = $moduleName; + return $this; + } + + /** + * Translate a phrase + * + * @return string + */ + public function __() + { + $args = func_get_args(); + $expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->getUsedModuleName()); + array_unshift($args, $expr); + return Mage::app()->getTranslator()->translate($args); + } + + /** + * Set referer url for redirect in responce + * + * Is overriden here to set defaultUrl to admin url + * + * @param string $defaultUrl + * @return Mage_Adminhtml_Controller_Action + */ + protected function _redirectReferer($defaultUrl=null) + { + $defaultUrl = empty($defaultUrl) ? $this->getUrl('*') : $defaultUrl; + parent::_redirectReferer($defaultUrl); + return $this; + } + + /** + * Declare headers and content file in responce for file download + * + * @param string $fileName + * @param string $content set to null to avoid starting output, $contentLength should be set explicitly in that case + * @param string $contentType + * @param int $contentLength explicit content length, if strlen($content) isn't applicable + * @return Mage_Adminhtml_Controller_Action + */ + protected function _prepareDownloadResponse($fileName, $content, $contentType = 'application/octet-stream', $contentLength = null) + { + $session = Mage::getSingleton('admin/session'); + if ($session->isFirstPageAfterLogin()) { + $this->_redirect($session->getUser()->getStartupPageUrl()); + return $this; + } + $this->getResponse() + ->setHttpResponseCode(200) + ->setHeader('Pragma', 'public', true) + ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) + ->setHeader('Content-type', $contentType, true) + ->setHeader('Content-Length', is_null($contentLength) ? strlen($content) : $contentLength) + ->setHeader('Content-Disposition', 'attachment; filename=' . $fileName) + ->setHeader('Last-Modified', date('r')); + if (!is_null($content)) { + $this->getResponse()->setBody($content); + } + return $this; + } + + /** + * Set redirect into responce + * + * @param string $path + * @param array $arguments + */ + protected function _redirect($path, $arguments=array()) + { + $this->_getSession()->setIsUrlNotice($this->getFlag('', self::FLAG_IS_URLS_CHECKED)); + $this->getResponse()->setRedirect($this->getUrl($path, $arguments)); + return $this; + } + + protected function _forward($action, $controller = null, $module = null, array $params = null) + { + $this->_getSession()->setIsUrlNotice($this->getFlag('', self::FLAG_IS_URLS_CHECKED)); + return parent::_forward($action, $controller, $module, $params); + } + + /** + * Generate url by route and parameters + * + * @param string $route + * @param array $params + * @return string + */ + public function getUrl($route='', $params=array()) + { + return Mage::helper('adminhtml')->getUrl($route, $params); + } +} diff --git a/app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php b/app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php index 6c41ec0de8..576ee92bb8 100644 --- a/app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php +++ b/app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php @@ -211,12 +211,26 @@ public function initFromOrder(Mage_Sales_Model_Order $order) $this->getQuote()->setCustomerIsGuest(true); } + if ($this->getSession()->getUseOldShippingMethod(true)) { + /* + * if we are making reorder or editing old order + * we need to show old shipping as preselected + * so for this we need to collect shipping rates + */ + $this->collectShippingRates(); + } else { + /* + * if we are creating new order then we don't need to collect + * shipping rates before customer hit appropriate button + */ + $this->collectRates(); + } + // Make collect rates when user click "Get shipping methods and rates" in order creating // $this->getQuote()->getShippingAddress()->setCollectShippingRates(true); // $this->getQuote()->getShippingAddress()->collectShippingRates(); - $this->getQuote()->collectTotals() - ->save(); + $this->getQuote()->save(); return $this; } diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Catalog/Inventory/Managestock.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Catalog/Inventory/Managestock.php new file mode 100644 index 0000000000..7a5170fc62 --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Catalog/Inventory/Managestock.php @@ -0,0 +1,57 @@ + + */ +class Mage_Adminhtml_Model_System_Config_Backend_Catalog_Inventory_Managestock + extends Mage_Core_Model_Config_Data +{ +/** + * After change Catalog Inventory Manage value process + * + * @return Mage_Adminhtml_Model_System_Config_Backend_Catalog_Inventory_Managestock + */ + protected function _afterSave() + { + $newValue = $this->getValue(); + $oldValue = Mage::getConfig()->getNode( + Mage_CatalogSearch_Model_Fulltext::XML_PATH_CATALOG_SEARCH_TYPE, + $this->getScope(), + $this->getScopeId() + ); + if ($newValue != $oldValue) { + Mage::getSingleton('cataloginventory/stock_status')->rebuild(); + } + + return $this; + } +} diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Log/Cron.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Log/Cron.php index bdab0a0f80..bb3586cdf2 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Log/Cron.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Log/Cron.php @@ -44,10 +44,10 @@ class Mage_Adminhtml_Model_System_Config_Backend_Log_Cron extends Mage_Core_Mode */ protected function _afterSave() { - $enabled = $this->getData('groups/log/enabled/value'); + $enabled = $this->getData('groups/log/fields/enabled/value'); $time = $this->getData('groups/log/fields/time/value'); - $frequncy = $this->getData('groups/log/frequency/value'); - $errorEmail = $this->getData('groups/log/error_email/value'); + $frequncy = $this->getData('groups/log/fields/frequency/value'); + $errorEmail = $this->getData('groups/log/fields/error_email/value'); $frequencyDaily = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_DAILY; $frequencyWeekly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_WEEKLY; diff --git a/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php b/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php index ab261fc65d..14a3f66ccf 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php @@ -128,10 +128,21 @@ public function editAction() $this->getRequest()->setParam('id', $_prevCategoryId); } - if (!$category = $this->_initCategory(true)) { + if (!($category = $this->_initCategory(true))) { return; } + /** + * Check if we have data in session (if duering category save was exceprion) + */ + $data = Mage::getSingleton('adminhtml/session')->getCategoryData(true); + if (isset($data['general'])) { + $category->addData($data['general']); + } + + /** + * Build response for ajax request + */ if ($this->getRequest()->getQuery('isAjax')) { // prepare breadcrumbs of selected category, if any $breadcrumbsPath = $category->getPath(); @@ -170,11 +181,6 @@ public function editAction() $this->getLayout()->getBlock('head')->setCanLoadExtJs(true) ->setContainerCssClass('catalog-categories'); - $data = Mage::getSingleton('adminhtml/session')->getCategoryData(true); - if (isset($data['general'])) { - $category->addData($data['general']); - } - $this->_addBreadcrumb(Mage::helper('catalog')->__('Manage Catalog Categories'), Mage::helper('catalog')->__('Manage Categories') ); @@ -260,21 +266,20 @@ public function saveAction() )); try { - // if( $this->getRequest()->getParam('image') ) - $category->save(); - Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('catalog')->__('Category saved')); + $refreshTree = 'true'; } catch (Exception $e){ $this->_getSession()->addError($e->getMessage()) ->setCategoryData($data); - $this->getResponse()->setRedirect($this->getUrl('*/*/edit', array('_current'=> true, 'id'=>$category->getId()))); - return; + $refreshTree = 'false'; } } $url = $this->getUrl('*/*/edit', array('_current' => true, 'id' => $category->getId())); - $this->getResponse()->setBody(''); + $this->getResponse()->setBody( + '' + ); } /** diff --git a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php index 2ebee6cd60..fac76407ab 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php @@ -193,6 +193,11 @@ public function saveAction() try { $model->save(); Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('catalog')->__('Product attribute was successfully saved')); + + /** + * Clear translation cache because attribute labels are stored in translation + */ + Mage::app()->cleanCache(array(Mage_Core_Model_Translate::CACHE_TAG)); Mage::getSingleton('adminhtml/session')->setAttributeData(false); if ($this->getRequest()->getParam('popup')) { $this->_redirect('adminhtml/catalog_product/addAttribute', array( diff --git a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreateController.php b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreateController.php index 05ec510954..985106a714 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreateController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreateController.php @@ -279,6 +279,7 @@ public function reorderAction() if ($order->getId()) { $order->setReordered(true); + $this->_getSession()->setUseOldShippingMethod(true); $this->_getOrderCreateModel()->initFromOrder($order); $this->_redirect('*/*'); diff --git a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/EditController.php b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/EditController.php index 866a90379a..20a606d30d 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/EditController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/EditController.php @@ -53,6 +53,7 @@ public function startAction() $order = Mage::getModel('sales/order')->load($orderId); if ($order->getId()) { + $this->_getSession()->setUseOldShippingMethod(true); $this->_getOrderCreateModel()->initFromOrder($order); $this->_redirect('*/*'); } diff --git a/app/code/core/Mage/Adminhtml/controllers/System/CacheController.php b/app/code/core/Mage/Adminhtml/controllers/System/CacheController.php index e875999d6f..1d96051263 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/CacheController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/CacheController.php @@ -205,6 +205,19 @@ public function saveAction() } break; + case 'rebuild_inventory_stock_status': + try { + Mage::getSingleton('cataloginventory/stock_status')->rebuild(); + $this->_getSession()->addSuccess(Mage::helper('adminhtml')->__('CatalogInventory Stock Status was rebuilded successfully')); + } + catch (Mage_Core_Exception $e) { + $this->_getSession()->addError($e->getMessage()); + } + catch (Exception $e) { + $this->_getSession()->addException($e, Mage::helper('adminhtml')->__('Error while rebuilded CatalogInventory Stock Status. Please try again later')); + } + break; + default: break; } diff --git a/app/code/core/Mage/Adminhtml/controllers/System/Convert/GuiController.php b/app/code/core/Mage/Adminhtml/controllers/System/Convert/GuiController.php index e2a16ac2a7..eae1cc2267 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/Convert/GuiController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/Convert/GuiController.php @@ -129,21 +129,21 @@ public function downloadAction() protected function _isAllowed() { - switch ($this->getRequest()->getActionName()) { - case 'index': - $aclResource = 'admin/system/convert/gui'; - break; - case 'grid': - $aclResource = 'admin/system/convert/gui'; - break; - case 'run': - $aclResource = 'admin/system/convert/gui/run'; - break; - default: - $aclResource = 'admin/system/convert/gui/edit'; - break; - } - - return Mage::getSingleton('admin/session')->isAllowed($aclResource); +// switch ($this->getRequest()->getActionName()) { +// case 'index': +// $aclResource = 'admin/system/convert/gui'; +// break; +// case 'grid': +// $aclResource = 'admin/system/convert/gui'; +// break; +// case 'run': +// $aclResource = 'admin/system/convert/gui/run'; +// break; +// default: +// $aclResource = 'admin/system/convert/gui/edit'; +// break; +// } + + return Mage::getSingleton('admin/session')->isAllowed('admin/system/convert/gui'); } } \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/controllers/System/Convert/ProfileController.php b/app/code/core/Mage/Adminhtml/controllers/System/Convert/ProfileController.php index cb955b8f0c..63461eb736 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/Convert/ProfileController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/Convert/ProfileController.php @@ -293,21 +293,21 @@ public function historyAction() { protected function _isAllowed() { - switch ($this->getRequest()->getActionName()) { - case 'index': - $aclResource = 'admin/system/convert/profiles'; - break; - case 'grid': - $aclResource = 'admin/system/convert/profiles'; - break; - case 'run': - $aclResource = 'admin/system/convert/profiles/run'; - break; - default: - $aclResource = 'admin/system/convert/profiles/edit'; - break; - } - - return Mage::getSingleton('admin/session')->isAllowed($aclResource); +// switch ($this->getRequest()->getActionName()) { +// case 'index': +// $aclResource = 'admin/system/convert/profiles'; +// break; +// case 'grid': +// $aclResource = 'admin/system/convert/profiles'; +// break; +// case 'run': +// $aclResource = 'admin/system/convert/profiles/run'; +// break; +// default: +// $aclResource = 'admin/system/convert/profiles/edit'; +// break; +// } + + return Mage::getSingleton('admin/session')->isAllowed('admin/system/convert/profiles'); } } diff --git a/app/code/core/Mage/Api/Model/Mysql4/User.php b/app/code/core/Mage/Api/Model/Mysql4/User.php index eb2ec63ce1..56b948c026 100644 --- a/app/code/core/Mage/Api/Model/Mysql4/User.php +++ b/app/code/core/Mage/Api/Model/Mysql4/User.php @@ -37,10 +37,26 @@ class Mage_Api_Model_Mysql4_User extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('api/user', 'user_id'); - $this->_uniqueFields = array( - array('field' => 'email', 'title' => Mage::helper('api')->__('Email')), - array('field' => 'username', 'title' => Mage::helper('api')->__('User Name')), - ); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array( + array( + 'field' => 'email', + 'title' => Mage::helper('api')->__('Email') + ), + array( + 'field' => 'username', + 'title' => Mage::helper('api')->__('User Name') + ), + ); + return $this; } /** diff --git a/app/code/core/Mage/Api/Model/Server/Handler/Abstract.php b/app/code/core/Mage/Api/Model/Server/Handler/Abstract.php index 885e663e2e..26140a056c 100644 --- a/app/code/core/Mage/Api/Model/Server/Handler/Abstract.php +++ b/app/code/core/Mage/Api/Model/Server/Handler/Abstract.php @@ -40,7 +40,7 @@ public function __construct() static public function handlePhpError($errorCode, $errorMessage, $errorFile) { - Mage::log($errorMessage, null, $errorFile); + Mage::log($errorMessage . $errorFile); if (in_array($errorCode, array(E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR))) { $this->_fault('internal'); } diff --git a/app/code/core/Mage/Bundle/Model/Mysql4/Selection.php b/app/code/core/Mage/Bundle/Model/Mysql4/Selection.php index ace394d9b0..c6314e53ab 100644 --- a/app/code/core/Mage/Bundle/Model/Mysql4/Selection.php +++ b/app/code/core/Mage/Bundle/Model/Mysql4/Selection.php @@ -1,96 +1,180 @@ - - */ -class Mage_Bundle_Model_Mysql4_Selection extends Mage_Core_Model_Mysql4_Abstract -{ - protected function _construct() - { - $this->_init('bundle/selection', 'selection_id'); - } -/* - protected function _afterSave(Mage_Core_Model_Abstract $object) - { - parent::_afterSave($object); - - $condition = $this->_getWriteAdapter()->quoteInto('option_id = ?', $object->getId()); - $condition .= ' and ' . $this->_getWriteAdapter()->quoteInto('store_id = ?', $object->getStoreId()); - - $this->_getWriteAdapter()->delete($this->getTable('option_value'), $condition); - - $data = new Varien_Object(); - $data->setOptionId($object->getId()) - ->setStoreId($object->getStoreId()) - ->setTitle($object->getTitle()); - - $this->_getWriteAdapter()->insert($this->getTable('option_value'), $data->getData()); - - return $this; - } - - protected function _afterDelete(Mage_Core_Model_Abstract $object) - { - parent::_afterDelete($object); - - $condition = $this->_getWriteAdapter()->quoteInto('option_id = ?', $object->getId()); - $this->_getWriteAdapter()->delete($this->getTable('option_value'), $condition); - - return $this; - } -*/ - - public function getPriceFromIndex($productId, $qty, $storeId, $groupId) { - $select = clone $this->_getReadAdapter()->select(); - $select->reset(); - - $attrPriceId = Mage::getSingleton('eav/entity_attribute')->getIdByCode('catalog_product', 'price'); - $attrTierPriceId = Mage::getSingleton('eav/entity_attribute')->getIdByCode('catalog_product', 'tier_price'); - - $websiteId = Mage::app()->getStore($storeId)->getWebsiteId(); - - $select->from(array("price_index" => $this->getTable('catalogindex/price')), array('price' => 'SUM(value)')) - ->where('entity_id in (?)', $productId) - ->where('website_id = ?', $websiteId) - ->where('customer_group_id = ?', $groupId) - ->where('attribute_id in (?)', array($attrPriceId, $attrTierPriceId)) - ->where('qty <= ?', $qty) - ->group('entity_id'); - - $price = $this->_getReadAdapter()->fetchCol($select); - if (!empty($price)) { - return array_shift($price); - } else { - return 0; - } - } - + + */ +class Mage_Bundle_Model_Mysql4_Selection extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('bundle/selection', 'selection_id'); + } +/* + protected function _afterSave(Mage_Core_Model_Abstract $object) + { + parent::_afterSave($object); + + $condition = $this->_getWriteAdapter()->quoteInto('option_id = ?', $object->getId()); + $condition .= ' and ' . $this->_getWriteAdapter()->quoteInto('store_id = ?', $object->getStoreId()); + + $this->_getWriteAdapter()->delete($this->getTable('option_value'), $condition); + + $data = new Varien_Object(); + $data->setOptionId($object->getId()) + ->setStoreId($object->getStoreId()) + ->setTitle($object->getTitle()); + + $this->_getWriteAdapter()->insert($this->getTable('option_value'), $data->getData()); + + return $this; + } + + protected function _afterDelete(Mage_Core_Model_Abstract $object) + { + parent::_afterDelete($object); + + $condition = $this->_getWriteAdapter()->quoteInto('option_id = ?', $object->getId()); + $this->_getWriteAdapter()->delete($this->getTable('option_value'), $condition); + + return $this; + } +*/ + + /** + * Retrieve Price From index + * + * @param int $productId + * @param float $qty + * @param int $storeId + * @param int $groupId + * @return mixed + */ + public function getPriceFromIndex($productId, $qty, $storeId, $groupId) { + $select = clone $this->_getReadAdapter()->select(); + $select->reset(); + + $attrPriceId = Mage::getSingleton('eav/entity_attribute')->getIdByCode('catalog_product', 'price'); + $attrTierPriceId = Mage::getSingleton('eav/entity_attribute')->getIdByCode('catalog_product', 'tier_price'); + + $websiteId = Mage::app()->getStore($storeId)->getWebsiteId(); + + $select->from(array("price_index" => $this->getTable('catalogindex/price')), array('price' => 'SUM(value)')) + ->where('entity_id in (?)', $productId) + ->where('website_id = ?', $websiteId) + ->where('customer_group_id = ?', $groupId) + ->where('attribute_id in (?)', array($attrPriceId, $attrTierPriceId)) + ->where('qty <= ?', $qty) + ->group('entity_id'); + + $price = $this->_getReadAdapter()->fetchCol($select); + if (!empty($price)) { + return array_shift($price); + } else { + return 0; + } + } + + /** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param bool $required + * @return array + */ + public function getChildrenIds($parentId, $required = true) + { + $childrenIds = array(); + $notRequired = array(); + $select = $this->_getReadAdapter()->select() + ->from( + array('tbl_selection' => $this->getMainTable()), + array('product_id', 'parent_product_id', 'option_id')) + ->join( + array('tbl_option' => $this->getTable('bundle/option')), + '`tbl_option`.`option_id` = `tbl_selection`.`option_id`', + array('required') + ) + ->where('`tbl_selection`.`parent_product_id`=?', $parentId); + foreach ($this->_getReadAdapter()->fetchAll($select) as $row) { + if ($row['required']) { + $childrenIds[$row['option_id']][$row['product_id']] = $row['product_id']; + } + else { + $notRequired[$row['option_id']][$row['product_id']] = $row['product_id']; + } + } + + if (!$required) { + $childrenIds = array_merge($childrenIds, $notRequired); + } + else { + if (!$childrenIds) { + foreach ($notRequired as $groupedChildrenIds) { + foreach ($groupedChildrenIds as $childId) { + $childrenIds[0][$childId] = $childId; + } + } + } + } + + return $childrenIds; + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @return array + */ + public function getParentIdsByChild($childId) + { + $parentIds = array(); + + $select = $this->_getReadAdapter()->select() + ->from( + array('tbl_selection' => $this->getMainTable()), + array('product_id', 'parent_product_id', 'option_id')) + ->join( + array('tbl_option' => $this->getTable('bundle/option')), + '`tbl_option`.`option_id` = `tbl_selection`.`option_id`', + array('required') + ) + ->where('`tbl_selection`.`product_id`=?', $childId); + foreach ($this->_getReadAdapter()->fetchAll($select) as $row) { + $parentIds[] = $row['parent_product_id']; + } + + return $parentIds; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Bundle/Model/Product/Type.php b/app/code/core/Mage/Bundle/Model/Product/Type.php index 8abc51c1e3..6e4d101451 100644 --- a/app/code/core/Mage/Bundle/Model/Product/Type.php +++ b/app/code/core/Mage/Bundle/Model/Product/Type.php @@ -1,678 +1,716 @@ - - */ -class Mage_Bundle_Model_Product_Type extends Mage_Catalog_Model_Product_Type_Abstract -{ - protected $_isComposite = true; - - protected $_optionsCollection; - protected $_selectionsCollection; - protected $_storeFilter = null; - - protected $_usedSelections = null; - protected $_usedSelectionsIds = null; - protected $_usedOptions = null; - protected $_usedOptionsIds = null; - - protected $_optionsCount = null; - - /** - * Return relation info about used products - * - * @return Varien_Object Object with information data - */ - public function getRelationInfo() - { - $info = new Varien_Object(); - $info->setTable('bundle/selection') - ->setParentFieldName('parent_product_id') - ->setChildFieldName('product_id'); - return $info; - } - - /** - * Return product sku based on sku_type attribute - * - * @return string - */ - public function getSku() - { - $sku = parent::getSku(); - - if ($this->getProduct()->getData('sku_type')) { - return $sku; - } else { - $skuParts = array($sku); - - if ($this->getProduct()->hasCustomOptions()) { - $customOption = $this->getProduct()->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); - $selections = $this->getSelectionsByIds($selectionIds); - foreach ($selections->getItems() as $selection) { - $skuParts[] = $selection->getSku(); - } - } - - return implode('-', $skuParts); - } - } - - /** - * Return product weight based on weight_type attribute - * - * @return decimal - */ - public function getWeight() - { - if ($this->getProduct()->getData('weight_type')) { - return $this->getProduct()->getData('weight'); - } else { - $weight = 0; - - if ($this->getProduct()->hasCustomOptions()) { - $customOption = $this->getProduct()->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); - $selections = $this->getSelectionsByIds($selectionIds); - foreach ($selections->getItems() as $selection) { - $weight += $selection->getWeight(); - } - } - return $weight; - } - } - - /** - * Check is virtual product - * - * @return bool - */ - public function isVirtual() - { - if ($this->getProduct()->hasCustomOptions()) { - $customOption = $this->getProduct()->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); - $selections = $this->getSelectionsByIds($selectionIds); - $virtualCount = 0; - foreach ($selections->getItems() as $selection) { - if ($selection->getTypeInstance()->IsVirtual()) { - $virtualCount++; - } - } - if ($virtualCount == count($selections)) { - return true; - } - } - return false; - } - - public function beforeSave() - { - parent::beforeSave(); - - $this->getProduct()->canAffectOptions(false); - - if ($this->getProduct()->getCanSaveBundleSelections()) { - $this->getProduct()->canAffectOptions(true); - if ($selections = $this->getProduct()->getBundleSelectionsData()) { - if (!empty($selections)) { - if ($options = $this->getProduct()->getBundleOptionsData()) { - foreach ($options as $option) { - if (empty($option['delete']) || 1 != (int)$option['delete']) { - $this->getProduct()->setTypeHasOptions(true); - if (1 == (int)$option['required']) { - $this->getProduct()->setTypeHasRequiredOptions(true); - break; - } - } - } - } - } - } - } - } - - public function save() - { - parent::save(); - - if ($options = $this->getProduct()->getBundleOptionsData()) { - - foreach ($options as $key => $option) { - if (isset($option['option_id']) && $option['option_id'] == '') { - unset($option['option_id']); - } - - $optionModel = Mage::getModel('bundle/option') - ->setData($option) - ->setParentId($this->getProduct()->getId()) - ->setStoreId($this->getProduct()->getStoreId()); - - $optionModel->isDeleted((bool)$option['delete']); - $optionModel->save(); - - $options[$key]['option_id'] = $optionModel->getOptionId(); - } - - $excludeSelectionIds = array(); - - if ($selections = $this->getProduct()->getBundleSelectionsData()) { - foreach ($selections as $index => $group) { - foreach ($group as $key => $selection) { - if (isset($selection['selection_id']) && $selection['selection_id'] == '') { - unset($selection['selection_id']); - } - - if (!isset($selection['is_default'])) { - $selection['is_default'] = 0; - } - - $selectionModel = Mage::getModel('bundle/selection') - ->setData($selection) - ->setOptionId($options[$index]['option_id']) - ->setParentProductId($this->getProduct()->getId()); - - $selectionModel->isDeleted((bool)$selection['delete']); - $selectionModel->save(); - - $selection['selection_id'] = $selectionModel->getSelectionId(); - - if ($selectionModel->getSelectionId()) { - $excludeSelectionIds[] = $selectionModel->getSelectionId(); - } - } - } - Mage::getResourceModel('bundle/bundle')->dropAllUnneededSelections($this->getProduct()->getId(), $excludeSelectionIds); - } - - if ($this->getProduct()->getData('price_type') != $this->getProduct()->getOrigData('price_type')) { - Mage::getResourceModel('bundle/bundle')->dropAllQuoteChildItems($this->getProduct()->getId()); - } - } - - return $this; - } - - /** - * Retrieve bundle options items - * - * @return array - */ - public function getOptions() - { - return $this->getOptionsCollection()->getItems(); - } - - /** - * Retrieve bundle options ids - * - * @return array - */ - public function getOptionsIds() - { - return $this->getOptionsCollection()->getAllIds(); - } - - /** - * Retrieve bundle option collection - * - * @return Mage_Bundle_Model_Mysql4_Option_Collection - */ - public function getOptionsCollection() - { - if (!$this->_optionsCollection) { - $this->_optionsCollection = Mage::getModel('bundle/option')->getResourceCollection() - ->setProductIdFilter($this->getProduct()->getId()) - ->setPositionOrder() - ->joinValues($this->getStoreFilter()); - } - return $this->_optionsCollection; - } - - /** - * Retrive bundle selections collection based on used options - * - * @param array $optionIds - * @return Mage_Bundle_Model_Mysql4_Selection_Collection - */ - public function getSelectionsCollection($optionIds) - { - if (!$this->_selectionsCollection) { - $this->_selectionsCollection = Mage::getResourceModel('bundle/selection_collection') - ->addAttributeToSelect('*') - ->setPositionOrder() - ->addStoreFilter($this->getStoreFilter()) - ->addFilterByRequiredOptions() - ->setOptionIdsFilter($optionIds); - } - return $this->_selectionsCollection; - } - - /** - * Method is needed for specific actions to change given quote options values - * according current product type logic - * Example: the cataloginventory validation of decimal qty can change qty to int, - * so need to change quote item qty option value too. - * - * @param array $options - * @param Varien_Object $option - * @param mixed $value - * - * @return object Mage_Bundle_Model_Product_Type - */ - public function updateQtyOption($options, Varien_Object $option, $value) - { - $optionProduct = $option->getProduct(); - - $optionCollection = $this->getOptionsCollection(); - - $selections = $this->getSelectionsCollection($optionCollection->getAllIds()); - - foreach ($selections as $selection) { - if ($selection->getProductId() == $optionProduct->getId()) { - foreach ($options as &$option) { - if ($option->getCode() == 'selection_qty_'.$selection->getSelectionId()) { - $option->setValue($value); - } - } - } - } - - return $this; - } - - /** - * Prepare Quote Item Quantity - * - * @param mixed $qty - * @return int - */ - public function prepareQuoteItemQty($qty) - { - return intval($qty); - } - - /** - * Checking if we can sale this bundle - * - * @return bool - */ - public function isSalable() - { - if (!parent::isSalable()) { - return false; - } - - $optionCollection = $this->getOptionsCollection(); - - if (!count($optionCollection->getItems())) { - return false; - } - - $requiredOptionIds = array(); - - foreach ($optionCollection->getItems() as $option) { - if ($option->getRequired()) { - $requiredOptionIds[$option->getId()] = 0; - } - } - - $selectionCollection = $this->getSelectionsCollection($optionCollection->getAllIds()); - - if (!count($selectionCollection->getItems())) { - return false; - } - $salableSelectionCount = 0; - foreach ($selectionCollection as $selection) { - if ($selection->isSalable()) { - $requiredOptionIds[$selection->getOptionId()] = 1; - $salableSelectionCount++; - } - - } - - return (array_sum($requiredOptionIds) == count($requiredOptionIds) && $salableSelectionCount); - } - - /** - * Initialize product(s) for add to cart process - * - * @param Varien_Object $buyRequest - * @return unknown - */ - public function prepareForCart(Varien_Object $buyRequest) - { - $result = parent::prepareForCart($buyRequest); - - if (is_string($result)) { - return $result; - } - - $selections = array(); - - $product = $this->getProduct(); - - $_appendAllSelections = false; - if ($product->getSkipCheckRequiredOption()) { - $_appendAllSelections = true; - } - - if ($options = $buyRequest->getBundleOption()) { - $qtys = $buyRequest->getBundleOptionQty(); - foreach ($options as $_optionId => $_selections) { - if (empty($_selections)) { - unset($options[$_optionId]); - } - } - $optionIds = array_keys($options); - - if (empty($optionIds)) { - return Mage::helper('bundle')->__('Please select options for product.'); - } - - //$optionsCollection = $this->getOptionsByIds($optionIds); - $product->getTypeInstance()->setStoreFilter($product->getStoreId()); - $optionsCollection = $this->getOptionsCollection(); - if (!$this->getProduct()->getSkipCheckRequiredOption()) { - foreach ($optionsCollection->getItems() as $option) { - if ($option->getRequired() && !isset($options[$option->getId()])) { - return Mage::helper('bundle')->__('Required options not selected.'); - } - } - } - $selectionIds = array(); - - foreach ($options as $optionId => $selectionId) { - if (!is_array($selectionId)) { - if ($selectionId != '') { - $selectionIds[] = $selectionId; - } - } else { - foreach ($selectionId as $id) { - if ($id != '') { - $selectionIds[] = $id; - } - } - } - } - - $selections = $this->getSelectionsByIds($selectionIds); - - /** - * checking if selections that where added are still on sale - */ - foreach ($selections->getItems() as $key => $selection) { - if (!$selection->isSalable()) { - $_option = $optionsCollection->getItemById($selection->getOptionId()); - if (is_array($options[$_option->getId()]) && count($options[$_option->getId()]) > 1){ - $moreSelections = true; - } else { - $moreSelections = false; - } - if ($_option->getRequired() && (!$_option->isMultiSelection() || ($_option->isMultiSelection() && !$moreSelections))) { - return Mage::helper('bundle')->__('Selected required options not available.'); - } - } - } - - $optionsCollection->appendSelections($selections, false, $_appendAllSelections); - - $selections = $selections->getItems(); - } else { - $product->getTypeInstance()->setStoreFilter($product->getStoreId()); - - $optionCollection = $product->getTypeInstance()->getOptionsCollection(); - - $optionIds = $product->getTypeInstance()->getOptionsIds(); - $selectionIds = array(); - - $selectionCollection = $product->getTypeInstance()->getSelectionsCollection( - $product->getTypeInstance()->getOptionsIds() - ); - - $options = $optionCollection->appendSelections($selectionCollection, false, $_appendAllSelections); - - foreach ($options as $option) { - if ($option->getRequired() && count($option->getSelections()) == 1) { - $selections = array_merge($selections, $option->getSelections()); - } else { - $selections = array(); - break; - } - } - } - if (count($selections) > 0) { - $uniqueKey = array($product->getId()); - $selectionIds = array(); - - /* - * shaking selection array :) by option position - */ - usort($selections, array($this, "shakeSelections")); - - foreach ($selections as $selection) { - if ($selection->getSelectionCanChangeQty() && isset($qtys[$selection->getOptionId()])) { - $qty = $qtys[$selection->getOptionId()] > 0 ? $qtys[$selection->getOptionId()] : 1; - } else { - $qty = $selection->getSelectionQty() ? $selection->getSelectionQty() : 1; - } - - $product->addCustomOption('selection_qty_' . $selection->getSelectionId(), $qty, $selection); - $selection->addCustomOption('selection_id', $selection->getSelectionId()); - - if ($customOption = $product->getCustomOption('product_qty_' . $selection->getId())) { - $customOption->setValue($customOption->getValue() + $qty); - } else { - $product->addCustomOption('product_qty_' . $selection->getId(), $qty, $selection); - } - - /* - * creating extra attributes that will be converted - * to product options in order item - * for selection (not for all bundle) - */ - $price = $product->getPriceModel()->getSelectionPrice($product, $selection, $qty); - $attributes = array( - 'price' => Mage::app()->getStore()->convertPrice($price), - 'qty' => $qty, - 'option_label' => $selection->getOption()->getTitle(), - 'option_id' => $selection->getOption()->getId() - ); - - //if (!$product->getPriceType()) { - $result[] = $selection->setParentProductId($product->getId()) - ->addCustomOption('bundle_option_ids', serialize($optionIds)) - ->addCustomOption('bundle_selection_attributes', serialize($attributes)) - ->setCartQty($qty); - //} - - $selectionIds[] = $selection->getSelectionId(); - $uniqueKey[] = $selection->getSelectionId(); - $uniqueKey[] = $qty; - } - /** - * "unique" key for bundle selection and add it to selections and bundle for selections - */ - $uniqueKey = implode('_', $uniqueKey); - foreach ($result as $item) { - $item->addCustomOption('bundle_identity', $uniqueKey); - } - $product->addCustomOption('bundle_option_ids', serialize($optionIds)); - $product->addCustomOption('bundle_selection_ids', serialize($selectionIds)); - - return $result; - } - return Mage::helper('bundle')->__('Please specify product option(s)'); - } - - /** - * Retrieve bundle selections collection based on ids - * - * @param array $selectionIds - * @return Mage_Bundle_Model_Mysql4_Selection_Collection - */ - public function getSelectionsByIds($selectionIds) - { - sort($selectionIds); - if (!$this->_usedSelections || serialize($this->_usedSelectionsIds) != serialize($selectionIds)) { - $this->_usedSelections = Mage::getResourceModel('bundle/selection_collection') - ->addAttributeToSelect('*') - ->addStoreFilter($this->getStoreFilter()) - ->setPositionOrder() - ->addFilterByRequiredOptions() - ->setSelectionIdsFilter($selectionIds); - $this->_usedSelectionsIds = $selectionIds; - } - return $this->_usedSelections; - } - - /** - * Retrieve bundle options collection based on ids - * - * @param array $optionIds - * @return Mage_Bundle_Model_Mysql4_Option_Collection - */ - public function getOptionsByIds($optionIds) - { - sort($optionIds); - if (!$this->_usedOptions || serialize($this->_usedOptionsIds) != serialize($optionIds)) { - $this->_usedOptions = Mage::getModel('bundle/option')->getResourceCollection() - ->setProductIdFilter($this->getProduct()->getId()) - ->setPositionOrder() - ->joinValues(Mage::app()->getStore()->getId()) - ->setIdFilter($optionIds); - $this->_usedOptionsIds = $optionIds; - } - return $this->_usedOptions; - } - - /** - * Prepare additional options/information for order item which will be - * created from this product - * - * @return array - */ - - public function getOrderOptions() - { - $optionArr = parent::getOrderOptions(); - - $bundleOptions = array(); - - $product = $this->getProduct(); - - if ($product->hasCustomOptions()) { - $customOption = $product->getCustomOption('bundle_option_ids'); - $optionIds = unserialize($customOption->getValue()); - $options = $this->getOptionsByIds($optionIds); - $customOption = $product->getCustomOption('bundle_selection_ids'); - $selectionIds = unserialize($customOption->getValue()); - $selections = $this->getSelectionsByIds($selectionIds); - foreach ($selections->getItems() as $selection) { - if ($selection->isSalable()) { - $selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId()); - if ($selectionQty) { - $price = $product->getPriceModel()->getSelectionPrice($product, $selection, $selectionQty->getValue()); - - $option = $options->getItemById($selection->getOptionId()); - if (!isset($bundleOptions[$option->getId()])) { - $bundleOptions[$option->getId()] = array( - 'option_id' => $option->getId(), - 'label' => $option->getTitle(), - 'value' => array() - ); - } - - $bundleOptions[$option->getId()]['value'][] = array( - 'title' => $selection->getName(), - 'qty' => $selectionQty->getValue(), - 'price' => Mage::app()->getStore()->convertPrice($price) - ); - - } - } - } - } - - $optionArr['bundle_options'] = $bundleOptions; - - /** - * Product Prices calculations save - */ - if ($product->getPriceType()) { - $optionArr['product_calculations'] = self::CALCULATE_PARENT; - } else { - $optionArr['product_calculations'] = self::CALCULATE_CHILD; - } - - $optionArr['shipment_type'] = $product->getShipmentType(); - - return $optionArr; - } - - public function shakeSelections($a, $b) - { - $aPosition = ($a->getOption()->getPosition()+1)*($a->getPosition()+1); - $bPosition = ($b->getOption()->getPosition()+1)*($b->getPosition()+1); - if ($aPosition == $bPosition) { - if ($a->getSelectionId() == $b->getSelectionId()) { - return 0; - } - return ($a->getSelectionId() < $b->getSelectionId()) ? -1 : 1; - } - return ($aPosition < $bPosition) ? -1 : 1; - } - - /** - * Return true if product has options - * - * @return bool - */ - public function hasOptions() - { - if (count($this->getSelectionsCollection($this->getOptionsCollection()->getAllIds())->getItems()) || $this->getProduct()->getOptions()) { - return true; - } - return false; - } - - /** - * Allow for updates of chidren qty's - * - * @return boolean true - */ - public function getForceChildItemQtyChanges() - { - return true; - } - -} + + */ +class Mage_Bundle_Model_Product_Type extends Mage_Catalog_Model_Product_Type_Abstract +{ + protected $_isComposite = true; + + protected $_optionsCollection; + protected $_selectionsCollection; + protected $_storeFilter = null; + + protected $_usedSelections = null; + protected $_usedSelectionsIds = null; + protected $_usedOptions = null; + protected $_usedOptionsIds = null; + + protected $_optionsCount = null; + + /** + * Return relation info about used products + * + * @return Varien_Object Object with information data + */ + public function getRelationInfo() + { + $info = new Varien_Object(); + $info->setTable('bundle/selection') + ->setParentFieldName('parent_product_id') + ->setChildFieldName('product_id'); + return $info; + } + + /** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param bool $required + * @return array + */ + public function getChildrenIds($parentId, $required = true) + { + return Mage::getResourceSingleton('bundle/selection') + ->getChildrenIds($parentId, $required); + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @return array + */ + public function getParentIdsByChild($childId) + { + return Mage::getResourceSingleton('bundle/selection') + ->getParentIdsByChild($childId); + } + + /** + * Return product sku based on sku_type attribute + * + * @return string + */ + public function getSku() + { + $sku = parent::getSku(); + + if ($this->getProduct()->getData('sku_type')) { + return $sku; + } else { + $skuParts = array($sku); + + if ($this->getProduct()->hasCustomOptions()) { + $customOption = $this->getProduct()->getCustomOption('bundle_selection_ids'); + $selectionIds = unserialize($customOption->getValue()); + $selections = $this->getSelectionsByIds($selectionIds); + foreach ($selections->getItems() as $selection) { + $skuParts[] = $selection->getSku(); + } + } + + return implode('-', $skuParts); + } + } + + /** + * Return product weight based on weight_type attribute + * + * @return decimal + */ + public function getWeight() + { + if ($this->getProduct()->getData('weight_type')) { + return $this->getProduct()->getData('weight'); + } else { + $weight = 0; + + if ($this->getProduct()->hasCustomOptions()) { + $customOption = $this->getProduct()->getCustomOption('bundle_selection_ids'); + $selectionIds = unserialize($customOption->getValue()); + $selections = $this->getSelectionsByIds($selectionIds); + foreach ($selections->getItems() as $selection) { + $weight += $selection->getWeight(); + } + } + return $weight; + } + } + + /** + * Check is virtual product + * + * @return bool + */ + public function isVirtual() + { + if ($this->getProduct()->hasCustomOptions()) { + $customOption = $this->getProduct()->getCustomOption('bundle_selection_ids'); + $selectionIds = unserialize($customOption->getValue()); + $selections = $this->getSelectionsByIds($selectionIds); + $virtualCount = 0; + foreach ($selections->getItems() as $selection) { + if ($selection->getTypeInstance()->IsVirtual()) { + $virtualCount++; + } + } + if ($virtualCount == count($selections)) { + return true; + } + } + return false; + } + + public function beforeSave() + { + parent::beforeSave(); + + $this->getProduct()->canAffectOptions(false); + + if ($this->getProduct()->getCanSaveBundleSelections()) { + $this->getProduct()->canAffectOptions(true); + if ($selections = $this->getProduct()->getBundleSelectionsData()) { + if (!empty($selections)) { + if ($options = $this->getProduct()->getBundleOptionsData()) { + foreach ($options as $option) { + if (empty($option['delete']) || 1 != (int)$option['delete']) { + $this->getProduct()->setTypeHasOptions(true); + if (1 == (int)$option['required']) { + $this->getProduct()->setTypeHasRequiredOptions(true); + break; + } + } + } + } + } + } + } + } + + public function save() + { + parent::save(); + + if ($options = $this->getProduct()->getBundleOptionsData()) { + + foreach ($options as $key => $option) { + if (isset($option['option_id']) && $option['option_id'] == '') { + unset($option['option_id']); + } + + $optionModel = Mage::getModel('bundle/option') + ->setData($option) + ->setParentId($this->getProduct()->getId()) + ->setStoreId($this->getProduct()->getStoreId()); + + $optionModel->isDeleted((bool)$option['delete']); + $optionModel->save(); + + $options[$key]['option_id'] = $optionModel->getOptionId(); + } + + $excludeSelectionIds = array(); + + if ($selections = $this->getProduct()->getBundleSelectionsData()) { + foreach ($selections as $index => $group) { + foreach ($group as $key => $selection) { + if (isset($selection['selection_id']) && $selection['selection_id'] == '') { + unset($selection['selection_id']); + } + + if (!isset($selection['is_default'])) { + $selection['is_default'] = 0; + } + + $selectionModel = Mage::getModel('bundle/selection') + ->setData($selection) + ->setOptionId($options[$index]['option_id']) + ->setParentProductId($this->getProduct()->getId()); + + $selectionModel->isDeleted((bool)$selection['delete']); + $selectionModel->save(); + + $selection['selection_id'] = $selectionModel->getSelectionId(); + + if ($selectionModel->getSelectionId()) { + $excludeSelectionIds[] = $selectionModel->getSelectionId(); + } + } + } + Mage::getResourceModel('bundle/bundle')->dropAllUnneededSelections($this->getProduct()->getId(), $excludeSelectionIds); + } + + if ($this->getProduct()->getData('price_type') != $this->getProduct()->getOrigData('price_type')) { + Mage::getResourceModel('bundle/bundle')->dropAllQuoteChildItems($this->getProduct()->getId()); + } + } + + return $this; + } + + /** + * Retrieve bundle options items + * + * @return array + */ + public function getOptions() + { + return $this->getOptionsCollection()->getItems(); + } + + /** + * Retrieve bundle options ids + * + * @return array + */ + public function getOptionsIds() + { + return $this->getOptionsCollection()->getAllIds(); + } + + /** + * Retrieve bundle option collection + * + * @return Mage_Bundle_Model_Mysql4_Option_Collection + */ + public function getOptionsCollection() + { + if (!$this->_optionsCollection) { + $this->_optionsCollection = Mage::getModel('bundle/option')->getResourceCollection() + ->setProductIdFilter($this->getProduct()->getId()) + ->setPositionOrder() + ->joinValues($this->getStoreFilter()); + } + return $this->_optionsCollection; + } + + /** + * Retrive bundle selections collection based on used options + * + * @param array $optionIds + * @return Mage_Bundle_Model_Mysql4_Selection_Collection + */ + public function getSelectionsCollection($optionIds) + { + if (!$this->_selectionsCollection) { + $this->_selectionsCollection = Mage::getResourceModel('bundle/selection_collection') + ->addAttributeToSelect('*') + ->setPositionOrder() + ->addStoreFilter($this->getStoreFilter()) + ->addFilterByRequiredOptions() + ->setOptionIdsFilter($optionIds); + } + return $this->_selectionsCollection; + } + + /** + * Method is needed for specific actions to change given quote options values + * according current product type logic + * Example: the cataloginventory validation of decimal qty can change qty to int, + * so need to change quote item qty option value too. + * + * @param array $options + * @param Varien_Object $option + * @param mixed $value + * + * @return object Mage_Bundle_Model_Product_Type + */ + public function updateQtyOption($options, Varien_Object $option, $value) + { + $optionProduct = $option->getProduct(); + + $optionCollection = $this->getOptionsCollection(); + + $selections = $this->getSelectionsCollection($optionCollection->getAllIds()); + + foreach ($selections as $selection) { + if ($selection->getProductId() == $optionProduct->getId()) { + foreach ($options as &$option) { + if ($option->getCode() == 'selection_qty_'.$selection->getSelectionId()) { + $option->setValue($value); + } + } + } + } + + return $this; + } + + /** + * Prepare Quote Item Quantity + * + * @param mixed $qty + * @return int + */ + public function prepareQuoteItemQty($qty) + { + return intval($qty); + } + + /** + * Checking if we can sale this bundle + * + * @return bool + */ + public function isSalable() + { + $salable = parent::isSalable(); + if (!is_null($salable)) { + return $salable; + } + + $optionCollection = $this->getOptionsCollection(); + + if (!count($optionCollection->getItems())) { + return false; + } + + $requiredOptionIds = array(); + + foreach ($optionCollection->getItems() as $option) { + if ($option->getRequired()) { + $requiredOptionIds[$option->getId()] = 0; + } + } + + $selectionCollection = $this->getSelectionsCollection($optionCollection->getAllIds()); + + if (!count($selectionCollection->getItems())) { + return false; + } + $salableSelectionCount = 0; + foreach ($selectionCollection as $selection) { + if ($selection->isSalable()) { + $requiredOptionIds[$selection->getOptionId()] = 1; + $salableSelectionCount++; + } + + } + + return (array_sum($requiredOptionIds) == count($requiredOptionIds) && $salableSelectionCount); + } + + /** + * Initialize product(s) for add to cart process + * + * @param Varien_Object $buyRequest + * @return unknown + */ + public function prepareForCart(Varien_Object $buyRequest) + { + $result = parent::prepareForCart($buyRequest); + + if (is_string($result)) { + return $result; + } + + $selections = array(); + + $product = $this->getProduct(); + + $_appendAllSelections = false; + if ($product->getSkipCheckRequiredOption()) { + $_appendAllSelections = true; + } + + if ($options = $buyRequest->getBundleOption()) { + $qtys = $buyRequest->getBundleOptionQty(); + foreach ($options as $_optionId => $_selections) { + if (empty($_selections)) { + unset($options[$_optionId]); + } + } + $optionIds = array_keys($options); + + if (empty($optionIds)) { + return Mage::helper('bundle')->__('Please select options for product.'); + } + + //$optionsCollection = $this->getOptionsByIds($optionIds); + $product->getTypeInstance()->setStoreFilter($product->getStoreId()); + $optionsCollection = $this->getOptionsCollection(); + if (!$this->getProduct()->getSkipCheckRequiredOption()) { + foreach ($optionsCollection->getItems() as $option) { + if ($option->getRequired() && !isset($options[$option->getId()])) { + return Mage::helper('bundle')->__('Required options not selected.'); + } + } + } + $selectionIds = array(); + + foreach ($options as $optionId => $selectionId) { + if (!is_array($selectionId)) { + if ($selectionId != '') { + $selectionIds[] = $selectionId; + } + } else { + foreach ($selectionId as $id) { + if ($id != '') { + $selectionIds[] = $id; + } + } + } + } + + $selections = $this->getSelectionsByIds($selectionIds); + + /** + * checking if selections that where added are still on sale + */ + foreach ($selections->getItems() as $key => $selection) { + if (!$selection->isSalable()) { + $_option = $optionsCollection->getItemById($selection->getOptionId()); + if (is_array($options[$_option->getId()]) && count($options[$_option->getId()]) > 1){ + $moreSelections = true; + } else { + $moreSelections = false; + } + if ($_option->getRequired() && (!$_option->isMultiSelection() || ($_option->isMultiSelection() && !$moreSelections))) { + return Mage::helper('bundle')->__('Selected required options not available.'); + } + } + } + + $optionsCollection->appendSelections($selections, false, $_appendAllSelections); + + $selections = $selections->getItems(); + } else { + $product->getTypeInstance()->setStoreFilter($product->getStoreId()); + + $optionCollection = $product->getTypeInstance()->getOptionsCollection(); + + $optionIds = $product->getTypeInstance()->getOptionsIds(); + $selectionIds = array(); + + $selectionCollection = $product->getTypeInstance()->getSelectionsCollection( + $product->getTypeInstance()->getOptionsIds() + ); + + $options = $optionCollection->appendSelections($selectionCollection, false, $_appendAllSelections); + + foreach ($options as $option) { + if ($option->getRequired() && count($option->getSelections()) == 1) { + $selections = array_merge($selections, $option->getSelections()); + } else { + $selections = array(); + break; + } + } + } + if (count($selections) > 0) { + $uniqueKey = array($product->getId()); + $selectionIds = array(); + + /* + * shaking selection array :) by option position + */ + usort($selections, array($this, "shakeSelections")); + + foreach ($selections as $selection) { + if ($selection->getSelectionCanChangeQty() && isset($qtys[$selection->getOptionId()])) { + $qty = $qtys[$selection->getOptionId()] > 0 ? $qtys[$selection->getOptionId()] : 1; + } else { + $qty = $selection->getSelectionQty() ? $selection->getSelectionQty() : 1; + } + + $product->addCustomOption('selection_qty_' . $selection->getSelectionId(), $qty, $selection); + $selection->addCustomOption('selection_id', $selection->getSelectionId()); + + if ($customOption = $product->getCustomOption('product_qty_' . $selection->getId())) { + $customOption->setValue($customOption->getValue() + $qty); + } else { + $product->addCustomOption('product_qty_' . $selection->getId(), $qty, $selection); + } + + /* + * creating extra attributes that will be converted + * to product options in order item + * for selection (not for all bundle) + */ + $price = $product->getPriceModel()->getSelectionPrice($product, $selection, $qty); + $attributes = array( + 'price' => Mage::app()->getStore()->convertPrice($price), + 'qty' => $qty, + 'option_label' => $selection->getOption()->getTitle(), + 'option_id' => $selection->getOption()->getId() + ); + + //if (!$product->getPriceType()) { + $_result = $selection->getTypeInstance()->prepareForCart($buyRequest); + if (is_string($_result) && !is_array($_result)) { + return $_result; + } + + if (!isset($_result[0])) { + return Mage::helper('checkout')->__('Can not add item to shopping cart'); + } + + $result[] = $_result[0]->setParentProductId($product->getId()) + ->addCustomOption('bundle_option_ids', serialize($optionIds)) + ->addCustomOption('bundle_selection_attributes', serialize($attributes)) + ->setCartQty($qty); + //} + + $selectionIds[] = $_result[0]->getSelectionId(); + $uniqueKey[] = $_result[0]->getSelectionId(); + $uniqueKey[] = $qty; + } + /** + * "unique" key for bundle selection and add it to selections and bundle for selections + */ + $uniqueKey = implode('_', $uniqueKey); + foreach ($result as $item) { + $item->addCustomOption('bundle_identity', $uniqueKey); + } + $product->addCustomOption('bundle_option_ids', serialize($optionIds)); + $product->addCustomOption('bundle_selection_ids', serialize($selectionIds)); + + return $result; + } + return Mage::helper('bundle')->__('Please specify product option(s)'); + } + + /** + * Retrieve bundle selections collection based on ids + * + * @param array $selectionIds + * @return Mage_Bundle_Model_Mysql4_Selection_Collection + */ + public function getSelectionsByIds($selectionIds) + { + sort($selectionIds); + if (!$this->_usedSelections || serialize($this->_usedSelectionsIds) != serialize($selectionIds)) { + $this->_usedSelections = Mage::getResourceModel('bundle/selection_collection') + ->addAttributeToSelect('*') + ->addStoreFilter($this->getStoreFilter()) + ->setPositionOrder() + ->addFilterByRequiredOptions() + ->setSelectionIdsFilter($selectionIds); + $this->_usedSelectionsIds = $selectionIds; + } + return $this->_usedSelections; + } + + /** + * Retrieve bundle options collection based on ids + * + * @param array $optionIds + * @return Mage_Bundle_Model_Mysql4_Option_Collection + */ + public function getOptionsByIds($optionIds) + { + sort($optionIds); + if (!$this->_usedOptions || serialize($this->_usedOptionsIds) != serialize($optionIds)) { + $this->_usedOptions = Mage::getModel('bundle/option')->getResourceCollection() + ->setProductIdFilter($this->getProduct()->getId()) + ->setPositionOrder() + ->joinValues(Mage::app()->getStore()->getId()) + ->setIdFilter($optionIds); + $this->_usedOptionsIds = $optionIds; + } + return $this->_usedOptions; + } + + /** + * Prepare additional options/information for order item which will be + * created from this product + * + * @return array + */ + + public function getOrderOptions() + { + $optionArr = parent::getOrderOptions(); + + $bundleOptions = array(); + + $product = $this->getProduct(); + + if ($product->hasCustomOptions()) { + $customOption = $product->getCustomOption('bundle_option_ids'); + $optionIds = unserialize($customOption->getValue()); + $options = $this->getOptionsByIds($optionIds); + $customOption = $product->getCustomOption('bundle_selection_ids'); + $selectionIds = unserialize($customOption->getValue()); + $selections = $this->getSelectionsByIds($selectionIds); + foreach ($selections->getItems() as $selection) { + if ($selection->isSalable()) { + $selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId()); + if ($selectionQty) { + $price = $product->getPriceModel()->getSelectionPrice($product, $selection, $selectionQty->getValue()); + + $option = $options->getItemById($selection->getOptionId()); + if (!isset($bundleOptions[$option->getId()])) { + $bundleOptions[$option->getId()] = array( + 'option_id' => $option->getId(), + 'label' => $option->getTitle(), + 'value' => array() + ); + } + + $bundleOptions[$option->getId()]['value'][] = array( + 'title' => $selection->getName(), + 'qty' => $selectionQty->getValue(), + 'price' => Mage::app()->getStore()->convertPrice($price) + ); + + } + } + } + } + + $optionArr['bundle_options'] = $bundleOptions; + + /** + * Product Prices calculations save + */ + if ($product->getPriceType()) { + $optionArr['product_calculations'] = self::CALCULATE_PARENT; + } else { + $optionArr['product_calculations'] = self::CALCULATE_CHILD; + } + + $optionArr['shipment_type'] = $product->getShipmentType(); + + return $optionArr; + } + + public function shakeSelections($a, $b) + { + $aPosition = ($a->getOption()->getPosition()+1)*($a->getPosition()+1); + $bPosition = ($b->getOption()->getPosition()+1)*($b->getPosition()+1); + if ($aPosition == $bPosition) { + if ($a->getSelectionId() == $b->getSelectionId()) { + return 0; + } + return ($a->getSelectionId() < $b->getSelectionId()) ? -1 : 1; + } + return ($aPosition < $bPosition) ? -1 : 1; + } + + /** + * Return true if product has options + * + * @return bool + */ + public function hasOptions() + { + if (count($this->getSelectionsCollection($this->getOptionsCollection()->getAllIds())->getItems()) || $this->getProduct()->getOptions()) { + return true; + } + return false; + } + + /** + * Allow for updates of chidren qty's + * + * @return boolean true + */ + public function getForceChildItemQtyChanges() + { + return true; + } + +} diff --git a/app/code/core/Mage/Bundle/etc/config.xml b/app/code/core/Mage/Bundle/etc/config.xml index 9cbcf86515..29186c71e3 100644 --- a/app/code/core/Mage/Bundle/etc/config.xml +++ b/app/code/core/Mage/Bundle/etc/config.xml @@ -85,7 +85,6 @@ - bundle/product_price bundle/catalogIndex_data_bundle diff --git a/app/code/core/Mage/Catalog/Block/Navigation.php b/app/code/core/Mage/Catalog/Block/Navigation.php index ba09ed33cf..82a20783d2 100644 --- a/app/code/core/Mage/Catalog/Block/Navigation.php +++ b/app/code/core/Mage/Catalog/Block/Navigation.php @@ -155,7 +155,7 @@ public function drawItem($category, $level=0, $last=false) } $html.= ' class="level'.$level; - $html.= ' nav-'.str_replace('/', '-', $category->getRequestPath()); + $html.= ' nav-'.str_replace('/', '-', Mage::helper('catalog/category')->getCategoryUrlPath($category->getRequestPath())); if ($this->isCategoryActive($category)) { $html.= ' active'; } diff --git a/app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php b/app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php index 507fba5bf2..0e94a1dbaa 100644 --- a/app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php +++ b/app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php @@ -291,7 +291,8 @@ public function getOrderUrl($order, $direction) } return $this->getPagerUrl(array( $this->getOrderVarName()=>$order, - $this->getDirectionVarName()=>$direction + $this->getDirectionVarName()=>$direction, + $this->getPageVarName() => null )); } @@ -359,7 +360,7 @@ public function setModes($modes) */ public function getModeUrl($mode) { - return $this->getPagerUrl(array($this->getModeVarName()=>$mode)); + return $this->getPagerUrl( array($this->getModeVarName()=>$mode, $this->getPageVarName() => null) ); } /** @@ -520,4 +521,18 @@ public function getLimit() $limits = array_keys($limits); return $limits[0]; } + + /** + * Retrieve Limit Pager URL + * + * @param int $limit + * @return string + */ + public function getLimitUrl($limit) + { + return $this->getPagerUrl(array( + $this->getLimitVarName() => $limit, + $this->getPageVarName() => null + )); + } } diff --git a/app/code/core/Mage/Catalog/Block/Product/View/Attributes.php b/app/code/core/Mage/Catalog/Block/Product/View/Attributes.php index bc52ea6792..7af29b7012 100644 --- a/app/code/core/Mage/Catalog/Block/Product/View/Attributes.php +++ b/app/code/core/Mage/Catalog/Block/Product/View/Attributes.php @@ -67,7 +67,7 @@ public function getAdditionalData(array $excludeAttr = array()) if (strlen($value) && $product->hasData($attribute->getAttributeCode())) { if ($attribute->getFrontendInput() == 'price') { $value = Mage::app()->getStore()->convertPrice($value,true); - } else { + } elseif (!$attribute->getIsHtmlAllowedOnFront()) { $value = $this->htmlEscape($value); } $data[$attribute->getAttributeCode()] = array( diff --git a/app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php b/app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php index 7446cfd329..dac34f4f4f 100644 --- a/app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php +++ b/app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php @@ -140,14 +140,33 @@ public function getJsonConfig() /*echo '
';
         print_r($this->_prices);
         echo '
';die();*/ + + $_request = Mage::getSingleton('tax/calculation')->getRateRequest(false, false, false); + $_request->setProductClassId($this->getProduct()->getTaxClassId()); + $defaultTax = Mage::getSingleton('tax/calculation')->getRate($_request); + + $_request = Mage::getSingleton('tax/calculation')->getRateRequest(); + $_request->setProductClassId($this->getProduct()->getTaxClassId()); + $currentTax = Mage::getSingleton('tax/calculation')->getRate($_request); + + $taxConfig = array( + 'includeTax' => Mage::helper('tax')->priceIncludesTax(), + 'showIncludeTax' => Mage::helper('tax')->displayPriceIncludingTax(), + 'showBothPrices' => Mage::helper('tax')->displayBothPrices(), + 'defaultTax' => $defaultTax, + 'currentTax' => $currentTax, + 'inclTaxTitle' => Mage::helper('catalog')->__('Incl. Tax'), + ); + $config = array( - 'attributes'=> $attributes, - 'template' => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()), -// 'prices' => $this->_prices, - 'basePrice' => $this->_registerJsPrice($this->_convertPrice($this->getProduct()->getFinalPrice())), - 'oldPrice' => $this->_registerJsPrice($this->_convertPrice($this->getProduct()->getPrice())), - 'productId' => $this->getProduct()->getId(), - 'chooseText'=> Mage::helper('catalog')->__('Choose option...'), + 'attributes' => $attributes, + 'template' => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()), +// 'prices' => $this->_prices, + 'basePrice' => $this->_registerJsPrice($this->_convertPrice($this->getProduct()->getFinalPrice())), + 'oldPrice' => $this->_registerJsPrice($this->_convertPrice($this->getProduct()->getPrice())), + 'productId' => $this->getProduct()->getId(), + 'chooseText' => Mage::helper('catalog')->__('Choose option...'), + 'taxConfig' => $taxConfig, ); return Zend_Json::encode($config); diff --git a/app/code/core/Mage/Catalog/Helper/Data.php b/app/code/core/Mage/Catalog/Helper/Data.php index d66420e08a..040e57048e 100644 --- a/app/code/core/Mage/Catalog/Helper/Data.php +++ b/app/code/core/Mage/Catalog/Helper/Data.php @@ -47,6 +47,7 @@ public function getBreadcrumbPath() ->addAttributeToSelect('name') ->addAttributeToSelect('url_key') ->addFieldToFilter('entity_id', array('in'=>$pathIds)) + ->addFieldToFilter('is_active', 1) ->load() ->getItems(); diff --git a/app/code/core/Mage/Catalog/Model/Category.php b/app/code/core/Mage/Catalog/Model/Category.php index daea518955..f3741613b3 100644 --- a/app/code/core/Mage/Catalog/Model/Category.php +++ b/app/code/core/Mage/Catalog/Model/Category.php @@ -85,8 +85,10 @@ public function getUrlInstance() } /** - * @return Mage_Core_Model_Url_Rewrite - */ + * Get url rewrite model + * + * @return Mage_Core_Model_Url_Rewrite + */ public function getUrlRewrite() { if (!self::$_urlRewrite) { @@ -320,16 +322,35 @@ public function getUrlPath() return $path; } + /** + * Get parent category object + * + * @return Mage_Catalog_Model_Category + */ public function getParentCategory() { return Mage::getModel('catalog/category')->load($this->getParentId()); } + /** + * Get parent category identifier + * + * @return int + */ public function getParentId() { - $parentPath = explode('/', $this->getPath()); - array_pop($parentPath); - return intval(array_pop($parentPath)); + $parentIds = $this->getParentIds(); + return intval(array_pop($parentIds)); + } + + /** + * Get all parent categories ids + * + * @return array + */ + public function getParentIds() + { + return array_diff($this->getPathIds(), array($this->getId())); } public function getCustomDesignDate() @@ -410,6 +431,12 @@ public function checkId($id) return $this->_getResource()->checkId($id); } + /** + * Get array categories ids which are part of category path + * Result array contain id of current category because it is part of the path + * + * @return array + */ public function getPathIds() { $ids = $this->getData('path_ids'); diff --git a/app/code/core/Mage/Catalog/Model/Layer/Filter/Category.php b/app/code/core/Mage/Catalog/Model/Layer/Filter/Category.php index aae9649604..83a3aaea31 100644 --- a/app/code/core/Mage/Catalog/Model/Layer/Filter/Category.php +++ b/app/code/core/Mage/Catalog/Model/Layer/Filter/Category.php @@ -76,7 +76,9 @@ public function apply(Zend_Controller_Request_Abstract $request, $filterBlock) { $filter = (int) $request->getParam($this->getRequestVar()); $this->_categoryId = $filter; - $this->_appliedCategory = Mage::getModel('catalog/category')->load($filter); + $this->_appliedCategory = Mage::getModel('catalog/category') + ->setStoreId(Mage::app()->getStore()->getId()) + ->load($filter); if ($this->_isValidCategory($this->_appliedCategory)) { $this->getLayer()->getProductCollection() diff --git a/app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php b/app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php index 6e3cd45700..f2953597bd 100644 --- a/app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php +++ b/app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php @@ -56,7 +56,10 @@ public function getFilter() */ public function getUrl() { - $query = array($this->getFilter()->getRequestVar()=>$this->getValue()); + $query = array( + $this->getFilter()->getRequestVar()=>$this->getValue(), + Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls + ); return Mage::getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query)); } diff --git a/app/code/core/Mage/Catalog/Model/Product.php b/app/code/core/Mage/Catalog/Model/Product.php index a38d335a31..3daa7e5c2f 100644 --- a/app/code/core/Mage/Catalog/Model/Product.php +++ b/app/code/core/Mage/Catalog/Model/Product.php @@ -1,1344 +1,1358 @@ - - */ -class Mage_Catalog_Model_Product extends Mage_Catalog_Model_Abstract -{ - const CACHE_TAG = 'catalog_product'; - protected $_cacheTag = 'catalog_product'; - protected $_eventPrefix = 'catalog_product'; - protected $_eventObject = 'product'; - protected $_canAffectOptions = false; - - /** - * Product type instance - * - * @var Mage_Catalog_Model_Product_Type_Abstract - */ - protected $_typeInstance = null; - - /** - * Product link instance - * - * @var Mage_Catalog_Model_Product_Link - */ - protected $_linkInstance; - - /** - * Product object customization (not stored in DB) - * - * @var array - */ - protected $_customOptions = array(); - - /** - * Product Url Instance - * - * @var Mage_Catalog_Model_Product_Url - */ - protected $_urlModel = null; - - protected static $_url; - protected static $_urlRewrite; - - protected $_errors = array(); - - protected $_optionInstance; - - protected $_options = array(); - - /** - * Product reserved attribute codes - */ - protected $_reservedAttributes; - - /** - * Initialize resources - */ - protected function _construct() - { - $this->_urlModel = Mage::getSingleton('catalog/product_url'); - $this->_init('catalog/product'); - } - - public function validate() - { - $this->_getResource()->validate($this); - return $this; - } - - public function getName() - { - return $this->_getData('name'); - } - - /** - * Get product price throught type instance - * - * @return unknown - */ - public function getPrice() - { - return $this->getPriceModel()->getPrice($this); - } - - public function getTypeId() - { - return $this->_getData('type_id'); - } - - public function getStatus() - { - return $this->_getData('status'); - } - - /** - * Retrieve type instance - * - * Type instance implement type depended logic - * - * @return Mage_Catalog_Model_Product_Type_Abstract - */ - public function getTypeInstance() - { - if ($this->_typeInstance === null) { - $this->_typeInstance = Mage::getSingleton('catalog/product_type')->factory($this); - } - return $this->_typeInstance; - } - - public function setTypeInstance($instance) - { - $this->_typeInstance = $instance; - return $this; - } - - /** - * Retrieve type instance - * - * @return Mage_Catalog_Model_Product_Link - */ - public function getLinkInstance() - { - if (!$this->_linkInstance) { - $this->_linkInstance = Mage::getSingleton('catalog/product_link'); - } - return $this->_linkInstance; - } - - /** - * Retrive product id by sku - * - * @param string $sku - * @return integer - */ - public function getIdBySku($sku) - { - return $this->_getResource()->getIdBySku($sku); - } - - /** - * Retrieve product category id - * - * @return int - */ - public function getCategoryId() - { - if ($category = Mage::registry('current_category')) { - return $category->getId(); - } - return false; - } - - /** - * Retrieve product category - * - * @return Mage_Catalog_Model_Category - */ - public function getCategory() - { - $category = $this->getData('category'); - if (is_null($category) && $this->getCategoryId()) { - $category = Mage::getModel('catalog/category')->load($this->getCategoryId()); - $this->setCategory($category); - } - return $category; - } - - public function setCategoryIds($ids) - { - if (is_string($ids)) { - $ids = explode(',', $ids); - } elseif (!is_array($ids)) { - Mage::throwException(Mage::helper('catalog')->__('Invalid category IDs')); - } - foreach ($ids as $i=>$v) { - if (empty($v)) { - unset($ids[$i]); - } - } - $this->setData('category_ids', $ids); - return $this; - } - - public function getCategoryIds() - { - if ($this->hasData('category_ids')) { - $ids = $this->getData('category_ids'); - if (!is_array($ids)) { - $ids = !empty($ids) ? explode(',', $ids) : array(); - $this->setData('category_ids', $ids); - } - } else { - $ids = $this->_getResource()->getCategoryIds($this); - $this->setData('category_ids', $ids); - } - return $this->getData('category_ids'); - } - - /** - * Retrieve product categories - * - * @return Varien_Data_Collection - */ - public function getCategoryCollection() - { - return $this->getResource()->getCategoryCollection($this); - } - - /** - * Retrieve product websites identifiers - * - * @return array - */ - public function getWebsiteIds() - { - if (!$this->hasWebsiteIds()) { - $ids = $this->_getResource()->getWebsiteIds($this); - $this->setWebsiteIds($ids); - } - return $this->getData('website_ids'); - } - - public function getStoreIds() - { - if (!$this->hasStoreIds()) { - $storeIds = array(); - if ($websiteIds = $this->getWebsiteIds()) { - foreach ($websiteIds as $websiteId) { - $websiteStores = Mage::app()->getWebsite($websiteId)->getStoreIds(); - $storeIds = array_merge($storeIds, $websiteStores); - } - } - $this->setStoreIds($storeIds); - } - return $this->getData('store_ids'); - } - - /** - * Retrieve product attributes - * - * if $groupId is null - retrieve all product attributes - * - * @param int $groupId - * @return array - */ - public function getAttributes($groupId = null, $skipSuper=false) - { - $productAttributes = $this->getTypeInstance()->getEditableAttributes(); - if ($groupId) { - $attributes = array(); - foreach ($productAttributes as $attribute) { - if ($attribute->isInGroup($this->getAttributeSetId(), $groupId)) { - $attributes[] = $attribute; - } - } - } - else { - $attributes = $productAttributes; - } - - return $attributes; - } - - /** - * Check product options and type options and save them, too - * - */ - protected function _beforeSave() - { - $this->cleanCache(); - $this->setTypeHasOptions(false); - $this->setTypeHasRequiredOptions(false); - - $this->getTypeInstance()->beforeSave(); - - $hasOptions = false; - $hasRequiredOptions = false; - $this->canAffectOptions($this->_canAffectOptions && $this->getCanSaveCustomOptions()); - if ($this->getCanSaveCustomOptions()) { - $options = $this->getProductOptions(); - if (is_array($options)) { - foreach ($this->getProductOptions() as $option) { - $this->getOptionInstance()->addOption($option); - if ((!isset($option['is_delete'])) || $option['is_delete'] != '1') { - $hasOptions = true; - } - } - foreach ($this->getOptionInstance()->getOptions() as $option) { - if ($option['is_require'] == '1') { - $hasRequiredOptions = true; - break; - } - } - } - } - - /** - * Set true, if any - * Set false, ONLY if options have been affected by Options tab and Type instance tab - */ - if ($hasOptions || (bool)$this->getTypeHasOptions()) { - $this->setHasOptions(true); - if ($hasRequiredOptions || (bool)$this->getTypeHasRequiredOptions()) { - $this->setRequiredOptions(true); - } - elseif ($this->canAffectOptions()) { - $this->setRequiredOptions(false); - } - } - elseif ($this->canAffectOptions()) { - $this->setHasOptions(false); - $this->setRequiredOptions(false); - } - - parent::_beforeSave(); - } - - /** - * Check/set if options can be affected when saving product - * If value specified, it will be set. - * - * @param bool $value - * @return bool - */ - public function canAffectOptions($value = null) - { - if (null !== $value) { - $this->_canAffectOptions = (bool)$value; - } - return $this->_canAffectOptions; - } - - /** - * Saving product type related data - * - * @return unknown - */ - protected function _afterSave() - { - $this->getLinkInstance()->saveProductRelations($this); - $this->getTypeInstance()->save(); - - /** - * Product Custom Options - */ - /* @var $optionModel Mage_Catalog_Model_Product_Option */ -// $options = $this->getProductOptions(); -// if (is_array($options)) { -// foreach ($this->getProductOptions() as $option) { -// $this->getOptionInstance()->addOption($option); -// } -// } - $this->getOptionInstance()->setProduct($this) - ->saveOptions(); - - parent::_afterSave(); - } - - protected function _beforeDelete() - { - $this->cleanCache(); - $this->_protectFromNonAdmin(); - return parent::_beforeDelete(); - } - - /** - * deprecated - * @see Mage_Sales_Model_Observer::substractQtyFromQuotes() - */ - protected function _substractQtyFromQuotes() - { - // kept for legacy purposes - } - - protected function _afterLoad() - { - parent::_afterLoad(); - foreach ($this->getProductOptionsCollection() as $option) { - $option->setProduct($this); - $this->addOption($option); - } - - return $this; - } - - public function cleanCache() - { - Mage::app()->cleanCache('catalog_product_'.$this->getId()); - } - - public function getPriceModel() - { - return Mage::getSingleton('catalog/product_type')->priceFactory($this->getTypeId()); - } - - /** - * Get product tier price by qty - * - * @param double $qty - * @return double - */ - public function getTierPrice($qty=null) - { - return $this->getPriceModel()->getTierPrice($qty, $this); - } - - /** - * Count how many tier prices we have for the product - * - * @return int - */ - public function getTierPriceCount() - { - return $this->getPriceModel()->getTierPriceCount($this); - } - - /** - * Get formated by currency tier price - * - * @param double $qty - * @return array || double - */ - public function getFormatedTierPrice($qty=null) - { - return $this->getPriceModel()->getFormatedTierPrice($qty, $this); - } - - /** - * Get formated by currency product price - * - * @return array || double - */ - public function getFormatedPrice() - { - return $this->getPriceModel()->getFormatedPrice($this); - } - - /** - * Get product final price - * - * @param double $qty - * @return double - */ - public function getFinalPrice($qty=null) - { - return $this->getPriceModel()->getFinalPrice($qty, $this); - } - - public function getCalculatedFinalPrice() - { - return $this->_getData('calculated_final_price'); - } - - public function getMinimalPrice() - { - return $this->_getData('minimal_price'); - } - - public function getSpecialPrice() - { - return $this->_getData('special_price'); - } - - public function getSpecialFromDate() - { - return $this->_getData('special_from_date'); - } - - public function getSpecialToDate() - { - return $this->_getData('special_to_date'); - } - - -/******************************************************************************* - ** Linked products API - */ - /** - * Retrieve array of related roducts - * - * @return array - */ - public function getRelatedProducts() - { - if (!$this->hasRelatedProducts()) { - $products = array(); - $collection = $this->getRelatedProductCollection(); - foreach ($collection as $product) { - $products[] = $product; - } - $this->setRelatedProducts($products); - } - return $this->getData('related_products'); - } - - /** - * Retrieve related products identifiers - * - * @return array - */ - public function getRelatedProductIds() - { - if (!$this->hasRelatedProductIds()) { - $ids = array(); - foreach ($this->getRelatedProducts() as $product) { - $ids[] = $product->getId(); - } - $this->setRelatedProductIds($ids); - } - return $this->getData('related_product_ids'); - } - - /** - * Retrieve collection related product - */ - public function getRelatedProductCollection() - { - $collection = $this->getLinkInstance()->useRelatedLinks() - ->getProductCollection() - ->setIsStrongMode(); - $collection->setProduct($this); - return $collection; - } - - /** - * Retrieve collection related link - */ - public function getRelatedLinkCollection() - { - $collection = $this->getLinkInstance()->useRelatedLinks() - ->getLinkCollection(); - $collection->setProduct($this); - $collection->addLinkTypeIdFilter(); - $collection->addProductIdFilter(); - $collection->joinAttributes(); - return $collection; - } - - /** - * Retrieve array of up sell products - * - * @return array - */ - public function getUpSellProducts() - { - if (!$this->hasUpSellProducts()) { - $products = array(); - foreach ($this->getUpSellProductCollection() as $product) { - $products[] = $product; - } - $this->setUpSellProducts($products); - } - return $this->getData('up_sell_products'); - } - - /** - * Retrieve up sell products identifiers - * - * @return array - */ - public function getUpSellProductIds() - { - if (!$this->hasUpSellProductIds()) { - $ids = array(); - foreach ($this->getUpSellProducts() as $product) { - $ids[] = $product->getId(); - } - $this->setUpSellProductIds($ids); - } - return $this->getData('up_sell_product_ids'); - } - - /** - * Retrieve collection up sell product - */ - public function getUpSellProductCollection() - { - $collection = $this->getLinkInstance()->useUpSellLinks() - ->getProductCollection() - ->setIsStrongMode(); - $collection->setProduct($this); - return $collection; - } - - /** - * Retrieve collection up sell link - */ - public function getUpSellLinkCollection() - { - $collection = $this->getLinkInstance()->useUpSellLinks() - ->getLinkCollection(); - $collection->setProduct($this); - $collection->addLinkTypeIdFilter(); - $collection->addProductIdFilter(); - $collection->joinAttributes(); - return $collection; - } - - /** - * Retrieve array of cross sell products - * - * @return array - */ - public function getCrossSellProducts() - { - if (!$this->hasCrossSellProducts()) { - $products = array(); - foreach ($this->getCrossSellProductCollection() as $product) { - $products[] = $product; - } - $this->setCrossSellProducts($products); - } - return $this->getData('cross_sell_products'); - } - - /** - * Retrieve cross sell products identifiers - * - * @return array - */ - public function getCrossSellProductIds() - { - if (!$this->hasCrossSellProductIds()) { - $ids = array(); - foreach ($this->getCrossSellProducts() as $product) { - $ids[] = $product->getId(); - } - $this->setCrossSellProductIds($ids); - } - return $this->getData('cross_sell_product_ids'); - } - - /** - * Retrieve collection cross sell product - * - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection - */ - public function getCrossSellProductCollection() - { - $collection = $this->getLinkInstance()->useCrossSellLinks() - ->getProductCollection() - ->setIsStrongMode(); - $collection->setProduct($this); - return $collection; - } - - /** - * Retrieve collection cross sell link - */ - public function getCrossSellLinkCollection() - { - $collection = $this->getLinkInstance()->useCrossSellLinks() - ->getLinkCollection(); - $collection->setProduct($this); - $collection->addLinkTypeIdFilter(); - $collection->addProductIdFilter(); - $collection->joinAttributes(); - return $collection; - } - - /** - * Retrieve collection grouped link - */ - public function getGroupedLinkCollection() - { - $collection = $this->getLinkInstance()->useGroupedLinks() - ->getLinkCollection(); - $collection->setProduct($this); - $collection->addLinkTypeIdFilter(); - $collection->addProductIdFilter(); - $collection->joinAttributes(); - return $collection; - } - -/******************************************************************************* - ** Media API - */ - /** - * Retrive attributes for media gallery - * - * @return array - */ - public function getMediaAttributes() - { - if (!$this->hasMediaAttributes()) { - $mediaAttributes = array(); - foreach ($this->getAttributes() as $attribute) { - if($attribute->getFrontend()->getInputType() == 'media_image') { - $mediaAttributes[$attribute->getAttributeCode()] = $attribute; - } - } - $this->setMediaAttributes($mediaAttributes); - } - return $this->getData('media_attributes'); - } - - /** - * Retrive media gallery images - * - * @return Varien_Data_Collection - */ - public function getMediaGalleryImages() - { - if(!$this->hasData('media_gallery_images') && is_array($this->getMediaGallery('images'))) { - $images = new Varien_Data_Collection(); - foreach ($this->getMediaGallery('images') as $image) { - if ($image['disabled']) { - continue; - } - $image['url'] = $this->getMediaConfig()->getMediaUrl($image['file']); - $image['id'] = isset($image['value_id']) ? $image['value_id'] : null; - $image['path'] = $this->getMediaConfig()->getMediaPath($image['file']); - $images->addItem(new Varien_Object($image)); - } - $this->setData('media_gallery_images', $images); - } - - return $this->getData('media_gallery_images'); - } - - /** - * Add image to media gallery - * - * @param string $file file path of image in file system - * @param string|array $mediaAttribute code of attribute with type 'media_image', - * leave blank if image should be only in gallery - * @param boolean $move if true, it will move source file - * @param boolean $exclude mark image as disabled in product page view - */ - public function addImageToMediaGallery($file, $mediaAttribute=null, $move=false, $exclude=true) - { - $attributes = $this->getTypeInstance()->getSetAttributes(); - if (!isset($attributes['media_gallery'])) { - return $this; - } - $mediaGalleryAttribute = $attributes['media_gallery']; - /* @var $mediaGalleryAttribute Mage_Catalog_Model_Resource_Eav_Attribute */ - $mediaGalleryAttribute->getBackend()->addImage($this, $file, $mediaAttribute, $move, $exclude); - return $this; - } - - /** - * Retrive product media config - * - * @return Mage_Catalog_Model_Product_Media_Config - */ - public function getMediaConfig() - { - return Mage::getSingleton('catalog/product_media_config'); - } - - /** - * Create duplicate - * - * @return Mage_Catalog_Model_Product - */ - public function duplicate() - { - $this->getWebsiteIds(); - $this->getCategoryIds(); - - $newProduct = Mage::getModel('catalog/product')->setData($this->getData()) - ->setIsDuplicate(true) - ->setOriginalId($this->getId()) - ->setSku(null) - ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED) - ->setCreatedAt(null) - ->setUpdatedAt(null) - ->setId(null) - ->setStoreId(Mage::app()->getStore()->getId()); - - Mage::dispatchEvent('catalog_model_product_duplicate', array('current_product'=>$this, 'new_product'=>$newProduct)); - - /* @var $newProduct Mage_Catalog_Model_Product */ - -// $newOptionsArray = array(); -// $newProduct->setCanSaveCustomOptions(true); -// foreach ($this->getOptions() as $_option) { -// /* @var $_option Mage_Catalog_Model_Product_Option */ -// $newOptionsArray[] = $_option->prepareOptionForDuplicate(); -// } -// $newProduct->setProductOptions($newOptionsArray); - - /* Prepare Related*/ - $data = array(); - $this->getLinkInstance()->useRelatedLinks(); - $attributes = array(); - foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { - if (isset($_attribute['code'])) { - $attributes[]=$_attribute['code']; - } - } - foreach ($this->getRelatedLinkCollection() as $_link) { - $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); - } - $newProduct->setRelatedLinkData($data); - - /* Prepare UpSell*/ - $data = array(); - $this->getLinkInstance()->useUpSellLinks(); - $attributes = array(); - foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { - if (isset($_attribute['code'])) { - $attributes[]=$_attribute['code']; - } - } - foreach ($this->getUpSellLinkCollection() as $_link) { - $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); - } - $newProduct->setUpSellLinkData($data); - - /* Prepare Cross Sell */ - $data = array(); - $this->getLinkInstance()->useCrossSellLinks(); - $attributes = array(); - foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { - if (isset($_attribute['code'])) { - $attributes[]=$_attribute['code']; - } - } - foreach ($this->getCrossSellLinkCollection() as $_link) { - $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); - } - $newProduct->setCrossSellLinkData($data); - - /* Prepare Grouped */ - $data = array(); - $this->getLinkInstance()->useGroupedLinks(); - $attributes = array(); - foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { - if (isset($_attribute['code'])) { - $attributes[]=$_attribute['code']; - } - } - foreach ($this->getGroupedLinkCollection() as $_link) { - $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); - } - $newProduct->setGroupedLinkData($data); - - $newProduct->save(); - - $this->getOptionInstance()->duplicate($this->getId(), $newProduct->getId()); - $this->getResource()->duplicate($this->getId(), $newProduct->getId()); - - // TODO - duplicate product on all stores of the websites it is associated with - /*if ($storeIds = $this->getWebsiteIds()) { - foreach ($storeIds as $storeId) { - $this->setStoreId($storeId) - ->load($this->getId()); - - $newProduct->setData($this->getData()) - ->setSku(null) - ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED) - ->setId($newId) - ->save(); - } - }*/ - return $newProduct; - } - - public function isSuperGroup() - { - return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED; - } - - public function isSuperConfig() - { - return $this->isConfigurable(); - } - /** - * Check is product grouped - * - * @return bool - */ - public function isGrouped() - { - return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED; - } - - /** - * Check is product configurable - * - * @return bool - */ - public function isConfigurable() - { - return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE; - } - - public function isSuper() - { - return $this->isConfigurable() || $this->isGrouped(); - } - - public function getVisibleInCatalogStatuses() - { - return Mage::getSingleton('catalog/product_status')->getVisibleStatusIds(); - } - - public function isVisibleInCatalog() - { - return in_array($this->getStatus(), $this->getVisibleInCatalogStatuses()); - } - - public function getVisibleInSiteVisibilities() - { - return Mage::getSingleton('catalog/product_visibility')->getVisibleInSiteIds(); - } - - public function isVisibleInSiteVisibility() - { - return in_array($this->getVisibility(), $this->getVisibleInSiteVisibilities()); - } - - /** - * Check is product available for sale - * - * @return bool - */ - public function isSalable() - { - return $this->getTypeInstance()->isSalable(); - } - - public function isSaleable() - { - return $this->isSalable(); - } - - public function isInStock() - { - return $this->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED; - } - - public function getAttributeText($attributeCode) - { - return $this->getResource() - ->getAttribute($attributeCode) - ->getSource() - ->getOptionText($this->getData($attributeCode)); - } - - public function getCustomDesignDate() - { - $result = array(); - $result['from'] = $this->getData('custom_design_from'); - $result['to'] = $this->getData('custom_design_to'); - - return $result; - } - - /** - * Get product url - * - * @param bool $useSid - * @return string - */ - public function getProductUrl($useSid = true) - { - return $this->_urlModel->getProductUrl($this, $useSid); - } - - public function formatUrlKey($str) - { - return $this->_urlModel->formatUrlKey($str); - } - - /** - * Retrieve Product Url Path (include category) - * - * @param Mage_Catalog_Model_Category $category - * @return string - */ - public function getUrlPath($category=null) - { - return $this->_urlModel->getUrlPath($this, $category); - } - - public function addAttributeUpdate($code, $value, $store) - { - $oldValue = $this->getData($code); - $oldStore = $this->getStoreId(); - - $this->setData($code, $value); - $this->setStoreId($store); - $this->getResource()->saveAttribute($this, $code); - - $this->setData($code, $oldValue); - $this->setStoreId($oldStore); - } - - public function toArray(array $arrAttributes=array()) - { - $data = parent::toArray($arrAttributes); - if ($stock = $this->getStockItem()) { - $data['stock_item'] = $stock->toArray(); - } - unset($data['stock_item']['product']); - return $data; - } - - public function fromArray($data) - { - if (isset($data['stock_item'])) { - $stockItem = Mage::getModel('cataloginventory/stock_item') - ->setData($data['stock_item']) - ->setProduct($this); - $this->setStockItem($stockItem); - unset($data['stock_item']); - } - $this->setData($data); - return $this; - } - - public function loadParentProductIds() - { - return $this->setParentProductIds($this->_getResource()->getParentProductIds($this)); - } - - public function delete() - { - parent::delete(); - Mage::dispatchEvent($this->_eventPrefix.'_delete_after_done', array($this->_eventObject=>$this)); - return $this; - } - - public function getRequestPath() - { - return $this->_getData('request_path'); - } - - /** - * Custom function for other modules - */ - - public function getGiftMessageAvailable() - { - return $this->_getData('gift_message_available'); - } - - public function getRatingSummary() - { - return $this->_getData('rating_summary'); - } - - /** - * Check is product composite - * - * @return bool - */ - public function isComposite() - { - return $this->getTypeInstance()->isComposite(); - } - - /** - * Retrieve sku through type instance - * - * @return string - */ - public function getSku() - { - return $this->getTypeInstance()->getSku(); - } - - /** - * Retrieve weight throught type instance - * - * @return unknown - */ - public function getWeight() - { - return $this->getTypeInstance()->getWeight(); - } - - /** - * Retrieve option instance - * - * @return Mage_Catalog_Model_Product_Option - */ - public function getOptionInstance() - { - if (!$this->_optionInstance) { - $this->_optionInstance = Mage::getSingleton('catalog/product_option'); - } - return $this->_optionInstance; - } - - /** - * Retrieve options collection of product - * - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Option_Collection - */ - public function getProductOptionsCollection() - { - $collection = $this->getOptionInstance() - ->getProductOptionCollection($this); - - return $collection; - } - - /** - * Add option to array of product options - * - * @param Mage_Catalog_Model_Product_Option $option - * @return Mage_Catalog_Model_Product - */ - public function addOption(Mage_Catalog_Model_Product_Option $option) - { - $this->_options[$option->getId()] = $option; - return $this; - } - - /** - * Get option from options array of product by given option id - * - * @param int $optionId - * @return Mage_Catalog_Model_Product_Option | null - */ - public function getOptionById($optionId) - { - if (isset($this->_options[$optionId])) { - return $this->_options[$optionId]; - } - - return null; - } - - /** - * Get all options of product - * - * @return array - */ - public function getOptions() - { - return $this->_options; - } - - /** - * Retrieve is a virtual product - * - * @return bool - */ - public function getIsVirtual() - { - return $this->getTypeInstance()->isVirtual(); - } - - /** - * Add custom option information to product - * - * @param string $code - * @param mixed $value - * @param int $productId - * @return Mage_Catalog_Model_Product - */ - public function addCustomOption($code, $value, $product=null) - { - $product = $product ? $product : $this; - $this->_customOptions[$code] = new Varien_Object(array( - 'product_id'=> $product->getId(), - 'product' => $product, - 'code' => $code, - 'value' => $value, - )); - return $this; - } - - public function setCustomOptions(array $options) - { - $this->_customOptions = $options; - } - - /** - * Get all custom options of the product - * - * @return array - */ - public function getCustomOptions() - { - return $this->_customOptions; - } - - /** - * Get product custom option info - * - * @param string $code - * @return array - */ - public function getCustomOption($code) - { - if (isset($this->_customOptions[$code])) { - return $this->_customOptions[$code]; - } - return null; - } - - /** - * Checks if there custom option for this product - * - * @return bool - */ - public function hasCustomOptions() - { - if (count($this->_customOptions)) { - return true; - } else { - return false; - } - } - - /** - * Check availability display product in category - * - * @param int $categoryId - * @return bool - */ - public function canBeShowInCategory($categoryId) - { - return $this->_getResource()->canBeShowInCategory($this, $categoryId); - } - - - public function getAvailableInCategories() - { - $allCategories = array(); - if (is_null($this->getData('_available_in_categories'))) { - $assigned = $this->getCategoryIds(); - foreach ($assigned as $one) { - $allCategories[] = $one; - $anchors = Mage::getModel('catalog/category')->load($one)->getAnchorsAbove(); - foreach ($anchors as $anchor) { - $allCategories[] = $anchor; - } - } - - $this->setData('_available_in_categories', $allCategories); - } - return $this->getData('_available_in_categories'); - } - - - /** - * Retrieve default attribute set id - * - * @return int - */ - public function getDefaultAttributeSetId() - { - return $this->getResource()->getEntityType()->getDefaultAttributeSetId(); - } - - - /** - * Deprecated since 1.1.5 - */ - public function getImageUrl() - { - return (string)Mage::helper('catalog/image')->init($this, 'image')->resize(265); - } - - /** - * Deprecated since 1.1.5 - */ - public function getSmallImageUrl($width = 88, $height = 77) - { - return (string)Mage::helper('catalog/image')->init($this, 'small_image')->resize($width, $height); - } - - /** - * Deprecated since 1.1.5 - */ - public function getThumbnailUrl($width = 75, $height = 75) - { - return (string)Mage::helper('catalog/image')->init($this, 'thumbnail')->resize($width, $height); - } - - /** - * Returns system reserved attribute codes - * - * @param none - * @return array Reserved attribute names - */ - public function getReservedAttributes() - { - if ($this->_reservedAttributes === null) { - $_reserved = array(); - $methods = get_class_methods(__CLASS__); - foreach ($methods as $method) { - if (preg_match('/^get([A-Z]{1}.+)/', $method, $matches)) { - $method = $matches[1]; - $tmp = strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $method)); - $_reserved[] = $tmp; - } - } - $_allowed = array( - 'type_id','calculated_final_price','request_path','rating_summary' - ); - $this->_reservedAttributes = array_diff($_reserved, $_allowed); - } - return $this->_reservedAttributes; - } - - /** - * Check whether attribute reserved or not - * - * @param Mage_Eav_Model_Entity_Attribute $attribute Attribute model object - * @return boolean - */ - public function isReservedAttribute ($attribute) - { - return $attribute->getIsUserDefined() - && in_array($attribute->getAttributeCode(), $this->getReservedAttributes()); - } -} + + */ +class Mage_Catalog_Model_Product extends Mage_Catalog_Model_Abstract +{ + const CACHE_TAG = 'catalog_product'; + protected $_cacheTag = 'catalog_product'; + protected $_eventPrefix = 'catalog_product'; + protected $_eventObject = 'product'; + protected $_canAffectOptions = false; + + /** + * Product type instance + * + * @var Mage_Catalog_Model_Product_Type_Abstract + */ + protected $_typeInstance = null; + + /** + * Product link instance + * + * @var Mage_Catalog_Model_Product_Link + */ + protected $_linkInstance; + + /** + * Product object customization (not stored in DB) + * + * @var array + */ + protected $_customOptions = array(); + + /** + * Product Url Instance + * + * @var Mage_Catalog_Model_Product_Url + */ + protected $_urlModel = null; + + protected static $_url; + protected static $_urlRewrite; + + protected $_errors = array(); + + protected $_optionInstance; + + protected $_options = array(); + + /** + * Product reserved attribute codes + */ + protected $_reservedAttributes; + + /** + * Initialize resources + */ + protected function _construct() + { + $this->_urlModel = Mage::getSingleton('catalog/product_url'); + $this->_init('catalog/product'); + } + + public function validate() + { + $this->_getResource()->validate($this); + return $this; + } + + public function getName() + { + return $this->_getData('name'); + } + + /** + * Get product price throught type instance + * + * @return unknown + */ + public function getPrice() + { + return $this->getPriceModel()->getPrice($this); + } + + public function getTypeId() + { + return $this->_getData('type_id'); + } + + public function getStatus() + { + return $this->_getData('status'); + } + + /** + * Retrieve type instance + * + * Type instance implement type depended logic + * + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function getTypeInstance() + { + if ($this->_typeInstance === null) { + $this->_typeInstance = Mage::getSingleton('catalog/product_type')->factory($this); + } + return $this->_typeInstance; + } + + public function setTypeInstance($instance) + { + $this->_typeInstance = $instance; + return $this; + } + + /** + * Retrieve type instance + * + * @return Mage_Catalog_Model_Product_Link + */ + public function getLinkInstance() + { + if (!$this->_linkInstance) { + $this->_linkInstance = Mage::getSingleton('catalog/product_link'); + } + return $this->_linkInstance; + } + + /** + * Retrive product id by sku + * + * @param string $sku + * @return integer + */ + public function getIdBySku($sku) + { + return $this->_getResource()->getIdBySku($sku); + } + + /** + * Retrieve product category id + * + * @return int + */ + public function getCategoryId() + { + if ($category = Mage::registry('current_category')) { + return $category->getId(); + } + return false; + } + + /** + * Retrieve product category + * + * @return Mage_Catalog_Model_Category + */ + public function getCategory() + { + $category = $this->getData('category'); + if (is_null($category) && $this->getCategoryId()) { + $category = Mage::getModel('catalog/category')->load($this->getCategoryId()); + $this->setCategory($category); + } + return $category; + } + + public function setCategoryIds($ids) + { + if (is_string($ids)) { + $ids = explode(',', $ids); + } elseif (!is_array($ids)) { + Mage::throwException(Mage::helper('catalog')->__('Invalid category IDs')); + } + foreach ($ids as $i=>$v) { + if (empty($v)) { + unset($ids[$i]); + } + } + $this->setData('category_ids', $ids); + return $this; + } + + public function getCategoryIds() + { + if ($this->hasData('category_ids')) { + $ids = $this->getData('category_ids'); + if (!is_array($ids)) { + $ids = !empty($ids) ? explode(',', $ids) : array(); + $this->setData('category_ids', $ids); + } + } else { + $ids = $this->_getResource()->getCategoryIds($this); + $this->setData('category_ids', $ids); + } + return $this->getData('category_ids'); + } + + /** + * Retrieve product categories + * + * @return Varien_Data_Collection + */ + public function getCategoryCollection() + { + return $this->getResource()->getCategoryCollection($this); + } + + /** + * Retrieve product websites identifiers + * + * @return array + */ + public function getWebsiteIds() + { + if (!$this->hasWebsiteIds()) { + $ids = $this->_getResource()->getWebsiteIds($this); + $this->setWebsiteIds($ids); + } + return $this->getData('website_ids'); + } + + public function getStoreIds() + { + if (!$this->hasStoreIds()) { + $storeIds = array(); + if ($websiteIds = $this->getWebsiteIds()) { + foreach ($websiteIds as $websiteId) { + $websiteStores = Mage::app()->getWebsite($websiteId)->getStoreIds(); + $storeIds = array_merge($storeIds, $websiteStores); + } + } + $this->setStoreIds($storeIds); + } + return $this->getData('store_ids'); + } + + /** + * Retrieve product attributes + * + * if $groupId is null - retrieve all product attributes + * + * @param int $groupId + * @return array + */ + public function getAttributes($groupId = null, $skipSuper=false) + { + $productAttributes = $this->getTypeInstance()->getEditableAttributes(); + if ($groupId) { + $attributes = array(); + foreach ($productAttributes as $attribute) { + if ($attribute->isInGroup($this->getAttributeSetId(), $groupId)) { + $attributes[] = $attribute; + } + } + } + else { + $attributes = $productAttributes; + } + + return $attributes; + } + + /** + * Check product options and type options and save them, too + * + */ + protected function _beforeSave() + { + $this->cleanCache(); + $this->setTypeHasOptions(false); + $this->setTypeHasRequiredOptions(false); + + $this->getTypeInstance()->beforeSave(); + + $hasOptions = false; + $hasRequiredOptions = false; + + $this->canAffectOptions($this->_canAffectOptions || $this->getCanSaveCustomOptions()); + if ($this->getCanSaveCustomOptions()) { + $options = $this->getProductOptions(); + if (is_array($options)) { + foreach ($this->getProductOptions() as $option) { + $this->getOptionInstance()->addOption($option); + if ((!isset($option['is_delete'])) || $option['is_delete'] != '1') { + $hasOptions = true; + } + } + foreach ($this->getOptionInstance()->getOptions() as $option) { + if ($option['is_require'] == '1') { + $hasRequiredOptions = true; + break; + } + } + } + } + + /** + * Set true, if any + * Set false, ONLY if options have been affected by Options tab and Type instance tab + */ + if ($hasOptions || (bool)$this->getTypeHasOptions()) { + $this->setHasOptions(true); + if ($hasRequiredOptions || (bool)$this->getTypeHasRequiredOptions()) { + $this->setRequiredOptions(true); + } + elseif ($this->canAffectOptions()) { + $this->setRequiredOptions(false); + } + } + elseif ($this->canAffectOptions()) { + $this->setHasOptions(false); + $this->setRequiredOptions(false); + } + parent::_beforeSave(); + } + + /** + * Check/set if options can be affected when saving product + * If value specified, it will be set. + * + * @param bool $value + * @return bool + */ + public function canAffectOptions($value = null) + { + if (null !== $value) { + $this->_canAffectOptions = (bool)$value; + } + return $this->_canAffectOptions; + } + + /** + * Saving product type related data + * + * @return unknown + */ + protected function _afterSave() + { + $this->getLinkInstance()->saveProductRelations($this); + $this->getTypeInstance()->save(); + + /** + * Product Custom Options + */ + /* @var $optionModel Mage_Catalog_Model_Product_Option */ +// $options = $this->getProductOptions(); +// if (is_array($options)) { +// foreach ($this->getProductOptions() as $option) { +// $this->getOptionInstance()->addOption($option); +// } +// } + $this->getOptionInstance()->setProduct($this) + ->saveOptions(); + + parent::_afterSave(); + } + + protected function _beforeDelete() + { + $this->cleanCache(); + $this->_protectFromNonAdmin(); + return parent::_beforeDelete(); + } + + /** + * deprecated + * @see Mage_Sales_Model_Observer::substractQtyFromQuotes() + */ + protected function _substractQtyFromQuotes() + { + // kept for legacy purposes + } + + protected function _afterLoad() + { + parent::_afterLoad(); + foreach ($this->getProductOptionsCollection() as $option) { + $option->setProduct($this); + $this->addOption($option); + } + + return $this; + } + + public function cleanCache() + { + Mage::app()->cleanCache('catalog_product_'.$this->getId()); + } + + public function getPriceModel() + { + return Mage::getSingleton('catalog/product_type')->priceFactory($this->getTypeId()); + } + + /** + * Get product tier price by qty + * + * @param double $qty + * @return double + */ + public function getTierPrice($qty=null) + { + return $this->getPriceModel()->getTierPrice($qty, $this); + } + + /** + * Count how many tier prices we have for the product + * + * @return int + */ + public function getTierPriceCount() + { + return $this->getPriceModel()->getTierPriceCount($this); + } + + /** + * Get formated by currency tier price + * + * @param double $qty + * @return array || double + */ + public function getFormatedTierPrice($qty=null) + { + return $this->getPriceModel()->getFormatedTierPrice($qty, $this); + } + + /** + * Get formated by currency product price + * + * @return array || double + */ + public function getFormatedPrice() + { + return $this->getPriceModel()->getFormatedPrice($this); + } + + /** + * Get product final price + * + * @param double $qty + * @return double + */ + public function getFinalPrice($qty=null) + { + return $this->getPriceModel()->getFinalPrice($qty, $this); + } + + public function getCalculatedFinalPrice() + { + return $this->_getData('calculated_final_price'); + } + + public function getMinimalPrice() + { + return $this->_getData('minimal_price'); + } + + public function getSpecialPrice() + { + return $this->_getData('special_price'); + } + + public function getSpecialFromDate() + { + return $this->_getData('special_from_date'); + } + + public function getSpecialToDate() + { + return $this->_getData('special_to_date'); + } + + +/******************************************************************************* + ** Linked products API + */ + /** + * Retrieve array of related roducts + * + * @return array + */ + public function getRelatedProducts() + { + if (!$this->hasRelatedProducts()) { + $products = array(); + $collection = $this->getRelatedProductCollection(); + foreach ($collection as $product) { + $products[] = $product; + } + $this->setRelatedProducts($products); + } + return $this->getData('related_products'); + } + + /** + * Retrieve related products identifiers + * + * @return array + */ + public function getRelatedProductIds() + { + if (!$this->hasRelatedProductIds()) { + $ids = array(); + foreach ($this->getRelatedProducts() as $product) { + $ids[] = $product->getId(); + } + $this->setRelatedProductIds($ids); + } + return $this->getData('related_product_ids'); + } + + /** + * Retrieve collection related product + */ + public function getRelatedProductCollection() + { + $collection = $this->getLinkInstance()->useRelatedLinks() + ->getProductCollection() + ->setIsStrongMode(); + $collection->setProduct($this); + return $collection; + } + + /** + * Retrieve collection related link + */ + public function getRelatedLinkCollection() + { + $collection = $this->getLinkInstance()->useRelatedLinks() + ->getLinkCollection(); + $collection->setProduct($this); + $collection->addLinkTypeIdFilter(); + $collection->addProductIdFilter(); + $collection->joinAttributes(); + return $collection; + } + + /** + * Retrieve array of up sell products + * + * @return array + */ + public function getUpSellProducts() + { + if (!$this->hasUpSellProducts()) { + $products = array(); + foreach ($this->getUpSellProductCollection() as $product) { + $products[] = $product; + } + $this->setUpSellProducts($products); + } + return $this->getData('up_sell_products'); + } + + /** + * Retrieve up sell products identifiers + * + * @return array + */ + public function getUpSellProductIds() + { + if (!$this->hasUpSellProductIds()) { + $ids = array(); + foreach ($this->getUpSellProducts() as $product) { + $ids[] = $product->getId(); + } + $this->setUpSellProductIds($ids); + } + return $this->getData('up_sell_product_ids'); + } + + /** + * Retrieve collection up sell product + */ + public function getUpSellProductCollection() + { + $collection = $this->getLinkInstance()->useUpSellLinks() + ->getProductCollection() + ->setIsStrongMode(); + $collection->setProduct($this); + return $collection; + } + + /** + * Retrieve collection up sell link + */ + public function getUpSellLinkCollection() + { + $collection = $this->getLinkInstance()->useUpSellLinks() + ->getLinkCollection(); + $collection->setProduct($this); + $collection->addLinkTypeIdFilter(); + $collection->addProductIdFilter(); + $collection->joinAttributes(); + return $collection; + } + + /** + * Retrieve array of cross sell products + * + * @return array + */ + public function getCrossSellProducts() + { + if (!$this->hasCrossSellProducts()) { + $products = array(); + foreach ($this->getCrossSellProductCollection() as $product) { + $products[] = $product; + } + $this->setCrossSellProducts($products); + } + return $this->getData('cross_sell_products'); + } + + /** + * Retrieve cross sell products identifiers + * + * @return array + */ + public function getCrossSellProductIds() + { + if (!$this->hasCrossSellProductIds()) { + $ids = array(); + foreach ($this->getCrossSellProducts() as $product) { + $ids[] = $product->getId(); + } + $this->setCrossSellProductIds($ids); + } + return $this->getData('cross_sell_product_ids'); + } + + /** + * Retrieve collection cross sell product + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection + */ + public function getCrossSellProductCollection() + { + $collection = $this->getLinkInstance()->useCrossSellLinks() + ->getProductCollection() + ->setIsStrongMode(); + $collection->setProduct($this); + return $collection; + } + + /** + * Retrieve collection cross sell link + */ + public function getCrossSellLinkCollection() + { + $collection = $this->getLinkInstance()->useCrossSellLinks() + ->getLinkCollection(); + $collection->setProduct($this); + $collection->addLinkTypeIdFilter(); + $collection->addProductIdFilter(); + $collection->joinAttributes(); + return $collection; + } + + /** + * Retrieve collection grouped link + */ + public function getGroupedLinkCollection() + { + $collection = $this->getLinkInstance()->useGroupedLinks() + ->getLinkCollection(); + $collection->setProduct($this); + $collection->addLinkTypeIdFilter(); + $collection->addProductIdFilter(); + $collection->joinAttributes(); + return $collection; + } + +/******************************************************************************* + ** Media API + */ + /** + * Retrive attributes for media gallery + * + * @return array + */ + public function getMediaAttributes() + { + if (!$this->hasMediaAttributes()) { + $mediaAttributes = array(); + foreach ($this->getAttributes() as $attribute) { + if($attribute->getFrontend()->getInputType() == 'media_image') { + $mediaAttributes[$attribute->getAttributeCode()] = $attribute; + } + } + $this->setMediaAttributes($mediaAttributes); + } + return $this->getData('media_attributes'); + } + + /** + * Retrive media gallery images + * + * @return Varien_Data_Collection + */ + public function getMediaGalleryImages() + { + if(!$this->hasData('media_gallery_images') && is_array($this->getMediaGallery('images'))) { + $images = new Varien_Data_Collection(); + foreach ($this->getMediaGallery('images') as $image) { + if ($image['disabled']) { + continue; + } + $image['url'] = $this->getMediaConfig()->getMediaUrl($image['file']); + $image['id'] = isset($image['value_id']) ? $image['value_id'] : null; + $image['path'] = $this->getMediaConfig()->getMediaPath($image['file']); + $images->addItem(new Varien_Object($image)); + } + $this->setData('media_gallery_images', $images); + } + + return $this->getData('media_gallery_images'); + } + + /** + * Add image to media gallery + * + * @param string $file file path of image in file system + * @param string|array $mediaAttribute code of attribute with type 'media_image', + * leave blank if image should be only in gallery + * @param boolean $move if true, it will move source file + * @param boolean $exclude mark image as disabled in product page view + */ + public function addImageToMediaGallery($file, $mediaAttribute=null, $move=false, $exclude=true) + { + $attributes = $this->getTypeInstance()->getSetAttributes(); + if (!isset($attributes['media_gallery'])) { + return $this; + } + $mediaGalleryAttribute = $attributes['media_gallery']; + /* @var $mediaGalleryAttribute Mage_Catalog_Model_Resource_Eav_Attribute */ + $mediaGalleryAttribute->getBackend()->addImage($this, $file, $mediaAttribute, $move, $exclude); + return $this; + } + + /** + * Retrive product media config + * + * @return Mage_Catalog_Model_Product_Media_Config + */ + public function getMediaConfig() + { + return Mage::getSingleton('catalog/product_media_config'); + } + + /** + * Create duplicate + * + * @return Mage_Catalog_Model_Product + */ + public function duplicate() + { + $this->getWebsiteIds(); + $this->getCategoryIds(); + + $newProduct = Mage::getModel('catalog/product')->setData($this->getData()) + ->setIsDuplicate(true) + ->setOriginalId($this->getId()) + ->setSku(null) + ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED) + ->setCreatedAt(null) + ->setUpdatedAt(null) + ->setId(null) + ->setStoreId(Mage::app()->getStore()->getId()); + + Mage::dispatchEvent('catalog_model_product_duplicate', array('current_product'=>$this, 'new_product'=>$newProduct)); + + /* @var $newProduct Mage_Catalog_Model_Product */ + +// $newOptionsArray = array(); +// $newProduct->setCanSaveCustomOptions(true); +// foreach ($this->getOptions() as $_option) { +// /* @var $_option Mage_Catalog_Model_Product_Option */ +// $newOptionsArray[] = $_option->prepareOptionForDuplicate(); +// } +// $newProduct->setProductOptions($newOptionsArray); + + /* Prepare Related*/ + $data = array(); + $this->getLinkInstance()->useRelatedLinks(); + $attributes = array(); + foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { + if (isset($_attribute['code'])) { + $attributes[]=$_attribute['code']; + } + } + foreach ($this->getRelatedLinkCollection() as $_link) { + $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); + } + $newProduct->setRelatedLinkData($data); + + /* Prepare UpSell*/ + $data = array(); + $this->getLinkInstance()->useUpSellLinks(); + $attributes = array(); + foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { + if (isset($_attribute['code'])) { + $attributes[]=$_attribute['code']; + } + } + foreach ($this->getUpSellLinkCollection() as $_link) { + $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); + } + $newProduct->setUpSellLinkData($data); + + /* Prepare Cross Sell */ + $data = array(); + $this->getLinkInstance()->useCrossSellLinks(); + $attributes = array(); + foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { + if (isset($_attribute['code'])) { + $attributes[]=$_attribute['code']; + } + } + foreach ($this->getCrossSellLinkCollection() as $_link) { + $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); + } + $newProduct->setCrossSellLinkData($data); + + /* Prepare Grouped */ + $data = array(); + $this->getLinkInstance()->useGroupedLinks(); + $attributes = array(); + foreach ($this->getLinkInstance()->getAttributes() as $_attribute) { + if (isset($_attribute['code'])) { + $attributes[]=$_attribute['code']; + } + } + foreach ($this->getGroupedLinkCollection() as $_link) { + $data[$_link->getLinkedProductId()] = $_link->toArray($attributes); + } + $newProduct->setGroupedLinkData($data); + + $newProduct->save(); + + $this->getOptionInstance()->duplicate($this->getId(), $newProduct->getId()); + $this->getResource()->duplicate($this->getId(), $newProduct->getId()); + + // TODO - duplicate product on all stores of the websites it is associated with + /*if ($storeIds = $this->getWebsiteIds()) { + foreach ($storeIds as $storeId) { + $this->setStoreId($storeId) + ->load($this->getId()); + + $newProduct->setData($this->getData()) + ->setSku(null) + ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED) + ->setId($newId) + ->save(); + } + }*/ + return $newProduct; + } + + public function isSuperGroup() + { + return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED; + } + + public function isSuperConfig() + { + return $this->isConfigurable(); + } + /** + * Check is product grouped + * + * @return bool + */ + public function isGrouped() + { + return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED; + } + + /** + * Check is product configurable + * + * @return bool + */ + public function isConfigurable() + { + return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE; + } + + public function isSuper() + { + return $this->isConfigurable() || $this->isGrouped(); + } + + public function getVisibleInCatalogStatuses() + { + return Mage::getSingleton('catalog/product_status')->getVisibleStatusIds(); + } + + public function isVisibleInCatalog() + { + return in_array($this->getStatus(), $this->getVisibleInCatalogStatuses()); + } + + public function getVisibleInSiteVisibilities() + { + return Mage::getSingleton('catalog/product_visibility')->getVisibleInSiteIds(); + } + + public function isVisibleInSiteVisibility() + { + return in_array($this->getVisibility(), $this->getVisibleInSiteVisibilities()); + } + + /** + * Check is product available for sale + * + * @return bool + */ + public function isSalable() + { + Mage::dispatchEvent('catalog_product_is_salable_before', array( + 'product' => $this + )); + + $salable = $this->getTypeInstance()->isSalable(); + + $object = new Varien_Object(array( + 'product' => $this, + 'is_salable' => $salable + )); + Mage::dispatchEvent('catalog_product_is_salable_after', array( + 'product' => $this, + 'salable' => $object + )); + return $object->getIsSalable(); + } + + public function isSaleable() + { + return $this->isSalable(); + } + + public function isInStock() + { + return $this->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED; + } + + public function getAttributeText($attributeCode) + { + return $this->getResource() + ->getAttribute($attributeCode) + ->getSource() + ->getOptionText($this->getData($attributeCode)); + } + + public function getCustomDesignDate() + { + $result = array(); + $result['from'] = $this->getData('custom_design_from'); + $result['to'] = $this->getData('custom_design_to'); + + return $result; + } + + /** + * Get product url + * + * @param bool $useSid + * @return string + */ + public function getProductUrl($useSid = true) + { + return $this->_urlModel->getProductUrl($this, $useSid); + } + + public function formatUrlKey($str) + { + return $this->_urlModel->formatUrlKey($str); + } + + /** + * Retrieve Product Url Path (include category) + * + * @param Mage_Catalog_Model_Category $category + * @return string + */ + public function getUrlPath($category=null) + { + return $this->_urlModel->getUrlPath($this, $category); + } + + public function addAttributeUpdate($code, $value, $store) + { + $oldValue = $this->getData($code); + $oldStore = $this->getStoreId(); + + $this->setData($code, $value); + $this->setStoreId($store); + $this->getResource()->saveAttribute($this, $code); + + $this->setData($code, $oldValue); + $this->setStoreId($oldStore); + } + + public function toArray(array $arrAttributes=array()) + { + $data = parent::toArray($arrAttributes); + if ($stock = $this->getStockItem()) { + $data['stock_item'] = $stock->toArray(); + } + unset($data['stock_item']['product']); + return $data; + } + + public function fromArray($data) + { + if (isset($data['stock_item'])) { + $stockItem = Mage::getModel('cataloginventory/stock_item') + ->setData($data['stock_item']) + ->setProduct($this); + $this->setStockItem($stockItem); + unset($data['stock_item']); + } + $this->setData($data); + return $this; + } + + public function loadParentProductIds() + { + return $this->setParentProductIds($this->_getResource()->getParentProductIds($this)); + } + + public function delete() + { + parent::delete(); + Mage::dispatchEvent($this->_eventPrefix.'_delete_after_done', array($this->_eventObject=>$this)); + return $this; + } + + public function getRequestPath() + { + return $this->_getData('request_path'); + } + + /** + * Custom function for other modules + */ + + public function getGiftMessageAvailable() + { + return $this->_getData('gift_message_available'); + } + + public function getRatingSummary() + { + return $this->_getData('rating_summary'); + } + + /** + * Check is product composite + * + * @return bool + */ + public function isComposite() + { + return $this->getTypeInstance()->isComposite(); + } + + /** + * Retrieve sku through type instance + * + * @return string + */ + public function getSku() + { + return $this->getTypeInstance()->getSku(); + } + + /** + * Retrieve weight throught type instance + * + * @return unknown + */ + public function getWeight() + { + return $this->getTypeInstance()->getWeight(); + } + + /** + * Retrieve option instance + * + * @return Mage_Catalog_Model_Product_Option + */ + public function getOptionInstance() + { + if (!$this->_optionInstance) { + $this->_optionInstance = Mage::getSingleton('catalog/product_option'); + } + return $this->_optionInstance; + } + + /** + * Retrieve options collection of product + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Option_Collection + */ + public function getProductOptionsCollection() + { + $collection = $this->getOptionInstance() + ->getProductOptionCollection($this); + + return $collection; + } + + /** + * Add option to array of product options + * + * @param Mage_Catalog_Model_Product_Option $option + * @return Mage_Catalog_Model_Product + */ + public function addOption(Mage_Catalog_Model_Product_Option $option) + { + $this->_options[$option->getId()] = $option; + return $this; + } + + /** + * Get option from options array of product by given option id + * + * @param int $optionId + * @return Mage_Catalog_Model_Product_Option | null + */ + public function getOptionById($optionId) + { + if (isset($this->_options[$optionId])) { + return $this->_options[$optionId]; + } + + return null; + } + + /** + * Get all options of product + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Retrieve is a virtual product + * + * @return bool + */ + public function getIsVirtual() + { + return $this->getTypeInstance()->isVirtual(); + } + + /** + * Add custom option information to product + * + * @param string $code + * @param mixed $value + * @param int $productId + * @return Mage_Catalog_Model_Product + */ + public function addCustomOption($code, $value, $product=null) + { + $product = $product ? $product : $this; + $this->_customOptions[$code] = new Varien_Object(array( + 'product_id'=> $product->getId(), + 'product' => $product, + 'code' => $code, + 'value' => $value, + )); + return $this; + } + + public function setCustomOptions(array $options) + { + $this->_customOptions = $options; + } + + /** + * Get all custom options of the product + * + * @return array + */ + public function getCustomOptions() + { + return $this->_customOptions; + } + + /** + * Get product custom option info + * + * @param string $code + * @return array + */ + public function getCustomOption($code) + { + if (isset($this->_customOptions[$code])) { + return $this->_customOptions[$code]; + } + return null; + } + + /** + * Checks if there custom option for this product + * + * @return bool + */ + public function hasCustomOptions() + { + if (count($this->_customOptions)) { + return true; + } else { + return false; + } + } + + /** + * Check availability display product in category + * + * @param int $categoryId + * @return bool + */ + public function canBeShowInCategory($categoryId) + { + return $this->_getResource()->canBeShowInCategory($this, $categoryId); + } + + + public function getAvailableInCategories() + { + $allCategories = array(); + if (is_null($this->getData('_available_in_categories'))) { + $assigned = $this->getCategoryIds(); + foreach ($assigned as $one) { + $allCategories[] = $one; + $anchors = Mage::getModel('catalog/category')->load($one)->getAnchorsAbove(); + foreach ($anchors as $anchor) { + $allCategories[] = $anchor; + } + } + + $this->setData('_available_in_categories', $allCategories); + } + return $this->getData('_available_in_categories'); + } + + + /** + * Retrieve default attribute set id + * + * @return int + */ + public function getDefaultAttributeSetId() + { + return $this->getResource()->getEntityType()->getDefaultAttributeSetId(); + } + + + /** + * Deprecated since 1.1.5 + */ + public function getImageUrl() + { + return (string)Mage::helper('catalog/image')->init($this, 'image')->resize(265); + } + + /** + * Deprecated since 1.1.5 + */ + public function getSmallImageUrl($width = 88, $height = 77) + { + return (string)Mage::helper('catalog/image')->init($this, 'small_image')->resize($width, $height); + } + + /** + * Deprecated since 1.1.5 + */ + public function getThumbnailUrl($width = 75, $height = 75) + { + return (string)Mage::helper('catalog/image')->init($this, 'thumbnail')->resize($width, $height); + } + + /** + * Returns system reserved attribute codes + * + * @param none + * @return array Reserved attribute names + */ + public function getReservedAttributes() + { + if ($this->_reservedAttributes === null) { + $_reserved = array(); + $methods = get_class_methods(__CLASS__); + foreach ($methods as $method) { + if (preg_match('/^get([A-Z]{1}.+)/', $method, $matches)) { + $method = $matches[1]; + $tmp = strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $method)); + $_reserved[] = $tmp; + } + } + $_allowed = array( + 'type_id','calculated_final_price','request_path','rating_summary' + ); + $this->_reservedAttributes = array_diff($_reserved, $_allowed); + } + return $this->_reservedAttributes; + } + + /** + * Check whether attribute reserved or not + * + * @param Mage_Eav_Model_Entity_Attribute $attribute Attribute model object + * @return boolean + */ + public function isReservedAttribute ($attribute) + { + return $attribute->getIsUserDefined() + && in_array($attribute->getAttributeCode(), $this->getReservedAttributes()); + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Status.php b/app/code/core/Mage/Catalog/Model/Product/Status.php index 272d43c4aa..672e00a59b 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Status.php +++ b/app/code/core/Mage/Catalog/Model/Product/Status.php @@ -1,118 +1,136 @@ - - */ -class Mage_Catalog_Model_Product_Status extends Mage_Core_Model_Abstract -{ - protected function _construct() - { - $this->_init('catalog/product_status'); - } - - const STATUS_ENABLED = 1; - const STATUS_DISABLED = 2; - - public function addVisibleFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection) - { - //$collection->addAttributeToFilter('status', array('in'=>$this->getVisibleStatusIds())); - return $this; - } - - public function addSaleableFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection) - { - //$collection->addAttributeToFilter('status', array('in'=>$this->getSaleableStatusIds())); - return $this; - } - - public function getVisibleStatusIds() - { - return array(self::STATUS_ENABLED); - } - - public function getSaleableStatusIds() - { - return array(self::STATUS_ENABLED); - } - - static public function getOptionArray() - { - return array( - self::STATUS_ENABLED => Mage::helper('catalog')->__('Enabled'), - self::STATUS_DISABLED => Mage::helper('catalog')->__('Disabled') - ); - } - - static public function getAllOption() - { - $options = self::getOptionArray(); - array_unshift($options, array('value'=>'', 'label'=>'')); - return $options; - } - - static public function getAllOptions() - { - $res = array(); - $res[] = array('value'=>'', 'label'=> Mage::helper('catalog')->__('-- Please Select --')); - foreach (self::getOptionArray() as $index => $value) { - $res[] = array( - 'value' => $index, - 'label' => $value - ); - } - return $res; - } - - static public function getOptionText($optionId) - { - $options = self::getOptionArray(); - return isset($options[$optionId]) ? $options[$optionId] : null; - } - - /** - * Update status value for product - * - * @param int $productId - * @param int $storeId - * @param int $value - * @return Mage_Catalog_Model_Product_Status - */ - public function updateProductStatus($productId, $storeId, $value) - { - $this->_getResource()->updateProductStatus($productId, $storeId, $value); - Mage::getResourceModel('catalog/category')->refreshProductIndex( - array(), - array($productId), - $storeId ? array($storeId) : array() - ); - return $this; - } + + */ +class Mage_Catalog_Model_Product_Status extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('catalog/product_status'); + } + + const STATUS_ENABLED = 1; + const STATUS_DISABLED = 2; + + public function addVisibleFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection) + { + //$collection->addAttributeToFilter('status', array('in'=>$this->getVisibleStatusIds())); + return $this; + } + + public function addSaleableFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection) + { + //$collection->addAttributeToFilter('status', array('in'=>$this->getSaleableStatusIds())); + return $this; + } + + public function getVisibleStatusIds() + { + return array(self::STATUS_ENABLED); + } + + public function getSaleableStatusIds() + { + return array(self::STATUS_ENABLED); + } + + static public function getOptionArray() + { + return array( + self::STATUS_ENABLED => Mage::helper('catalog')->__('Enabled'), + self::STATUS_DISABLED => Mage::helper('catalog')->__('Disabled') + ); + } + + static public function getAllOption() + { + $options = self::getOptionArray(); + array_unshift($options, array('value'=>'', 'label'=>'')); + return $options; + } + + static public function getAllOptions() + { + $res = array(); + $res[] = array('value'=>'', 'label'=> Mage::helper('catalog')->__('-- Please Select --')); + foreach (self::getOptionArray() as $index => $value) { + $res[] = array( + 'value' => $index, + 'label' => $value + ); + } + return $res; + } + + static public function getOptionText($optionId) + { + $options = self::getOptionArray(); + return isset($options[$optionId]) ? $options[$optionId] : null; + } + + /** + * Update status value for product + * + * @param int $productId + * @param int $storeId + * @param int $value + * @return Mage_Catalog_Model_Product_Status + */ + public function updateProductStatus($productId, $storeId, $value) + { + $this->_getResource()->updateProductStatus($productId, $storeId, $value); + Mage::getResourceModel('catalog/category')->refreshProductIndex( + array(), + array($productId), + $storeId ? array($storeId) : array() + ); + Mage::dispatchEvent('catalog_product_status_update', array( + 'product_id' => $productId, + 'store_id' => $storeId, + 'status' => $value + )); + return $this; + } + + /** + * Retrieve Product(s) status for store + * Return array where key is product, value - status + * + * @param int|array $productIds + * @param int $storeId + * @return array + */ + public function getProductStatus($productIds, $storeId = null) + { + return $this->getResource()->getProductStatus($productIds, $storeId); + } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Model/Product/Type/Abstract.php b/app/code/core/Mage/Catalog/Model/Product/Type/Abstract.php index 5310767e1d..24d3c26edd 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type/Abstract.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type/Abstract.php @@ -1,581 +1,610 @@ - - */ -abstract class Mage_Catalog_Model_Product_Type_Abstract -{ - - /** - * Product model instance - * - * @var Mage_Catalog_Model_Product - */ - protected $_product; - protected $_typeId; - protected $_setAttributes; - protected $_editableAttributes; - protected $_isComposite = false; - protected $_storeFilter = null; - - const CALCULATE_CHILD = 0; - const CALCULATE_PARENT = 1; - - /** - * values for shipment type (invoice etc) - * - */ - const SHIPMENT_SEPARATELY = 1; - const SHIPMENT_TOGETHER = 0; - - /** - * Specify type instance product - * - * @param Mage_Catalog_Model_Product $product - * @return Mage_Catalog_Model_Product_Type_Abstract - */ - public function setProduct($product) - { - $this->_product = $product; - return $this; - } - - /** - * Specify type identifier - * - * @param string $typeId - * @return Mage_Catalog_Model_Product_Type_Abstract - */ - public function setTypeId($typeId) - { - $this->_typeId = $typeId; - return $this; - } - - /** - * Retrieve catalog product object - * - * @return Mage_Catalog_Model_Product - */ - public function getProduct() - { - return $this->_product; - } - - /** - * Return relation info about used products for specific type instance - * - * @return Varien_Object Object with information data - */ - public function getRelationInfo() - { - return new Varien_Object(); - } - - /** - * Get array of product set attributes - * - * @return array - */ - public function getSetAttributes() - { - if (is_null($this->_setAttributes)) { - $attributes = $this->getProduct()->getResource() - ->loadAllAttributes($this->getProduct()) - ->getAttributesByCode(); - $this->_setAttributes = array(); - foreach ($attributes as $attribute) { - if ($attribute->isInSet($this->getProduct()->getAttributeSetId())) { - $attribute->setDataObject($this->getProduct()); - $this->_setAttributes[$attribute->getAttributeCode()] = $attribute; - } - } - - uasort($this->_setAttributes, array($this, 'attributesCompare')); - } - return $this->_setAttributes; - } - - public function attributesCompare($attribute1, $attribute2) - { - $sortPath = 'attribute_set_info/' . $this->getProduct()->getAttributeSetId() . '/sort'; - $groupSortPath = 'attribute_set_info/' . $this->getProduct()->getAttributeSetId() . '/group_sort'; - - $sort1 = ($attribute1->getData($groupSortPath) * 1000) + ($attribute1->getData($sortPath) * 0.0001); - $sort2 = ($attribute2->getData($groupSortPath) * 1000) + ($attribute2->getData($sortPath) * 0.0001); - - if ($sort1 > $sort2) { - return 1; - } elseif ($sort1 < $sort2) { - return -1; - } - - return 0; - } - - /** - * Retrieve product type attributes - * - * @return array - */ - public function getEditableAttributes() - { - if (is_null($this->_editableAttributes)) { - $this->_editableAttributes = array(); - foreach ($this->getSetAttributes() as $attributeCode => $attribute) { - if (!is_array($attribute->getApplyTo()) - || count($attribute->getApplyTo())==0 - || in_array($this->getProduct()->getTypeId(), $attribute->getApplyTo())) { - $this->_editableAttributes[$attributeCode] = $attribute; - } - } - } - return $this->_editableAttributes; - } - - /** - * Retrieve product attribute by identifier - * - * @param int $attributeId - * @return Mage_Eav_Model_Entity_Attribute_Abstract - */ - public function getAttributeById($attributeId) - { - foreach ($this->getSetAttributes() as $attribute) { - if ($attribute->getId() == $attributeId) { - return $attribute; - } - } - return null; - } - - /** - * Check is virtual product - * - * @return bool - */ - public function isVirtual() - { - return false; - } - - /** - * Check is product available for sale - * - * @return bool - */ - public function isSalable() - { - $salable = $this->getProduct()->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED; - if ($salable && $this->getProduct()->hasData('is_salable')) { - return $this->getProduct()->getData('is_salable'); - } - return $salable; - } - - /** - * Initialize product(s) for add to cart process - * - * @param Varien_Object $buyRequest - * @return array|string - */ - public function prepareForCart(Varien_Object $buyRequest) - { - $product = $this->getProduct(); - /* @var Mage_Catalog_Model_Product $product */ - - // try to add custom options - $options = $this->_prepareOptionsForCart($buyRequest->getOptions()); - if (is_string($options)) { - return $options; - } - // try to found super product configuration - // (if product was buying within grouped product) - $superProductConfig = $buyRequest->getSuperProductConfig(); - if (!empty($superProductConfig['product_id']) - && !empty($superProductConfig['product_type'])) { - $superProductId = (int) $superProductConfig['product_id']; - if ($superProductId) { - if (!$superProduct = Mage::registry('used_super_product_'.$superProductId)) { - $superProduct = Mage::getModel('catalog/product')->load($superProductId); - Mage::register('used_super_product_'.$superProductId, $superProduct); - } - if ($superProduct->getId()) { - $assocProductIds = $superProduct->getTypeInstance()->getAssociatedProductIds(); - if (in_array($product->getId(), $assocProductIds)) { - $productType = $superProductConfig['product_type']; - $product->addCustomOption('product_type', $productType, $superProduct); - - $buyRequest->setData('super_product_config', array( - 'product_type' => $productType, - 'product_id' => $superProduct->getId() - ) - ); - } - } - } - } - - $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData())); - - if ($options) { - $optionIds = array_keys($options); - $product->addCustomOption('option_ids', implode(',', $optionIds)); - foreach ($options as $optionId => $optionValue) { - $product->addCustomOption('option_'.$optionId, $optionValue); - } - } - // set quantity in cart - $product->setCartQty($buyRequest->getQty()); - - return array($product); - } - - /** - * Check custom defined options for product - * - * @param array $options - * @return array || string - */ - protected function _prepareOptionsForCart($options) - { - $newOptions = array(); - - foreach ($this->getProduct()->getOptions() as $_option) { - /* @var $_option Mage_Catalog_Model_Product_Option */ - if (!isset($options[$_option->getId()]) && $_option->getIsRequire() && !$this->getProduct()->getSkipCheckRequiredOption()) { - return Mage::helper('catalog')->__('Please specify the product required option(s)'); - } - if (!isset($options[$_option->getId()])) { - continue; - } - if ($_option->getGroupByType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_TEXT) { - $options[$_option->getId()] = trim($options[$_option->getId()]); - if (strlen($options[$_option->getId()]) == 0 && $_option->getIsRequire()) { - return Mage::helper('catalog')->__('Please specify the product required option(s)'); - } - if (strlen($options[$_option->getId()]) > $_option->getMaxCharacters() && $_option->getMaxCharacters() > 0) { - return Mage::helper('catalog')->__('Length of text is too long'); - } - if (strlen($options[$_option->getId()]) == 0) continue; - } - if ($_option->getGroupByType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_FILE) { - - } - if ($_option->getGroupByType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_DATE) { - - } - - if ($_option->getGroupbyType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { - if (($_option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_DROP_DOWN - || $_option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_RADIO) - && strlen($options[$_option->getId()])== 0) { - continue; - } - $valuesCollection = $_option->getOptionValuesByOptionId( - $options[$_option->getId()], $this->getProduct()->getStoreId() - )->load(); - - if ($valuesCollection->count() != count($options[$_option->getId()])) { - return Mage::helper('catalog')->__('Please specify the product required option(s)'); - } - } - if (is_array($options[$_option->getId()])) { - $options[$_option->getId()] = implode(',', $options[$_option->getId()]); - } - $newOptions[$_option->getId()] = $options[$_option->getId()]; - } - - return $newOptions; - } - - /** - * Check if product can be bought - * - * @return Mage_Catalog_Model_Product_Type_Abstract - * @throws Mage_Core_Exception - */ - public function checkProductBuyState() - { - if (!$this->getProduct()->getSkipCheckRequiredOption()) { - foreach ($this->getProduct()->getOptions() as $option) { - if ($option->getIsRequire() && (!$this->getProduct()->getCustomOption('option_'.$option->getId()) - || strlen($this->getProduct()->getCustomOption('option_'.$option->getId())->getValue()) == 0)) { - Mage::throwException( - Mage::helper('catalog')->__('Product has required options') - ); - break; - } - } - } - - return $this; - } - - /** - * Prepare additional options/information for order item which will be - * created from this product - * - * @return attay - */ - public function getOrderOptions() - { - $optionArr = array(); - if ($info = $this->getProduct()->getCustomOption('info_buyRequest')) { - $optionArr['info_buyRequest'] = unserialize($info->getValue()); - } - - if ($optionIds = $this->getProduct()->getCustomOption('option_ids')) { - foreach (explode(',', $optionIds->getValue()) as $optionId) { - if ($option = $this->getProduct()->getOptionById($optionId)) { - $formatedValue = ''; - $optionGroup = $option->getGroupByType($option->getType()); - $optionValue = $this->getProduct()->getCustomOption('option_'.$option->getId())->getValue(); - if ($option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX - || $option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_MULTIPLE) { - foreach (explode(',', $optionValue) as $value) { - $formatedValue .= $option->getValueById($value)->getTitle() . ', '; - } - $formatedValue = Mage::helper('core/string')->substr($formatedValue, 0, -2); - } elseif ($optionGroup == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { - $formatedValue = $option->getValueById($optionValue)->getTitle(); - } else { - $formatedValue = $optionValue; - } - $optionArr['options'][] = array( - 'label' => $option->getTitle(), - 'value' => $formatedValue, - 'option_id' => $option->getId(), - 'option_value' => $optionValue - ); - } - } - } - - if ($productTypeConfig = $this->getProduct()->getCustomOption('product_type')) { - $optionArr['super_product_config'] = array( - 'product_code' => $productTypeConfig->getCode(), - 'product_type' => $productTypeConfig->getValue(), - 'product_id' => $productTypeConfig->getProductId() - ); - } - - return $optionArr; - } - - /** - * Save type related data - * - * @return Mage_Catalog_Model_Product_Type_Abstract - */ - public function save() - { - return $this; - } - - /** - * Before save type related data - * - * @return unknown - */ - public function beforeSave() - { - $this->getProduct()->canAffectOptions(true); - return $this; - } - - /** - * Check if product is composite (grouped, configurable, etc) - * - * @return bool - */ - public function isComposite() - { - return $this->_isComposite; - } - - /** - * Default action to get sku of product - * - * @return string - */ - public function getSku() - { - $skuDelimiter = '-'; - $sku = $this->getProduct()->getData('sku'); - if ($optionIds = $this->getProduct()->getCustomOption('option_ids')) { - $optionIds = split(',', $optionIds->getValue()); - foreach ($optionIds as $optionId) { - $productOption = $this->getProduct()->getOptionById($optionId); - if ($productOption = $this->getProduct()->getOptionById($optionId)) { - $optionValue = $this->getProduct()->getCustomOption('option_' . $optionId)->getValue(); - - if ($productOption->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX - || $productOption->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_MULTIPLE) { - foreach(split(',', $optionValue) as $value) { - if ($optionSku = $productOption->getValueById($value)->getSku()) { - $sku .= $skuDelimiter . $optionSku; - } - } - $optionSku = null; - } - elseif ($productOption->getGroupByType() == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { - $optionSku = $productOption->getValueById($optionValue)->getSku(); - } - else { - $optionSku = $productOption->getSku(); - } - - if (!empty($optionSku)) { - $sku .= $skuDelimiter . $optionSku; - } - } - } - } - return $sku; - } - - /** - * Default action to get weight of product - * - * @return decimal - */ - public function getWeight() - { - return $this->getProduct()->getData('weight'); - } - - /** - * Return true if product has options - * - * @return bool - */ - public function hasOptions() - { - if ($this->getProduct()->getHasOptions()) { - return true; - } - return false; - } - - /** - * Method is needed for specific actions to change given quote options values - * according current product type logic - * Example: the cataloginventory validation of decimal qty can change qty to int, - * so need to change quote item qty option value too. - * - * @param array $options - * @param Varien_Object $option - * @param mixed $value - * - * @return object Mage_Catalog_Model_Product_Type_Abstract - */ - public function updateQtyOption($options, Varien_Object $option, $value) - { - return $this; - } - - /** - * Check if product has required options - * - * @return bool - */ - public function hasRequiredOptions() - { - if ($this->getProduct()->getRequiredOptions()) { - return true; - } - return false; - } - - /** - * Retrive store filter for associated products - * - * @return int|Mage_Core_Model_Store - */ - public function getStoreFilter() - { - return $this->_storeFilter; - } - - /** - * Set store filter for associated products - * - * @param $store int|Mage_Core_Model_Store - * @return Mage_Catalog_Model_Product_Type_Configurable - */ - public function setStoreFilter($store=null) - { - $this->_storeFilter = $store; - return $this; - } - - /** - * Allow for updates of chidren qty's - * (applicable for complicated product types. As default returns false) - * - * @return boolean false - */ - public function getForceChildItemQtyChanges() - { - return false; - } - - /** - * Prepare Quote Item Quantity - * - * @param mixed $qty - * @return float - */ - public function prepareQuoteItemQty($qty) - { - return floatval($qty); - } - - /** - * Implementation of product specify logic of which product needs to be assigned to option. - * For example if product which was added to option already removed from catalog. - * - * @param Mage_Catalog_Model_Product $optionProduct - * @param Mage_Sales_Model_Quote_Item_Option $option - * @return Mage_Catalog_Model_Product_Type_Abstract - */ - public function assignProductToOption($optionProduct, $option) - { - if ($optionProduct) { - $option->setProduct($optionProduct); - } else { - $option->setProduct($this->getProduct()); - } - - return $this; - } -} + + */ +abstract class Mage_Catalog_Model_Product_Type_Abstract +{ + /** + * Product model instance + * + * @var Mage_Catalog_Model_Product + */ + protected $_product; + protected $_typeId; + protected $_setAttributes; + protected $_editableAttributes; + protected $_isComposite = false; + protected $_storeFilter = null; + + const CALCULATE_CHILD = 0; + const CALCULATE_PARENT = 1; + + /** + * values for shipment type (invoice etc) + * + */ + const SHIPMENT_SEPARATELY = 1; + const SHIPMENT_TOGETHER = 0; + + /** + * Specify type instance product + * + * @param Mage_Catalog_Model_Product $product + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function setProduct($product) + { + $this->_product = $product; + return $this; + } + + /** + * Specify type identifier + * + * @param string $typeId + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function setTypeId($typeId) + { + $this->_typeId = $typeId; + return $this; + } + + /** + * Retrieve catalog product object + * + * @return Mage_Catalog_Model_Product + */ + public function getProduct() + { + return $this->_product; + } + + /** + * Return relation info about used products for specific type instance + * + * @return Varien_Object Object with information data + */ + public function getRelationInfo() + { + return new Varien_Object(); + } + + /** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param bool $required + * @return array + */ + public function getChildrenIds($parentId, $required = true) + { + return array(); + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @return array + */ + public function getParentIdsByChild($childId) + { + return array(); + } + + /** + * Get array of product set attributes + * + * @return array + */ + public function getSetAttributes() + { + if (is_null($this->_setAttributes)) { + $attributes = $this->getProduct()->getResource() + ->loadAllAttributes($this->getProduct()) + ->getAttributesByCode(); + $this->_setAttributes = array(); + foreach ($attributes as $attribute) { + if ($attribute->isInSet($this->getProduct()->getAttributeSetId())) { + $attribute->setDataObject($this->getProduct()); + $this->_setAttributes[$attribute->getAttributeCode()] = $attribute; + } + } + + uasort($this->_setAttributes, array($this, 'attributesCompare')); + } + return $this->_setAttributes; + } + + public function attributesCompare($attribute1, $attribute2) + { + $sortPath = 'attribute_set_info/' . $this->getProduct()->getAttributeSetId() . '/sort'; + $groupSortPath = 'attribute_set_info/' . $this->getProduct()->getAttributeSetId() . '/group_sort'; + + $sort1 = ($attribute1->getData($groupSortPath) * 1000) + ($attribute1->getData($sortPath) * 0.0001); + $sort2 = ($attribute2->getData($groupSortPath) * 1000) + ($attribute2->getData($sortPath) * 0.0001); + + if ($sort1 > $sort2) { + return 1; + } elseif ($sort1 < $sort2) { + return -1; + } + + return 0; + } + + /** + * Retrieve product type attributes + * + * @return array + */ + public function getEditableAttributes() + { + if (is_null($this->_editableAttributes)) { + $this->_editableAttributes = array(); + foreach ($this->getSetAttributes() as $attributeCode => $attribute) { + if (!is_array($attribute->getApplyTo()) + || count($attribute->getApplyTo())==0 + || in_array($this->getProduct()->getTypeId(), $attribute->getApplyTo())) { + $this->_editableAttributes[$attributeCode] = $attribute; + } + } + } + return $this->_editableAttributes; + } + + /** + * Retrieve product attribute by identifier + * + * @param int $attributeId + * @return Mage_Eav_Model_Entity_Attribute_Abstract + */ + public function getAttributeById($attributeId) + { + foreach ($this->getSetAttributes() as $attribute) { + if ($attribute->getId() == $attributeId) { + return $attribute; + } + } + return null; + } + + /** + * Check is virtual product + * + * @return bool + */ + public function isVirtual() + { + return false; + } + + /** + * Check is product available for sale + * + * @return bool + */ + public function isSalable() + { + $salable = $this->getProduct()->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED; + if ($salable && $this->getProduct()->hasData('is_salable')) { + $salable = $this->getProduct()->getData('is_salable'); + } + elseif ($salable && $this->isComposite()) { + $salable = null; + } + + return $salable; + } + + /** + * Initialize product(s) for add to cart process + * + * @param Varien_Object $buyRequest + * @return array|string + */ + public function prepareForCart(Varien_Object $buyRequest) + { + $product = $this->getProduct(); + /* @var Mage_Catalog_Model_Product $product */ + + // try to add custom options + $options = $this->_prepareOptionsForCart($buyRequest->getOptions()); + if (is_string($options)) { + return $options; + } + // try to found super product configuration + // (if product was buying within grouped product) + $superProductConfig = $buyRequest->getSuperProductConfig(); + if (!empty($superProductConfig['product_id']) + && !empty($superProductConfig['product_type'])) { + $superProductId = (int) $superProductConfig['product_id']; + if ($superProductId) { + if (!$superProduct = Mage::registry('used_super_product_'.$superProductId)) { + $superProduct = Mage::getModel('catalog/product')->load($superProductId); + Mage::register('used_super_product_'.$superProductId, $superProduct); + } + if ($superProduct->getId()) { + $assocProductIds = $superProduct->getTypeInstance()->getAssociatedProductIds(); + if (in_array($product->getId(), $assocProductIds)) { + $productType = $superProductConfig['product_type']; + $product->addCustomOption('product_type', $productType, $superProduct); + + $buyRequest->setData('super_product_config', array( + 'product_type' => $productType, + 'product_id' => $superProduct->getId() + ) + ); + } + } + } + } + + $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData())); + + if ($options) { + $optionIds = array_keys($options); + $product->addCustomOption('option_ids', implode(',', $optionIds)); + foreach ($options as $optionId => $optionValue) { + $product->addCustomOption('option_'.$optionId, $optionValue); + } + } + // set quantity in cart + $product->setCartQty($buyRequest->getQty()); + + return array($product); + } + + /** + * Check custom defined options for product + * + * @param array $options + * @return array || string + */ + protected function _prepareOptionsForCart($options) + { + $newOptions = array(); + + foreach ($this->getProduct()->getOptions() as $_option) { + /* @var $_option Mage_Catalog_Model_Product_Option */ + if (!isset($options[$_option->getId()]) && $_option->getIsRequire() && !$this->getProduct()->getSkipCheckRequiredOption()) { + return Mage::helper('catalog')->__('Please specify the product required option(s)'); + } + if (!isset($options[$_option->getId()])) { + continue; + } + if ($_option->getGroupByType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_TEXT) { + $options[$_option->getId()] = trim($options[$_option->getId()]); + if (strlen($options[$_option->getId()]) == 0 && $_option->getIsRequire()) { + return Mage::helper('catalog')->__('Please specify the product required option(s)'); + } + if (strlen($options[$_option->getId()]) > $_option->getMaxCharacters() && $_option->getMaxCharacters() > 0) { + return Mage::helper('catalog')->__('Length of text is too long'); + } + if (strlen($options[$_option->getId()]) == 0) continue; + } + if ($_option->getGroupByType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_FILE) { + + } + if ($_option->getGroupByType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_DATE) { + + } + + if ($_option->getGroupbyType($_option->getType()) == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { + if (($_option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_DROP_DOWN + || $_option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_RADIO) + && strlen($options[$_option->getId()])== 0) { + continue; + } + $valuesCollection = $_option->getOptionValuesByOptionId( + $options[$_option->getId()], $this->getProduct()->getStoreId() + )->load(); + + if ($valuesCollection->count() != count($options[$_option->getId()])) { + return Mage::helper('catalog')->__('Please specify the product required option(s)'); + } + } + if (is_array($options[$_option->getId()])) { + $options[$_option->getId()] = implode(',', $options[$_option->getId()]); + } + $newOptions[$_option->getId()] = $options[$_option->getId()]; + } + + return $newOptions; + } + + /** + * Check if product can be bought + * + * @return Mage_Catalog_Model_Product_Type_Abstract + * @throws Mage_Core_Exception + */ + public function checkProductBuyState() + { + if (!$this->getProduct()->getSkipCheckRequiredOption()) { + foreach ($this->getProduct()->getOptions() as $option) { + if ($option->getIsRequire() && (!$this->getProduct()->getCustomOption('option_'.$option->getId()) + || strlen($this->getProduct()->getCustomOption('option_'.$option->getId())->getValue()) == 0)) { + Mage::throwException( + Mage::helper('catalog')->__('Product has required options') + ); + break; + } + } + } + + return $this; + } + + /** + * Prepare additional options/information for order item which will be + * created from this product + * + * @return attay + */ + public function getOrderOptions() + { + $optionArr = array(); + if ($info = $this->getProduct()->getCustomOption('info_buyRequest')) { + $optionArr['info_buyRequest'] = unserialize($info->getValue()); + } + + if ($optionIds = $this->getProduct()->getCustomOption('option_ids')) { + foreach (explode(',', $optionIds->getValue()) as $optionId) { + if ($option = $this->getProduct()->getOptionById($optionId)) { + $formatedValue = ''; + $optionGroup = $option->getGroupByType($option->getType()); + $optionValue = $this->getProduct()->getCustomOption('option_'.$option->getId())->getValue(); + if ($option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX + || $option->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_MULTIPLE) { + foreach (explode(',', $optionValue) as $value) { + $formatedValue .= $option->getValueById($value)->getTitle() . ', '; + } + $formatedValue = Mage::helper('core/string')->substr($formatedValue, 0, -2); + } elseif ($optionGroup == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { + $formatedValue = $option->getValueById($optionValue)->getTitle(); + } else { + $formatedValue = $optionValue; + } + $optionArr['options'][] = array( + 'label' => $option->getTitle(), + 'value' => $formatedValue, + 'option_id' => $option->getId(), + 'option_value' => $optionValue + ); + } + } + } + + if ($productTypeConfig = $this->getProduct()->getCustomOption('product_type')) { + $optionArr['super_product_config'] = array( + 'product_code' => $productTypeConfig->getCode(), + 'product_type' => $productTypeConfig->getValue(), + 'product_id' => $productTypeConfig->getProductId() + ); + } + + return $optionArr; + } + + /** + * Save type related data + * + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function save() + { + return $this; + } + + /** + * Before save type related data + * + * @return unknown + */ + public function beforeSave() + { + $this->getProduct()->canAffectOptions(true); + return $this; + } + + /** + * Check if product is composite (grouped, configurable, etc) + * + * @return bool + */ + public function isComposite() + { + return $this->_isComposite; + } + + /** + * Default action to get sku of product + * + * @return string + */ + public function getSku() + { + $skuDelimiter = '-'; + $sku = $this->getProduct()->getData('sku'); + if ($optionIds = $this->getProduct()->getCustomOption('option_ids')) { + $optionIds = split(',', $optionIds->getValue()); + foreach ($optionIds as $optionId) { + $productOption = $this->getProduct()->getOptionById($optionId); + if ($productOption = $this->getProduct()->getOptionById($optionId)) { + $optionValue = $this->getProduct()->getCustomOption('option_' . $optionId)->getValue(); + + if ($productOption->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX + || $productOption->getType() == Mage_Catalog_Model_Product_Option::OPTION_TYPE_MULTIPLE) { + foreach(split(',', $optionValue) as $value) { + if ($optionSku = $productOption->getValueById($value)->getSku()) { + $sku .= $skuDelimiter . $optionSku; + } + } + $optionSku = null; + } + elseif ($productOption->getGroupByType() == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { + $optionSku = $productOption->getValueById($optionValue)->getSku(); + } + else { + $optionSku = $productOption->getSku(); + } + + if (!empty($optionSku)) { + $sku .= $skuDelimiter . $optionSku; + } + } + } + } + return $sku; + } + + /** + * Default action to get weight of product + * + * @return decimal + */ + public function getWeight() + { + return $this->getProduct()->getData('weight'); + } + + /** + * Return true if product has options + * + * @return bool + */ + public function hasOptions() + { + if ($this->getProduct()->getHasOptions()) { + return true; + } + return false; + } + + /** + * Method is needed for specific actions to change given quote options values + * according current product type logic + * Example: the cataloginventory validation of decimal qty can change qty to int, + * so need to change quote item qty option value too. + * + * @param array $options + * @param Varien_Object $option + * @param mixed $value + * + * @return object Mage_Catalog_Model_Product_Type_Abstract + */ + public function updateQtyOption($options, Varien_Object $option, $value) + { + return $this; + } + + /** + * Check if product has required options + * + * @return bool + */ + public function hasRequiredOptions() + { + if ($this->getProduct()->getRequiredOptions()) { + return true; + } + return false; + } + + /** + * Retrive store filter for associated products + * + * @return int|Mage_Core_Model_Store + */ + public function getStoreFilter() + { + return $this->_storeFilter; + } + + /** + * Set store filter for associated products + * + * @param $store int|Mage_Core_Model_Store + * @return Mage_Catalog_Model_Product_Type_Configurable + */ + public function setStoreFilter($store=null) + { + $this->_storeFilter = $store; + return $this; + } + + /** + * Allow for updates of chidren qty's + * (applicable for complicated product types. As default returns false) + * + * @return boolean false + */ + public function getForceChildItemQtyChanges() + { + return false; + } + + /** + * Prepare Quote Item Quantity + * + * @param mixed $qty + * @return float + */ + public function prepareQuoteItemQty($qty) + { + return floatval($qty); + } + + /** + * Implementation of product specify logic of which product needs to be assigned to option. + * For example if product which was added to option already removed from catalog. + * + * @param Mage_Catalog_Model_Product $optionProduct + * @param Mage_Sales_Model_Quote_Item_Option $option + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function assignProductToOption($optionProduct, $option) + { + if ($optionProduct) { + $option->setProduct($optionProduct); + } else { + $option->setProduct($this->getProduct()); + } + + return $this; + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php b/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php index 90949e676d..4a17d84a4e 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php @@ -1,527 +1,564 @@ - - */ -class Mage_Catalog_Model_Product_Type_Configurable extends Mage_Catalog_Model_Product_Type_Abstract -{ - const TYPE_CODE = 'configurable'; - /** - * Attributes which used for configurable product - * - * @var array - */ - protected $_usedProductAttributeIds = null; - protected $_usedProductAttributes = null; - protected $_usedAttributes = null; - protected $_configurableAttributes = null; - protected $_usedProductIds = null; - protected $_usedProducts = null; - - protected $_isComposite = true; - - /** - * Return relation info about used products - * - * @return Varien_Object Object with information data - */ - public function getRelationInfo() - { - $info = new Varien_Object(); - $info->setTable('catalog/product_super_link') - ->setParentFieldName('parent_id') - ->setChildFieldName('product_id'); - return $info; - } - - /** - * Retrieve product type attributes - * - * @return array - */ - public function getEditableAttributes() - { - if (is_null($this->_editableAttributes)) { - $this->_editableAttributes = parent::getEditableAttributes(); - foreach ($this->_editableAttributes as $index => $attribute) { - if ($this->getUsedProductAttributeIds() - && in_array($attribute->getAttributeId(), $this->getUsedProductAttributeIds())) { - unset($this->_editableAttributes[$index]); - } - } - } - return $this->_editableAttributes; - } - - /** - * Checkin attribute availability for create superproduct - * - * @param Mage_Eav_Model_Entity_Attribute $attribute - * @return bool - */ - public function canUseAttribute(Mage_Eav_Model_Entity_Attribute $attribute) - { - $allow = $attribute->getIsGlobal() == Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL - && $attribute->getIsVisible() - && $attribute->getIsConfigurable() - && $attribute->usesSource(); - - return $allow; - } - - /** - * Declare attribute identifiers used for asign subproducts - * - * @param array $ids - * @return Mage_Catalog_Model_Product_Type_Configurable - */ - public function setUsedProductAttributeIds($ids) - { - $this->_usedProductAttributes = array(); - $this->_configurableAttributes= array(); - - foreach ($ids as $attributeId) { - $this->_usedProductAttributes[] = $this->getAttributeById($attributeId); - $this->_configurableAttributes[]= Mage::getModel('catalog/product_type_configurable_attribute') - ->setProductAttribute($this->getAttributeById($attributeId)); - } - $this->_usedProductAttributeIds = $ids; - return $this; - } - - /** - * Retrieve identifiers of used product attributes - * - * @return array - */ - public function getUsedProductAttributeIds() - { - if (is_null($this->_usedProductAttributeIds)) { - $this->_usedProductAttributeIds = array(); - foreach ($this->getUsedProductAttributes() as $attribute) { - $this->_usedProductAttributeIds[] = $attribute->getId(); - } - } - return $this->_usedProductAttributeIds; - } - - /** - * Retrieve used product attributes - * - * @return array - */ - public function getUsedProductAttributes() - { - if (is_null($this->_usedProductAttributes)) { - $this->_usedProductAttributes = array(); - $this->_usedAttributes = array(); - foreach ($this->getConfigurableAttributes() as $attribute) { - $id = $attribute->getProductAttribute()->getId(); - $this->_usedProductAttributes[$id] = $attribute->getProductAttribute(); - $this->_usedAttributes[$id] = $attribute; - } - } - return $this->_usedProductAttributes; - } - - /** - * Retrieve configurable attrbutes data - * - * @return array - */ - public function getConfigurableAttributes() - { - Varien_Profiler::start('CONFIGURABLE:'.__METHOD__); - if (is_null($this->_configurableAttributes)) { - $this->_configurableAttributes = $this->getConfigurableAttributeCollection() - ->orderByPosition() - ->load(); - - } - Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__); - return $this->_configurableAttributes; - } - - public function getConfigurableAttributesAsArray() - { - $res = array(); - foreach ($this->getConfigurableAttributes() as $attribute) { - $label = $attribute->getLabel() ? $attribute->getLabel() : $attribute->getProductAttribute()->getFrontend()->getLabel(); - $res[] = array( - 'id' => $attribute->getId(), - 'label' => $label, - 'position' => $attribute->getPosition(), - 'values' => $attribute->getPrices() ? $attribute->getPrices() : array(), - 'attribute_id' => $attribute->getProductAttribute()->getId(), - 'attribute_code'=> $attribute->getProductAttribute()->getAttributeCode(), - 'frontend_label'=> $attribute->getProductAttribute()->getFrontend()->getLabel(), - ); - } - return $res; - } - - public function getConfigurableAttributeCollection() - { - return Mage::getResourceModel('catalog/product_type_configurable_attribute_collection') - ->setProductFilter($this->getProduct()); - } - - - /** - * Retrieve subproducts identifiers - * - * @return array - */ - public function getUsedProductIds() - { - if (is_null($this->_usedProductIds)) { - $this->_usedProductIds = array(); - foreach ($this->getUsedProducts() as $product) { - $this->_usedProductIds[] = $product->getId(); - } - } - return $this->_usedProductIds; - } - - /** - * Retrieve array of "subproducts" - * - * @return array - */ - public function getUsedProducts($requiredAttributeIds=null) - { - Varien_Profiler::start('CONFIGURABLE:'.__METHOD__); - if (is_null($this->_usedProducts)) { - if (is_null($requiredAttributeIds) && is_null($this->_configurableAttributes)) { - // If used products load before attributes, we will load attributes. - $this->getConfigurableAttributes(); - // After attributes loading products loaded too. - return $this->_usedProducts; - } - - $this->_usedProducts = array(); - $collection = $this->getUsedProductCollection() - ->addAttributeToSelect('*') - ->addFilterByRequiredOptions(); - - if (is_array($requiredAttributeIds)) { - foreach ($requiredAttributeIds as $attributeId) { - $attribute = $this->getAttributeById($attributeId); - if (!is_null($attribute)) - $collection->addAttributeToFilter($attribute->getAttributeCode(), array('notnull'=>1)); - } - } - - foreach ($collection as $product) { - $this->_usedProducts[] = $product; - } - } - Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__); - return $this->_usedProducts; - } - - /** - * Retrieve related products collection - * - * @return unknown - */ - public function getUsedProductCollection() - { - $collection = Mage::getResourceModel('catalog/product_type_configurable_product_collection') - ->setProductFilter($this->getProduct()); - if (!is_null($this->getStoreFilter())) { - $collection->addStoreFilter($this->getStoreFilter()); - } - return $collection; - } - - public function beforeSave() - { - parent::beforeSave(); - - $this->getProduct()->canAffectOptions(false); - - if ($this->getProduct()->getCanSaveConfigurableAttributes()) { - $this->getProduct()->canAffectOptions(true); - if ($data = $this->getProduct()->getConfigurableAttributesData()) { - if (!empty($data)) { - foreach ($data as $attribute) { - if (!empty($attribute['values'])) { - $this->getProduct()->setTypeHasOptions(true); - $this->getProduct()->setTypeHasRequiredOptions(true); - break; - } - } - } - } - } - } - - /** - * Save configurable product depended data - * - * @return Mage_Catalog_Model_Product_Type_Configurable - */ - public function save() - { - parent::save(); - /** - * Save Attributes Information - */ - if ($data = $this->getProduct()->getConfigurableAttributesData()) { - foreach ($data as $attributeData) { - $id = isset($attributeData['id']) ? $attributeData['id'] : null; - $attribute = Mage::getModel('catalog/product_type_configurable_attribute') - ->setData($attributeData) - ->setId($id) - ->setStoreId($this->getProduct()->getStoreId()) - ->setProductId($this->getProduct()->getId()) - ->save(); - } - } - - /** - * Save product relations - */ - $data = $this->getProduct()->getConfigurableProductsData(); - if (is_array($data)) { - $productIds = array_keys($data); - Mage::getResourceModel('catalog/product_type_configurable') - ->saveProducts($this->getProduct()->getId(), $productIds); - } - return $this; - } - - /** - * Check is product available for sale - * - * @return bool - */ - public function isSalable() - { - $salable = $this->getProduct()->getIsSalable(); - if (!is_null($salable) && !$salable) { - return $salable; - } - - $salable = false; - foreach ($this->getUsedProducts() as $product) { - $salable = $salable || $product->isSalable(); - } - return $salable; - } - - /** - * Retrieve used product by attribute values - * $attrbutesInfo = array( - * $attributeId => $attributeValue - * ) - * @param array $attrbutesInfo - * @return - */ - public function getProductByAttributes($attributesInfo) - { - foreach ($this->getUsedProducts() as $product) { - $checkRes = true; - foreach ($attributesInfo as $attributeId => $attributeValue) { - $code = $this->getAttributeById($attributeId)->getAttributeCode(); - if ($product->getData($code) != $attributeValue) { - $checkRes = false; - } - } - if ($checkRes) { - return $product; - } - } - return null; - } - - public function getSelectedAttributesInfo() - { - $attributes = array(); - Varien_Profiler::start('CONFIGURABLE:'.__METHOD__); - if ($attributesOption = $this->getProduct()->getCustomOption('attributes')) { - $data = unserialize($attributesOption->getValue()); - $this->getUsedProductAttributeIds(); - - foreach ($data as $attributeId => $attributeValue) { - if (isset($this->_usedAttributes[$attributeId])) { - $attribute = $this->_usedAttributes[$attributeId]; - $label = $attribute->getLabel(); - $value = $attribute->getProductAttribute(); - if ($value->getSourceModel()) { - $value = $value->getSource()->getOptionText($attributeValue); - } - else { - $value = ''; - } - - $attributes[] = array('label'=>$label, 'value'=>$value); - } - } - } - Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__); - return $attributes; - } - - /** - * Initialize product(s) for add to cart process - * - * @param Varien_Object $buyRequest - * @return unknown - */ - public function prepareForCart(Varien_Object $buyRequest) - { - if ($attributes = $buyRequest->getSuperAttribute()) { - $result = parent::prepareForCart($buyRequest); - if (is_array($result)) { - $product = $this->getProduct(); - /** - * $attributes = array($attributeId=>$attributeValue) - */ - if ($subProduct = $this->getProductByAttributes($attributes)) { - $product->addCustomOption('attributes', serialize($attributes)); - $product->addCustomOption('product_qty_'.$subProduct->getId(), 1, $subProduct); - $product->addCustomOption('simple_product', $subProduct->getId(), $subProduct); - - $subProduct->setParentProductId($product->getId()) - ->setCartQty(1); - - $result[] = $subProduct; - - return $result; - } - } - } - return Mage::helper('catalog')->__('Please specify the product option(s)'); - } - - /** - * Prepare additional options/information for order item which will be - * created from this product - * - * @return attay - */ - public function getOrderOptions() - { - $options = parent::getOrderOptions(); - $options['attributes_info'] = $this->getSelectedAttributesInfo(); - if ($simpleOption = $this->getProduct()->getCustomOption('simple_product')) { - $options['simple_name'] = $simpleOption->getProduct()->getName(); - $options['simple_sku'] = $simpleOption->getProduct()->getSku(); - } - - $options['product_calculations'] = self::CALCULATE_PARENT; - $options['shipment_type'] = self::SHIPMENT_TOGETHER; - - return $options; - } - - /** - * Check is virtual product - * - * @return bool - */ - public function isVirtual() - { - if ($productOption = $this->getProduct()->getCustomOption('simple_product')) { - if ($product = $productOption->getProduct()) { - /* @var $product Mage_Catalog_Model_Product */ - return $product->getTypeInstance()->isVirtual(); - } - } - return parent::isVirtual(); - } - - /** - * Return true if product has options - * - * @return bool - */ - public function hasOptions() - { - if ($this->getProduct()->getOptions()) { - return true; - } - - $attributes = $this->getConfigurableAttributes(); - if (count($attributes)) { - foreach ($attributes as $key => $attribute) { - /** @var Mage_Catalog_Model_Product_Type_Configurable_Attribute $attribute */ - if ($attribute->getData('prices')) { - return true; - } - } - } - - return false; - } - - /** - * Return product weight based on simple product - * weight or configurable product weight - * - * @return decimal - */ - public function getWeight() - { - if ($this->getProduct()->hasCustomOptions() && ($simpleProductOption = $this->getProduct()->getCustomOption('simple_product'))) { - $simpleProduct = $simpleProductOption->getProduct(); - if ($simpleProduct) { - return $simpleProduct->getWeight(); - } - } - - return $this->getProduct()->getData('weight'); - } - - /** - * Implementation of product specify logic of which product needs to be assigned to option. - * For example if product which was added to option already removed from catalog. - * - * @param Mage_Catalog_Model_Product $optionProduct - * @param Mage_Sales_Model_Quote_Item_Option $option - * @return Mage_Catalog_Model_Product_Type_Abstract - */ - public function assignProductToOption($optionProduct, $option) - { - if ($optionProduct) { - $option->setProduct($optionProduct); - } else { - $option->getItem()->setHasError('error'); - $option->getItem()->addMessage(Mage::helper('catalog')->__('Selected configuration is not available.', $this->getProduct()->getName())); - } - - return $this; - } -} + + */ +class Mage_Catalog_Model_Product_Type_Configurable extends Mage_Catalog_Model_Product_Type_Abstract +{ + const TYPE_CODE = 'configurable'; + /** + * Attributes which used for configurable product + * + * @var array + */ + protected $_usedProductAttributeIds = null; + protected $_usedProductAttributes = null; + protected $_usedAttributes = null; + protected $_configurableAttributes = null; + protected $_usedProductIds = null; + protected $_usedProducts = null; + + protected $_isComposite = true; + + /** + * Return relation info about used products + * + * @return Varien_Object Object with information data + */ + public function getRelationInfo() + { + $info = new Varien_Object(); + $info->setTable('catalog/product_super_link') + ->setParentFieldName('parent_id') + ->setChildFieldName('product_id'); + return $info; + } + + /** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param bool $required + * @return array + */ + public function getChildrenIds($parentId, $required = true) + { + return Mage::getResourceSingleton('catalog/product_type_configurable') + ->getChildrenIds($parentId, $required); + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @return array + */ + public function getParentIdsByChild($childId) + { + return Mage::getResourceSingleton('catalog/product_type_configurable') + ->getParentIdsByChild($childId); + } + + /** + * Retrieve product type attributes + * + * @return array + */ + public function getEditableAttributes() + { + if (is_null($this->_editableAttributes)) { + $this->_editableAttributes = parent::getEditableAttributes(); + foreach ($this->_editableAttributes as $index => $attribute) { + if ($this->getUsedProductAttributeIds() + && in_array($attribute->getAttributeId(), $this->getUsedProductAttributeIds())) { + unset($this->_editableAttributes[$index]); + } + } + } + return $this->_editableAttributes; + } + + /** + * Checkin attribute availability for create superproduct + * + * @param Mage_Eav_Model_Entity_Attribute $attribute + * @return bool + */ + public function canUseAttribute(Mage_Eav_Model_Entity_Attribute $attribute) + { + $allow = $attribute->getIsGlobal() == Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL + && $attribute->getIsVisible() + && $attribute->getIsConfigurable() + && $attribute->usesSource(); + + return $allow; + } + + /** + * Declare attribute identifiers used for asign subproducts + * + * @param array $ids + * @return Mage_Catalog_Model_Product_Type_Configurable + */ + public function setUsedProductAttributeIds($ids) + { + $this->_usedProductAttributes = array(); + $this->_configurableAttributes= array(); + + foreach ($ids as $attributeId) { + $this->_usedProductAttributes[] = $this->getAttributeById($attributeId); + $this->_configurableAttributes[]= Mage::getModel('catalog/product_type_configurable_attribute') + ->setProductAttribute($this->getAttributeById($attributeId)); + } + $this->_usedProductAttributeIds = $ids; + return $this; + } + + /** + * Retrieve identifiers of used product attributes + * + * @return array + */ + public function getUsedProductAttributeIds() + { + if (is_null($this->_usedProductAttributeIds)) { + $this->_usedProductAttributeIds = array(); + foreach ($this->getUsedProductAttributes() as $attribute) { + $this->_usedProductAttributeIds[] = $attribute->getId(); + } + } + return $this->_usedProductAttributeIds; + } + + /** + * Retrieve used product attributes + * + * @return array + */ + public function getUsedProductAttributes() + { + if (is_null($this->_usedProductAttributes)) { + $this->_usedProductAttributes = array(); + $this->_usedAttributes = array(); + foreach ($this->getConfigurableAttributes() as $attribute) { + $id = $attribute->getProductAttribute()->getId(); + $this->_usedProductAttributes[$id] = $attribute->getProductAttribute(); + $this->_usedAttributes[$id] = $attribute; + } + } + return $this->_usedProductAttributes; + } + + /** + * Retrieve configurable attrbutes data + * + * @return array + */ + public function getConfigurableAttributes() + { + Varien_Profiler::start('CONFIGURABLE:'.__METHOD__); + if (is_null($this->_configurableAttributes)) { + $this->_configurableAttributes = $this->getConfigurableAttributeCollection() + ->orderByPosition() + ->load(); + + } + Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__); + return $this->_configurableAttributes; + } + + public function getConfigurableAttributesAsArray() + { + $res = array(); + foreach ($this->getConfigurableAttributes() as $attribute) { + $label = $attribute->getLabel() ? $attribute->getLabel() : $attribute->getProductAttribute()->getFrontend()->getLabel(); + $res[] = array( + 'id' => $attribute->getId(), + 'label' => $label, + 'position' => $attribute->getPosition(), + 'values' => $attribute->getPrices() ? $attribute->getPrices() : array(), + 'attribute_id' => $attribute->getProductAttribute()->getId(), + 'attribute_code'=> $attribute->getProductAttribute()->getAttributeCode(), + 'frontend_label'=> $attribute->getProductAttribute()->getFrontend()->getLabel(), + ); + } + return $res; + } + + public function getConfigurableAttributeCollection() + { + return Mage::getResourceModel('catalog/product_type_configurable_attribute_collection') + ->setProductFilter($this->getProduct()); + } + + + /** + * Retrieve subproducts identifiers + * + * @return array + */ + public function getUsedProductIds() + { + if (is_null($this->_usedProductIds)) { + $this->_usedProductIds = array(); + foreach ($this->getUsedProducts() as $product) { + $this->_usedProductIds[] = $product->getId(); + } + } + return $this->_usedProductIds; + } + + /** + * Retrieve array of "subproducts" + * + * @return array + */ + public function getUsedProducts($requiredAttributeIds=null) + { + Varien_Profiler::start('CONFIGURABLE:'.__METHOD__); + if (is_null($this->_usedProducts)) { + if (is_null($requiredAttributeIds) && is_null($this->_configurableAttributes)) { + // If used products load before attributes, we will load attributes. + $this->getConfigurableAttributes(); + // After attributes loading products loaded too. + return $this->_usedProducts; + } + + $this->_usedProducts = array(); + $collection = $this->getUsedProductCollection() + ->addAttributeToSelect('*') + ->addFilterByRequiredOptions(); + + if (is_array($requiredAttributeIds)) { + foreach ($requiredAttributeIds as $attributeId) { + $attribute = $this->getAttributeById($attributeId); + if (!is_null($attribute)) + $collection->addAttributeToFilter($attribute->getAttributeCode(), array('notnull'=>1)); + } + } + + foreach ($collection as $product) { + $this->_usedProducts[] = $product; + } + } + Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__); + return $this->_usedProducts; + } + + /** + * Retrieve related products collection + * + * @return unknown + */ + public function getUsedProductCollection() + { + $collection = Mage::getResourceModel('catalog/product_type_configurable_product_collection') + ->setProductFilter($this->getProduct()); + if (!is_null($this->getStoreFilter())) { + $collection->addStoreFilter($this->getStoreFilter()); + } + return $collection; + } + + public function beforeSave() + { + parent::beforeSave(); + + $this->getProduct()->canAffectOptions(false); + + if ($this->getProduct()->getCanSaveConfigurableAttributes()) { + $this->getProduct()->canAffectOptions(true); + if ($data = $this->getProduct()->getConfigurableAttributesData()) { + if (!empty($data)) { + foreach ($data as $attribute) { + if (!empty($attribute['values'])) { + $this->getProduct()->setTypeHasOptions(true); + $this->getProduct()->setTypeHasRequiredOptions(true); + break; + } + } + } + } + } + } + + /** + * Save configurable product depended data + * + * @return Mage_Catalog_Model_Product_Type_Configurable + */ + public function save() + { + parent::save(); + /** + * Save Attributes Information + */ + if ($data = $this->getProduct()->getConfigurableAttributesData()) { + foreach ($data as $attributeData) { + $id = isset($attributeData['id']) ? $attributeData['id'] : null; + $attribute = Mage::getModel('catalog/product_type_configurable_attribute') + ->setData($attributeData) + ->setId($id) + ->setStoreId($this->getProduct()->getStoreId()) + ->setProductId($this->getProduct()->getId()) + ->save(); + } + } + + /** + * Save product relations + */ + $data = $this->getProduct()->getConfigurableProductsData(); + if (is_array($data)) { + $productIds = array_keys($data); + Mage::getResourceModel('catalog/product_type_configurable') + ->saveProducts($this->getProduct()->getId(), $productIds); + } + return $this; + } + + /** + * Check is product available for sale + * + * @return bool + */ + public function isSalable() + { + $salable = parent::isSalable(); + if (!is_null($salable)) { + return $salable; + } + + $salable = false; + foreach ($this->getUsedProducts() as $product) { + $salable = $salable || $product->isSalable(); + } + return $salable; + } + + /** + * Retrieve used product by attribute values + * $attrbutesInfo = array( + * $attributeId => $attributeValue + * ) + * @param array $attrbutesInfo + * @return + */ + public function getProductByAttributes($attributesInfo) + { + foreach ($this->getUsedProducts() as $product) { + $checkRes = true; + foreach ($attributesInfo as $attributeId => $attributeValue) { + $code = $this->getAttributeById($attributeId)->getAttributeCode(); + if ($product->getData($code) != $attributeValue) { + $checkRes = false; + } + } + if ($checkRes) { + return $product; + } + } + return null; + } + + public function getSelectedAttributesInfo() + { + $attributes = array(); + Varien_Profiler::start('CONFIGURABLE:'.__METHOD__); + if ($attributesOption = $this->getProduct()->getCustomOption('attributes')) { + $data = unserialize($attributesOption->getValue()); + $this->getUsedProductAttributeIds(); + + foreach ($data as $attributeId => $attributeValue) { + if (isset($this->_usedAttributes[$attributeId])) { + $attribute = $this->_usedAttributes[$attributeId]; + $label = $attribute->getLabel(); + $value = $attribute->getProductAttribute(); + if ($value->getSourceModel()) { + $value = $value->getSource()->getOptionText($attributeValue); + } + else { + $value = ''; + } + + $attributes[] = array('label'=>$label, 'value'=>$value); + } + } + } + Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__); + return $attributes; + } + + /** + * Initialize product(s) for add to cart process + * + * @param Varien_Object $buyRequest + * @return unknown + */ + public function prepareForCart(Varien_Object $buyRequest) + { + if ($attributes = $buyRequest->getSuperAttribute()) { + $result = parent::prepareForCart($buyRequest); + if (is_array($result)) { + $product = $this->getProduct(); + /** + * $attributes = array($attributeId=>$attributeValue) + */ + if ($subProduct = $this->getProductByAttributes($attributes)) { + $product->addCustomOption('attributes', serialize($attributes)); + $product->addCustomOption('product_qty_'.$subProduct->getId(), 1, $subProduct); + $product->addCustomOption('simple_product', $subProduct->getId(), $subProduct); + + $_result = $subProduct->getTypeInstance()->prepareForCart($buyRequest); + if (is_string($_result) && !is_array($_result)) { + return $_result; + } + + if (!isset($_result[0])) { + return Mage::helper('checkout')->__('Can not add item to shopping cart'); + } + + $_result[0]->setParentProductId($product->getId()) + ->setCartQty(1); + + $result[] = $_result[0]; + + return $result; + } + } + } + return Mage::helper('catalog')->__('Please specify the product option(s)'); + } + + /** + * Prepare additional options/information for order item which will be + * created from this product + * + * @return attay + */ + public function getOrderOptions() + { + $options = parent::getOrderOptions(); + $options['attributes_info'] = $this->getSelectedAttributesInfo(); + if ($simpleOption = $this->getProduct()->getCustomOption('simple_product')) { + $options['simple_name'] = $simpleOption->getProduct()->getName(); + $options['simple_sku'] = $simpleOption->getProduct()->getSku(); + } + + $options['product_calculations'] = self::CALCULATE_PARENT; + $options['shipment_type'] = self::SHIPMENT_TOGETHER; + + return $options; + } + + /** + * Check is virtual product + * + * @return bool + */ + public function isVirtual() + { + if ($productOption = $this->getProduct()->getCustomOption('simple_product')) { + if ($product = $productOption->getProduct()) { + /* @var $product Mage_Catalog_Model_Product */ + return $product->getTypeInstance()->isVirtual(); + } + } + return parent::isVirtual(); + } + + /** + * Return true if product has options + * + * @return bool + */ + public function hasOptions() + { + if ($this->getProduct()->getOptions()) { + return true; + } + + $attributes = $this->getConfigurableAttributes(); + if (count($attributes)) { + foreach ($attributes as $key => $attribute) { + /** @var Mage_Catalog_Model_Product_Type_Configurable_Attribute $attribute */ + if ($attribute->getData('prices')) { + return true; + } + } + } + + return false; + } + + /** + * Return product weight based on simple product + * weight or configurable product weight + * + * @return decimal + */ + public function getWeight() + { + if ($this->getProduct()->hasCustomOptions() && ($simpleProductOption = $this->getProduct()->getCustomOption('simple_product'))) { + $simpleProduct = $simpleProductOption->getProduct(); + if ($simpleProduct) { + return $simpleProduct->getWeight(); + } + } + + return $this->getProduct()->getData('weight'); + } + + /** + * Implementation of product specify logic of which product needs to be assigned to option. + * For example if product which was added to option already removed from catalog. + * + * @param Mage_Catalog_Model_Product $optionProduct + * @param Mage_Sales_Model_Quote_Item_Option $option + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function assignProductToOption($optionProduct, $option) + { + if ($optionProduct) { + $option->setProduct($optionProduct); + } else { + $option->getItem()->setHasError('error'); + $option->getItem()->addMessage(Mage::helper('catalog')->__('Selected configuration is not available.', $this->getProduct()->getName())); + } + + return $this; + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Type/Grouped.php b/app/code/core/Mage/Catalog/Model/Product/Type/Grouped.php index 5f92f34983..4b3ecefb1c 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type/Grouped.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type/Grouped.php @@ -1,224 +1,264 @@ - - */ -class Mage_Catalog_Model_Product_Type_Grouped extends Mage_Catalog_Model_Product_Type_Abstract -{ - const TYPE_CODE = 'grouped'; - protected $_associatedProducts = null; - protected $_associatedProductIds= null; - protected $_statusFilters = null; - protected $_isComposite = true; - - /** - * Return relation info about used products - * - * @return Varien_Object Object with information data - */ - public function getRelationInfo() - { - $info = new Varien_Object(); - $info->setTable('catalog/product_link') - ->setParentFieldName('product_id') - ->setChildFieldName('linked_product_id') - ->setWhere('link_type_id=' . Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED); - return $info; - } - - /** - * Retrieve array of associated products - * - * @return array - */ - public function getAssociatedProducts() - { - if (is_null($this->_associatedProducts)) { - $this->_associatedProducts = array(); - - if (!Mage::app()->getStore()->isAdmin()) { - $this->setSaleableStatus(); - } - - $collection = $this->getAssociatedProductCollection() - ->addAttributeToSelect('*') - ->addFilterByRequiredOptions() - ->setPositionOrder() - ->addStoreFilter($this->getStoreFilter()) - ->addAttributeToFilter('status', array('in' => $this->getStatusFilters())); - - foreach ($collection as $product) { - $this->_associatedProducts[] = $product; - } - } - return $this->_associatedProducts; - } - - /** - * Add status filter to collection - * - * @param int $status - * @return void - */ - public function addStatusFilter ($status) - { - $this->_statusFilters[] = $status; - return $this; - } - - /** - * Set only saleable filter - * - * @param none - * @return void - */ - public function setSaleableStatus () - { - $this->_statusFilters = Mage::getSingleton('catalog/product_status')->getSaleableStatusIds(); - return $this; - } - - /** - * Return all assigned status filters - * - * @param none - * @return void - */ - public function getStatusFilters () - { - if ($this->_statusFilters === null) { - return array( - Mage_Catalog_Model_Product_Status::STATUS_ENABLED, - Mage_Catalog_Model_Product_Status::STATUS_DISABLED - ); - } - return $this->_statusFilters; - } - - /** - * Retrieve related products identifiers - * - * @return array - */ - public function getAssociatedProductIds() - { - if (is_null($this->_associatedProductIds)) { - $this->_associatedProductIds = array(); - foreach ($this->getAssociatedProducts() as $product) { - $this->_associatedProductIds[] = $product->getId(); - } - } - return $this->_associatedProductIds; - } - - /** - * Retrieve collection of associated products - */ - public function getAssociatedProductCollection() - { - $collection = $this->getProduct()->getLinkInstance()->useGroupedLinks() - ->getProductCollection() - ->setIsStrongMode(); - $collection->setProduct($this->getProduct()); - return $collection; - } - - /** - * Check is product available for sale - * - * @return bool - */ - public function isSalable() - { - $salable = $this->getProduct()->getIsSalable(); - if (!is_null($salable) && !$salable) { - return $salable; - } - - $salable = false; - foreach ($this->getAssociatedProducts() as $product) { - $salable = $salable || $product->isSalable(); - } - return $salable; - } - - /** - * Save type related data - * - * @return unknown - */ - public function save() - { - parent::save(); - $this->getProduct()->getLinkInstance()->saveGroupedLinks($this->getProduct()); - return $this; - } - - /** - * Initialize product(s) for add to cart process - * - * @param Varien_Object $buyRequest - * @return array || string || false - */ - public function prepareForCart(Varien_Object $buyRequest) - { - $productsInfo = $buyRequest->getSuperGroup(); - if (!empty($productsInfo) && is_array($productsInfo)) { - $products = array(); - $associatedProducts = $this->getAssociatedProducts(); - if ($associatedProducts) { - $productId = $this->getProduct()->getId(); - foreach ($associatedProducts as $subProduct) { - if(isset($productsInfo[$subProduct->getId()])) { - $qty = $productsInfo[$subProduct->getId()]; - if (!empty($qty)) { - $subProduct->setCartQty($qty); - $subProduct->addCustomOption('product_type', self::TYPE_CODE, $this->getProduct()); - $subProduct->addCustomOption('info_buyRequest', - serialize(array( - 'super_product_config' => array( - 'product_type' => self::TYPE_CODE, - 'product_id' => $this->getProduct()->getId() - ) - )) - ); - $products[] = $subProduct; - } - } - } - } - if (count($products)) { - return $products; - } - } - return Mage::helper('catalog')->__('Please specify the product(s) quantity'); - } + + */ +class Mage_Catalog_Model_Product_Type_Grouped extends Mage_Catalog_Model_Product_Type_Abstract +{ + const TYPE_CODE = 'grouped'; + protected $_associatedProducts = null; + protected $_associatedProductIds= null; + protected $_statusFilters = null; + protected $_isComposite = true; + + /** + * Return relation info about used products + * + * @return Varien_Object Object with information data + */ + public function getRelationInfo() + { + $info = new Varien_Object(); + $info->setTable('catalog/product_link') + ->setParentFieldName('product_id') + ->setChildFieldName('linked_product_id') + ->setWhere('link_type_id=' . Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED); + return $info; + } + + /** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param bool $required + * @return array + */ + public function getChildrenIds($parentId, $required = true) + { + return Mage::getResourceSingleton('catalog/product_link') + ->getChildrenIds($parentId, + Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED); + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @return array + */ + public function getParentIdsByChild($childId) + { + return Mage::getResourceSingleton('catalog/product_link') + ->getParentIdsByChild($childId, + Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED); + } + + /** + * Retrieve array of associated products + * + * @return array + */ + public function getAssociatedProducts() + { + if (is_null($this->_associatedProducts)) { + $this->_associatedProducts = array(); + + if (!Mage::app()->getStore()->isAdmin()) { + $this->setSaleableStatus(); + } + + $collection = $this->getAssociatedProductCollection() + ->addAttributeToSelect('*') + ->addFilterByRequiredOptions() + ->setPositionOrder() + ->addStoreFilter($this->getStoreFilter()) + ->addAttributeToFilter('status', array('in' => $this->getStatusFilters())); + + foreach ($collection as $product) { + $this->_associatedProducts[] = $product; + } + } + return $this->_associatedProducts; + } + + /** + * Add status filter to collection + * + * @param int $status + * @return void + */ + public function addStatusFilter ($status) + { + $this->_statusFilters[] = $status; + return $this; + } + + /** + * Set only saleable filter + * + * @param none + * @return void + */ + public function setSaleableStatus () + { + $this->_statusFilters = Mage::getSingleton('catalog/product_status')->getSaleableStatusIds(); + return $this; + } + + /** + * Return all assigned status filters + * + * @param none + * @return void + */ + public function getStatusFilters () + { + if ($this->_statusFilters === null) { + return array( + Mage_Catalog_Model_Product_Status::STATUS_ENABLED, + Mage_Catalog_Model_Product_Status::STATUS_DISABLED + ); + } + return $this->_statusFilters; + } + + /** + * Retrieve related products identifiers + * + * @return array + */ + public function getAssociatedProductIds() + { + if (is_null($this->_associatedProductIds)) { + $this->_associatedProductIds = array(); + foreach ($this->getAssociatedProducts() as $product) { + $this->_associatedProductIds[] = $product->getId(); + } + } + return $this->_associatedProductIds; + } + + /** + * Retrieve collection of associated products + */ + public function getAssociatedProductCollection() + { + $collection = $this->getProduct()->getLinkInstance()->useGroupedLinks() + ->getProductCollection() + ->setIsStrongMode(); + $collection->setProduct($this->getProduct()); + return $collection; + } + + /** + * Check is product available for sale + * + * @return bool + */ + public function isSalable() + { + $salable = parent::isSalable(); + if (!is_null($salable)) { + return $salable; + } + + $salable = false; + foreach ($this->getAssociatedProducts() as $product) { + $salable = $salable || $product->isSalable(); + } + return $salable; + } + + /** + * Save type related data + * + * @return unknown + */ + public function save() + { + parent::save(); + $this->getProduct()->getLinkInstance()->saveGroupedLinks($this->getProduct()); + return $this; + } + + /** + * Initialize product(s) for add to cart process + * + * @param Varien_Object $buyRequest + * @return array || string || false + */ + public function prepareForCart(Varien_Object $buyRequest) + { + $productsInfo = $buyRequest->getSuperGroup(); + if (!empty($productsInfo) && is_array($productsInfo)) { + $products = array(); + $associatedProducts = $this->getAssociatedProducts(); + if ($associatedProducts) { + $productId = $this->getProduct()->getId(); + foreach ($associatedProducts as $subProduct) { + if(isset($productsInfo[$subProduct->getId()])) { + $qty = $productsInfo[$subProduct->getId()]; + if (!empty($qty)) { + + $_result = $subProduct->getTypeInstance()->prepareForCart($buyRequest); + if (is_string($_result) && !is_array($_result)) { + return $_result; + } + + if (!isset($_result[0])) { + return Mage::helper('checkout')->__('Can not add item to shopping cart'); + } + + $_result[0]->setCartQty($qty); + $_result[0]->addCustomOption('product_type', self::TYPE_CODE, $this->getProduct()); + $_result[0]->addCustomOption('info_buyRequest', + serialize(array( + 'super_product_config' => array( + 'product_type' => self::TYPE_CODE, + 'product_id' => $this->getProduct()->getId() + ) + )) + ); + $products[] = $_result[0]; + } + } + } + } + if (count($products)) { + return $products; + } + } + return Mage::helper('catalog')->__('Please specify the product(s) quantity'); + } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Model/Product/Type/Price.php b/app/code/core/Mage/Catalog/Model/Product/Type/Price.php index 5bc855d1af..74a21b5a3d 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type/Price.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type/Price.php @@ -192,8 +192,8 @@ protected function _applySpecialPrice($product, $finalPrice) $fromDate = Mage::app()->getLocale()->date($product->getSpecialFromDate(), null, null, false); $toDate = Mage::app()->getLocale()->date($product->getSpecialToDate(), null, null, false); - if ($product->getSpecialFromDate() && $storeDate->compare($fromDate)<0) { - } elseif ($product->getSpecialToDate() && $storeDate->compare($toDate)>0) { + if ($product->getSpecialFromDate() && $storeDate->compare($fromDate, Zend_Date::DATES)<0) { + } elseif ($product->getSpecialToDate() && $storeDate->compare($toDate, Zend_Date::DATES)>0) { } else { $finalPrice = min($finalPrice, $specialPrice); } @@ -325,8 +325,8 @@ public static function calculatePrice($basePrice, $specialPrice, $specialPriceFr $toDate = Mage::app()->getLocale()->date($specialPriceTo, null, null, false); if ($specialPrice !== null && $specialPrice !== false) { - if ($specialPriceFrom && $storeDate->compare($fromDate)<0) { - } elseif ($specialPriceTo && $storeDate->compare($toDate)>0) { + if ($specialPriceFrom && $storeDate->compare($fromDate, Zend_Date::DATES)<0) { + } elseif ($specialPriceTo && $storeDate->compare($toDate, Zend_Date::DATES)>0) { } else { $finalPrice = min($finalPrice, $specialPrice); } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category.php index 5239022d11..86e9c454d5 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category.php @@ -95,23 +95,45 @@ protected function _beforeDelete(Varien_Object $object) { parent::_beforeDelete($object); - $toUpdateChild = explode('/',substr($object->getPath(),0,strrpos($object->getPath(),'/'))); - $child = $this->getChildrenCount($object->getId()); - $child+=1; - + /** + * Update children count for all parent categories + */ + $parentIds = $object->getParentIds(); + $childDecrease = $object->getChildrenCount() + 1; // +1 is itself $this->_getWriteAdapter()->update( $this->getEntityTable(), - array('children_count'=>new Zend_Db_Expr('`children_count`-'.$child)), - $this->_getWriteAdapter()->quoteInto('entity_id IN(?)', $toUpdateChild) + array('children_count'=>new Zend_Db_Expr('`children_count`-'.$childDecrease)), + $this->_getWriteAdapter()->quoteInto('entity_id IN(?)', $parentIds) ); - if ($child = $this->_getTree()->getNodeById($object->getId())) { + /** + * Recursion use a lot of memmory, that why we run one request for delete children + */ + /*if ($child = $this->_getTree()->getNodeById($object->getId())) { $children = $child->getChildren(); foreach ($children as $child) { $childObject = Mage::getModel('catalog/category')->load($child->getId())->delete(); } + }*/ + + $select = $this->_getWriteAdapter()->select() + ->from($this->getEntityTable(), array('entity_id')) + ->where($this->_getWriteAdapter()->quoteInto('`path` LIKE ?', $object->getPath().'/%')); + + $childrenIds = $this->_getWriteAdapter()->fetchCol($select); + + if (!empty($childrenIds)) { + $this->_getWriteAdapter()->delete( + $this->getEntityTable(), + $this->_getWriteAdapter()->quoteInto('entity_id IN (?)', $childrenIds) + ); } + /** + * Add deleted children ids to object + * This data can be used in after delete event + */ + $object->setDeletedChildrenIds($childrenIds); return $this; } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product.php index 833b445d8d..c66316d0a2 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product.php @@ -237,15 +237,18 @@ protected function _saveCategories(Varien_Object $object) public function refreshIndex($product) { + /** + * Ids of all categories where product is assigned (not related with store) + */ $categoryIds = $product->getCategoryIds(); /** - * Refresh category/product index + * Clear previos index data related with product */ -// $this->_getWriteAdapter()->delete( -// $this->getTable('catalog/category_product_index'), -// 'product_id='.$product->getId() -// ); + $this->_getWriteAdapter()->delete( + $this->getTable('catalog/category_product_index'), + $this->_getWriteAdapter()->quoteInto('product_id=?', $product->getId()) + ); if (!empty($categoryIds)) { $categoriesSelect = $this->_getWriteAdapter()->select() @@ -269,16 +272,6 @@ public function refreshIndex($product) $indexCategoryIds = array_unique($indexCategoryIds); $indexProductIds = array($product->getId()); Mage::getResourceSingleton('catalog/category')->refreshProductIndex($indexCategoryIds, $indexProductIds); -// foreach ($indexCategoryIds as $categoryId) { -// $position = isset($categoriesPositions[(int)$categoryId]) ? $categoriesPositions[(int)$categoryId] : 0; -// $data = array( -// 'category_id' => $categoryId, -// 'product_id' => $product->getId(), -// 'position' => $position, -// 'is_parent' => in_array($categoryId, $categoryIds) -// ); -// $this->_getWriteAdapter()->insert($this->getTable('catalog/category_product_index'), $data); -// } } /** diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php index ddf4d15b6b..6a9d2e8b62 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php @@ -283,6 +283,7 @@ public function addCategoryFilter(Mage_Catalog_Model_Category $category, $render array('position'=>'position') ); $this->_categoryIndexJoined = true; + $this->_joinFields['position'] = array('table'=>$alias, 'field'=>'position' ); // $this->joinField( // $alias, // 'catalog/category_product_index', @@ -466,6 +467,7 @@ public function addCountToCategories($categoryCollection) $select = clone $this->getSelect(); $select->reset(Zend_Db_Select::COLUMNS); $select->reset(Zend_Db_Select::GROUP); + $select->reset(Zend_Db_Select::ORDER); $select->distinct(false); $select->join( array('category_count_table' => $this->_productCategoryTable), @@ -792,7 +794,12 @@ public function setOrder($attribute, $dir='desc') array() ); $this->getSelect()->order('_price_order_table.value ' . $dir); - $this->getSelect()->group('e.entity_id'); + + /** + * Distinct we are using for remove duplicates of products which have + * several rows in price index (like grouped products) + */ + $this->getSelect()->distinct(true); } else { parent::setOrder($attribute, $dir); } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Link.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Link.php index 6504af76d6..0f9c394ff8 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Link.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Link.php @@ -1,92 +1,138 @@ - - */ -class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link extends Mage_Core_Model_Mysql4_Abstract -{ - protected $_attributesTable; - protected function _construct() - { - $this->_init('catalog/product_link', 'link_id'); - $this->_attributesTable = $this->getTable('catalog/product_link_attribute'); - } - - public function saveProductLinks($product, $data, $typeId) - { - if (!is_array($data)) { - $data = array(); - } - $attributes = $this->getAttributesByType($typeId); - $deleteCondition = $this->_getWriteAdapter()->quoteInto('product_id=?', $product->getId()) - . $this->_getWriteAdapter()->quoteInto(' AND link_type_id=?', $typeId); - - $this->_getWriteAdapter()->delete($this->getMainTable(), $deleteCondition); - - foreach ($data as $linkedProductId => $linkInfo) { - $this->_getWriteAdapter()->insert($this->getMainTable(), array( - 'product_id' => $product->getId(), - 'linked_product_id' => $linkedProductId, - 'link_type_id' => $typeId - )); - $linkId = $this->_getWriteAdapter()->lastInsertId(); - foreach ($attributes as $attributeInfo) { - $attributeTable = $this->getAttributeTypeTable($attributeInfo['type']); - if ($attributeTable && isset($linkInfo[$attributeInfo['code']])) { - $this->_getWriteAdapter()->insert($attributeTable, array( - 'product_link_attribute_id' => $attributeInfo['id'], - 'link_id' => $linkId, - 'value' => $linkInfo[$attributeInfo['code']] - )); - } - } - } - return $this; - } - - public function getAttributesByType($typeId) - { - $select = $this->_getReadAdapter()->select() - ->from($this->_attributesTable, array( - 'id' => 'product_link_attribute_id', - 'code' => 'product_link_attribute_code', - 'type' => 'data_type' - )) - ->where('link_type_id=?', $typeId); - return $this->_getReadAdapter()->fetchAll($select); - } - - public function getAttributeTypeTable($type) - { - return $this->getTable('catalog/product_link_attribute_'.$type); - } + + */ +class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link extends Mage_Core_Model_Mysql4_Abstract +{ + protected $_attributesTable; + protected function _construct() + { + $this->_init('catalog/product_link', 'link_id'); + $this->_attributesTable = $this->getTable('catalog/product_link_attribute'); + } + + public function saveProductLinks($product, $data, $typeId) + { + if (!is_array($data)) { + $data = array(); + } + $attributes = $this->getAttributesByType($typeId); + $deleteCondition = $this->_getWriteAdapter()->quoteInto('product_id=?', $product->getId()) + . $this->_getWriteAdapter()->quoteInto(' AND link_type_id=?', $typeId); + + $this->_getWriteAdapter()->delete($this->getMainTable(), $deleteCondition); + + foreach ($data as $linkedProductId => $linkInfo) { + $this->_getWriteAdapter()->insert($this->getMainTable(), array( + 'product_id' => $product->getId(), + 'linked_product_id' => $linkedProductId, + 'link_type_id' => $typeId + )); + $linkId = $this->_getWriteAdapter()->lastInsertId(); + foreach ($attributes as $attributeInfo) { + $attributeTable = $this->getAttributeTypeTable($attributeInfo['type']); + if ($attributeTable && isset($linkInfo[$attributeInfo['code']])) { + $this->_getWriteAdapter()->insert($attributeTable, array( + 'product_link_attribute_id' => $attributeInfo['id'], + 'link_id' => $linkId, + 'value' => $linkInfo[$attributeInfo['code']] + )); + } + } + } + return $this; + } + + public function getAttributesByType($typeId) + { + $select = $this->_getReadAdapter()->select() + ->from($this->_attributesTable, array( + 'id' => 'product_link_attribute_id', + 'code' => 'product_link_attribute_code', + 'type' => 'data_type' + )) + ->where('link_type_id=?', $typeId); + return $this->_getReadAdapter()->fetchAll($select); + } + + public function getAttributeTypeTable($type) + { + return $this->getTable('catalog/product_link_attribute_'.$type); + } + +/** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param int $typeId + * @return array + */ + public function getChildrenIds($parentId, $typeId) + { + $childrenIds = array(); + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable(), array('product_id', 'linked_product_id')) + ->where('product_id=?', $parentId) + ->where('link_type_id=?', $typeId); + foreach ($this->_getReadAdapter()->fetchAll($select) as $row) { + $childrenIds[$typeId][$row['linked_product_id']] = $row['linked_product_id']; + } + + return $childrenIds; + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @param int $typeId + * @return array + */ + public function getParentIdsByChild($childId, $typeId) + { + $parentIds = array(); + + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable(), array('product_id', 'linked_product_id')) + ->where('linked_product_id=?', $childId) + ->where('link_type_id=?', $typeId); + foreach ($this->_getReadAdapter()->fetchAll($select) as $row) { + $parentIds[] = $row['product_id']; + } + + return $parentIds; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Status.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Status.php index bc79a9a0f5..6523f1c060 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Status.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Status.php @@ -1,160 +1,215 @@ - - */ -class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Status extends Mage_Core_Model_Mysql4_Abstract -{ - protected function _construct() - { - $this->_init('catalog/product_enabled_index', 'product_id'); - } - - /** - * Product atrribute cache - * - * @var array - */ - protected $_productAttributes = array(); - - /** - * Retrieve product attribute - * - * @return Mage_Eav_Model_Entity_Attribute_Abstract - */ - protected function _getProductAttribute($attribute) - { - if (empty($this->_productAttributes[$attribute])) { - $this->_productAttributes[$attribute] = Mage::getSingleton('catalog/product')->getResource()->getAttribute($attribute); - } - return $this->_productAttributes[$attribute]; - } - - /** - * Refresh enabled index cache - * - * @param int $productId - * @param int $storeId - * - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Status - */ - public function refreshEnabledIndex($productId, $storeId) - { - $statusAttributeId = $this->_getProductAttribute('status')->getId(); - $visibilityAttributeId = $this->_getProductAttribute('visibility')->getId(); - $statusTable = $this->_getProductAttribute('status')->getBackend()->getTable(); - $visibilityTable = $this->_getProductAttribute('visibility')->getBackend()->getTable(); - - $indexTable = $this->getTable('catalog/product_enabled_index'); - - if ($storeId == 0) { - foreach (Mage::app()->getStores() as $store) { - $this->refreshEnabledIndex($productId, $store->getId()); - } - - return $this; - } - - $this->_getWriteAdapter()->delete($indexTable, array( - $this->_getWriteAdapter()->quoteInto('product_id=?', $productId), - $this->_getWriteAdapter()->quoteInto('store_id=?', $storeId) - )); - - $query = "INSERT INTO $indexTable - SELECT - {$productId}, {$storeId}, IFNULL(t_v.value, t_v_default.value) - FROM - {$visibilityTable} AS t_v_default - LEFT JOIN {$visibilityTable} AS `t_v` - ON (t_v.entity_id = t_v_default.entity_id) AND (t_v.attribute_id='{$visibilityAttributeId}') AND (t_v.store_id='{$storeId}') - INNER JOIN {$statusTable} AS `t_s_default` - ON (t_s_default.entity_id = t_v_default.entity_id) AND (t_s_default.attribute_id='{$statusAttributeId}') AND t_s_default.store_id=0 - LEFT JOIN {$statusTable} AS `t_s` - ON (t_s.entity_id = t_v_default.entity_id) AND (t_s.attribute_id='{$statusAttributeId}') AND (t_s.store_id='{$storeId}') - WHERE - t_v_default.entity_id={$productId} - AND t_v_default.attribute_id='{$visibilityAttributeId}' AND t_v_default.store_id=0 - AND (IFNULL(t_s.value, t_s_default.value)=".Mage_Catalog_Model_Product_Status::STATUS_ENABLED.")"; - $this->_getWriteAdapter()->query($query); - - return $this; - } - - /** - * Update product status for store - * - * @param int $productId - * @param int $storId - * @param int $value - * - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Status - */ - public function updateProductStatus($productId, $storId, $value) - { - $statusAttributeId = $this->_getProductAttribute('status')->getId(); - $statusEntityTypeId = $this->_getProductAttribute('status')->getEntityTypeId(); - $statusTable = $this->_getProductAttribute('status')->getBackend()->getTable(); - $refreshIndex = true; - - $prop = array( - 'entity_type_id' => $statusEntityTypeId, - 'attribute_id' => $statusAttributeId, - 'store_id' => $storId, - 'entity_id' => $productId, - 'value' => $value - ); - - $select = $this->_getWriteAdapter()->select() - ->from($statusTable) - ->where('attribute_id=?', $statusAttributeId) - ->where('store_id=?', $storId) - ->where('entity_id=?', $productId); - $row = $this->_getWriteAdapter()->fetchRow($select); - - if ($row) { - if ($row['value'] == $value) { - $refreshIndex = false; - } - else { - $this->_getWriteAdapter()->update($statusTable, $prop, $this->_getWriteAdapter()->quoteInto('value_id=?', $row['value_id'])); - } - } - else { - $this->_getWriteAdapter()->insert($statusTable, $prop); - } - - if ($refreshIndex) { - $this->refreshEnabledIndex($productId, $storId); - } - - return $this; - } + + */ +class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Status extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('catalog/product_enabled_index', 'product_id'); + } + + /** + * Product atrribute cache + * + * @var array + */ + protected $_productAttributes = array(); + + /** + * Retrieve product attribute + * + * @return Mage_Eav_Model_Entity_Attribute_Abstract + */ + protected function _getProductAttribute($attribute) + { + if (empty($this->_productAttributes[$attribute])) { + $this->_productAttributes[$attribute] = Mage::getSingleton('catalog/product')->getResource()->getAttribute($attribute); + } + return $this->_productAttributes[$attribute]; + } + + /** + * Refresh enabled index cache + * + * @param int $productId + * @param int $storeId + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Status + */ + public function refreshEnabledIndex($productId, $storeId) + { + $statusAttributeId = $this->_getProductAttribute('status')->getId(); + $visibilityAttributeId = $this->_getProductAttribute('visibility')->getId(); + $statusTable = $this->_getProductAttribute('status')->getBackend()->getTable(); + $visibilityTable = $this->_getProductAttribute('visibility')->getBackend()->getTable(); + + $indexTable = $this->getTable('catalog/product_enabled_index'); + + if ($storeId == 0) { + foreach (Mage::app()->getStores() as $store) { + $this->refreshEnabledIndex($productId, $store->getId()); + } + + return $this; + } + + $this->_getWriteAdapter()->delete($indexTable, array( + $this->_getWriteAdapter()->quoteInto('product_id=?', $productId), + $this->_getWriteAdapter()->quoteInto('store_id=?', $storeId) + )); + + $query = "INSERT INTO $indexTable + SELECT + {$productId}, {$storeId}, IFNULL(t_v.value, t_v_default.value) + FROM + {$visibilityTable} AS t_v_default + LEFT JOIN {$visibilityTable} AS `t_v` + ON (t_v.entity_id = t_v_default.entity_id) AND (t_v.attribute_id='{$visibilityAttributeId}') AND (t_v.store_id='{$storeId}') + INNER JOIN {$statusTable} AS `t_s_default` + ON (t_s_default.entity_id = t_v_default.entity_id) AND (t_s_default.attribute_id='{$statusAttributeId}') AND t_s_default.store_id=0 + LEFT JOIN {$statusTable} AS `t_s` + ON (t_s.entity_id = t_v_default.entity_id) AND (t_s.attribute_id='{$statusAttributeId}') AND (t_s.store_id='{$storeId}') + WHERE + t_v_default.entity_id={$productId} + AND t_v_default.attribute_id='{$visibilityAttributeId}' AND t_v_default.store_id=0 + AND (IFNULL(t_s.value, t_s_default.value)=".Mage_Catalog_Model_Product_Status::STATUS_ENABLED.")"; + $this->_getWriteAdapter()->query($query); + + return $this; + } + + /** + * Update product status for store + * + * @param int $productId + * @param int $storId + * @param int $value + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Status + */ + public function updateProductStatus($productId, $storId, $value) + { + $statusAttributeId = $this->_getProductAttribute('status')->getId(); + $statusEntityTypeId = $this->_getProductAttribute('status')->getEntityTypeId(); + $statusTable = $this->_getProductAttribute('status')->getBackend()->getTable(); + $refreshIndex = true; + + $prop = array( + 'entity_type_id' => $statusEntityTypeId, + 'attribute_id' => $statusAttributeId, + 'store_id' => $storId, + 'entity_id' => $productId, + 'value' => $value + ); + + $select = $this->_getWriteAdapter()->select() + ->from($statusTable) + ->where('attribute_id=?', $statusAttributeId) + ->where('store_id=?', $storId) + ->where('entity_id=?', $productId); + $row = $this->_getWriteAdapter()->fetchRow($select); + + if ($row) { + if ($row['value'] == $value) { + $refreshIndex = false; + } + else { + $this->_getWriteAdapter()->update($statusTable, $prop, $this->_getWriteAdapter()->quoteInto('value_id=?', $row['value_id'])); + } + } + else { + $this->_getWriteAdapter()->insert($statusTable, $prop); + } + + if ($refreshIndex) { + $this->refreshEnabledIndex($productId, $storId); + } + + return $this; + } + + /** + * Retrieve Product(s) status for store + * Return array where key is a product_id, value - status + * + * @param array|int $productIds + * @param int $storeId + * @return array + */ + public function getProductStatus($productIds, $storeId = null) + { + $statuses = array(); + + $attribute = $this->_getProductAttribute('status'); + $attributeTable = $attribute->getBackend()->getTable(); + + if (!is_array($productIds)) { + $productIds = array($productIds); + } + + if (is_null($storeId) || $storeId == 0) { + $select = $this->_getReadAdapter()->select() + ->from($attributeTable, array('entity_id', 'value')) + ->where('entity_id IN(?)', $productIds) + ->where('attribute_id=?', $attribute->getAttributeId()) + ->where('store_id=?', 0); + $rows = $this->_getWriteAdapter()->fetchPairs($select); + } + else { + $select = $this->_getReadAdapter()->select() + ->from( + array('t1' => $attributeTable), + array('entity_id', 'IFNULL(t2.value, t1.value) as value')) + ->joinLeft( + array('t2' => $attributeTable), + $this->_getReadAdapter()->quoteInto('t1.entity_id = t2.entity_id AND t1.attribute_id = t2.attribute_id AND t2.store_id=?', $storeId), + array() + ) + ->where('t1.store_id = ?', 0) + ->where('t1.attribute_id = ?', $attribute->getAttributeId()) + ->where('t1.entity_id IN(?)', $productIds); + $rows = $this->_getWriteAdapter()->fetchPairs($select); + } + + foreach ($productIds as $productId) { + if (isset($rows[$productId])) { + $statuses[$productId] = $rows[$productId]; + } + else { + $statuses[$productId] = -1; + } + } + + return $statuses; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable.php index ad52a2ae89..dc05b7a099 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable.php @@ -1,54 +1,108 @@ - - */ -class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Type_Configurable extends Mage_Core_Model_Mysql4_Abstract -{ - protected function _construct() - { - $this->_init('catalog/product_super_link', 'link_id'); - } - - public function saveProducts($mainProductId, $productIds) - { - $this->_getWriteAdapter()->delete($this->getMainTable(), - $this->_getWriteAdapter()->quoteInto('parent_id=?', $mainProductId) - ); - foreach ($productIds as $productId) { - $this->_getWriteAdapter()->insert($this->getMainTable(), array( - 'product_id' => $productId, - 'parent_id' => $mainProductId - )); - } - return $this; - } + + */ +class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Type_Configurable extends Mage_Core_Model_Mysql4_Abstract +{ + /** + * Init resource + * + */ + protected function _construct() + { + $this->_init('catalog/product_super_link', 'link_id'); + } + + /** + * Save product + * + * @param int $mainProductId the parent id + * @param array $productIds the children id array + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Type_Configurable + */ + public function saveProducts($mainProductId, $productIds) + { + $this->_getWriteAdapter()->delete($this->getMainTable(), + $this->_getWriteAdapter()->quoteInto('parent_id=?', $mainProductId) + ); + foreach ($productIds as $productId) { + $this->_getWriteAdapter()->insert($this->getMainTable(), array( + 'product_id' => $productId, + 'parent_id' => $mainProductId + )); + } + return $this; + } + + /** + * Retrieve Required children ids + * Return grouped array, ex array( + * group => array(ids) + * ) + * + * @param int $parentId + * @param bool $required + * @return array + */ + public function getChildrenIds($parentId, $required = true) + { + $childrenIds = array(); + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable(), array('product_id', 'parent_id')) + ->where('parent_id=?', $parentId); + foreach ($this->_getReadAdapter()->fetchAll($select) as $row) { + $childrenIds[0][$row['product_id']] = $row['product_id']; + } + + return $childrenIds; + } + + /** + * Retrieve parent ids array by requered child + * + * @param int $childId + * @return array + */ + public function getParentIdsByChild($childId) + { + $parentIds = array(); + + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable(), array('product_id', 'parent_id')) + ->where('product_id=?', $childId); + foreach ($this->_getReadAdapter()->fetchAll($select) as $row) { + $parentIds[] = $row['parent_id']; + } + + return $parentIds; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Model/Sendfriend.php b/app/code/core/Mage/Catalog/Model/Sendfriend.php index 02b6acb127..829b670e19 100644 --- a/app/code/core/Mage/Catalog/Model/Sendfriend.php +++ b/app/code/core/Mage/Catalog/Model/Sendfriend.php @@ -41,7 +41,6 @@ protected function _construct() $this->_init('catalog/sendfriend'); } - public function toOptionArray() { if(!$collection = Mage::registry('config_system_email_template')) { @@ -237,7 +236,7 @@ private function _getSendToFriendCheckType() private function _amountByCookies() { $newTimes = array(); - $oldTimes = Mage::getSingleton('core/cookie')->get($this->_cookieName); + $oldTimes = Mage::app()->getCookie()->get($this->_cookieName); if ($oldTimes){ $oldTimes = explode(',', $oldTimes); foreach ($oldTimes as $time){ @@ -249,7 +248,7 @@ private function _amountByCookies() $amount = count($newTimes); $newTimes[] = time(); - Mage::getSingleton('core/cookie') + Mage::app()->getCookie() ->set($this->_cookieName, implode(',', $newTimes), $this->_period); return $amount; diff --git a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-install-0.7.0.php b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-install-0.7.0.php index 6a8b0dc559..296329ee45 100644 --- a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-install-0.7.0.php +++ b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-install-0.7.0.php @@ -485,7 +485,7 @@ -- DROP TABLE IF EXISTS {$this->getTable('catalog_product_type')}; CREATE TABLE {$this->getTable('catalog_product_type')} ( `type_id` tinyint(3) unsigned NOT NULL auto_increment, - `code` varchar(32) character set cp1251 NOT NULL default '', + `code` varchar(32) NOT NULL default '', PRIMARY KEY (`type_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/app/code/core/Mage/CatalogIndex/Model/Indexer.php b/app/code/core/Mage/CatalogIndex/Model/Indexer.php index cfd9bc3443..ee67e0e436 100644 --- a/app/code/core/Mage/CatalogIndex/Model/Indexer.php +++ b/app/code/core/Mage/CatalogIndex/Model/Indexer.php @@ -348,6 +348,13 @@ public function plainReindex($products = null, $attributes = null, $stores = nul } $store = $group->getDefaultStore(); + /** + * It can happens when website with store was created but store view not yet + */ + if (!$store) { + continue; + } + foreach ($this->_getPriorifiedProductTypes() as $type) { $collection = $this->_getProductCollection($store, $products); $collection->addAttributeToFilter('status', array('in'=>Mage::getModel('catalog/product_status')->getSaleableStatusIds())); diff --git a/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Item/Collection.php b/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Item/Collection.php index c6f20e4e88..3f941dfa44 100644 --- a/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Item/Collection.php +++ b/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Item/Collection.php @@ -1,118 +1,138 @@ - - */ -class Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract -{ - protected function _construct() - { - $this->_init('cataloginventory/stock_item'); - } - - /** - * Add stock filter to collection - * - * @param mixed $stock - * @return Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection - */ - public function addStockFilter($stock) - { - if ($stock instanceof Mage_CatalogInventory_Model_Stock) { - $this->addFieldToFilter('stock_id', $stock->getId()); - } - else { - $this->addFieldToFilter('stock_id', $stock); - } - return $this; - } - - /** - * Add product filter to collection - * - * @param mixed $products - * @return Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection - */ - public function addProductsFilter($products) - { - $productIds = array(); - foreach ($products as $product) { - if ($product instanceof Mage_Catalog_Model_Product) { - $productIds[] = $product->getId(); - } - else { - $productIds[] = $product; - } - } - if (empty($productIds)) { - $productIds[] = false; - $this->_setIsLoaded(true); - } - $this->addFieldToFilter('product_id', array('in'=>$productIds)); - return $this; - } - - public function addManagedFilter($isStockManagedInConfig) - { - if ($isStockManagedInConfig) { - $this->getSelect()->where('(manage_stock = 1 OR use_config_manage_stock = 1)'); - } else { - $this->addFieldToFilter('manage_stock', 1); - } - - return $this; - } - - public function addQtyFilter($comparsionMethod, $qty) - { - $allowedMethods = array('<', '>', '=', '<=', '>=', '<>'); - if (!in_array($comparsionMethod, $allowedMethods)) { - Mage::throwException(Mage::helper('cataloginventory')->__('%s is not correct comparsion method.', $comparsionMethod)); - } - $this->getSelect()->where("qty {$comparsionMethod} ?", $qty); - return $this; - } - - /** - * Load data - * - * @return Varien_Data_Collection_Db - */ - public function load($printQuery = false, $logQuery = false) - { - if (!$this->isLoaded()) { - $this->getSelect()->joinInner(array('_products_table' => $this->getTable('catalog/product')), - 'main_table.product_id=_products_table.entity_id', 'type_id' - ); - } - return parent::load($printQuery, $logQuery); - } -} + + */ +class Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('cataloginventory/stock_item'); + } + + /** + * Add stock filter to collection + * + * @param mixed $stock + * @return Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection + */ + public function addStockFilter($stock) + { + if ($stock instanceof Mage_CatalogInventory_Model_Stock) { + $this->addFieldToFilter('main_table.stock_id', $stock->getId()); + } + else { + $this->addFieldToFilter('main_table.stock_id', $stock); + } + return $this; + } + + /** + * Add product filter to collection + * + * @param mixed $products + * @return Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection + */ + public function addProductsFilter($products) + { + $productIds = array(); + foreach ($products as $product) { + if ($product instanceof Mage_Catalog_Model_Product) { + $productIds[] = $product->getId(); + } + else { + $productIds[] = $product; + } + } + if (empty($productIds)) { + $productIds[] = false; + $this->_setIsLoaded(true); + } + $this->addFieldToFilter('main_table.product_id', array('in'=>$productIds)); + return $this; + } + + /** + * Join Stock Status to collection + * + * @param int $storeId + * @return Mage_CatalogInventory_Model_Mysql4_Stock_Item_Collection + */ + public function joinStockStatus($storeId = null) + { + $websiteId = Mage::app()->getStore($storeId)->getWebsiteId(); + $this->getSelect()->joinLeft( + array('status_table' => $this->getTable('cataloginventory/stock_status')), + '`main_table`.`product_id`=`status_table`.`product_id`' + . ' AND `main_table`.`stock_id`=`status_table`.`stock_id`' + . $this->getConnection()->quoteInto(' AND `status_table`.`website_id`=?', $websiteId), + array('stock_status') + ); + + return $this; + } + + public function addManagedFilter($isStockManagedInConfig) + { + if ($isStockManagedInConfig) { + $this->getSelect()->where('(manage_stock = 1 OR use_config_manage_stock = 1)'); + } else { + $this->addFieldToFilter('manage_stock', 1); + } + + return $this; + } + + public function addQtyFilter($comparsionMethod, $qty) + { + $allowedMethods = array('<', '>', '=', '<=', '>=', '<>'); + if (!in_array($comparsionMethod, $allowedMethods)) { + Mage::throwException(Mage::helper('cataloginventory')->__('%s is not correct comparsion method.', $comparsionMethod)); + } + $this->getSelect()->where("main_table.qty {$comparsionMethod} ?", $qty); + return $this; + } + + /** + * Load data + * + * @return Varien_Data_Collection_Db + */ + public function load($printQuery = false, $logQuery = false) + { + if (!$this->isLoaded()) { + $this->getSelect()->joinInner(array('_products_table' => $this->getTable('catalog/product')), + 'main_table.product_id=_products_table.entity_id', 'type_id' + ); + } + return parent::load($printQuery, $logQuery); + } +} diff --git a/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Status.php b/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Status.php new file mode 100644 index 0000000000..472422356b --- /dev/null +++ b/app/code/core/Mage/CatalogInventory/Model/Mysql4/Stock/Status.php @@ -0,0 +1,197 @@ + + */ +class Mage_CatalogInventory_Model_Mysql4_Stock_Status extends Mage_Core_Model_Mysql4_Abstract +{ + /** + * Resource model initialization + * + */ + protected function _construct() + { + $this->_init('cataloginventory/stock_status', 'product_id'); + } + + /** + * Save Product Status per website + * + * @param Mage_CatalogInventory_Model_Stock_Status $object + * @param int $productId + * @param int $status + * @param float $qty + * @param int $stockId + * @param int|null $websiteId + * @return Mage_CatalogInventory_Model_Mysql4_Stock_Status + */ + public function saveProductStatus(Mage_CatalogInventory_Model_Stock_Status $object, + $productId, $status, $qty = 0, $stockId = 1, $websiteId = null) + { + $websites = array_keys($object->getWebsites($websiteId)); + + foreach ($websites as $websiteId) { + $select = $this->_getWriteAdapter()->select() + ->from($this->getMainTable()) + ->where('product_id=?', $productId) + ->where('website_id=?', $websiteId) + ->where('stock_id=?', $stockId); + if ($row = $this->_getWriteAdapter()->fetchRow($select)) { + $bind = array( + 'qty' => $qty, + 'stock_status' => $status + ); + $where = array( + $this->_getWriteAdapter()->quoteInto('product_id=?', $row['product_id']), + $this->_getWriteAdapter()->quoteInto('website_id=?', $row['website_id']), + $this->_getWriteAdapter()->quoteInto('stock_id=?', $row['stock_id']), + ); + $this->_getWriteAdapter()->update($this->getMainTable(), $bind, $where); + } + else { + $bind = array( + 'product_id' => $productId, + 'website_id' => $websiteId, + 'stock_id' => $stockId, + 'qty' => $qty, + 'stock_status' => $status + ); + $this->_getWriteAdapter()->insert($this->getMainTable(), $bind); + } + } + + return $this; + } + + /** + * Retrieve product status + * Return array as key product id, value - stock status + * + * @param int|array $productIds + * @param int $websiteId + * @param int $stockId + * + * @return array + */ + public function getProductStatus($productIds, $websiteId, $stockId = 1) + { + if (!is_array($productIds)) { + $productIds = array($productIds); + } + + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable(), array('product_id', 'stock_status')) + ->where('product_id IN(?)', $productIds) + ->where('stock_id=?', $stockId) + ->where('website_id=?', $websiteId); + return $this->_getReadAdapter()->fetchPairs($select); + } + + /** + * Retrieve product(s) data array + * + * @param int|array $productIds + * @param int $websiteId + * @param int $stockId + * + * @return array + */ + public function getProductData($productIds, $websiteId, $stockId = 1) + { + if (!is_array($productIds)) { + $productIds = array($productIds); + } + + $data = array(); + + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable()) + ->where('product_id IN(?)', $productIds) + ->where('stock_id=?', $stockId) + ->where('website_id=?', $websiteId); + $query = $this->_getReadAdapter()->query($select); + while ($row = $query->fetch()) { + $data[$row['product_id']] = $row; + } + return $data; + } + + /** + * Retrieve websites and default stores + * Return array as key website_id, value store_id + * + * @return array + */ + public function getWebsiteStores() { + $select = Mage::getModel('core/website')->getDefaultStoresSelect(false); + return $this->_getReadAdapter()->fetchPairs($select); + } + + /** + * Retrieve Product Type + * + * @param array|int $productIds + * @return array + */ + public function getProductsType($productIds) + { + if (!is_array($productIds)) { + $productIds = array($productIds); + } + + $select = $this->_getReadAdapter()->select() + ->from( + array('e' => $this->getTable('catalog/product')), + array('entity_id', 'type_id')) + ->where('entity_id IN(?)', $productIds); + return $this->_getReadAdapter()->fetchPairs($select); + } + + /** + * Retrieve Product part Collection array + * Return array as key product id, value product type + * + * @param int $lastEntityId + * @param int $limit + * @return array + */ + public function getProductCollection($lastEntityId = 0, $limit = 1000) { + $select = $this->_getReadAdapter()->select() + ->from( + array('e' => $this->getTable('catalog/product')), + array('entity_id', 'type_id')) + ->order('entity_id ASC') + ->where('entity_id>?', $lastEntityId) + ->limit($limit); + return $this->_getReadAdapter()->fetchPairs($select); + } +} + diff --git a/app/code/core/Mage/CatalogInventory/Model/Observer.php b/app/code/core/Mage/CatalogInventory/Model/Observer.php index a5d48e9acf..5d2cc72aff 100644 --- a/app/code/core/Mage/CatalogInventory/Model/Observer.php +++ b/app/code/core/Mage/CatalogInventory/Model/Observer.php @@ -1,399 +1,417 @@ - - */ -class Mage_CatalogInventory_Model_Observer -{ - /** - * Product qty's checked - * data is valid if you check quote item qty and use singleton instance - * - * @var array - */ - protected $_checkedProductsQty = array(); - - /** - * Add stock information to product - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function addInventoryData($observer) - { - $product = $observer->getEvent()->getProduct(); - if($product instanceof Mage_Catalog_Model_Product) { - Mage::getModel('cataloginventory/stock_item')->assignProduct($product); - } - return $this; - } - - public function addInventoryDataToCollection($observer) - { - $productCollection = $observer->getEvent()->getCollection(); - Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection); - return $this; - } - - /** - * Saving product inventory data - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function saveInventoryData($observer) - { - $product = $observer->getEvent()->getProduct(); - - if (is_null($product->getStockData())) { - return $this; - } - - $item = $product->getStockItem(); - if (!$item) { - $item = Mage::getModel('cataloginventory/stock_item'); - } - $this->_prepareItemForSave($item, $product); - $item->save(); - return $this; - } - - /** - * Copy product inventory data - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function copyInventoryData($observer) - { - $newProduct = $observer->getEvent()->getNewProduct(); - - $newProduct->unsStockItem(); - $newProduct->setStockData(array( - 'use_config_min_qty' => 1, - 'use_config_min_sale_qty' => 1, - 'use_config_max_sale_qty' => 1, - 'use_config_backorders' => 1, - 'use_config_notify_stock_qty'=> 1 - )); - - return $this; - } - - protected function _prepareItemForSave($item, $product) - { - $item->addData($product->getStockData()) - ->setProductId($product->getId()) - ->setStockId($item->getStockId()) - ->setProduct($product); - if (!is_null($product->getData('stock_data/min_qty')) - && is_null($product->getData('stock_data/use_config_min_qty'))) { - $item->setData('use_config_min_qty', false); - } - if (!is_null($product->getData('stock_data/min_sale_qty')) - && is_null($product->getData('stock_data/use_config_min_sale_qty'))) { - $item->setData('use_config_min_sale_qty', false); - } - if (!is_null($product->getData('stock_data/max_sale_qty')) - && is_null($product->getData('stock_data/use_config_max_sale_qty'))) { - $item->setData('use_config_max_sale_qty', false); - } - if (!is_null($product->getData('stock_data/backorders')) - && is_null($product->getData('stock_data/use_config_backorders'))) { - $item->setData('use_config_backorders', false); - } - if (!is_null($product->getData('stock_data/notify_stock_qty')) - && is_null($product->getData('stock_data/use_config_notify_stock_qty'))) { - $item->setData('use_config_notify_stock_qty', false); - } - return $this; - - } - - /** - * Check product inventory data when quote item quantity declaring - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function checkQuoteItemQty($observer) - { - $quoteItem = $observer->getEvent()->getItem(); - /* @var $quoteItem Mage_Sales_Model_Quote_Item */ - if (!$quoteItem || !$quoteItem->getProductId() || $quoteItem->getQuote()->getIsSuperMode()) { - return $this; - } - - /** - * Get Qty - */ - $qty = $quoteItem->getQty(); - - /** - * Check item for options - */ - if (($options = $quoteItem->getQtyOptions()) && $qty > 0) { - $qty = $quoteItem->getProduct()->getTypeInstance()->prepareQuoteItemQty($qty); - $quoteItem->setData('qty', $qty); - - foreach ($options as $option) { - /* @var $option Mage_Sales_Model_Quote_Item_Option */ - $optionQty = $qty * $option->getValue(); - $increaseOptionQty = ($quoteItem->getQtyToAdd() ? $quoteItem->getQtyToAdd() : $qty) * $option->getValue(); - - $stockItem = $option->getProduct()->getStockItem(); - /* @var $stockItem Mage_CatalogInventory_Model_Stock_Item */ - if (!$stockItem instanceof Mage_CatalogInventory_Model_Stock_Item) { - Mage::throwException(Mage::helper('cataloginventory')->__('Stock item for Product in option is not valid')); - } - - $qtyForCheck = $this->_getProductQtyForCheck($option->getProduct()->getId(), $increaseOptionQty); - - $result = $stockItem->checkQuoteItemQty($optionQty, $qtyForCheck, $option->getValue()); - - if (!is_null($result->getItemIsQtyDecimal())) { - $option->setIsQtyDecimal($result->getItemIsQtyDecimal()); - } - - if ($result->getHasQtyOptionUpdate()) { - $quoteItem->updateQtyOption($option, $result->getOrigQty()); - $option->setValue($result->getOrigQty()); - /** - * if option's qty was updates we also need to update quote item qty - */ - $quoteItem->setData('qty', intval($qty)); - } - if (!is_null($result->getMessage())) { - $option->setMessage($result->getMessage()); - } - if (!is_null($result->getItemBackorders())) { - $option->setBackorders($result->getItemBackorders()); - } - - if ($result->getHasError()) { - $option->setHasError(true); - $quoteItem->setHasError(true) - ->setMessage($result->getQuoteMessage()); - $quoteItem->getQuote()->setHasError(true) - ->addMessage($result->getQuoteMessage(), $result->getQuoteMessageIndex()); - } - } - } - else { - $stockItem = $quoteItem->getProduct()->getStockItem(); - /* @var $stockItem Mage_CatalogInventory_Model_Stock_Item */ - if (!$stockItem instanceof Mage_CatalogInventory_Model_Stock_Item) { - Mage::throwException(Mage::helper('cataloginventory')->__('Stock item for Product is not valid')); - } - - - /** - * When we work with subitem (as subproduct of bundle or configurable product) - */ - if ($quoteItem->getParentItem()) { - $rowQty = $quoteItem->getParentItem()->getQty()*$qty; - /** - * we are using 0 because original qty was processed - */ - $qtyForCheck = $this->_getProductQtyForCheck($quoteItem->getProduct()->getId(), 0); - } - else { - $increaseQty = $quoteItem->getQtyToAdd() ? $quoteItem->getQtyToAdd() : $qty; - $rowQty = $qty; - $qtyForCheck = $this->_getProductQtyForCheck($quoteItem->getProduct()->getId(), $increaseQty); - } - - $result = $stockItem->checkQuoteItemQty($rowQty, $qtyForCheck, $qty); - - if (!is_null($result->getItemIsQtyDecimal())) { - $quoteItem->setIsQtyDecimal($result->getItemIsQtyDecimal()); - if ($quoteItem->getParentItem()) { - $quoteItem->getParentItem()->setIsQtyDecimal($result->getItemIsQtyDecimal()); - } - } - - /** - * Just base (parent) item qty can be changed - * qty of child products are declared just during add process - * exception for updating also managed by product type - */ - if ($result->getHasQtyOptionUpdate() - && (!$quoteItem->getParentItem() - || $quoteItem->getParentItem()->getProduct()->getTypeInstance() - ->getForceChildItemQtyChanges())) { - $quoteItem->setData('qty', $result->getOrigQty()); - } - - if (!is_null($result->getItemUseOldQty())) { - $quoteItem->setUseOldQty($result->getItemUseOldQty()); - } - if (!is_null($result->getMessage())) { - $quoteItem->setMessage($result->getMessage()); - if ($quoteItem->getParentItem()) { - $quoteItem->getParentItem()->setMessage($result->getMessage()); - } - } - if (!is_null($result->getItemBackorders())) { - $quoteItem->setBackorders($result->getItemBackorders()); - } - - if ($result->getHasError()) { - $quoteItem->setHasError(true); - $quoteItem->getQuote()->setHasError(true) - ->addMessage($result->getQuoteMessage(), $result->getQuoteMessageIndex()); - } - } - - return $this; - } - - /** - * Get product qty includes information from all quote items - * Need be used only in sungleton mode - * - * @param int $productId - * @param float $itemQty - */ - protected function _getProductQtyForCheck($productId, $itemQty) - { - $qty = $itemQty; - if (isset($this->_checkedProductsQty[$productId])) { - $qty += $this->_checkedProductsQty[$productId]; - } - $this->_checkedProductsQty[$productId] = $qty; - return $qty; - } - - /** - * Lock DB rows for order products - * - * We need do it for resolving problems with inventory on placing - * some orders in one time - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function lockOrderInventoryData($observer) - { - $order = $observer->getEvent()->getOrder(); - $productIds = array(); - - /** - * Do lock only for new order - */ - if ($order->getId()) { - return $this; - } - - if ($order) { - foreach ($order->getAllItems() as $item) { - $productIds[] = $item->getProductId(); - } - } - - if (!empty($productIds)) { - Mage::getSingleton('cataloginventory/stock')->lockProductItems($productIds); - } - - return $this; - } - - /** - * Register saving order item - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function createOrderItem($observer) - { - $item = $observer->getEvent()->getItem(); - /** - * Before creating order item need subtract ordered qty from product stock - */ - - $children = $item->getChildrenItems(); - - if (!$item->getId() && empty($children)) { - Mage::getSingleton('cataloginventory/stock')->registerItemSale($item); - } - - return $this; - } - - /** - * Cancel order item - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function cancelOrderItem($observer) - { - $item = $observer->getEvent()->getItem(); - - $children = $item->getChildrenItems(); - $qty = $item->getQtyOrdered() - max($item->getQtyShipped(), $item->getQtyInvoiced()) - $item->getQtyCanceled(); - - if ($item->getId() && ($productId = $item->getProductId()) && empty($children) && $qty) { - Mage::getSingleton('cataloginventory/stock')->backItemQty($productId, $qty); - } - - return $this; - } - - /** - * Back refunded item qty to stock - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function refundOrderItem($observer) - { - $item = $observer->getEvent()->getCreditmemoItem(); - if ($item->getId() && $item->getBackToStock() && ($productId = $item->getProductId()) && ($qty = $item->getQty())) { - Mage::getSingleton('cataloginventory/stock')->backItemQty($productId, $qty); - } - return $this; - } - - /** - * Update items stock status and low stock date. - * - * @param Varien_Event_Observer $observer - * @return Mage_CatalogInventory_Model_Observer - */ - public function updateItemsStockUponConfigChange($observer) - { - Mage::getResourceSingleton('cataloginventory/stock')->updateSetOutOfStock(); - Mage::getResourceSingleton('cataloginventory/stock')->updateSetInStock(); - Mage::getResourceSingleton('cataloginventory/stock')->updateLowStockDate(); - return $this; - } -} + + */ +class Mage_CatalogInventory_Model_Observer +{ + /** + * Product qty's checked + * data is valid if you check quote item qty and use singleton instance + * + * @var array + */ + protected $_checkedProductsQty = array(); + + /** + * Add stock information to product + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function addInventoryData($observer) + { + $product = $observer->getEvent()->getProduct(); + if ($product instanceof Mage_Catalog_Model_Product) { + Mage::getModel('cataloginventory/stock_item')->assignProduct($product); + } + return $this; + } + + public function addInventoryDataToCollection($observer) + { + $productCollection = $observer->getEvent()->getCollection(); + Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection); + return $this; + } + + /** + * Saving product inventory data + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function saveInventoryData($observer) + { + $product = $observer->getEvent()->getProduct(); + + if (is_null($product->getStockData())) { + if ($product->dataHasChangedFor('status')) { + Mage::getSingleton('cataloginventory/stock_status') + ->updateStatus($product->getId()); + } + return $this; + } + + $item = $product->getStockItem(); + if (!$item) { + $item = Mage::getModel('cataloginventory/stock_item'); + } + $this->_prepareItemForSave($item, $product); + $item->save(); + return $this; + } + + /** + * Copy product inventory data + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function copyInventoryData($observer) + { + $newProduct = $observer->getEvent()->getNewProduct(); + + $newProduct->unsStockItem(); + $newProduct->setStockData(array( + 'use_config_min_qty' => 1, + 'use_config_min_sale_qty' => 1, + 'use_config_max_sale_qty' => 1, + 'use_config_backorders' => 1, + 'use_config_notify_stock_qty'=> 1 + )); + + return $this; + } + + protected function _prepareItemForSave($item, $product) + { + $item->addData($product->getStockData()) + ->setProductId($product->getId()) + ->setStockId($item->getStockId()) + ->setProduct($product); + if (!is_null($product->getData('stock_data/min_qty')) + && is_null($product->getData('stock_data/use_config_min_qty'))) { + $item->setData('use_config_min_qty', false); + } + if (!is_null($product->getData('stock_data/min_sale_qty')) + && is_null($product->getData('stock_data/use_config_min_sale_qty'))) { + $item->setData('use_config_min_sale_qty', false); + } + if (!is_null($product->getData('stock_data/max_sale_qty')) + && is_null($product->getData('stock_data/use_config_max_sale_qty'))) { + $item->setData('use_config_max_sale_qty', false); + } + if (!is_null($product->getData('stock_data/backorders')) + && is_null($product->getData('stock_data/use_config_backorders'))) { + $item->setData('use_config_backorders', false); + } + if (!is_null($product->getData('stock_data/notify_stock_qty')) + && is_null($product->getData('stock_data/use_config_notify_stock_qty'))) { + $item->setData('use_config_notify_stock_qty', false); + } + return $this; + + } + + /** + * Check product inventory data when quote item quantity declaring + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function checkQuoteItemQty($observer) + { + $quoteItem = $observer->getEvent()->getItem(); + /* @var $quoteItem Mage_Sales_Model_Quote_Item */ + if (!$quoteItem || !$quoteItem->getProductId() || $quoteItem->getQuote()->getIsSuperMode()) { + return $this; + } + + /** + * Get Qty + */ + $qty = $quoteItem->getQty(); + + /** + * Check item for options + */ + if (($options = $quoteItem->getQtyOptions()) && $qty > 0) { + $qty = $quoteItem->getProduct()->getTypeInstance()->prepareQuoteItemQty($qty); + $quoteItem->setData('qty', $qty); + + foreach ($options as $option) { + /* @var $option Mage_Sales_Model_Quote_Item_Option */ + $optionQty = $qty * $option->getValue(); + $increaseOptionQty = ($quoteItem->getQtyToAdd() ? $quoteItem->getQtyToAdd() : $qty) * $option->getValue(); + + $stockItem = $option->getProduct()->getStockItem(); + /* @var $stockItem Mage_CatalogInventory_Model_Stock_Item */ + if (!$stockItem instanceof Mage_CatalogInventory_Model_Stock_Item) { + Mage::throwException(Mage::helper('cataloginventory')->__('Stock item for Product in option is not valid')); + } + + $qtyForCheck = $this->_getProductQtyForCheck($option->getProduct()->getId(), $increaseOptionQty); + + $result = $stockItem->checkQuoteItemQty($optionQty, $qtyForCheck, $option->getValue()); + + if (!is_null($result->getItemIsQtyDecimal())) { + $option->setIsQtyDecimal($result->getItemIsQtyDecimal()); + } + + if ($result->getHasQtyOptionUpdate()) { + $quoteItem->updateQtyOption($option, $result->getOrigQty()); + $option->setValue($result->getOrigQty()); + /** + * if option's qty was updates we also need to update quote item qty + */ + $quoteItem->setData('qty', intval($qty)); + } + if (!is_null($result->getMessage())) { + $option->setMessage($result->getMessage()); + } + if (!is_null($result->getItemBackorders())) { + $option->setBackorders($result->getItemBackorders()); + } + + if ($result->getHasError()) { + $option->setHasError(true); + $quoteItem->setHasError(true) + ->setMessage($result->getQuoteMessage()); + $quoteItem->getQuote()->setHasError(true) + ->addMessage($result->getQuoteMessage(), $result->getQuoteMessageIndex()); + } + } + } + else { + $stockItem = $quoteItem->getProduct()->getStockItem(); + /* @var $stockItem Mage_CatalogInventory_Model_Stock_Item */ + if (!$stockItem instanceof Mage_CatalogInventory_Model_Stock_Item) { + Mage::throwException(Mage::helper('cataloginventory')->__('Stock item for Product is not valid')); + } + + + /** + * When we work with subitem (as subproduct of bundle or configurable product) + */ + if ($quoteItem->getParentItem()) { + $rowQty = $quoteItem->getParentItem()->getQty()*$qty; + /** + * we are using 0 because original qty was processed + */ + $qtyForCheck = $this->_getProductQtyForCheck($quoteItem->getProduct()->getId(), 0); + } + else { + $increaseQty = $quoteItem->getQtyToAdd() ? $quoteItem->getQtyToAdd() : $qty; + $rowQty = $qty; + $qtyForCheck = $this->_getProductQtyForCheck($quoteItem->getProduct()->getId(), $increaseQty); + } + + $result = $stockItem->checkQuoteItemQty($rowQty, $qtyForCheck, $qty); + + if (!is_null($result->getItemIsQtyDecimal())) { + $quoteItem->setIsQtyDecimal($result->getItemIsQtyDecimal()); + if ($quoteItem->getParentItem()) { + $quoteItem->getParentItem()->setIsQtyDecimal($result->getItemIsQtyDecimal()); + } + } + + /** + * Just base (parent) item qty can be changed + * qty of child products are declared just during add process + * exception for updating also managed by product type + */ + if ($result->getHasQtyOptionUpdate() + && (!$quoteItem->getParentItem() + || $quoteItem->getParentItem()->getProduct()->getTypeInstance() + ->getForceChildItemQtyChanges())) { + $quoteItem->setData('qty', $result->getOrigQty()); + } + + if (!is_null($result->getItemUseOldQty())) { + $quoteItem->setUseOldQty($result->getItemUseOldQty()); + } + if (!is_null($result->getMessage())) { + $quoteItem->setMessage($result->getMessage()); + if ($quoteItem->getParentItem()) { + $quoteItem->getParentItem()->setMessage($result->getMessage()); + } + } + if (!is_null($result->getItemBackorders())) { + $quoteItem->setBackorders($result->getItemBackorders()); + } + + if ($result->getHasError()) { + $quoteItem->setHasError(true); + $quoteItem->getQuote()->setHasError(true) + ->addMessage($result->getQuoteMessage(), $result->getQuoteMessageIndex()); + } + } + + return $this; + } + + /** + * Get product qty includes information from all quote items + * Need be used only in sungleton mode + * + * @param int $productId + * @param float $itemQty + */ + protected function _getProductQtyForCheck($productId, $itemQty) + { + $qty = $itemQty; + if (isset($this->_checkedProductsQty[$productId])) { + $qty += $this->_checkedProductsQty[$productId]; + } + $this->_checkedProductsQty[$productId] = $qty; + return $qty; + } + + /** + * Lock DB rows for order products + * + * We need do it for resolving problems with inventory on placing + * some orders in one time + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function lockOrderInventoryData($observer) + { + $order = $observer->getEvent()->getOrder(); + $productIds = array(); + + /** + * Do lock only for new order + */ + if ($order->getId()) { + return $this; + } + + if ($order) { + foreach ($order->getAllItems() as $item) { + $productIds[] = $item->getProductId(); + } + } + + if (!empty($productIds)) { + Mage::getSingleton('cataloginventory/stock')->lockProductItems($productIds); + } + + return $this; + } + + /** + * Register saving order item + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function createOrderItem($observer) + { + $item = $observer->getEvent()->getItem(); + /** + * Before creating order item need subtract ordered qty from product stock + */ + + $children = $item->getChildrenItems(); + + if (!$item->getId() && empty($children)) { + Mage::getSingleton('cataloginventory/stock')->registerItemSale($item); + } + + return $this; + } + + /** + * Cancel order item + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function cancelOrderItem($observer) + { + $item = $observer->getEvent()->getItem(); + + $children = $item->getChildrenItems(); + $qty = $item->getQtyOrdered() - max($item->getQtyShipped(), $item->getQtyInvoiced()) - $item->getQtyCanceled(); + + if ($item->getId() && ($productId = $item->getProductId()) && empty($children) && $qty) { + Mage::getSingleton('cataloginventory/stock')->backItemQty($productId, $qty); + } + + return $this; + } + + /** + * Back refunded item qty to stock + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function refundOrderItem($observer) + { + $item = $observer->getEvent()->getCreditmemoItem(); + if ($item->getId() && $item->getBackToStock() && ($productId = $item->getProductId()) && ($qty = $item->getQty())) { + Mage::getSingleton('cataloginventory/stock')->backItemQty($productId, $qty); + } + return $this; + } + + /** + * Update items stock status and low stock date. + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function updateItemsStockUponConfigChange($observer) + { + Mage::getResourceSingleton('cataloginventory/stock')->updateSetOutOfStock(); + Mage::getResourceSingleton('cataloginventory/stock')->updateSetInStock(); + Mage::getResourceSingleton('cataloginventory/stock')->updateLowStockDate(); + return $this; + } + + /** + * Update Only product status observer + * + * @param Varien_Event_Observer $observer + * @return Mage_CatalogInventory_Model_Observer + */ + public function productStatusUpdate(Varien_Event_Observer $observer) + { + $productId = $observer->getEvent()->getProductId(); + Mage::getSingleton('cataloginventory/stock_status') + ->updateStatus($productId); + return $this; + } +} diff --git a/app/code/core/Mage/CatalogInventory/Model/Source/Stock.php b/app/code/core/Mage/CatalogInventory/Model/Source/Stock.php new file mode 100644 index 0000000000..d8364388ce --- /dev/null +++ b/app/code/core/Mage/CatalogInventory/Model/Source/Stock.php @@ -0,0 +1,55 @@ + + */ +class Mage_CatalogInventory_Model_Source_Stock +{ + /** + * Retrieve option array + * + * @return array + */ + public function toOptionArray() + { + return array( + array( + 'value' => Mage_CatalogInventory_Model_Stock::STOCK_IN_STOCK, + 'label' => Mage::helper('cataloginventory')->__('In Stock') + ), + array( + 'value' => Mage_CatalogInventory_Model_Stock::STOCK_OUT_OF_STOCK, + 'label' => Mage::helper('cataloginventory')->__('Out of Stock') + ), + ); + } +} diff --git a/app/code/core/Mage/CatalogInventory/Model/Stock.php b/app/code/core/Mage/CatalogInventory/Model/Stock.php index f5d0e00beb..f82730dfcf 100644 --- a/app/code/core/Mage/CatalogInventory/Model/Stock.php +++ b/app/code/core/Mage/CatalogInventory/Model/Stock.php @@ -1,161 +1,165 @@ - - */ -class Mage_CatalogInventory_Model_Stock extends Mage_Core_Model_Abstract -{ - const BACKORDERS_NO = 0; - const BACKORDERS_YES_NONOTIFY = 1; - const BACKORDERS_YES_NOTIFY = 2; - - /* deprecated */ - const BACKORDERS_BELOW = 1; - const BACKORDERS_YES = 2; - - protected function _construct() - { - $this->_init('cataloginventory/stock'); - } - - /** - * Retrieve stock identifier - * - * @return int - */ - public function getId() - { - return 1; - } - - /** - * Add stock item objects to products - * - * @param collection $products - * @return Mage_CatalogInventory_Model_Stock - */ - public function addItemsToProducts($productCollection) - { - $items = $this->getItemCollection() - ->addProductsFilter($productCollection) - ->load(); - foreach ($items as $item) { - foreach($productCollection as $product){ - if($product->getId()==$item->getProductId()){ - if($product instanceof Mage_Catalog_Model_Product) { - $item->assignProduct($product); - } - } - } - } - return $this; - } - - /** - * Retrieve items collection object with stock filter - * - * @return unknown - */ - public function getItemCollection() - { - return Mage::getResourceModel('cataloginventory/stock_item_collection') - ->addStockFilter($this->getId()); - } - - /** - * Subtract ordered qty for product - * - * @param Varien_Object $item - * @return Mage_CatalogInventory_Model_Stock - */ - public function registerItemSale(Varien_Object $item) - { - if ($productId = $item->getProductId()) { - $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId); - if (Mage::helper('catalogInventory')->isQty($stockItem->getTypeId())) { - if ($item->getStoreId()) { - $stockItem->setStoreId($item->getStoreId()); - } - if ($stockItem->checkQty($item->getQtyOrdered()) || Mage::app()->getStore()->isAdmin()) { - $stockItem->subtractQty($item->getQtyOrdered()); - $stockItem->save(); - } - } - } - else { - Mage::throwException(Mage::helper('cataloginventory')->__('Can not specify product identifier for order item')); - } - return $this; - } - - /** - * Get back to stock (when order is canceled or whatever else) - * - * @param int $productId - * @param numeric $qty - * @return Mage_CatalogInventory_Model_Stock - */ - public function backItemQty($productId, $qty) - { - $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId); - if ($stockItem->getId() && Mage::helper('catalogInventory')->isQty($stockItem->getTypeId())) { - $stockItem->addQty($qty); - if ($stockItem->getCanBackInStock() && $stockItem->getQty() > $stockItem->getMinQty()) { - $stockItem->setIsInStock(true) - ->setStockStatusChangedAutomaticallyFlag(true); - } - $stockItem->save(); - } - return $this; - } - - /** - * Lock stock items for product ids array - * - * @param array $productIds - * @return Mage_CatalogInventory_Model_Stock - */ - public function lockProductItems($productIds) - { - $this->_getResource()->lockProductItems($this, $productIds); - return $this; - } - - /** - * Enter description here... - * - * @param Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection $collection - * @return Mage_CatalogInventory_Model_Stock $this - */ - public function addInStockFilterToCollection($collection) - { - $this->getResource()->setInStockFilterToCollection($collection); - return $this; - } -} + + */ +class Mage_CatalogInventory_Model_Stock extends Mage_Core_Model_Abstract +{ + const BACKORDERS_NO = 0; + const BACKORDERS_YES_NONOTIFY = 1; + const BACKORDERS_YES_NOTIFY = 2; + + /* deprecated */ + const BACKORDERS_BELOW = 1; + const BACKORDERS_YES = 2; + + const STOCK_OUT_OF_STOCK = 0; + const STOCK_IN_STOCK = 1; + + protected function _construct() + { + $this->_init('cataloginventory/stock'); + } + + /** + * Retrieve stock identifier + * + * @return int + */ + public function getId() + { + return 1; + } + + /** + * Add stock item objects to products + * + * @param collection $products + * @return Mage_CatalogInventory_Model_Stock + */ + public function addItemsToProducts($productCollection) + { + $items = $this->getItemCollection() + ->addProductsFilter($productCollection) + ->joinStockStatus($productCollection->getStoreId()) + ->load(); + foreach ($items as $item) { + foreach($productCollection as $product){ + if($product->getId()==$item->getProductId()){ + if($product instanceof Mage_Catalog_Model_Product) { + $item->assignProduct($product); + } + } + } + } + return $this; + } + + /** + * Retrieve items collection object with stock filter + * + * @return unknown + */ + public function getItemCollection() + { + return Mage::getResourceModel('cataloginventory/stock_item_collection') + ->addStockFilter($this->getId()); + } + + /** + * Subtract ordered qty for product + * + * @param Varien_Object $item + * @return Mage_CatalogInventory_Model_Stock + */ + public function registerItemSale(Varien_Object $item) + { + if ($productId = $item->getProductId()) { + $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId); + if (Mage::helper('catalogInventory')->isQty($stockItem->getTypeId())) { + if ($item->getStoreId()) { + $stockItem->setStoreId($item->getStoreId()); + } + if ($stockItem->checkQty($item->getQtyOrdered()) || Mage::app()->getStore()->isAdmin()) { + $stockItem->subtractQty($item->getQtyOrdered()); + $stockItem->save(); + } + } + } + else { + Mage::throwException(Mage::helper('cataloginventory')->__('Can not specify product identifier for order item')); + } + return $this; + } + + /** + * Get back to stock (when order is canceled or whatever else) + * + * @param int $productId + * @param numeric $qty + * @return Mage_CatalogInventory_Model_Stock + */ + public function backItemQty($productId, $qty) + { + $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId); + if ($stockItem->getId() && Mage::helper('catalogInventory')->isQty($stockItem->getTypeId())) { + $stockItem->addQty($qty); + if ($stockItem->getCanBackInStock() && $stockItem->getQty() > $stockItem->getMinQty()) { + $stockItem->setIsInStock(true) + ->setStockStatusChangedAutomaticallyFlag(true); + } + $stockItem->save(); + } + return $this; + } + + /** + * Lock stock items for product ids array + * + * @param array $productIds + * @return Mage_CatalogInventory_Model_Stock + */ + public function lockProductItems($productIds) + { + $this->_getResource()->lockProductItems($this, $productIds); + return $this; + } + + /** + * Enter description here... + * + * @param Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection $collection + * @return Mage_CatalogInventory_Model_Stock $this + */ + public function addInStockFilterToCollection($collection) + { + $this->getResource()->setInStockFilterToCollection($collection); + return $this; + } +} diff --git a/app/code/core/Mage/CatalogInventory/Model/Stock/Item.php b/app/code/core/Mage/CatalogInventory/Model/Stock/Item.php index 5e204ec8a6..6b3f1c0bc1 100644 --- a/app/code/core/Mage/CatalogInventory/Model/Stock/Item.php +++ b/app/code/core/Mage/CatalogInventory/Model/Stock/Item.php @@ -1,413 +1,423 @@ - - */ -class Mage_CatalogInventory_Model_Stock_Item extends Mage_Core_Model_Abstract -{ - const XML_PATH_GLOBAL = 'cataloginventory/options/'; - const XML_PATH_CAN_SUBTRACT = 'cataloginventory/options/can_subtract'; - const XML_PATH_CAN_BACK_IN_STOCK = 'cataloginventory/options/can_back_in_stock'; - - const XML_PATH_ITEM = 'cataloginventory/item_options/'; - const XML_PATH_MIN_QTY = 'cataloginventory/item_options/min_qty'; - const XML_PATH_MIN_SALE_QTY = 'cataloginventory/item_options/min_sale_qty'; - const XML_PATH_MAX_SALE_QTY = 'cataloginventory/item_options/max_sale_qty'; - const XML_PATH_BACKORDERS = 'cataloginventory/item_options/backorders'; - const XML_PATH_NOTIFY_STOCK_QTY = 'cataloginventory/item_options/notify_stock_qty'; - const XML_PATH_MANAGE_STOCK = 'cataloginventory/item_options/manage_stock'; - - // cataloginventory/cart_options/... - - protected function _construct() - { - $this->_init('cataloginventory/stock_item'); - } - - /** - * Retrieve stock identifier - * - * @todo multi stock - * @return int - */ - public function getStockId() - { - return 1; - } - - public function getProductId() - { - return $this->_getData('product_id'); - } - - /** - * Load item data by product - * - * @param mixed $product - * @return Mage_CatalogInventory_Model_Stock_Item - */ - public function loadByProduct($product) - { - if ($product instanceof Mage_Catalog_Model_Product) { - $product = $product->getId(); - } - $this->_getResource()->loadByProductId($this, $product); - $this->setOrigData(); - return $this; - } - - /** - * Subtract quote item quantity - * - * @param decimal $qty - * @return Mage_CatalogInventory_Model_Stock_Item - */ - public function subtractQty($qty) - { - if (!$this->getManageStock()) { - return $this; - } - $config = Mage::getStoreConfigFlag(self::XML_PATH_CAN_SUBTRACT); - if (!$config) { - return $this; - } - $this->setQty($this->getQty()-$qty); - return $this; - } - - public function addQty($qty) - { - if (!$this->getManageStock()) { - return $this; - } - $config = Mage::getStoreConfigFlag(self::XML_PATH_CAN_SUBTRACT); - if (!$config) { - return $this; - } - - $this->setQty($this->getQty()+$qty); - return $this; - } - - public function getStoreId() - { - $storeId = $this->getData('store_id'); - if (is_null($storeId)) { - if ($this->getProduct()) { - $storeId = $this->getProduct()->getStoreId(); - } - else { - $storeId = Mage::app()->getStore()->getId(); - } - $this->setData('store_id', $storeId); - } - return $storeId; - } - - /** - * Adding stoc data to product - * - * @param Mage_Catalog_Model_Product $product - * @return Mage_CatalogInventory_Model_Stock_Item - */ - public function assignProduct(Mage_Catalog_Model_Product $product) - { - if (!$this->getId() || !$this->getProductId()) { - $this->_getResource()->loadByProductId($this, $product->getId()); - } - - $product->setStockItem($this); - $this->setProduct($product); - $product->setIsSalable($this->getIsInStock()); - return $this; - } - - /** - * Retrieve minimal quantity available for item status in stock - * - * @return decimal - */ - public function getMinQty() - { - if ($this->getUseConfigMinQty()) { - return (float) Mage::getStoreConfig(self::XML_PATH_MIN_QTY); - } - return $this->getData('min_qty'); - } - - public function getMinSaleQty() - { - if ($this->getUseConfigMinSaleQty()) { - return (float) Mage::getStoreConfig(self::XML_PATH_MIN_SALE_QTY); - } - return $this->getData('min_sale_qty'); - } - - public function getMaxSaleQty() - { - if ($this->getUseConfigMaxSaleQty()) { - return (float) Mage::getStoreConfig(self::XML_PATH_MAX_SALE_QTY); - } - return $this->getData('max_sale_qty'); - } - - /** - * - * @return float - */ - public function getNotifyStockQty() - { - if ($this->getUseConfigNotifyStockQty()) { - return (float) Mage::getStoreConfig(self::XML_PATH_NOTIFY_STOCK_QTY); - } - return (float)$this->getData('notify_stock_qty'); - } - - /** - * Retrieve backorders status - * - * @return int - */ - public function getBackorders() - { - if ($this->getUseConfigBackorders()) { - return (int) Mage::getStoreConfig(self::XML_PATH_BACKORDERS); - } - return $this->getData('backorders'); - } - - public function getManageStock() - { - if ($this->getUseConfigManageStock()) { - return (int) Mage::getStoreConfigFlag(self::XML_PATH_MANAGE_STOCK); - } - return $this->getData('manage_stock'); - } - - public function getCanBackInStock() - { - return Mage::getStoreConfigFlag(self::XML_PATH_CAN_BACK_IN_STOCK); - } - - /** - * Check quantity - * - * @param decimal $qty - * @exception Mage_Core_Exception - * @return bool - */ - public function checkQty($qty) - { - if ($this->getQty() - $qty < 0) { - switch ($this->getBackorders()) { - case Mage_CatalogInventory_Model_Stock::BACKORDERS_YES_NONOTIFY: - case Mage_CatalogInventory_Model_Stock::BACKORDERS_YES_NOTIFY: - break; - default: - /*if ($this->getProduct()) { - Mage::throwException( - Mage::helper('cataloginventory')->__('The requested quantity for "%s" is not available.', $this->getProduct()->getName()) - ); - } - else { - Mage::throwException(Mage::helper('cataloginventory')->__('The requested quantity is not available.')); - }*/ - return false; - break; - } - } - return true; - } - - /** - * Checking quote item quantity - * - * @param mixed $qty quantity of this item (item qty x parent item qty) - * @param mixed $summaryQty quantity of this product in whole shopping cart which should be checked for stock availability - * @param mixed $origQty original qty of item (not multiplied on parent item qty) - * @return Varien_Object - */ - public function checkQuoteItemQty($qty, $summaryQty, $origQty = 0) - { - $result = new Varien_Object(); - $result->setHasError(false); - - if (!is_numeric($qty)) { - $qty = Mage::app()->getLocale()->getNumber($qty); - } - - /** - * Check quantity type - */ - $result->setItemIsQtyDecimal($this->getIsQtyDecimal()); - - if (!$this->getIsQtyDecimal()) { - $result->setHasQtyOptionUpdate(true); - $qty = intval($qty); - - /** - * Adding stock data to quote item - */ - $result->setItemQty($qty); - - if (!is_numeric($qty)) { - $qty = Mage::app()->getLocale()->getNumber($qty); - } - $origQty = intval($origQty); - $result->setOrigQty($origQty); - } - - if (!$this->getManageStock()) { - return $result; - } - - if (!$this->getIsInStock()) { - $result->setHasError(true) - ->setMessage(Mage::helper('cataloginventory')->__('This product is currently out of stock.')) - ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products are currently out of stock')) - ->setQuoteMessageIndex('stock'); - $result->setItemUseOldQty(true); - return $result; - } - - if ($this->getMinSaleQty() && ($qty) < $this->getMinSaleQty()) { - $result->setHasError(true) - ->setMessage(Mage::helper('cataloginventory')->__('The minimum quantity allowed for purchase is %s.', $this->getMinSaleQty() * 1)) - ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products cannot be ordered in the requested quantity')) - ->setQuoteMessageIndex('qty'); - return $result; - } - - if ($this->getMaxSaleQty() && ($qty) > $this->getMaxSaleQty()) { - $result->setHasError(true) - ->setMessage(Mage::helper('cataloginventory')->__('The maximum quantity allowed for purchase is %s.', $this->getMaxSaleQty() * 1)) - ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products can not be ordered in requested quantity')) - ->setQuoteMessageIndex('qty'); - return $result; - } - - if (!$this->checkQty($summaryQty)) { - $message = Mage::helper('cataloginventory')->__('The requested quantity for "%s" is not available.', $this->getProduct()->getName()); - $result->setHasError(true) - ->setMessage($message) - ->setQuoteMessage($message) - ->setQuoteMessageIndex('qty'); - return $result; - } - else { - if (($this->getQty() - $summaryQty) < 0) { - if ($this->getProduct()) { - $backorderQty = ($this->getQty() > 0) ? ($summaryQty - $this->getQty()) * 1 : $qty * 1; - if ($backorderQty>$qty) { - $backorderQty = $qty; - } - $result->setItemBackorders($backorderQty); - if ($this->getBackorders() == Mage_CatalogInventory_Model_Stock::BACKORDERS_YES_NOTIFY) { - $result->setMessage(Mage::helper('cataloginventory')->__('This product is not available in the requested quantity. %d of the items will be backordered.', - $backorderQty, - $this->getProduct()->getName()) - ) - ; - } - } - } - // no return intentionally - } - - return $result; - } - - /** - * Add join for catalog in stock field to product collection - * - * @param Mage_Catalog_Model_Entity_Product_Collection $productCollection - * @return Mage_CatalogInventory_Model_Stock_Item - */ - public function addCatalogInventoryToProductCollection($productCollection) - { - $this->_getResource()->addCatalogInventoryToProductCollection($productCollection); - return $this; - } - - protected function _addQuoteItemError(Mage_Sales_Model_Quote_Item $item, $itemError, $quoteError, $errorIndex='error') - { - $item->setHasError(true); - $item->setMessage($itemError); - $item->setQuoteMessage($quoteError); - $item->setQuoteMessageIndex($errorIndex); - return $this; - } - - protected function _beforeSave() - { - // see if quantity is defined for this item type - $typeId = $this->getTypeId(); - if ($product = $this->getProduct()) { - $typeId = $product->getTypeId(); - } - $isQty = Mage::helper('catalogInventory')->isQty($typeId); - - if ($isQty) { - if ($this->getBackorders() == Mage_CatalogInventory_Model_Stock::BACKORDERS_NO - && $this->getQty() <= $this->getMinQty()) { - $this->setIsInStock(false) - ->setStockStatusChangedAutomaticallyFlag(true); - } - - // if qty is below notify qty, update the low stock date to today date otherwise set null - $this->setLowStockDate(null); - if ((float)$this->getQty() < $this->getNotifyStockQty()) { - $this->setLowStockDate(Mage::app()->getLocale()->date(null, null, null, false) - ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT) - ); - } - - $this->setStockStatusChangedAutomatically(0); - if ($this->hasStockStatusChangedAutomaticallyFlag()) { - $this->setStockStatusChangedAutomatically((int)$this->getStockStatusChangedAutomaticallyFlag()); - } - } - else { - $this->setQty(0); - } - - Mage::dispatchEvent('cataloginventory_stock_item_save_before', array('item' => $this)); - return $this; - } - - public function getIsInStock() - { - if (!$this->getManageStock()) { - return true; - } - return $this->_getData('is_in_stock'); - } -} + + */ +class Mage_CatalogInventory_Model_Stock_Item extends Mage_Core_Model_Abstract +{ + const XML_PATH_GLOBAL = 'cataloginventory/options/'; + const XML_PATH_CAN_SUBTRACT = 'cataloginventory/options/can_subtract'; + const XML_PATH_CAN_BACK_IN_STOCK = 'cataloginventory/options/can_back_in_stock'; + + const XML_PATH_ITEM = 'cataloginventory/item_options/'; + const XML_PATH_MIN_QTY = 'cataloginventory/item_options/min_qty'; + const XML_PATH_MIN_SALE_QTY = 'cataloginventory/item_options/min_sale_qty'; + const XML_PATH_MAX_SALE_QTY = 'cataloginventory/item_options/max_sale_qty'; + const XML_PATH_BACKORDERS = 'cataloginventory/item_options/backorders'; + const XML_PATH_NOTIFY_STOCK_QTY = 'cataloginventory/item_options/notify_stock_qty'; + const XML_PATH_MANAGE_STOCK = 'cataloginventory/item_options/manage_stock'; + + // cataloginventory/cart_options/... + + protected function _construct() + { + $this->_init('cataloginventory/stock_item'); + } + + /** + * Retrieve stock identifier + * + * @todo multi stock + * @return int + */ + public function getStockId() + { + return 1; + } + + public function getProductId() + { + return $this->_getData('product_id'); + } + + /** + * Load item data by product + * + * @param mixed $product + * @return Mage_CatalogInventory_Model_Stock_Item + */ + public function loadByProduct($product) + { + if ($product instanceof Mage_Catalog_Model_Product) { + $product = $product->getId(); + } + $this->_getResource()->loadByProductId($this, $product); + $this->setOrigData(); + return $this; + } + + /** + * Subtract quote item quantity + * + * @param decimal $qty + * @return Mage_CatalogInventory_Model_Stock_Item + */ + public function subtractQty($qty) + { + if (!$this->getManageStock()) { + return $this; + } + $config = Mage::getStoreConfigFlag(self::XML_PATH_CAN_SUBTRACT); + if (!$config) { + return $this; + } + $this->setQty($this->getQty()-$qty); + return $this; + } + + public function addQty($qty) + { + if (!$this->getManageStock()) { + return $this; + } + $config = Mage::getStoreConfigFlag(self::XML_PATH_CAN_SUBTRACT); + if (!$config) { + return $this; + } + + $this->setQty($this->getQty()+$qty); + return $this; + } + + public function getStoreId() + { + $storeId = $this->getData('store_id'); + if (is_null($storeId)) { + if ($this->getProduct()) { + $storeId = $this->getProduct()->getStoreId(); + } + else { + $storeId = Mage::app()->getStore()->getId(); + } + $this->setData('store_id', $storeId); + } + return $storeId; + } + + /** + * Adding stoc data to product + * + * @param Mage_Catalog_Model_Product $product + * @return Mage_CatalogInventory_Model_Stock_Item + */ + public function assignProduct(Mage_Catalog_Model_Product $product) + { + if (!$this->getId() || !$this->getProductId()) { + $this->_getResource()->loadByProductId($this, $product->getId()); + $this->setOrigData(); + } + + $product->setStockItem($this); + $this->setProduct($product); + $product->setIsInStock($this->getIsInStock()); + Mage::getSingleton('cataloginventory/stock_status') + ->assignProduct($product, $this->getStockId(), $this->getStockStatus()); + return $this; + } + + /** + * Retrieve minimal quantity available for item status in stock + * + * @return decimal + */ + public function getMinQty() + { + if ($this->getUseConfigMinQty()) { + return (float) Mage::getStoreConfig(self::XML_PATH_MIN_QTY); + } + return $this->getData('min_qty'); + } + + public function getMinSaleQty() + { + if ($this->getUseConfigMinSaleQty()) { + return (float) Mage::getStoreConfig(self::XML_PATH_MIN_SALE_QTY); + } + return $this->getData('min_sale_qty'); + } + + public function getMaxSaleQty() + { + if ($this->getUseConfigMaxSaleQty()) { + return (float) Mage::getStoreConfig(self::XML_PATH_MAX_SALE_QTY); + } + return $this->getData('max_sale_qty'); + } + + /** + * + * @return float + */ + public function getNotifyStockQty() + { + if ($this->getUseConfigNotifyStockQty()) { + return (float) Mage::getStoreConfig(self::XML_PATH_NOTIFY_STOCK_QTY); + } + return (float)$this->getData('notify_stock_qty'); + } + + /** + * Retrieve backorders status + * + * @return int + */ + public function getBackorders() + { + if ($this->getUseConfigBackorders()) { + return (int) Mage::getStoreConfig(self::XML_PATH_BACKORDERS); + } + return $this->getData('backorders'); + } + + public function getManageStock() + { + if ($this->getUseConfigManageStock()) { + return (int) Mage::getStoreConfigFlag(self::XML_PATH_MANAGE_STOCK); + } + return $this->getData('manage_stock'); + } + + public function getCanBackInStock() + { + return Mage::getStoreConfigFlag(self::XML_PATH_CAN_BACK_IN_STOCK); + } + + /** + * Check quantity + * + * @param decimal $qty + * @exception Mage_Core_Exception + * @return bool + */ + public function checkQty($qty) + { + if ($this->getQty() - $qty < 0) { + switch ($this->getBackorders()) { + case Mage_CatalogInventory_Model_Stock::BACKORDERS_YES_NONOTIFY: + case Mage_CatalogInventory_Model_Stock::BACKORDERS_YES_NOTIFY: + break; + default: + /*if ($this->getProduct()) { + Mage::throwException( + Mage::helper('cataloginventory')->__('The requested quantity for "%s" is not available.', $this->getProduct()->getName()) + ); + } + else { + Mage::throwException(Mage::helper('cataloginventory')->__('The requested quantity is not available.')); + }*/ + return false; + break; + } + } + return true; + } + + /** + * Checking quote item quantity + * + * @param mixed $qty quantity of this item (item qty x parent item qty) + * @param mixed $summaryQty quantity of this product in whole shopping cart which should be checked for stock availability + * @param mixed $origQty original qty of item (not multiplied on parent item qty) + * @return Varien_Object + */ + public function checkQuoteItemQty($qty, $summaryQty, $origQty = 0) + { + $result = new Varien_Object(); + $result->setHasError(false); + + if (!is_numeric($qty)) { + $qty = Mage::app()->getLocale()->getNumber($qty); + } + + /** + * Check quantity type + */ + $result->setItemIsQtyDecimal($this->getIsQtyDecimal()); + + if (!$this->getIsQtyDecimal()) { + $result->setHasQtyOptionUpdate(true); + $qty = intval($qty); + + /** + * Adding stock data to quote item + */ + $result->setItemQty($qty); + + if (!is_numeric($qty)) { + $qty = Mage::app()->getLocale()->getNumber($qty); + } + $origQty = intval($origQty); + $result->setOrigQty($origQty); + } + + if ($this->getMinSaleQty() && ($qty) < $this->getMinSaleQty()) { + $result->setHasError(true) + ->setMessage(Mage::helper('cataloginventory')->__('The minimum quantity allowed for purchase is %s.', $this->getMinSaleQty() * 1)) + ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products cannot be ordered in the requested quantity')) + ->setQuoteMessageIndex('qty'); + return $result; + } + + if ($this->getMaxSaleQty() && ($qty) > $this->getMaxSaleQty()) { + $result->setHasError(true) + ->setMessage(Mage::helper('cataloginventory')->__('The maximum quantity allowed for purchase is %s.', $this->getMaxSaleQty() * 1)) + ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products can not be ordered in requested quantity')) + ->setQuoteMessageIndex('qty'); + return $result; + } + + if (!$this->getManageStock()) { + return $result; + } + + if (!$this->getIsInStock()) { + $result->setHasError(true) + ->setMessage(Mage::helper('cataloginventory')->__('This product is currently out of stock.')) + ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products are currently out of stock')) + ->setQuoteMessageIndex('stock'); + $result->setItemUseOldQty(true); + return $result; + } + + if (!$this->checkQty($summaryQty)) { + $message = Mage::helper('cataloginventory')->__('The requested quantity for "%s" is not available.', $this->getProduct()->getName()); + $result->setHasError(true) + ->setMessage($message) + ->setQuoteMessage($message) + ->setQuoteMessageIndex('qty'); + return $result; + } + else { + if (($this->getQty() - $summaryQty) < 0) { + if ($this->getProduct()) { + $backorderQty = ($this->getQty() > 0) ? ($summaryQty - $this->getQty()) * 1 : $qty * 1; + if ($backorderQty>$qty) { + $backorderQty = $qty; + } + $result->setItemBackorders($backorderQty); + if ($this->getBackorders() == Mage_CatalogInventory_Model_Stock::BACKORDERS_YES_NOTIFY) { + $result->setMessage(Mage::helper('cataloginventory')->__('This product is not available in the requested quantity. %d of the items will be backordered.', + $backorderQty, + $this->getProduct()->getName()) + ) + ; + } + } + } + // no return intentionally + } + + return $result; + } + + /** + * Add join for catalog in stock field to product collection + * + * @param Mage_Catalog_Model_Entity_Product_Collection $productCollection + * @return Mage_CatalogInventory_Model_Stock_Item + */ + public function addCatalogInventoryToProductCollection($productCollection) + { + $this->_getResource()->addCatalogInventoryToProductCollection($productCollection); + return $this; + } + + protected function _addQuoteItemError(Mage_Sales_Model_Quote_Item $item, $itemError, $quoteError, $errorIndex='error') + { + $item->setHasError(true); + $item->setMessage($itemError); + $item->setQuoteMessage($quoteError); + $item->setQuoteMessageIndex($errorIndex); + return $this; + } + + protected function _beforeSave() + { + // see if quantity is defined for this item type + $typeId = $this->getTypeId(); + if ($product = $this->getProduct()) { + $typeId = $product->getTypeId(); + } + $isQty = Mage::helper('catalogInventory')->isQty($typeId); + + if ($isQty) { + if ($this->getBackorders() == Mage_CatalogInventory_Model_Stock::BACKORDERS_NO + && $this->getQty() <= $this->getMinQty()) { + $this->setIsInStock(false) + ->setStockStatusChangedAutomaticallyFlag(true); + } + + // if qty is below notify qty, update the low stock date to today date otherwise set null + $this->setLowStockDate(null); + if ((float)$this->getQty() < $this->getNotifyStockQty()) { + $this->setLowStockDate(Mage::app()->getLocale()->date(null, null, null, false) + ->toString(Varien_Date::DATETIME_INTERNAL_FORMAT) + ); + } + + $this->setStockStatusChangedAutomatically(0); + if ($this->hasStockStatusChangedAutomaticallyFlag()) { + $this->setStockStatusChangedAutomatically((int)$this->getStockStatusChangedAutomaticallyFlag()); + } + } + else { + $this->setQty(0); + } + + if (($product && $product->dataHasChangedFor('status')) + OR $this->dataHasChangedFor('is_in_stock') + OR $this->dataHasChangedFor('manage_stock')) { + Mage::getSingleton('cataloginventory/stock_status') + ->changeItemStatus($this); + } + + Mage::dispatchEvent('cataloginventory_stock_item_save_before', array('item' => $this)); + return $this; + } + + public function getIsInStock() + { + if (!$this->getManageStock()) { + return true; + } + return $this->_getData('is_in_stock'); + } +} diff --git a/app/code/core/Mage/CatalogInventory/Model/Stock/Status.php b/app/code/core/Mage/CatalogInventory/Model/Stock/Status.php new file mode 100644 index 0000000000..768a8be3ca --- /dev/null +++ b/app/code/core/Mage/CatalogInventory/Model/Stock/Status.php @@ -0,0 +1,446 @@ + + */ +class Mage_CatalogInventory_Model_Stock_Status extends Mage_Core_Model_Abstract +{ + const STATUS_OUT_OF_STOCK = 0; + const STATUS_IN_STOCK = 1; + + /** + * Product Type Instances cache + * + * @var array + */ + protected $_productTypes; + + /** + * Websites cache + * + * @var array + */ + protected $_websites; + + /** + * Init resource model + * + */ + protected function _construct() + { + $this->_init('cataloginventory/stock_status'); + } + + /** + * Retrieve Product Type Instances + * as key - type code, value - instance model + * + * @return array + */ + public function getProductTypeInstances() + { + if (is_null($this->_productTypes)) { + $this->_productTypes = array(); + $productEmulator = new Varien_Object(); + + foreach (array_keys(Mage_Catalog_Model_Product_Type::getTypes()) as $typeId) { + $productEmulator->setTypeId($typeId); + $this->_productTypes[$typeId] = Mage::getSingleton('catalog/product_type') + ->factory($productEmulator); + } + } + return $this->_productTypes; + } + + /** + * Retrieve Product Type Instance By Product Type + * + * @param string $productType + * @return Mage_Catalog_Model_Product_Type_Abstract + */ + public function getProductTypeInstance($productType) + { + $types = $this->getProductTypeInstances(); + if (isset($types[$productType])) { + return $types[$productType]; + } + return false; + } + + /** + * Retrieve website models + * + * @return array + */ + public function getWebsites($websiteId = null) + { + if (is_null($this->_websites)) { + $this->_websites = $this->getResource()->getWebsiteStores(); + } + + $websites = $this->_websites; + if (!is_null($websiteId) && isset($this->_websites[$websiteId])) { + $websites = array($this->_websites[$websiteId]); + } + + return $this->_websites; + } + + /** + * Retrieve Default website store Id + * + * @param int $websiteId + * @return int + */ + public function getWebsiteDefaultStoreId($websiteId) + { + $websites = $this->getWebsites(); + if (isset($websites[$websiteId])) { + return $websites[$websiteId]; + } + return 0; + } + + /** + * Retrieve Catalog Product Status Model + * + * @return Mage_Catalog_Model_Product_Status + */ + public function getProductStatusModel() + { + return Mage::getSingleton('catalog/product_status'); + } + + /** + * Retrieve CatalogInventory empty Stock Item model + * + * @return Mage_CatalogInventory_Model_Stock_Item + */ + public function getStockItemModel() + { + return Mage::getModel('cataloginventory/stock_item'); + } + + /** + * Retrieve Product Status Enabled Constant + * + * @return int + */ + public function getProductStatusEnabled() + { + return Mage_Catalog_Model_Product_Status::STATUS_ENABLED; + } + + /** + * Change Stock Item status process + * + * @param Mage_CatalogInventory_Model_Stock_Item $item + * @return Mage_CatalogInventory_Model_Stock_Status + */ + public function changeItemStatus(Mage_CatalogInventory_Model_Stock_Item $item) + { + if ($product = $item->getProduct()) { + $productId = $product->getId(); + $productType = $product->getTypeId(); + } + else { + $productId = $item->getProductId(); + $productType = $this->getProductType($productId); + } + + $status = (int)$item->getIsInStock(); + $qty = (int)$item->getQty(); + + $this->_processChildren($productId, $productType, $qty, $status, $item->getStockId()); + $this->_processParents($productId, $item->getStockId()); + + return $this; + } + + /** + * Assign Stock Status to Product + * + * @param Mage_Catalog_Model_Product $product + * @param int $stockId + * @param int $stockStatus + * @return Mage_CatalogInventory_Model_Stock_Status + */ + public function assignProduct(Mage_Catalog_Model_Product $product, $stockId = 1, $stockStatus = null) + { + if (is_null($stockStatus)) { + $websiteId = $product->getStore()->getWebsiteId(); + $status = $this->getProductStatus($product->getId(), $websiteId, $stockId); + $stockStatus = isset($status[$product->getId()]) ? $status[$product->getId()] : null; + } + + $product->setIsSalable($stockStatus); + + return $this; + } + + /** + * Rebuild stock status for all products + * + * @param int $websiteId + * @return Mage_CatalogInventory_Model_Stock_Status + */ + public function rebuild($websiteId = null) + { + $lastProductId = 0; + while (true) { + $productCollection = $this->getResource()->getProductCollection($lastProductId); + if (!$productCollection) { + break; + } + + foreach ($productCollection as $productId => $productType) { + $lastProductId = $productId; + $this->updateStatus($productId, $productType, $websiteId); + } + } + + return $this; + } + + /** + * Update product status from stock item + * + * @param int $productId + * @param string $productType + * @param int $websiteId + * @return Mage_CatalogInventory_Model_Stock_Status + */ + public function updateStatus($productId, $productType = null, $websiteId = null) + { + if (is_null($productType)) { + $productType = $this->getProductType($productId); + } + + $item = $this->getStockItemModel()->loadByProduct($productId); + + $status = self::STATUS_IN_STOCK; + $qty = 0; + if ($item->getId()) { + $status = $item->getIsInStock(); + $qty = $item->getQty(); + } + + $this->_processChildren($productId, $productType, $qty, $status, $item->getStockId(), $websiteId); + $this->_processParents($productId, $item->getStockId(), $websiteId); + + return $this; + } + + /** + * Process children stock status + * + * @param int $productId + * @param string $productType + * @param float $qty + * @param int $status + * @param int $stockId + * @param int $websiteId + * @return Mage_CatalogInventory_Model_Stock_Status + */ + protected function _processChildren($productId, $productType, $qty = 0, $status = self::STATUS_IN_STOCK, $stockId = 1, $websiteId = null) + { + if ($status == self::STATUS_OUT_OF_STOCK) { + $this->saveProductStatus($productId, $status, $qty, $stockId, $websiteId); + return $this; + } + + $statuses = array(); + $websites = $this->getWebsites($websiteId); + + foreach (array_keys($websites) as $websiteId) { + /* @var $website Mage_Core_Model_Website */ + $statuses[$websiteId] = $status; + } + + if (!$typeInstance = $this->getProductTypeInstance($productType)) { + return $this; + } + + $requiredChildrenIds = $typeInstance->getChildrenIds($productId, true); + if ($requiredChildrenIds) { + $childrenIds = array(); + foreach ($requiredChildrenIds as $groupedChildrenIds) { + $childrenIds = array_merge($childrenIds, $groupedChildrenIds); + } + foreach ($websites as $websiteId => $storeId) { + $childrenStatus = $this->getProductStatusModel() + ->getProductStatus($childrenIds, $storeId); + $childrenStock = $this->getProductStatus($childrenIds, $websiteId, $stockId); + + $websiteStatus = $statuses[$websiteId]; + foreach ($requiredChildrenIds as $groupedChildrenIds) { + $optionStatus = false; + foreach ($groupedChildrenIds as $childId) { + if (isset($childrenStatus[$childId]) + and $childrenStatus[$childId] == $this->getProductStatusEnabled() + and isset($childrenStock[$childId]) + and $childrenStock[$childId] == self::STATUS_IN_STOCK + ) { + $optionStatus = true; + } + } + $websiteStatus = $websiteStatus && $optionStatus; + } + $statuses[$websiteId] = (int)$websiteStatus; + } + } + + foreach ($statuses as $websiteId => $websiteStatus) { + $this->saveProductStatus($productId, $websiteStatus, $qty, $stockId, $websiteId); + } + + return $this; + } + + /** + * Process Parents by child + * + * @param int $productId + * @param int $stockId + * @param int $websiteId + * @return Mage_CatalogInventory_Model_Stock_Status + */ + protected function _processParents($productId, $stockId = 1, $websiteId = null) + { + $parentIds = array(); + foreach ($this->getProductTypeInstances() as $typeInstance) { + /* @var $typeInstance Mage_Catalog_Model_Product_Type_Abstract */ + $parentIds = array_merge($parentIds, $typeInstance->getParentIdsByChild($productId)); + } + + if (!$parentIds) { + return $this; + } + + $productTypes = $this->getProductsType($parentIds); + $item = $this->getStockItemModel(); + + foreach ($parentIds as $parentId) { + $parentType = isset($productTypes[$parentId]) ? $productTypes[$parentId] : null; + $item->setData(array('stock_id' => $stockId)) + ->setOrigData() + ->loadByProduct($parentId); + $status = self::STATUS_IN_STOCK; + $qty = 0; + if ($item->getId()) { + $status = $item->getIsInStock(); + $qty = $item->getQty(); + } + + $this->_processChildren($parentId, $parentType, $qty, $status, $item->getStockId(), $websiteId); + } + + return $this; + } + + /** + * Save product status per website + * if website is null, saved for all websites + * + * @param int $productId + * @param int $status + * @param float $qty + * @param int $stockId + * @param int|null $websiteId + * @return Mage_CatalogInventory_Model_Stock_Status + */ + public function saveProductStatus($productId, $status, $qty = 0, $stockId = 1, $websiteId = null) + { + $this->getResource()->saveProductStatus($this, $productId, $status, $qty, $stockId, $websiteId); + return $this; + } + + /** + * Retrieve Product(s) status + * + * @param int|array $productIds + * @param int $websiteId + * @param int $stockId + * @return array + */ + public function getProductStatus($productIds, $websiteId, $stockId = 1) + { + return $this->getResource()->getProductStatus($productIds, $websiteId, $stockId); + } + + /** + * Retrieve Product(s) Data array + * + * @param int|array $productIds + * @param int $websiteId + * @param int $stockId + * @return array + */ + public function getProductData($productIds, $websiteId, $stockId = 1) + { + return $this->getResource()->getProductData($productIds, $websiteId, $stockId); + } + + /** + * Retrieve resource model wraper + * + * @return Mage_CatalogInventory_Model_Mysql4_Stock_Status + */ + public function getResource() { + return parent::getResource(); + } + + /** + * Retrieve Product Type + * + * @param int $productId + * @return string|false + */ + public function getProductType($productId) { + $types = $this->getResource()->getProductsType($productId); + if (isset($types[$productId])) { + return $types[$productId]; + } + return false; + } + + /** + * Retrieve Products Type as array + * Return array as key product_id, value type + * + * @param array|int $productIds + * @return array + */ + public function getProductsType($productIds) { + return $this->getResource()->getProductsType($productIds); + } +} + diff --git a/app/code/core/Mage/CatalogInventory/etc/config.xml b/app/code/core/Mage/CatalogInventory/etc/config.xml index 511c54c45a..7104fbf6cc 100644 --- a/app/code/core/Mage/CatalogInventory/etc/config.xml +++ b/app/code/core/Mage/CatalogInventory/etc/config.xml @@ -1,245 +1,257 @@ - - - - - - 0.7.4 - - - - - - Mage_CatalogInventory_Model - cataloginventory_mysql4 - - - Mage_CatalogInventory_Model_Mysql4 - - - cataloginventory_stock
-
- - cataloginventory_stock_item
-
-
-
-
- - - Mage_CatalogInventory_Helper - - - - - - Mage_CatalogInventory - Mage_Eav_Model_Entity_Setup - - - core_setup - - - - - core_write - - - - - core_read - - - - - - - - - singleton - cataloginventory/observer - addInventoryData - - - - - - - singleton - cataloginventory/observer - addInventoryDataToCollection - - - - - - - singleton - cataloginventory/observer - checkQuoteItemQty - - - - - - - singleton - cataloginventory/observer - lockOrderInventoryData - - - - - - - singleton - cataloginventory/observer - createOrderItem - - - - - - - singleton - cataloginventory/observer - cancelOrderItem - - - - - - - singleton - cataloginventory/observer - refundOrderItem - - - - - - - singleton - cataloginventory/observer - saveInventoryData - - - - - - - singleton - cataloginventory/observer - copyInventoryData - - - - - - - singleton - cataloginventory/observer - updateItemsStockUponConfigChange - - - - - - - - - - 1 - - - 1 - - - - -
- - - - - - - Mage_CatalogInventory.csv - - - - - - - - - - - - Mage_CatalogInventory.csv - - - - - - - - - - - - - - Inventory Section - - - - - - - - - - - - - - - 1 - 1 - - - 1 - 0 - 10000 - 1 - 0 - 1 - - - -
+ + + + + + 0.7.5 + + + + + + Mage_CatalogInventory_Model + cataloginventory_mysql4 + + + Mage_CatalogInventory_Model_Mysql4 + + + cataloginventory_stock
+
+ + cataloginventory_stock_item
+
+ + cataloginventory_stock_status
+
+
+
+
+ + + Mage_CatalogInventory_Helper + + + + + + Mage_CatalogInventory + Mage_Eav_Model_Entity_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + + + + + singleton + cataloginventory/observer + addInventoryData + + + + + + + singleton + cataloginventory/observer + addInventoryDataToCollection + + + + + + + singleton + cataloginventory/observer + productStatusUpdate + + + + + + + singleton + cataloginventory/observer + checkQuoteItemQty + + + + + + + singleton + cataloginventory/observer + lockOrderInventoryData + + + + + + + singleton + cataloginventory/observer + createOrderItem + + + + + + + singleton + cataloginventory/observer + cancelOrderItem + + + + + + + singleton + cataloginventory/observer + refundOrderItem + + + + + + + singleton + cataloginventory/observer + saveInventoryData + + + + + + + singleton + cataloginventory/observer + copyInventoryData + + + + + + + singleton + cataloginventory/observer + updateItemsStockUponConfigChange + + + + + + + + + + 1 + + + 1 + + + + +
+ + + + + + + Mage_CatalogInventory.csv + + + + + + + + + + + + Mage_CatalogInventory.csv + + + + + + + + + + + + + + Inventory Section + + + + + + + + + + + + + + + 1 + 1 + + + 1 + 0 + 10000 + 1 + 0 + 1 + + + +
diff --git a/app/code/core/Mage/CatalogInventory/etc/system.xml b/app/code/core/Mage/CatalogInventory/etc/system.xml index e6dcb573c9..0d12c7fdca 100644 --- a/app/code/core/Mage/CatalogInventory/etc/system.xml +++ b/app/code/core/Mage/CatalogInventory/etc/system.xml @@ -77,6 +77,7 @@ select adminhtml/system_config_source_yesno + adminhtml/system_config_backend_catalog_inventory_managestock 1 1 0 diff --git a/app/code/core/Mage/CatalogInventory/sql/cataloginventory_setup/mysql4-upgrade-0.7.4-0.7.5.php b/app/code/core/Mage/CatalogInventory/sql/cataloginventory_setup/mysql4-upgrade-0.7.4-0.7.5.php new file mode 100644 index 0000000000..f1109f2d4e --- /dev/null +++ b/app/code/core/Mage/CatalogInventory/sql/cataloginventory_setup/mysql4-upgrade-0.7.4-0.7.5.php @@ -0,0 +1,46 @@ +startSetup(); +$installer->run(" +CREATE TABLE `{$installer->getTable('cataloginventory_stock_status')}` ( + `product_id` int(10) unsigned NOT NULL, + `website_id` smallint(5) unsigned NOT NULL, + `stock_id` smallint(4) unsigned NOT NULL, + `qty` decimal(12,4) NOT NULL DEFAULT '0.0000', + `stock_status` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`product_id`,`website_id`,`stock_id`), + CONSTRAINT `FK_CATALOGINVENTORY_STOCK_STATUS_STOCK` FOREIGN KEY (`stock_id`) REFERENCES `{$installer->getTable('cataloginventory_stock')}` (`stock_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_CATALOGINVENTORY_STOCK_STATUS_PRODUCT` FOREIGN KEY (`product_id`) REFERENCES `{$installer->getTable('catalog_product_entity')}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_CATALOGINVENTORY_STOCK_STATUS_WEBSITE` FOREIGN KEY (`website_id`) REFERENCES `{$installer->getTable('core_website')}` (`website_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +"); +$installer->endSetup(); + +Mage::getModel('cataloginventory/stock_status')->rebuild(); diff --git a/app/code/core/Mage/CatalogRule/Model/Mysql4/Rule.php b/app/code/core/Mage/CatalogRule/Model/Mysql4/Rule.php index 17423b1395..47f895b6ef 100644 --- a/app/code/core/Mage/CatalogRule/Model/Mysql4/Rule.php +++ b/app/code/core/Mage/CatalogRule/Model/Mysql4/Rule.php @@ -239,23 +239,23 @@ public function getRuleProductsForDateRange($fromDate, $toDate, $productId=null) */ $productPrices = array(); foreach ($prices as $index => $priceData) { - $websiteId = Mage::app()->getStore($priceData['store_id'])->getWebsiteId(); + $websiteId = Mage::app()->getStore($priceData['store_id'])->getWebsiteId(); - if (!isset($productPrices[$priceData['entity_id']])) { - $productPrices[$priceData['entity_id']] = array( + if (!isset($productPrices[$priceData['entity_id']])) { + $productPrices[$priceData['entity_id']] = array( 'default' => $priceData['value'], 'websites' => array($websiteId=>$priceData['value']) - ); - } - else { + ); + } + else { $productPrices[$priceData['entity_id']]['websites'][$websiteId] = $priceData['value']; - } + } } foreach ($ruleProducts as &$p) { - if (isset($productPrices[$p['product_id']]['websites'][$p['website_id']])) { + if (isset($productPrices[$p['product_id']]['websites'][$p['website_id']])) { $p['price'] = $productPrices[$p['product_id']]['websites'][$p['website_id']]; - } + } elseif (isset($productPrices[$p['product_id']]['default'])) { $p['price'] = $productPrices[$p['product_id']]['default']; } @@ -312,7 +312,11 @@ protected function _getRuleProductsStmt($fromDate, $toDate, $productId=null) foreach (Mage::app()->getWebsites() as $website) { $websiteId = $website->getId(); - $storeId = $website->getDefaultGroup()->getDefaultStoreId(); + $defaultGroup = $website->getDefaultGroup(); + if (!$defaultGroup instanceof Mage_Core_Model_Store_Group) { + continue; + } + $storeId = $defaultGroup->getDefaultStoreId(); $tableAlias = 'pp'.$websiteId; $fieldAlias = 'website_'.$websiteId.'_price'; $select->joinLeft( @@ -517,7 +521,7 @@ protected function _saveRuleProductPrices($arrData) $data['rule_date'] = $this->formatDate($data['rule_date'], false); $data['latest_start_date'] = $this->formatDate($data['latest_start_date'], false); $data['earliest_end_date'] = $this->formatDate($data['earliest_end_date'], false); - $rows[] = '(' . $this->_getWriteAdapter()->quote($data) . ')'; + $rows[] = '(' . $this->_getWriteAdapter()->quote($data) . ')'; } $query = $header.join(',', $rows); $insertQuery = 'REPLACE INTO ' . $this->getTable('catalogrule/affected_product') . ' (product_id) VALUES ' . diff --git a/app/code/core/Mage/CatalogSearch/Block/Result.php b/app/code/core/Mage/CatalogSearch/Block/Result.php index 9ceda7d7c4..9169c99697 100644 --- a/app/code/core/Mage/CatalogSearch/Block/Result.php +++ b/app/code/core/Mage/CatalogSearch/Block/Result.php @@ -146,15 +146,6 @@ protected function _getProductCollection() { if (is_null($this->_productCollection)) { $this->_productCollection = Mage::getSingleton('catalogsearch/layer')->getProductCollection(); -/* - $this->_productCollection = Mage::getResourceModel('catalogsearch/fulltext_collection') - ->addSearchFilter($this->helper('catalogSearch')->getEscapedQueryText()) - ->setStore(Mage::app()->getStore()) - ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes()) - ->addUrlRewrite(); - - Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($this->_productCollection); - Mage::getSingleton('catalog/product_visibility')->addVisibleInSearchFilterToCollection($this->_productCollection);*/ } return $this->_productCollection; diff --git a/app/code/core/Mage/CatalogSearch/Model/Layer.php b/app/code/core/Mage/CatalogSearch/Model/Layer.php index bc1b864731..710290b4d4 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Layer.php +++ b/app/code/core/Mage/CatalogSearch/Model/Layer.php @@ -65,7 +65,7 @@ public function prepareProductCollection($collection) ->addUrlRewrite(); Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection); - Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($collection); + Mage::getSingleton('catalog/product_visibility')->addVisibleInSearchFilterToCollection($collection); return $this; } diff --git a/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php b/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php index 4c08811a01..efc54b2b54 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php +++ b/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php @@ -333,6 +333,8 @@ protected function _getSearchableAttributes($backendType = null) if (is_null($this->_searchableAttributes)) { $this->_searchableAttributes = array(); $entityType = $this->getEavConfig()->getEntityType('catalog_product'); + $entity = $entityType->getEntity(); + $select = $this->_getReadAdapter()->select() ->from($this->getTable('eav/attribute'), array('attribute_code')) ->where('entity_type_id=?', $entityType->getEntityTypeId()) @@ -341,6 +343,7 @@ protected function _getSearchableAttributes($backendType = null) $this->getEavConfig()->preloadAttributes($entityType, $attributeCodes); foreach ($attributeCodes as $attributeCode) { $attribute = $this->getEavConfig()->getAttribute($entityType, $attributeCode); + $attribute->setEntity($entity); $this->_searchableAttributes[$attribute->getId()] = $attribute; } } @@ -377,7 +380,7 @@ protected function _getSearchableAttribute($attribute) } } } - return $this->getEavConfig()->getAttribute($attribute); + return $this->getEavConfig()->getAttribute('catalog_product', $attribute); } /** diff --git a/app/code/core/Mage/Checkout/Block/Cart/Abstract.php b/app/code/core/Mage/Checkout/Block/Cart/Abstract.php index 809b4faa02..2dc237ccb7 100644 --- a/app/code/core/Mage/Checkout/Block/Cart/Abstract.php +++ b/app/code/core/Mage/Checkout/Block/Cart/Abstract.php @@ -49,16 +49,17 @@ public function __construct() /** * Add renderer for item product type * - * @param string $type - * @param string $block + * @param string $productType + * @param string $blockType * @param string $template * @return Mage_Checkout_Block_Cart_Abstract */ - public function addItemRender($type, $block, $template) + public function addItemRender($productType, $blockType, $template) { - $this->_itemRenders[$type] = array( - 'block' => $block, - 'template' => $template + $this->_itemRenders[$productType] = array( + 'block' => $blockType, + 'template' => $template, + 'blockInstance' => null ); return $this; } @@ -66,17 +67,52 @@ public function addItemRender($type, $block, $template) /** * Get renderer information by product type code * + * @deprecated please use getItemRendererInfo() method instead + * @see getItemRendererInfo() * @param string $type * @return array */ public function getItemRender($type) + { + return $this->getItemRendererInfo(); + } + + /** + * Get renderer information by product type code + * + * @param string $type + * @return array + */ + public function getItemRendererInfo($type) { if (isset($this->_itemRenders[$type])) { return $this->_itemRenders[$type]; } return $this->_itemRenders['default']; + } + + /** + * Get renderer block instance by product type code + * + * @param string $type + * @return array + */ + public function getItemRenderer($type) + { + if (!isset($this->_itemRenders[$type])) { + $type = 'default'; + } + if (is_null($this->_itemRenders[$type]['blockInstance'])) { + $this->_itemRenders[$type]['blockInstance'] = $this->getLayout() + ->createBlock($this->_itemRenders[$type]['block']) + ->setTemplate($this->_itemRenders[$type]['template']) + ->setRenderedBlock($this); + } + + return $this->_itemRenders[$type]['blockInstance']; } + /** * Get logged in customer * @@ -134,13 +170,8 @@ public function getItems() */ public function getItemHtml(Mage_Sales_Model_Quote_Item $item) { - $itemRenderInfo = $this->getItemRender($item->getProductType()); - $itemBlock = $this->getLayout() - ->createBlock($itemRenderInfo['block']) - ->setTemplate($itemRenderInfo['template']) - ->setItem($item); - - return $itemBlock->toHtml(); + $renderer = $this->getItemRenderer($item->getProductType())->setItem($item); + return $renderer->toHtml(); } public function getTotals() diff --git a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer.php b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer.php index d771d38dca..7a8ebd6147 100644 --- a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer.php +++ b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer.php @@ -34,6 +34,8 @@ class Mage_Checkout_Block_Cart_Item_Renderer extends Mage_Core_Block_Template { protected $_item; + protected $_productUrl = null; + protected $_productThumbnail = null; /** * Set item for render @@ -67,6 +69,12 @@ public function getProduct() return $this->getItem()->getProduct(); } + public function overrideProductThumbnail($productThumbnail) + { + $this->_productThumbnail = $productThumbnail; + return $this; + } + /** * Get product thumbnail image * @@ -74,9 +82,18 @@ public function getProduct() */ public function getProductThumbnail() { + if (!is_null($this->_productThumbnail)) { + return $this->_productThumbnail; + } return $this->helper('catalog/image')->init($this->getProduct(), 'thumbnail'); } + public function overrideProductUrl($productUrl) + { + $this->_productUrl = $productUrl; + return $this; + } + /** * Get url to item product * @@ -84,6 +101,9 @@ public function getProductThumbnail() */ public function getProductUrl() { + if (!is_null($this->_productUrl)) { + return $this->_productUrl; + } return $this->getProduct()->getProductUrl(); } diff --git a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Grouped.php b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Grouped.php index af156d8553..89da4dc78f 100644 --- a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Grouped.php @@ -74,4 +74,24 @@ public function getProductUrl() { return $this->getGroupedProduct()->getProductUrl(); } + + /** + * Prepare item html + * + * This method uses renderer for real product type + * + * @return string + */ + protected function _toHtml() + { + $renderer = $this->getRenderedBlock()->getItemRenderer($this->getItem()->getRealProductType()); + $renderer->setItem($this->getItem()); + $renderer->overrideProductUrl($this->getProductUrl()); + $renderer->overrideProductThumbnail($this->getProductThumbnail()); + $rendererHtml = $renderer->toHtml(); + $renderer->overrideProductUrl(null); + $renderer->overrideProductThumbnail(null); + return $rendererHtml; + } + } \ No newline at end of file diff --git a/app/code/core/Mage/Cms/Controller/Router.php b/app/code/core/Mage/Cms/Controller/Router.php index 647006d66e..595c7065ef 100644 --- a/app/code/core/Mage/Cms/Controller/Router.php +++ b/app/code/core/Mage/Cms/Controller/Router.php @@ -45,16 +45,16 @@ public function match(Zend_Controller_Request_Http $request) $identifier = trim($request->getPathInfo(), '/'); - $page = Mage::getSingleton('cms/page'); - $page->setStoreId(Mage::app()->getStore()->getId()); - if (!$page->load($identifier)->getId()) { + $page = Mage::getModel('cms/page'); + $pageId = $page->checkIdentifier($identifier, Mage::app()->getStore()->getId()); + if (!$pageId) { return false; } $request->setModuleName(isset($d[0]) ? $d[0] : 'cms') ->setControllerName(isset($d[1]) ? $d[1] : 'page') ->setActionName(isset($d[2]) ? $d[2] : 'view') - ->setParam('page_id', $page->getId()); + ->setParam('page_id', $pageId); $request->setAlias( Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, $identifier diff --git a/app/code/core/Mage/Cms/Helper/Page.php b/app/code/core/Mage/Cms/Helper/Page.php index ecce71422d..0fe38f8acf 100644 --- a/app/code/core/Mage/Cms/Helper/Page.php +++ b/app/code/core/Mage/Cms/Helper/Page.php @@ -54,12 +54,6 @@ public function renderPage(Mage_Core_Controller_Front_Action $action, $pageId=nu return false; } -// $customerSession = Mage::getSingleton('customer/session'); -// if (!$customerSession->authenticate($action)) { -// $customerSession->setBeforeAuthUrl(Mage::getBaseUrl().$page->getIdentifier()); -// return true; -// } - if ($page->getCustomTheme()) { $apply = true; $today = Mage::app()->getLocale()->date()->toValue(); diff --git a/app/code/core/Mage/Cms/Model/Mysql4/Page.php b/app/code/core/Mage/Cms/Model/Mysql4/Page.php index 379ce1953a..f4112f9a0a 100644 --- a/app/code/core/Mage/Cms/Model/Mysql4/Page.php +++ b/app/code/core/Mage/Cms/Model/Mysql4/Page.php @@ -34,14 +34,16 @@ class Mage_Cms_Model_Mysql4_Page extends Mage_Core_Model_Mysql4_Abstract { - + /** + * Initialize resource model + */ protected function _construct() { $this->_init('cms/page', 'page_id'); } /** - * + * Process page data before saving * * @param Mage_Core_Model_Abstract $object */ @@ -76,6 +78,7 @@ protected function _beforeSave(Mage_Core_Model_Abstract $object) } /** + * Assign page to store views * * @param Mage_Core_Model_Abstract $object */ @@ -135,7 +138,10 @@ protected function _getLoadSelect($field, $value, $object) $select = parent::_getLoadSelect($field, $value, $object); if ($object->getStoreId()) { - $select->join(array('cps' => $this->getTable('cms/page_store')), $this->getMainTable().'.page_id = `cps`.page_id') + $select->join( + array('cps' => $this->getTable('cms/page_store')), + $this->getMainTable().'.page_id = `cps`.page_id' + ) ->where('is_active=1 AND `cps`.store_id in (0, ?) ', $object->getStoreId()) ->order('store_id DESC') ->limit(1); @@ -178,4 +184,26 @@ protected function isNumericPageIdentifier (Mage_Core_Model_Abstract $object) { return preg_match('/^[0-9]+$/', $object->getData('identifier')); } + + /** + * Check if page identifier exist for specific store + * return page id if page exists + * + * @param string $identifier + * @param int $storeId + * @return int + */ + public function checkIdentifier($identifier, $storeId) + { + $select = $this->_getReadAdapter()->select()->from(array('main_table'=>$this->getMainTable()), 'page_id') + ->join( + array('cps' => $this->getTable('cms/page_store')), + 'main_table.page_id = `cps`.page_id' + ) + ->where('main_table.identifier=?', $identifier) + ->where('main_table.is_active=1 AND `cps`.store_id in (0, ?) ', $storeId) + ->order('store_id DESC'); + + return $this->_getReadAdapter()->fetchOne($select); + } } \ No newline at end of file diff --git a/app/code/core/Mage/Cms/Model/Page.php b/app/code/core/Mage/Cms/Model/Page.php index 12688470ef..a0c1765246 100644 --- a/app/code/core/Mage/Cms/Model/Page.php +++ b/app/code/core/Mage/Cms/Model/Page.php @@ -58,4 +58,16 @@ public function noRoutePage() return $this; } + /** + * Check if page identifier exist for specific store + * return page id if page exists + * + * @param string $identifier + * @param int $storeId + * @return int + */ + public function checkIdentifier($identifier, $storeId) + { + return $this->_getResource()->checkIdentifier($identifier, $storeId); + } } diff --git a/app/code/core/Mage/Core/Controller/Varien/Action.php b/app/code/core/Mage/Core/Controller/Varien/Action.php index a8da5c1a46..bf9b1f8fe8 100644 --- a/app/code/core/Mage/Core/Controller/Varien/Action.php +++ b/app/code/core/Mage/Core/Controller/Varien/Action.php @@ -49,6 +49,8 @@ abstract class Mage_Core_Controller_Varien_Action const PARAM_NAME_BASE64_URL = 'r64'; const PARAM_NAME_URL_ENCODED = 'uenc'; + const PROFILER_KEY = 'mage::dispatch::controller::action'; + /** * Request object * @@ -242,50 +244,62 @@ public function addActionLayoutHandles() public function loadLayoutUpdates() { - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName(); + $_profilerKey = self::PROFILER_KEY . '::' .$this->getFullActionName(); // dispatch event for adding handles to layout update - Mage::dispatchEvent('controller_action_layout_load_before', array('action'=>$this, 'layout'=>$this->getLayout())); + Mage::dispatchEvent( + 'controller_action_layout_load_before', + array('action'=>$this, 'layout'=>$this->getLayout()) + ); // load layout updates by specified handles - Varien_Profiler::start("$_profilerKey/load"); + Varien_Profiler::start("$_profilerKey::layout_load"); $this->getLayout()->getUpdate()->load(); - Varien_Profiler::stop("$_profilerKey/load"); + Varien_Profiler::stop("$_profilerKey::layout_load"); return $this; } public function generateLayoutXml() { - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName(); + $_profilerKey = self::PROFILER_KEY . '::' . $this->getFullActionName(); // dispatch event for adding text layouts if(!$this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) { - Mage::dispatchEvent('controller_action_layout_generate_xml_before', array('action'=>$this, 'layout'=>$this->getLayout())); + Mage::dispatchEvent( + 'controller_action_layout_generate_xml_before', + array('action'=>$this, 'layout'=>$this->getLayout()) + ); } // generate xml from collected text updates - Varien_Profiler::start("$_profilerKey/xml"); + Varien_Profiler::start("$_profilerKey::layout_generate_xml"); $this->getLayout()->generateXml(); - Varien_Profiler::stop("$_profilerKey/xml"); + Varien_Profiler::stop("$_profilerKey::layout_generate_xml"); return $this; } public function generateLayoutBlocks() { - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName(); + $_profilerKey = self::PROFILER_KEY . '::' . $this->getFullActionName(); // dispatch event for adding xml layout elements if(!$this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) { - Mage::dispatchEvent('controller_action_layout_generate_blocks_before', array('action'=>$this, 'layout'=>$this->getLayout())); + Mage::dispatchEvent( + 'controller_action_layout_generate_blocks_before', + array('action'=>$this, 'layout'=>$this->getLayout()) + ); } // generate blocks from xml layout - Varien_Profiler::start("$_profilerKey/blocks"); + Varien_Profiler::start("$_profilerKey::layout_generate_blocks"); $this->getLayout()->generateBlocks(); - Varien_Profiler::stop("$_profilerKey/blocks"); + Varien_Profiler::stop("$_profilerKey::layout_generate_blocks"); if(!$this->getFlag('', self::FLAG_NO_DISPATCH_BLOCK_EVENT)) { - Mage::dispatchEvent('controller_action_layout_generate_blocks_after', array('action'=>$this, 'layout'=>$this->getLayout())); + Mage::dispatchEvent( + 'controller_action_layout_generate_blocks_after', + array('action'=>$this, 'layout'=>$this->getLayout()) + ); } return $this; @@ -299,7 +313,7 @@ public function generateLayoutBlocks() */ public function renderLayout($output='') { - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName(); + $_profilerKey = self::PROFILER_KEY . '::' . $this->getFullActionName(); if ($this->getFlag('', 'no-renderLayout')) { return; @@ -309,7 +323,7 @@ public function renderLayout($output='') return; } - Varien_Profiler::start("$_profilerKey/render"); + Varien_Profiler::start("$_profilerKey::layout_render"); if (''!==$output) { @@ -325,7 +339,7 @@ public function renderLayout($output='') $output = $this->getLayout()->getOutput(); $this->getResponse()->appendBody($output); - Varien_Profiler::stop("$_profilerKey/render"); + Varien_Profiler::stop("$_profilerKey::layout_render"); return $this; } @@ -338,16 +352,24 @@ public function dispatch($action) $actionMethodName = 'norouteAction'; } + Varien_Profiler::start(self::PROFILER_KEY.'::predispatch'); $this->preDispatch(); + Varien_Profiler::stop(self::PROFILER_KEY.'::predispatch'); if ($this->getRequest()->isDispatched()) { - // preDispatch() didn't change the action, so we can continue + /** + * preDispatch() didn't change the action, so we can continue + */ if (!$this->getFlag('', self::FLAG_NO_DISPATCH)) { - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName(); + $_profilerKey = self::PROFILER_KEY.'::'.$this->getFullActionName(); + Varien_Profiler::start($_profilerKey); $this->$actionMethodName(); Varien_Profiler::stop($_profilerKey); + + Varien_Profiler::start(self::PROFILER_KEY.'::postdispatch'); $this->postDispatch(); + Varien_Profiler::stop(self::PROFILER_KEY.'::postdispatch'); } } } @@ -385,12 +407,15 @@ public function preDispatch() return; } - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName().'/pre'; - Varien_Profiler::start($_profilerKey); Mage::dispatchEvent('controller_action_predispatch', array('controller_action'=>$this)); - Mage::dispatchEvent('controller_action_predispatch_'.$this->getRequest()->getRouteName(), array('controller_action'=>$this)); - Mage::dispatchEvent('controller_action_predispatch_'.$this->getFullActionName(), array('controller_action'=>$this)); - Varien_Profiler::stop($_profilerKey); + Mage::dispatchEvent( + 'controller_action_predispatch_'.$this->getRequest()->getRouteName(), + array('controller_action'=>$this) + ); + Mage::dispatchEvent( + 'controller_action_predispatch_'.$this->getFullActionName(), + array('controller_action'=>$this) + ); } /** @@ -401,14 +426,16 @@ public function postDispatch() if ($this->getFlag('', self::FLAG_NO_POST_DISPATCH)) { return; } - $_profilerKey = 'ctrl/dispatch/'.$this->getFullActionName().'/post'; - Varien_Profiler::start($_profilerKey); - Mage::dispatchEvent('controller_action_postdispatch_'.$this->getFullActionName(), array('controller_action'=>$this)); - Mage::dispatchEvent('controller_action_postdispatch_'.$this->getRequest()->getRouteName(), array('controller_action'=>$this)); + Mage::dispatchEvent( + 'controller_action_postdispatch_'.$this->getFullActionName(), + array('controller_action'=>$this) + ); + Mage::dispatchEvent( + 'controller_action_postdispatch_'.$this->getRequest()->getRouteName(), + array('controller_action'=>$this) + ); Mage::dispatchEvent('controller_action_postdispatch', array('controller_action'=>$this)); - - Varien_Profiler::stop($_profilerKey); } public function norouteAction($coreRoute = null) @@ -599,7 +626,11 @@ protected function _getRealModuleName() { if (empty($this->_realModuleName)) { $class = get_class($this); - $this->_realModuleName = substr($class, 0, strpos(strtolower($class), '_' . strtolower($this->getRequest()->getControllerName() . 'Controller'))); + $this->_realModuleName = substr( + $class, + 0, + strpos(strtolower($class), '_' . strtolower($this->getRequest()->getControllerName() . 'Controller')) + ); } return $this->_realModuleName; } @@ -654,9 +685,11 @@ protected function _rewrite() } } - $this->_forward($t[2]==='*' ? $action : $t[2], + $this->_forward( + $t[2]==='*' ? $action : $t[2], $t[1]==='*' ? $controller : $t[1], - $t[0]==='*' ? $route : $t[0]); + $t[0]==='*' ? $route : $t[0] + ); return true; } diff --git a/app/code/core/Mage/Core/Controller/Varien/Front.php b/app/code/core/Mage/Core/Controller/Varien/Front.php index 9091294ce1..e9992b60e7 100644 --- a/app/code/core/Mage/Core/Controller/Varien/Front.php +++ b/app/code/core/Mage/Core/Controller/Varien/Front.php @@ -112,10 +112,9 @@ public function init() { Mage::dispatchEvent('controller_front_init_before', array('front'=>$this)); - Varien_Profiler::start('ctrl/init'); - $routersInfo = Mage::app()->getStore()->getConfig(self::XML_STORE_ROUTERS_PATH); + Varien_Profiler::start('mage::app::init_front_controller::collect_routers'); foreach ($routersInfo as $routerCode => $routerInfo) { if (isset($routerInfo['disabled']) && $routerInfo['disabled']) { continue; @@ -128,46 +127,31 @@ public function init() $this->addRouter($routerCode, $router); } } + Varien_Profiler::stop('mage::app::init_front_controller::collect_routers'); + Mage::dispatchEvent('controller_front_init_routers', array('front'=>$this)); // Add default router at the last $default = new Mage_Core_Controller_Varien_Router_Default(); $this->addRouter('default', $default); -// init admin modules router -// $admin = new Mage_Core_Controller_Varien_Router_Admin(); -// $admin->collectRoutes('admin', 'admin'); -// $this->addRouter('admin', $admin); -// -// init standard frontend modules router -// $standard = new Mage_Core_Controller_Varien_Router_Standard(); -// $standard->collectRoutes('frontend', 'standard'); -// $this->addRouter('standard', $standard); -// -// init custom routers -// Mage::dispatchEvent('controller_front_init_routers', array('front'=>$this)); -// -// init default router (articles and 404) -// $default = new Mage_Core_Controller_Varien_Router_Default(); -// $this->addRouter('default', $default); - - Varien_Profiler::stop('ctrl/init'); - return $this; } public function dispatch() { - Varien_Profiler::start('ctrl/dispatch'); - $request = $this->getRequest(); $request->setPathInfo()->setDispatched(false); + Varien_Profiler::start('mage::dispatch::db_url_rewrite'); Mage::getModel('core/url_rewrite')->rewrite(); - $this->rewrite(); + Varien_Profiler::stop('mage::dispatch::db_url_rewrite'); - Varien_Profiler::stop('app/init'); + Varien_Profiler::start('mage::dispatch::config_url_rewrite'); + $this->rewrite(); + Varien_Profiler::stop('mage::dispatch::config_url_rewrite'); + Varien_Profiler::start('mage::dispatch::routers_match'); $i = 0; while (!$request->isDispatched() && $i++<100) { foreach ($this->_routers as $router) { @@ -176,15 +160,14 @@ public function dispatch() } } } + Varien_Profiler::stop('mage::dispatch::routers_match'); if ($i>100) { Mage::throwException('Front controller reached 100 router match iterations'); } - Varien_Profiler::stop('ctrl/dispatch'); - - Varien_Profiler::start('ctrl/response'); + Varien_Profiler::start('mage::app::dispatch::send_response'); $this->getResponse()->sendResponse(); - Varien_Profiler::stop('ctrl/response'); + Varien_Profiler::stop('mage::app::dispatch::send_response'); return $this; } diff --git a/app/code/core/Mage/Core/Model/App.php b/app/code/core/Mage/Core/Model/App.php index e0dceb4305..39845653af 100644 --- a/app/code/core/Mage/Core/Model/App.php +++ b/app/code/core/Mage/Core/Model/App.php @@ -237,11 +237,15 @@ public function init($code, $type=null, $options=array()) $options = array('etc_dir'=>$options); } + Varien_Profiler::start('mage::app::init::config'); $this->_config = Mage::getConfig(); $this->_config->init($options); + Varien_Profiler::stop('mage::app::init::config'); if (Mage::isInstalled($options)) { + Varien_Profiler::start('mage::app::init::stores'); $this->_initStores(); + Varien_Profiler::stop('mage::app::init::stores'); if (empty($code) && !is_null($this->_website)) { $code = $this->_website->getCode(); @@ -270,6 +274,16 @@ public function init($code, $type=null, $options=array()) return $this; } + /** + * Retrieve cookie object + * + * @return Mage_Core_Model_Cookie + */ + public function getCookie() + { + return Mage::getSingleton('core/cookie'); + } + /** * Check get store * @@ -281,6 +295,10 @@ protected function _checkGetStore($type) return $this; } + /** + * @todo check XML_PATH_STORE_IN_URL + */ + if (!isset($_GET['___store'])) { return $this; } @@ -311,9 +329,7 @@ protected function _checkGetStore($type) } if ($this->_currentStore == $store) { - $cookie = Mage::getSingleton('core/cookie'); - /* @var $cookie Mage_Core_Model_Cookie */ - $cookie->set('store', $this->_currentStore); + $this->getCookie()->set('store', $this->_currentStore, true); } return $this; } @@ -326,13 +342,11 @@ protected function _checkGetStore($type) */ protected function _checkCookieStore($type) { - if (empty($_COOKIE)) { + if (!$this->getCookie()->get()) { return $this; } - $cookie = Mage::getSingleton('core/cookie'); - /* @var $cookie Mage_Core_Model_Cookie */ - $store = $cookie->get('store'); + $store = $this->getCookie()->get('store'); if ($store && isset($this->_stores[$store]) && $this->_stores[$store]->getId() && $this->_stores[$store]->getIsActive()) { @@ -499,7 +513,9 @@ protected function _initFrontController() { $this->_frontController = new Mage_Core_Controller_Varien_Front(); Mage::register('controller', $this->_frontController); + Varien_Profiler::start('mage::app::init_front_controller'); $this->_frontController->init(); + Varien_Profiler::stop('mage::app::init_front_controller'); return $this; } @@ -1199,5 +1215,4 @@ public function getUseSessionVar() { return $this->_useSessionVar; } - } diff --git a/app/code/core/Mage/Core/Model/App/Area.php b/app/code/core/Mage/Core/Model/App/Area.php index 72b8750ebc..b29e0ba259 100644 --- a/app/code/core/Mage/Core/Model/App/Area.php +++ b/app/code/core/Mage/Core/Model/App/Area.php @@ -108,7 +108,7 @@ protected function _loadPart($part) if (isset($this->_loadedParts[$part])) { return $this; } - Varien_Profiler::start('app/area/loadPart/'.$this->_code.'/'.$part); + Varien_Profiler::start('mage::dispatch::controller::action::predispatch::load_area::'.$this->_code.'::'.$part); switch ($part) { case self::PART_CONFIG: $this->_initConfig(); @@ -124,7 +124,7 @@ protected function _loadPart($part) break; } $this->_loadedParts[$part] = true; - Varien_Profiler::stop('app/area/loadPart/'.$this->_code.'/'.$part); + Varien_Profiler::stop('mage::dispatch::controller::action::predispatch::load_area::'.$this->_code.'::'.$part); return $this; } diff --git a/app/code/core/Mage/Core/Model/Config.php b/app/code/core/Mage/Core/Model/Config.php index c03be3d8cd..eace993716 100644 --- a/app/code/core/Mage/Core/Model/Config.php +++ b/app/code/core/Mage/Core/Model/Config.php @@ -121,6 +121,7 @@ public function init($options=array()) // check if local modules are disabled $disableLocalModules = (string)$this->getNode('global/disable_local_modules'); $disableLocalModules = !empty($disableLocalModules) && (('true' === $disableLocalModules) || ('1' === $disableLocalModules)); + if ($disableLocalModules) { /** * Reset include path @@ -145,17 +146,15 @@ public function init($options=array()) if (Mage::isInstalled()) { if (Mage::app()->useCache('config')) { - Varien_Profiler::start('config/load-cache'); + Varien_Profiler::start('mage::app::init::config::load_cache'); $loaded = $this->loadCache(); - Varien_Profiler::stop('config/load-cache'); + Varien_Profiler::stop('mage::app::init::config::load_cache'); if ($loaded) { return $this; } } } - Varien_Profiler::stop('config/load-cache'); - $mergeConfig = new Mage_Core_Model_Config_Base(); /** diff --git a/app/code/core/Mage/Core/Model/Cookie.php b/app/code/core/Mage/Core/Model/Cookie.php index 32441ad5bf..005fd7f4eb 100644 --- a/app/code/core/Mage/Core/Model/Cookie.php +++ b/app/code/core/Mage/Core/Model/Cookie.php @@ -30,89 +30,231 @@ * * @category Mage * @package Mage_Core - * @author Magento Core Team + * @author Magento Core Team */ class Mage_Core_Model_Cookie { + const XML_PATH_COOKIE_DOMAIN = 'web/cookie/cookie_domain'; + const XML_PATH_COOKIE_PATH = 'web/cookie/cookie_path'; + const XML_PATH_COOKIE_LIFETIME = 'web/cookie/cookie_lifetime'; + const XML_PATH_COOKIE_HTTPONLY = 'web/cookie/cookie_httponly'; - const COOKIE_NAME = 'magento'; + /** + * Store object + * + * @var Mage_Core_Model_Store + */ + protected $_store; - protected $_id = null; + /** + * Set Store object + * + * @param mixed $store + * @return Mage_Core_Model_Cookie + */ + public function setStore($store) + { + $this->_store = Mage::app()->getStore($store); + return $this; + } - public function __construct() + /** + * Retrieve Store object + * + * @return Mage_Core_Model_Store + */ + public function getStore() { - if (isset($_COOKIE[self::COOKIE_NAME])) { - $this->_id = $_COOKIE[self::COOKIE_NAME]; + if (is_null($this->_store)) { + $this->_store = Mage::app()->getStore(); } - else { - $this->_id = $this->randomSequence(); - setcookie(self::COOKIE_NAME, $this->_id, time()+60*60*24*30, $this->getCookiePath(), $this->getCookieDomain()); + return $this->_store; + } + + /** + * Retrieve Request object + * + * @return Mage_Core_Controller_Request_Http + */ + protected function _getRequest() + { + return Mage::app()->getRequest(); + } + + /** + * Retrieve Response object + * + * @return Mage_Core_Controller_Response_Http + */ + protected function _getResponse() + { + return Mage::app()->getResponse(); + } + + /** + * Retrieve Domain for cookie + * + * @return string + */ + public function getDomain() + { + $domain = Mage::getStoreConfig(self::XML_PATH_COOKIE_DOMAIN, $this->getStore()); + if (empty($domain)) { + $domain = $this->_getRequest()->getHttpHost(); } + return $domain; } - public function getCookieDomain() + /** + * Retrieve Path for cookie + * + * @return string + */ + public function getPath() { - $domain = Mage::getStoreConfig('web/cookie/cookie_domain'); - if (empty($domain) && isset($_SERVER['HTTP_HOST'])) { - $domainArr = explode(':', $_SERVER['HTTP_HOST']); - $domain = $domainArr[0]; - } - return $domain; + $path = Mage::getStoreConfig(self::XML_PATH_COOKIE_PATH, $this->getStore()); + if (empty($path)) { + $path = $this->_getRequest()->getBasePath(); + } + return $path; } - public function getCookiePath() + /** + * Retrieve cookie lifetime + * + * @return int + */ + public function getLifetime() { - $path = Mage::getStoreConfig('web/cookie/cookie_path'); - if (empty($path)) { - $request = new Mage_Core_Controller_Request_Http(); - $path = $request->getBasePath(); - } - return $path; + $lifetime = Mage::getStoreConfig(self::XML_PATH_COOKIE_LIFETIME, $this->getStore()); + if (!is_numeric($lifetime)) { + $lifetime = 3600; + } + return $lifetime; } - public function getId() + /** + * Retrieve use HTTP only flag + * + * @return bool + */ + public function getHttponly() { - return $this->_id; + $httponly = Mage::getStoreConfig(self::XML_PATH_COOKIE_HTTPONLY, $this->getStore()); + if (is_null($httponly)) { + return null; + } + return (bool)$httponly; } - public function randomSequence($length=32) + /** + * Is https secure request + * Use secure on adminhtml only + * + * @return bool + */ + public function isSecure() { - $id = ''; - $par = array(); - $char = array_merge(range('a','z'),range(0,9)); - $charLen = count($char)-1; - for ($i=0;$i<$length;$i++){ - $disc = mt_rand(0, $charLen); - $par[$i] = $char[$disc]; - $id = $id.$char[$disc]; - } - return $id; + if ($this->getStore()->isAdmin()) { + return $this->_getRequest()->isSecure(); + } + return false; } - public function set($cookieName, $value, $period=null) + /** + * Set cookie + * + * @param string $name The cookie name + * @param string $value The cookie value + * @param int $period Lifetime period + * @param string $path + * @param string $domain + * @param int|bool $secure + * @return Mage_Core_Model_Cookie + */ + public function set($name, $value, $period = null, $path = null, $domain = null, $secure = null, $httponly = null) { - if( !isset($period) ) { + /** + * Check headers sent + */ + if (!$this->_getResponse()->canSendHeaders(false)) { + return $this; + } + + if ($period === true) { $period = 3600 * 24 * 365; + } elseif (is_null($period)) { + $period = $this->getLifetime(); } - $expire = time() + $period; - $this->delete($cookieName); - setcookie($cookieName, $value, $expire, $this->getCookiePath(), $this->getCookieDomain()); + + if ($period == 0) { + $expire = 0; + } + else { + $expire = time() + $period; + } + if (is_null($path)) { + $path = $this->getPath(); + } + if (is_null($domain)) { + $domain = $this->getDomain(); + } + if (is_null($secure)) { + $secure = $this->isSecure(); + } + if (is_null($httponly)) { + $httponly = $this->getHttponly(); + } + + setcookie($name, $value, $expire, $path, $domain, $secure, $httponly); + return $this; } - public function get($cookieName) + /** + * Retrieve cookie or false if not exists + * + * @param string $neme The cookie name + * @return mixed + */ + public function get($name = null) { - if( isset($_COOKIE[$cookieName]) ) { - return $_COOKIE[$cookieName]; - } else { - return false; - } + return $this->_getRequest()->getCookie($name, false); } - public function delete($cookieName) + /** + * Delete cookie + * + * @param string $name + * @param string $path + * @param string $domain + * @param int|bool $secure + * @param int|bool $httponly + * @return Mage_Core_Model_Cookie + */ + public function delete($name, $path = null, $domain = null, $secure = null, $httponly = null) { - setcookie($cookieName, '', (time() - 3600) ); + /** + * Check headers sent + */ + if (!$this->_getResponse()->canSendHeaders(false)) { + return $this; + } + + if (is_null($path)) { + $path = $this->getPath(); + } + if (is_null($domain)) { + $domain = $this->getDomain(); + } + if (is_null($secure)) { + $secure = $this->isSecure(); + } + if (is_null($httponly)) { + $httponly = $this->getHttponly(); + } + + setcookie($name, null, null, $path, $domain, $secure, $httponly); return $this; } - } diff --git a/app/code/core/Mage/Core/Model/Mysql4/Abstract.php b/app/code/core/Mage/Core/Model/Mysql4/Abstract.php index 20b3c11169..540401e8e2 100644 --- a/app/code/core/Mage/Core/Model/Mysql4/Abstract.php +++ b/app/code/core/Mage/Core/Model/Mysql4/Abstract.php @@ -105,7 +105,7 @@ abstract class Mage_Core_Model_Mysql4_Abstract extends Mage_Core_Model_Resource_ * * @var array */ - protected $_uniqueFields = array(); + protected $_uniqueFields = null; /** * Standard resource model initialization @@ -372,16 +372,54 @@ public function delete(Mage_Core_Model_Abstract $object) return $this; } + /** + * Add unique field restriction + * + * @param array|string $field + * @return Mage_Core_Model_Mysql4_Abstract + */ public function addUniqueField($field) { - if( is_array($this->_uniqueFields) ) { + $this->_initUniqueFields(); + if(is_array($this->_uniqueFields) ) { $this->_uniqueFields[] = $field; } + return $this; } + /** + * Reset unique fields restrictions + * + * @return Mage_Core_Model_Mysql4_Abstract + */ public function resetUniqueField() { $this->_uniqueFields = array(); + return $this; + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(); + return $this; + } + + /** + * Get configuration of all unique fields + * + * @return array + */ + public function getUniqueFields() + { + if (is_null($this->_uniqueFields)) { + $this->_initUniqueFields(); + } + return $this->_uniqueFields; } /** @@ -431,19 +469,20 @@ protected function _prepareValueForSave($value, $type) /** * Check for unique values existence * - * @param Varien_Object $object - * @return Mage_Core_Model_Mysql4_Abstract - * @throws Mage_Core_Exception + * @param Varien_Object $object + * @return Mage_Core_Model_Mysql4_Abstract + * @throws Mage_Core_Exception */ protected function _checkUnique(Mage_Core_Model_Abstract $object) { $existent = array(); - if (!empty($this->_uniqueFields)) { - if (!is_array($this->_uniqueFields)) { + $fields = $this->getUniqueFields(); + if (!empty($fields)) { + if (!is_array($fields)) { $this->_uniqueFields = array( array( - 'field' => $this->_uniqueFields, - 'title' => $this->_uniqueFields + 'field' => $fields, + 'title' => $fields )); } @@ -451,7 +490,7 @@ protected function _checkUnique(Mage_Core_Model_Abstract $object) $select = $this->_getWriteAdapter()->select() ->from($this->getMainTable()); - foreach ($this->_uniqueFields as $unique) { + foreach ($fields as $unique) { $select->reset(Zend_Db_Select::WHERE); if (is_array($unique['field'])) { diff --git a/app/code/core/Mage/Core/Model/Mysql4/Store.php b/app/code/core/Mage/Core/Model/Mysql4/Store.php index 0f034adbd8..eabfb3e442 100644 --- a/app/code/core/Mage/Core/Model/Mysql4/Store.php +++ b/app/code/core/Mage/Core/Model/Mysql4/Store.php @@ -30,7 +30,20 @@ class Mage_Core_Model_Mysql4_Store extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('core/store', 'store_id'); - $this->_uniqueFields = array(array('field' => 'code', 'title' => Mage::helper('core')->__('Store with the same code'))); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => 'code', + 'title' => Mage::helper('core')->__('Store with the same code') + )); + return $this; } protected function _beforeSave(Mage_Core_Model_Abstract $model) diff --git a/app/code/core/Mage/Core/Model/Mysql4/Url/Rewrite.php b/app/code/core/Mage/Core/Model/Mysql4/Url/Rewrite.php index 5357a03f91..7db12ba479 100644 --- a/app/code/core/Mage/Core/Model/Mysql4/Url/Rewrite.php +++ b/app/code/core/Mage/Core/Model/Mysql4/Url/Rewrite.php @@ -41,6 +41,15 @@ protected function _construct() { $this->_init('core/url_rewrite', 'url_rewrite_id'); $this->_tagTable = $this->getTable('url_rewrite_tag'); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { $this->_uniqueFields = array( array( 'field' => array('id_path','store_id','is_system'), @@ -51,6 +60,7 @@ protected function _construct() 'title' => Mage::helper('core')->__('Request path for specified store'), ) ); + return $this; } /** diff --git a/app/code/core/Mage/Core/Model/Mysql4/Website.php b/app/code/core/Mage/Core/Model/Mysql4/Website.php index 9fc96eacdb..e99b898ef4 100644 --- a/app/code/core/Mage/Core/Model/Mysql4/Website.php +++ b/app/code/core/Mage/Core/Model/Mysql4/Website.php @@ -30,7 +30,20 @@ class Mage_Core_Model_Mysql4_Website extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('core/website', 'website_id'); - $this->_uniqueFields = array(array('field' => 'code', 'title' => Mage::helper('core')->__('Website with the same code'))); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => 'code', + 'title' => Mage::helper('core')->__('Website with the same code') + )); + return $this; } /** @@ -79,4 +92,27 @@ protected function _afterDelete(Mage_Core_Model_Abstract $model) ); return $this; } + + /** + * Retrieve default stores select object + * Select fields website_id, store_id + * + * @param $withDefault include/exclude default admin website + * @return Varien_Db_Select + */ + public function getDefaultStoresSelect($withDefault = false) { + $select = $this->_getReadAdapter()->select() + ->from(array('website_table' => $this->getTable('core/website')), array('website_id')) + ->joinLeft( + array('store_group_table' => $this->getTable('core/store_group')), + '`website_table`.`website_id`=`store_group_table`.`website_id`' + . ' AND `website_table`.`default_group_id`=`store_group_table`.`group_id`', + array('store_id' => 'IFNULL(`store_group_table`.`default_store_id`, 0)') + ); + if (!$withDefault) { + $select->where('`website_table`.`website_id` <> ?', 0); + } + return $select; + } + } \ No newline at end of file diff --git a/app/code/core/Mage/Core/Model/Resource/Setup.php b/app/code/core/Mage/Core/Model/Resource/Setup.php index 42059e20de..877c49b576 100644 --- a/app/code/core/Mage/Core/Model/Resource/Setup.php +++ b/app/code/core/Mage/Core/Model/Resource/Setup.php @@ -219,27 +219,32 @@ protected function _modifyResourceDb($actionType, $fromVersion, $toVersion) $sqlFilesDir = Mage::getModuleDir('sql', $modName).DS.$this->_resourceName; if (!is_dir($sqlFilesDir) || !is_readable($sqlFilesDir)) { - return false; + Mage::getResourceModel('core/resource')->setDbVersion($this->_resourceName, $toVersion); + return $toVersion; } // Read resource files $arrAvailableFiles = array(); $sqlDir = dir($sqlFilesDir); while (false !== ($sqlFile = $sqlDir->read())) { + $matches = array(); if (preg_match('#^'.$resModel.'-'.$actionType.'-(.*)\.(sql|php)$#i', $sqlFile, $matches)) { $arrAvailableFiles[$matches[1]] = $sqlFile; } } $sqlDir->close(); if (empty($arrAvailableFiles)) { - return false; + Mage::getResourceModel('core/resource')->setDbVersion($this->_resourceName, $toVersion); + return $toVersion; } // Get SQL files name $arrModifyFiles = $this->_getModifySqlFiles($actionType, $fromVersion, $toVersion, $arrAvailableFiles); if (empty($arrModifyFiles)) { - return false; + Mage::getResourceModel('core/resource')->setDbVersion($this->_resourceName, $toVersion); + return $toVersion; } + $modifyVersion = null; foreach ($arrModifyFiles as $resourceFile) { $sqlFile = $sqlFilesDir.DS.$resourceFile['fileName']; $fileType = pathinfo($resourceFile['fileName'], PATHINFO_EXTENSION); @@ -287,8 +292,17 @@ protected function _modifyResourceDb($actionType, $fromVersion, $toVersion) throw Mage::exception('Mage_Core', Mage::helper('core')->__('Error in file: "%s" - %s', $sqlFile, $e->getMessage())); } } - $toVersion = $resourceFile['toVersion']; + + $modifyVersion = $resourceFile['toVersion']; + } + + if ($actionType == 'upgrade' && $modifyVersion != $toVersion) { + Mage::getResourceModel('core/resource')->setDbVersion($this->_resourceName, $toVersion); } + else { + $toVersion = $modifyVersion; + } + self::$_hadUpdates = true; return $toVersion; } diff --git a/app/code/core/Mage/Core/Model/Session/Abstract.php b/app/code/core/Mage/Core/Model/Session/Abstract.php index 1cab53bd56..8c03fe0c00 100644 --- a/app/code/core/Mage/Core/Model/Session/Abstract.php +++ b/app/code/core/Mage/Core/Model/Session/Abstract.php @@ -55,20 +55,34 @@ public function init($namespace, $sessionName=null) return $this; } + /** + * Retrieve Cookie domain + * + * @return string + */ public function getCookieDomain() { - return Mage::getSingleton('core/cookie')->getCookieDomain(); + return $this->getCookie()->getDomain(); } + /** + * Retrieve cookie path + * + * @return string + */ public function getCookiePath() { - return Mage::getSingleton('core/cookie')->getCookiePath(); + return $this->getCookie()->getPath(); } + /** + * Retrieve cookie lifetime + * + * @return int + */ public function getCookieLifetime() { - $lifetime = Mage::getStoreConfig(self::XML_PATH_COOKIE_LIFETIME); - return $lifetime; + return $this->getCookie()->getLifetime(); } /** diff --git a/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php b/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php index 596212495c..0c862e47d7 100644 --- a/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php +++ b/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php @@ -33,6 +33,12 @@ class Mage_Core_Model_Session_Abstract_Varien extends Varien_Object const VALIDATOR_HTTP_VIA_KEY = 'http_via'; const VALIDATOR_REMOTE_ADDR_KEY = 'remote_addr'; + /** + * Conigure and start session + * + * @param string $sessionName + * @return Mage_Core_Model_Session_Abstract_Varien + */ public function start($sessionName=null) { if (isset($_SESSION)) { @@ -61,23 +67,14 @@ public function start($sessionName=null) break; } - if (intval($this->getCookieLifetime()) > 0) { - ini_set('session.gc_maxlifetime', $this->getCookieLifetime()); - ini_set('session.cookie_lifetime', $this->getCookieLifetime()); - } else { - /* - * if cookie life time is empty or 0, we put 0 - * session will be time out when we close the browser - */ - ini_set('session.gc_maxlifetime', 3600); - ini_set('session.cookie_lifetime', 3600); - } - if (!is_null($this->getCookiePath())) { - ini_set('session.cookie_path', $this->getCookiePath()); - } - if (!is_null($this->getCookieDomain()) && strpos($this->getCookieDomain(), '.')!==false) { - ini_set('session.cookie_domain', $this->getCookieDomain()); - } + // set session cookie params + session_set_cookie_params( + $this->getCookie()->getLifetime(), + $this->getCookie()->getPath(), + $this->getCookie()->getDomain(), + $this->getCookie()->isSecure(), + $this->getCookie()->getHttponly() + ); if (!empty($sessionName)) { session_name($sessionName); @@ -99,8 +96,26 @@ public function start($sessionName=null) return $this; } + /** + * Retrieve cookie object + * + * @return Mage_Core_Model_Cookie + */ + public function getCookie() + { + return Mage::getSingleton('core/cookie'); + } + + /** + * Revalidate cookie + * + * @return Mage_Core_Model_Session_Abstract_Varien + */ public function revalidateCookie() { + if (!$this->getCookie()->getLifetime()) { + return $this; + } if (empty($this->_data['_cookie_revalidate'])) { $time = time() + round(ini_get('session.gc_maxlifetime') / 4); $this->_data['_cookie_revalidate'] = $time; @@ -108,21 +123,24 @@ public function revalidateCookie() else { if ($this->_data['_cookie_revalidate'] < time()) { if (!headers_sent()) { - setcookie( - session_name(), - session_id(), - time() + ini_get('session.gc_maxlifetime'), - ini_get('session.cookie_path'), - ini_get('session.cookie_domain') - ); - - $time = time() + round(ini_get('session.gc_maxlifetime') / 4); + $this->getCookie()->set(session_name(), session_id()); + + $time = time() + round($this->getCookie()->getLifetime() / 4); $this->_data['_cookie_revalidate'] = $time; } } } + + return $this; } + /** + * Init session with namespace + * + * @param string $namespace + * @param string $sessionName + * @return Mage_Core_Model_Session_Abstract_Varien + */ public function init($namespace, $sessionName=null) { if (!isset($_SESSION)) { @@ -140,7 +158,14 @@ public function init($namespace, $sessionName=null) return $this; } - public function getData($key='', $clear=false) + /** + * Additional get data with clear mode + * + * @param string $key + * @param bool $clear + * @return mixed + */ + public function getData($key='', $clear = false) { $data = parent::getData($key); if ($clear && isset($this->_data[$key])) { @@ -149,11 +174,22 @@ public function getData($key='', $clear=false) return $data; } + /** + * Retrieve session Id + * + * @return string + */ public function getSessionId() { return session_id(); } + /** + * Set custom session id + * + * @param string $id + * @return Mage_Core_Model_Session_Abstract_Varien + */ public function setSessionId($id=null) { if (!is_null($id) && preg_match('#^[0-9a-zA-Z,-]+$#', $id)) { @@ -162,12 +198,22 @@ public function setSessionId($id=null) return $this; } + /** + * Unset all data + * + * @return Mage_Core_Model_Session_Abstract_Varien + */ public function unsetAll() { $this->unsetData(); return $this; } + /** + * Alias for unsetAll + * + * @return Mage_Core_Model_Session_Abstract_Varien + */ public function clear() { return $this->unsetAll(); @@ -257,16 +303,7 @@ public function validate() } else { if (!$this->_validate()) { - if (!headers_sent()) { - // remove session cookie - setcookie( - session_name(), - null, - null, - ini_get('session.cookie_path'), - ini_get('session.cookie_domain') - ); - } + $this->getCookie()->delete(session_name()); // throw core session exception throw new Mage_Core_Model_Session_Exception(''); } diff --git a/app/code/core/Mage/Core/Model/Store.php b/app/code/core/Mage/Core/Model/Store.php index 7cb4327cde..b75e9cbc2b 100644 --- a/app/code/core/Mage/Core/Model/Store.php +++ b/app/code/core/Mage/Core/Model/Store.php @@ -721,7 +721,11 @@ public function getCurrentUrl($fromStore = true) { $query = Mage::getSingleton('core/url')->escape(ltrim(Mage::app()->getRequest()->getRequestString(), '/')); - $parsedUrl = parse_url($this->getUrl('')); + if (Mage::app()->getStore()->isCurrentlySecure()) { + $parsedUrl = parse_url($this->getUrl('', array('_secure' => true))); + } else { + $parsedUrl = parse_url($this->getUrl('')); + } $parsedQuery = array(); if (isset($parsedUrl['query'])) { parse_str($parsedUrl['query'], $parsedQuery); diff --git a/app/code/core/Mage/Core/Model/Translate.php b/app/code/core/Mage/Core/Model/Translate.php index 52e22138f6..4c07c44cf9 100644 --- a/app/code/core/Mage/Core/Model/Translate.php +++ b/app/code/core/Mage/Core/Model/Translate.php @@ -33,6 +33,7 @@ class Mage_Core_Model_Translate { const CSV_SEPARATOR = ','; const SCOPE_SEPARATOR = '::'; + const CACHE_TAG = 'translate'; const CONFIG_KEY_AREA = 'area'; const CONFIG_KEY_LOCALE = 'locale'; @@ -221,6 +222,9 @@ protected function _loadModuleTranslation($moduleName, $files, $forceReload=fals protected function _addData($data, $scope, $forceReload=false) { foreach ($data as $key => $value) { + if ($key === $value) { + continue; + } $key = $this->_prepareDataString($key); $value = $this->_prepareDataString($value); if ($scope && isset($this->_dataScope[$key]) && !$forceReload ) { @@ -526,13 +530,17 @@ protected function _saveCache() if (!$this->_canUseCache()) { return $this; } - Mage::app()->saveCache(serialize($this->getData()), $this->getCacheId(), array('translate'), null); + Mage::app()->saveCache(serialize($this->getData()), $this->getCacheId(), array(self::CACHE_TAG), null); return $this; } + /** + * Check cache usage availability + * + * @return bool + */ protected function _canUseCache() { - //return $this->_useCache; return Mage::app()->useCache('translate'); } diff --git a/app/code/core/Mage/Core/Model/Website.php b/app/code/core/Mage/Core/Model/Website.php index 0483674425..da88d529d7 100644 --- a/app/code/core/Mage/Core/Model/Website.php +++ b/app/code/core/Mage/Core/Model/Website.php @@ -1,489 +1,512 @@ - - */ - -class Mage_Core_Model_Website extends Mage_Core_Model_Abstract -{ - const CACHE_TAG = 'website'; - protected $_cacheTag = true; - - /** - * Cache configuration array - * - * @var array - */ - protected $_configCache = array(); - - /** - * Website Group Coleection array - * - * @var array - */ - protected $_groups; - - /** - * Website group ids array - * - * @var array - */ - protected $_groupIds = array(); - - /** - * The number of groups in a website - * - * @var int - */ - protected $_groupsCount; - - /** - * Website Store collection array - * - * @var array - */ - protected $_stores; - - /** - * Website store ids array - * - * @var array - */ - protected $_storeIds = array(); - - /** - * Website store codes array - * - * @var array - */ - protected $_storeCodes = array(); - - /** - * The number of stores in a website - * - * @var int - */ - protected $_storesCount = 0; - - /** - * Website default group - * - * @var Mage_Core_Model_Store_Group - */ - protected $_defaultGroup; - - /** - * Website default store - * - * @var Mage_Core_Model_Store - */ - protected $_defaultStore; - - /** - * is can delete website - * - * @var bool - */ - protected $_isCanDelete; - - /** - * init model - * - */ - protected function _construct() - { - $this->_init('core/website'); - } - - /** - * Custom load - * - * @param int|string $id - * @param string $field - * @return Mage_Core_Model_Website - */ - public function load($id, $field = null) - { - if (!is_numeric($id) && is_null($field)) { - $this->_getResource()->load($this, $id, 'code'); - return $this; - } - return parent::load($id, $field); - } - - /** - * Load website configuration - * - * @param string $code - * @return Mage_Core_Model_Website - */ - public function loadConfig($code) - { - if (!Mage::getConfig()->getNode('websites')) { - return $this; - } - if (is_numeric($code)) { - foreach (Mage::getConfig()->getNode('websites')->children() as $websiteCode=>$website) { - if ((int)$website->system->website->id==$code) { - $code = $websiteCode; - break; - } - } - } else { - $website = Mage::getConfig()->getNode('websites/'.$code); - } - if (!empty($website)) { - $this->setCode($code); - $id = (int)$website->system->website->id; - $this->setId($id)->setStoreId($id); - } - return $this; - } - - /** - * Get website config data - * - * @param string $path - * @return mixed - */ - public function getConfig($path) { - if (!isset($this->_configCache[$path])) { - - $config = Mage::getConfig()->getNode('websites/'.$this->getCode().'/'.$path); - if (!$config) { - return false; - #throw Mage::exception('Mage_Core', Mage::helper('core')->__('Invalid websites configuration path: %s', $path)); - } - if (!$config->children()) { - $value = (string)$config; - } else { - $value = array(); - foreach ($config->children() as $k=>$v) { - $value[$k] = $v; - } - } - $this->_configCache[$path] = $value; - } - return $this->_configCache[$path]; - } - - /** - * Load group collection and set internal data - * - */ - protected function _loadGroups() - { - $this->_groups = array(); - $this->_groupsCount = 0; - foreach ($this->getGroupCollection() as $group) { - $this->_groups[$group->getId()] = $group; - $this->_groupIds[$group->getId()] = $group->getId(); - if ($this->getDefaultGroupId() == $group->getId()) { - $this->_defaultGroup = $group; - } - $this->_groupsCount ++; - } - } - - /** - * Set website groups - * - * @param array $groups - */ - public function setGroups($groups) - { - $this->_groups = array(); - $this->_groupsCount = 0; - foreach ($groups as $group) { - $this->_groups[$group->getId()] = $group; - $this->_groupIds[$group->getId()] = $group->getId(); - if ($this->getDefaultGroupId() == $group->getId()) { - $this->_defaultGroup = $group; - } - $this->_groupsCount ++; - } - return $this; - } - - /** - * Retrieve new (not loaded) Group collection object with website filter - * - * @return Mage_Core_Model_Mysql4_Store_Group_Collection - */ - public function getGroupCollection() - { - return Mage::getModel('core/store_group') - ->getCollection() - ->addWebsiteFilter($this->getId()); - } - - /** - * Retrieve website groups - * - * @return array - */ - public function getGroups() - { - if (is_null($this->_groups)) { - $this->_loadGroups(); - } - return $this->_groups; - } - - /** - * Retrieve website group ids - * - * @return array - */ - public function getGroupIds() - { - if (is_null($this->_groups)) { - $this->_loadGroups(); - } - return $this->_groupIds; - } - - /** - * Retrieve number groups in a website - * - * @return int - */ - public function getGroupsCount() - { - if (is_null($this->_groups)) { - $this->_loadGroups(); - } - return $this->_groupsCount; - } - - /** - * Retrieve default group model - * - * @return Mage_Core_Model_Store_Group - */ - public function getDefaultGroup() - { - if (!$this->getDefaultGroupId()) { - return false; - } - if (is_null($this->_groups)) { - $this->_loadGroups(); - } - return $this->_defaultGroup; - } - - /** - * Load store collection and set internal data - * - */ - protected function _loadStores() - { - $this->_stores = array(); - $this->_storesCount = 0; - foreach ($this->getStoreCollection() as $store) { - $this->_stores[$store->getId()] = $store; - $this->_storeIds[$store->getId()] = $store->getId(); - $this->_storeCodes[$store->getId()] = $store->getCode(); - if ($this->getDefaultGroup() && $this->getDefaultGroup()->getDefaultStoreId() == $store->getId()) { - $this->_defaultStore = $store; - } - $this->_storesCount ++; - } - } - - /** - * Set website stores - * - * @param array $stores - */ - public function setStores($stores) - { - $this->_stores = array(); - $this->_storesCount = 0; - foreach ($stores as $store) { - $this->_stores[$store->getId()] = $store; - $this->_storeIds[$store->getId()] = $store->getId(); - $this->_storeCodes[$store->getId()] = $store->getCode(); - if ($this->getDefaultGroup() && $this->getDefaultGroup()->getDefaultStoreId() == $store->getId()) { - $this->_defaultStore = $store; - } - $this->_storesCount ++; - } - } - - /** - * Retrieve new (not loaded) Store collection object with website filter - * - * @return Mage_Core_Model_Mysql4_Store_Collection - */ - public function getStoreCollection() - { - return Mage::getModel('core/store') - ->getCollection() - ->addWebsiteFilter($this->getId()); - } - - /** - * Retrieve wersite store objects - * - * @return array - */ - public function getStores() - { - if (is_null($this->_stores)) { - $this->_loadStores(); - } - return $this->_stores; - } - - /** - * Retrieve website store ids - * - * @return array - */ - public function getStoreIds() - { - if (is_null($this->_stores)) { - $this->_loadStores(); - } - return $this->_storeIds; - } - - /** - * Retrieve website store codes - * - * @return array - */ - public function getStoreCodes() - { - if (is_null($this->_stores)) { - $this->_loadStores(); - } - return $this->_storeCodes; - } - - /** - * Retrieve number stores in a website - * - * @return int - */ - public function getStoresCount() - { - if (is_null($this->_stores)) { - $this->_loadStores(); - } - return $this->_storesCount; - } - - /** - * is can delete website - * - * @return bool - */ - public function isCanDelete() - { - if (!$this->getId()) { - return false; - } - if (is_null($this->_isCanDelete)) { - $this->_isCanDelete = (Mage::getModel('core/website')->getCollection()->getSize() > 2) - && !$this->getIsDefault(); - } - return $this->_isCanDelete; - } - - /** - * Retrieve unique website-group-store key for collection with groups and stores - * - * @return string - */ - public function getWebsiteGroupStore() - { - return join('-', array($this->getWebsiteId(), $this->getGroupId(), $this->getStoreId())); - } - - public function getDefaultGroupId() - { - return $this->_getData('default_group_id'); - } - - public function getCode() - { - return $this->_getData('code'); - } - - protected function _beforeDelete() - { - $this->_protectFromNonAdmin(); - return parent::_beforeDelete(); - } - - /** - * rewrite in order to clear configuration cache - * - * @return Mage_Core_Model_Website - */ - protected function _afterDelete() - { - parent::_afterDelete(); - Mage::getConfig()->removeCache(); - return $this; - } - - /** - * Retrieve website base currency code - * - * @return string - */ - public function getBaseCurrencyCode() - { - if ($this->getConfig(Mage_Core_Model_Store::XML_PATH_PRICE_SCOPE) == Mage_Core_Model_Store::PRICE_SCOPE_GLOBAL) { - return Mage::app()->getBaseCurrencyCode(); - } else { - return $this->getConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_BASE); - } - } - - /** - * Retrieve website base currency - * - * @return Mage_Directory_Model_Currency - */ - public function getBaseCurrency() - { - $currency = $this->getData('base_currency'); - if (is_null($currency)) { - $currency = Mage::getModel('directory/currency')->load($this->getBaseCurrencyCode()); - $this->setData('base_currency', $currency); - } - return $currency; - } -} + + */ + +class Mage_Core_Model_Website extends Mage_Core_Model_Abstract +{ + const CACHE_TAG = 'website'; + protected $_cacheTag = true; + + /** + * Cache configuration array + * + * @var array + */ + protected $_configCache = array(); + + /** + * Website Group Coleection array + * + * @var array + */ + protected $_groups; + + /** + * Website group ids array + * + * @var array + */ + protected $_groupIds = array(); + + /** + * The number of groups in a website + * + * @var int + */ + protected $_groupsCount; + + /** + * Website Store collection array + * + * @var array + */ + protected $_stores; + + /** + * Website store ids array + * + * @var array + */ + protected $_storeIds = array(); + + /** + * Website store codes array + * + * @var array + */ + protected $_storeCodes = array(); + + /** + * The number of stores in a website + * + * @var int + */ + protected $_storesCount = 0; + + /** + * Website default group + * + * @var Mage_Core_Model_Store_Group + */ + protected $_defaultGroup; + + /** + * Website default store + * + * @var Mage_Core_Model_Store + */ + protected $_defaultStore; + + /** + * is can delete website + * + * @var bool + */ + protected $_isCanDelete; + + /** + * init model + * + */ + protected function _construct() + { + $this->_init('core/website'); + } + + /** + * Custom load + * + * @param int|string $id + * @param string $field + * @return Mage_Core_Model_Website + */ + public function load($id, $field = null) + { + if (!is_numeric($id) && is_null($field)) { + $this->_getResource()->load($this, $id, 'code'); + return $this; + } + return parent::load($id, $field); + } + + /** + * Load website configuration + * + * @param string $code + * @return Mage_Core_Model_Website + */ + public function loadConfig($code) + { + if (!Mage::getConfig()->getNode('websites')) { + return $this; + } + if (is_numeric($code)) { + foreach (Mage::getConfig()->getNode('websites')->children() as $websiteCode=>$website) { + if ((int)$website->system->website->id==$code) { + $code = $websiteCode; + break; + } + } + } else { + $website = Mage::getConfig()->getNode('websites/'.$code); + } + if (!empty($website)) { + $this->setCode($code); + $id = (int)$website->system->website->id; + $this->setId($id)->setStoreId($id); + } + return $this; + } + + /** + * Get website config data + * + * @param string $path + * @return mixed + */ + public function getConfig($path) { + if (!isset($this->_configCache[$path])) { + + $config = Mage::getConfig()->getNode('websites/'.$this->getCode().'/'.$path); + if (!$config) { + return false; + #throw Mage::exception('Mage_Core', Mage::helper('core')->__('Invalid websites configuration path: %s', $path)); + } + if (!$config->children()) { + $value = (string)$config; + } else { + $value = array(); + foreach ($config->children() as $k=>$v) { + $value[$k] = $v; + } + } + $this->_configCache[$path] = $value; + } + return $this->_configCache[$path]; + } + + /** + * Load group collection and set internal data + * + */ + protected function _loadGroups() + { + $this->_groups = array(); + $this->_groupsCount = 0; + foreach ($this->getGroupCollection() as $group) { + $this->_groups[$group->getId()] = $group; + $this->_groupIds[$group->getId()] = $group->getId(); + if ($this->getDefaultGroupId() == $group->getId()) { + $this->_defaultGroup = $group; + } + $this->_groupsCount ++; + } + } + + /** + * Set website groups + * + * @param array $groups + */ + public function setGroups($groups) + { + $this->_groups = array(); + $this->_groupsCount = 0; + foreach ($groups as $group) { + $this->_groups[$group->getId()] = $group; + $this->_groupIds[$group->getId()] = $group->getId(); + if ($this->getDefaultGroupId() == $group->getId()) { + $this->_defaultGroup = $group; + } + $this->_groupsCount ++; + } + return $this; + } + + /** + * Retrieve new (not loaded) Group collection object with website filter + * + * @return Mage_Core_Model_Mysql4_Store_Group_Collection + */ + public function getGroupCollection() + { + return Mage::getModel('core/store_group') + ->getCollection() + ->addWebsiteFilter($this->getId()); + } + + /** + * Retrieve website groups + * + * @return array + */ + public function getGroups() + { + if (is_null($this->_groups)) { + $this->_loadGroups(); + } + return $this->_groups; + } + + /** + * Retrieve website group ids + * + * @return array + */ + public function getGroupIds() + { + if (is_null($this->_groups)) { + $this->_loadGroups(); + } + return $this->_groupIds; + } + + /** + * Retrieve number groups in a website + * + * @return int + */ + public function getGroupsCount() + { + if (is_null($this->_groups)) { + $this->_loadGroups(); + } + return $this->_groupsCount; + } + + /** + * Retrieve default group model + * + * @return Mage_Core_Model_Store_Group + */ + public function getDefaultGroup() + { + if (!$this->getDefaultGroupId()) { + return false; + } + if (is_null($this->_groups)) { + $this->_loadGroups(); + } + return $this->_defaultGroup; + } + + /** + * Load store collection and set internal data + * + */ + protected function _loadStores() + { + $this->_stores = array(); + $this->_storesCount = 0; + foreach ($this->getStoreCollection() as $store) { + $this->_stores[$store->getId()] = $store; + $this->_storeIds[$store->getId()] = $store->getId(); + $this->_storeCodes[$store->getId()] = $store->getCode(); + if ($this->getDefaultGroup() && $this->getDefaultGroup()->getDefaultStoreId() == $store->getId()) { + $this->_defaultStore = $store; + } + $this->_storesCount ++; + } + } + + /** + * Set website stores + * + * @param array $stores + */ + public function setStores($stores) + { + $this->_stores = array(); + $this->_storesCount = 0; + foreach ($stores as $store) { + $this->_stores[$store->getId()] = $store; + $this->_storeIds[$store->getId()] = $store->getId(); + $this->_storeCodes[$store->getId()] = $store->getCode(); + if ($this->getDefaultGroup() && $this->getDefaultGroup()->getDefaultStoreId() == $store->getId()) { + $this->_defaultStore = $store; + } + $this->_storesCount ++; + } + } + + /** + * Retrieve new (not loaded) Store collection object with website filter + * + * @return Mage_Core_Model_Mysql4_Store_Collection + */ + public function getStoreCollection() + { + return Mage::getModel('core/store') + ->getCollection() + ->addWebsiteFilter($this->getId()); + } + + /** + * Retrieve wersite store objects + * + * @return array + */ + public function getStores() + { + if (is_null($this->_stores)) { + $this->_loadStores(); + } + return $this->_stores; + } + + /** + * Retrieve website store ids + * + * @return array + */ + public function getStoreIds() + { + if (is_null($this->_stores)) { + $this->_loadStores(); + } + return $this->_storeIds; + } + + /** + * Retrieve website store codes + * + * @return array + */ + public function getStoreCodes() + { + if (is_null($this->_stores)) { + $this->_loadStores(); + } + return $this->_storeCodes; + } + + /** + * Retrieve number stores in a website + * + * @return int + */ + public function getStoresCount() + { + if (is_null($this->_stores)) { + $this->_loadStores(); + } + return $this->_storesCount; + } + + /** + * is can delete website + * + * @return bool + */ + public function isCanDelete() + { + if (!$this->getId()) { + return false; + } + if (is_null($this->_isCanDelete)) { + $this->_isCanDelete = (Mage::getModel('core/website')->getCollection()->getSize() > 2) + && !$this->getIsDefault(); + } + return $this->_isCanDelete; + } + + /** + * Retrieve unique website-group-store key for collection with groups and stores + * + * @return string + */ + public function getWebsiteGroupStore() + { + return join('-', array($this->getWebsiteId(), $this->getGroupId(), $this->getStoreId())); + } + + public function getDefaultGroupId() + { + return $this->_getData('default_group_id'); + } + + public function getCode() + { + return $this->_getData('code'); + } + + protected function _beforeDelete() + { + $this->_protectFromNonAdmin(); + return parent::_beforeDelete(); + } + + /** + * rewrite in order to clear configuration cache + * + * @return Mage_Core_Model_Website + */ + protected function _afterDelete() + { + parent::_afterDelete(); + Mage::getConfig()->removeCache(); + return $this; + } + + /** + * Retrieve website base currency code + * + * @return string + */ + public function getBaseCurrencyCode() + { + if ($this->getConfig(Mage_Core_Model_Store::XML_PATH_PRICE_SCOPE) == Mage_Core_Model_Store::PRICE_SCOPE_GLOBAL) { + return Mage::app()->getBaseCurrencyCode(); + } else { + return $this->getConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_BASE); + } + } + + /** + * Retrieve website base currency + * + * @return Mage_Directory_Model_Currency + */ + public function getBaseCurrency() + { + $currency = $this->getData('base_currency'); + if (is_null($currency)) { + $currency = Mage::getModel('directory/currency')->load($this->getBaseCurrencyCode()); + $this->setData('base_currency', $currency); + } + return $currency; + } + + /** + * Retrieve Default Website Store or null + * + * @return Mage_Core_Model_Store + */ + public function getDefaultStore() + { + // init stores if not loaded + $this->getStores(); + return $this->_defaultStore; + } + + /** + * Retrieve default stores select object + * Select fields website_id, store_id + * + * @param $withDefault include/exclude default admin website + * @return Varien_Db_Select + */ + public function getDefaultStoresSelect($withDefault = false) { + return $this->getResource()->getDefaultStoresSelect($withDefault); + } +} diff --git a/app/code/core/Mage/Core/etc/config.xml b/app/code/core/Mage/Core/etc/config.xml index 284eed18b2..3f90c0cb3e 100644 --- a/app/code/core/Mage/Core/etc/config.xml +++ b/app/code/core/Mage/Core/etc/config.xml @@ -222,6 +222,7 @@ 3600 + 1 diff --git a/app/code/core/Mage/Core/etc/system.xml b/app/code/core/Mage/Core/etc/system.xml index 299278c865..825d6eaa92 100644 --- a/app/code/core/Mage/Core/etc/system.xml +++ b/app/code/core/Mage/Core/etc/system.xml @@ -1001,7 +1001,7 @@ text - 1 + 30 1 1 1 @@ -1009,7 +1009,7 @@ text - 1 + 10 1 1 1 @@ -1017,11 +1017,20 @@ text - 1 + 20 1 1 1 + + + select + adminhtml/system_config_source_yesno + 40 + 1 + 1 + 1 + diff --git a/app/code/core/Mage/Customer/Model/Config/Share.php b/app/code/core/Mage/Customer/Model/Config/Share.php index ec7ca2dc8f..5c514fca3b 100644 --- a/app/code/core/Mage/Customer/Model/Config/Share.php +++ b/app/code/core/Mage/Customer/Model/Config/Share.php @@ -33,20 +33,44 @@ */ class Mage_Customer_Model_Config_Share extends Mage_Core_Model_Config_Data { + /** + * Xml config path to customers sharing scope value + * + */ const XML_PATH_CUSTOMER_ACCOUNT_SHARE = 'customer/account_share/scope'; + + /** + * Possible customer sharing scopes + * + */ const SHARE_GLOBAL = 0; const SHARE_WEBSITE = 1; + /** + * Check whether current customers sharing scope is global + * + * @return bool + */ public function isGlobalScope() { return !$this->isWebsiteScope(); } + /** + * Check whether current customers sharing scope is website + * + * @return bool + */ public function isWebsiteScope() { return Mage::getStoreConfig(self::XML_PATH_CUSTOMER_ACCOUNT_SHARE) == self::SHARE_WEBSITE; } + /** + * Get possible sharing configuration options + * + * @return array + */ public function toOptionArray() { return array( @@ -55,18 +79,20 @@ public function toOptionArray() ); } + /** + * Check for email dublicates before saving customers sharing options + * + * @return Mage_Customer_Model_Config_Share + * @throws Mage_Core_Exception + */ public function _beforeSave() { $value = $this->getValue(); if ($value == self::SHARE_GLOBAL) { - $collection = Mage::getModel('customer/customer')->getCollection() - ->groupByEmail(); - foreach ($collection as $customer) { - if ($customer->getEmailCount()>1) { - Mage::throwException( - Mage::helper('customer')->__('Can\'t share customer accounts global. Because some customer accounts with same emails exist on multiple websites and cannot be merged.') - ); - } + if (Mage::getResourceSingleton('customer/customer')->findEmailDuplicates()) { + Mage::throwException( + Mage::helper('customer')->__('Can\'t share customer accounts global. Because some customer accounts with same emails exist on multiple websites and cannot be merged.') + ); } } return $this; diff --git a/app/code/core/Mage/Customer/Model/Entity/Customer.php b/app/code/core/Mage/Customer/Model/Entity/Customer.php index c5dbdceb00..b697fd3e93 100644 --- a/app/code/core/Mage/Customer/Model/Entity/Customer.php +++ b/app/code/core/Mage/Customer/Model/Entity/Customer.php @@ -33,6 +33,10 @@ */ class Mage_Customer_Model_Entity_Customer extends Mage_Eav_Model_Entity_Abstract { + /** + * Initiate resources + * + */ public function __construct() { $resource = Mage::getSingleton('core/resource'); @@ -61,6 +65,13 @@ protected function _getDefaultAttributes() ); } + /** + * Check customer scope, email and confirmation key before saving + * + * @param Varien_Object $customer + * @return Mage_Customer_Model_Entity_Customer + * @throws Mage_Core_Exception + */ protected function _beforeSave(Varien_Object $customer) { parent::_beforeSave($customer); @@ -105,7 +116,12 @@ protected function _afterSave(Varien_Object $customer) return parent::_afterSave($customer); } - + /** + * Save/delete customer address + * + * @param Mage_Customer_Model_Customer $customer + * @return Mage_Customer_Model_Entity_Customer + */ protected function _saveAddresses(Mage_Customer_Model_Customer $customer) { foreach ($customer->getAddresses() as $address) { @@ -137,7 +153,16 @@ protected function _getLoadRowSelect($object, $rowId) return $select; } - public function loadByEmail(Mage_Customer_Model_Customer $customer, $email, $testOnly=false) + /** + * Load customer by email + * + * @param Mage_Customer_Model_Customer $customer + * @param string $email + * @param bool $testOnly + * @return Mage_Customer_Model_Entity_Customer + * @throws Mage_Core_Exception + */ + public function loadByEmail(Mage_Customer_Model_Customer $customer, $email, $testOnly = false) { $select = $this->_getReadAdapter()->select() ->from($this->getEntityTable(), array($this->getEntityIdField())) @@ -178,4 +203,21 @@ public function changePassword(Mage_Customer_Model_Customer $customer, $newPassw $this->saveAttribute($customer, 'password_hash'); return $this; } + + /** + * Check whether there are email duplicates of customers in global scope + * + * @return bool + */ + public function findEmailDuplicates() + { + $lookup = $this->_getReadAdapter()->fetchRow("SELECT email, COUNT(*) AS `qty` + FROM `{$this->getTable('customer/entity')}` + GROUP BY 1 ORDER BY 2 DESC LIMIT 1 + "); + if (empty($lookup)) { + return false; + } + return $lookup['qty'] > 1; + } } diff --git a/app/code/core/Mage/Customer/Model/Entity/Group.php b/app/code/core/Mage/Customer/Model/Entity/Group.php index 9ed8785046..b946f06f47 100644 --- a/app/code/core/Mage/Customer/Model/Entity/Group.php +++ b/app/code/core/Mage/Customer/Model/Entity/Group.php @@ -34,10 +34,20 @@ class Mage_Customer_Model_Entity_Group extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('customer/customer_group', 'customer_group_id'); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { $this->_uniqueFields = array(array( 'field' => 'customer_group_code', 'title' => Mage::helper('customer')->__('Customer Group') )); + return $this; } protected function _beforeDelete(Mage_Core_Model_Abstract $group) diff --git a/app/code/core/Mage/Customer/Model/Session.php b/app/code/core/Mage/Customer/Model/Session.php index 0f29609fe7..c6a7c6b534 100644 --- a/app/code/core/Mage/Customer/Model/Session.php +++ b/app/code/core/Mage/Customer/Model/Session.php @@ -35,9 +35,24 @@ class Mage_Customer_Model_Session extends Mage_Core_Model_Session_Abstract { protected $_customer; + /** + * Retrieve customer sharing configuration model + * + * @return Mage_Customer_Model_Config_Share + */ + public function getCustomerConfigShare() + { + return Mage::getSingleton('customer/config_share'); + } + public function __construct() { - $this->init('customer'); + $namespace = 'customer'; + if ($this->getCustomerConfigShare()->isWebsiteScope()) { + $namespace .= '_' . (Mage::app()->getStore()->getWebsite()->getCode()); + } + + $this->init($namespace); Mage::dispatchEvent('customer_session_init', array('customer_session'=>$this)); } diff --git a/app/code/core/Mage/Directory/Model/Mysql4/Country/Format.php b/app/code/core/Mage/Directory/Model/Mysql4/Country/Format.php index 597c674cfb..6dfedbfe78 100644 --- a/app/code/core/Mage/Directory/Model/Mysql4/Country/Format.php +++ b/app/code/core/Mage/Directory/Model/Mysql4/Country/Format.php @@ -34,16 +34,22 @@ */ class Mage_Directory_Model_Mysql4_Country_Format extends Mage_Core_Model_Mysql4_Abstract { - protected function _construct() { $this->_init('directory/country_format', 'country_format_id'); - $this->_uniqueFields = array( - array( - 'field' => array('country_id', 'type'), - 'title' => Mage::helper('directory')->__('Country and Format Type combination should be unique') - ) - ); } + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => array('country_id', 'type'), + 'title' => Mage::helper('directory')->__('Country and Format Type combination should be unique') + )); + return $this; + } } diff --git a/app/code/core/Mage/Directory/Model/Mysql4/Region/Collection.php b/app/code/core/Mage/Directory/Model/Mysql4/Region/Collection.php index 4697956740..3eab19d4af 100644 --- a/app/code/core/Mage/Directory/Model/Mysql4/Region/Collection.php +++ b/app/code/core/Mage/Directory/Model/Mysql4/Region/Collection.php @@ -81,7 +81,7 @@ public function addRegionCodeFilter($regionCode) if (is_array($regionCode)) { $this->_select->where("region.code IN ('".implode("','", $regionCode)."')"); } else { - $this->_select->where("{region.code = '{$regionCode}'"); + $this->_select->where("region.code = '{$regionCode}'"); } } return $this; @@ -91,7 +91,7 @@ public function addRegionNameFilter($regionName) { if (!empty($regionName)) { if (is_array($regionName)) { - $this->_select->where("region.default_name in ('".implode("','", $regionName)."'"); + $this->_select->where("region.default_name in ('".implode("','", $regionName)."')"); } else { $this->_select->where("region.default_name = '{$regionName}'"); } diff --git a/app/code/core/Mage/Directory/Model/Region.php b/app/code/core/Mage/Directory/Model/Region.php index 1157a989d3..6c6d91d255 100644 --- a/app/code/core/Mage/Directory/Model/Region.php +++ b/app/code/core/Mage/Directory/Model/Region.php @@ -64,7 +64,7 @@ public function loadByCode($code, $countryId) public function loadByName($name, $countryId) { - $this->_getResource()->loadByCode($this, $name, $countryId); + $this->_getResource()->loadByName($this, $name, $countryId); return $this; } diff --git a/app/code/core/Mage/Directory/etc/config.xml b/app/code/core/Mage/Directory/etc/config.xml index 5260228b52..fe0ffd0a1a 100644 --- a/app/code/core/Mage/Directory/etc/config.xml +++ b/app/code/core/Mage/Directory/etc/config.xml @@ -28,7 +28,7 @@ - 0.8.3 + 0.8.4 diff --git a/app/code/core/Mage/Directory/sql/directory_setup/mysql4-upgrade-0.8.3-0.8.4.php b/app/code/core/Mage/Directory/sql/directory_setup/mysql4-upgrade-0.8.3-0.8.4.php new file mode 100644 index 0000000000..bf83948be7 --- /dev/null +++ b/app/code/core/Mage/Directory/sql/directory_setup/mysql4-upgrade-0.8.3-0.8.4.php @@ -0,0 +1,39 @@ +startSetup(); + +$installer->run(" +INSERT INTO `{$installer->getTable('directory_country')}` (`country_id`, `iso2_code`, `iso3_code`) VALUES +('GG', 'GG', 'GGY'),('IM', 'IM', 'IMN'),('JE', 'JE', 'JEY'),('ME', 'ME', 'MNE'), +('BL', 'BL', 'BLM'),('MF', 'MF', 'MAF'),('RS', 'RS', 'SRB'),('TL', 'TL', 'TLS'); +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php index 5a1f42602d..9520c2194b 100644 --- a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php +++ b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php @@ -86,6 +86,7 @@ public function getPurchasedSeparatelySelect() { $select = $this->getLayout()->createBlock('adminhtml/html_select') ->setName('product[links_purchased_separately]') + ->setId('downloadable_link_purchase_type') ->setOptions(Mage::getSingleton('adminhtml/system_config_source_yesno')->toOptionArray()) ->setValue($this->getProduct()->getLinksPurchasedSeparately()); @@ -160,10 +161,13 @@ public function getLinkData() Mage_Downloadable_Model_Link::getBasePath(), $item->getLinkFile() ); if ($item->getLinkFile() && is_file($file)) { + $name = '' . + Mage::helper('downloadable/file')->getFileFromPathFile($item->getLinkFile()) . + ''; $tmpLinkItem['file_save'] = array( array( 'file' => $item->getLinkFile(), - 'name' => Mage::helper('downloadable/file')->getFileFromPathFile($item->getLinkFile()), + 'name' => $name, 'size' => filesize($file), 'status' => 'old' )); diff --git a/app/code/core/Mage/Downloadable/Block/Checkout/Success.php b/app/code/core/Mage/Downloadable/Block/Checkout/Success.php index cef47032e2..74254c5ae7 100644 --- a/app/code/core/Mage/Downloadable/Block/Checkout/Success.php +++ b/app/code/core/Mage/Downloadable/Block/Checkout/Success.php @@ -43,7 +43,9 @@ public function getOrderHasDownloadable() { $hasDownloadableFlag = Mage::getSingleton('checkout/session') ->getHasDownloadableProducts(true); - + if (!$this->isOrderVisible()) { + return false; + } return $hasDownloadableFlag; } diff --git a/app/code/core/Mage/Downloadable/Block/Customer/Products/List.php b/app/code/core/Mage/Downloadable/Block/Customer/Products/List.php index 2869afe01f..83fd10c222 100644 --- a/app/code/core/Mage/Downloadable/Block/Customer/Products/List.php +++ b/app/code/core/Mage/Downloadable/Block/Customer/Products/List.php @@ -51,6 +51,7 @@ public function __construct() } $purchasedItems = Mage::getResourceModel('downloadable/link_purchased_item_collection') ->addFieldToFilter('purchased_id', array('in' => $purchasedIds)) + ->addFieldToFilter('status', array('nin' => Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_PENDING_PAYMENT)) ->setOrder('item_id', 'desc'); $this->setItems($purchasedItems); } @@ -92,6 +93,9 @@ public function getOrderViewUrl($orderId) */ public function getBackUrl() { + if ($this->getRefererUrl()) { + return $this->getRefererUrl(); + } return $this->getUrl('customer/account/'); } @@ -117,7 +121,7 @@ public function getRemainingDownloads($item) */ public function getDownloadUrl($item) { - return $this->getUrl('*/download/link', array('id' => $item->getId())); + return $this->getUrl('*/download/link', array('id' => $item->getLinkHash(), '_secure' => true)); } /** diff --git a/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php b/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php index f17c1b768f..986d88af9b 100644 --- a/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php +++ b/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Downloadable.php @@ -62,6 +62,6 @@ public function getLinksTitle() public function getPurchasedLinkUrl($item) { - return $this->getUrl('downloadable/download/link', array('id' => $item->getId(), '_secure' => true)); + return $this->getUrl('downloadable/download/link', array('id' => $item->getLinkHash(), '_secure' => true)); } } \ No newline at end of file diff --git a/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Order/Downloadable.php b/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Order/Downloadable.php index f6a9339253..9ebbaaafb0 100644 --- a/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Order/Downloadable.php +++ b/app/code/core/Mage/Downloadable/Block/Sales/Order/Email/Items/Order/Downloadable.php @@ -62,6 +62,6 @@ public function getLinksTitle() public function getPurchasedLinkUrl($item) { - return $this->getUrl('downloadable/download/link', array('id' => $item->getId(), '_secure' => true)); + return $this->getUrl('downloadable/download/link', array('id' => $item->getLinkHash(), '_secure' => true)); } } \ No newline at end of file diff --git a/app/code/core/Mage/Downloadable/Helper/File.php b/app/code/core/Mage/Downloadable/Helper/File.php index 6cf95e248f..70b6223cf5 100644 --- a/app/code/core/Mage/Downloadable/Helper/File.php +++ b/app/code/core/Mage/Downloadable/Helper/File.php @@ -104,7 +104,7 @@ public function getFileFromPathFile($pathFile) { $file = ''; - $file = substr($pathFile, strrpos($pathFile, DS)+1); + $file = substr($pathFile, strrpos($this->_prepareFileForPath($pathFile), DS)+1); return $file; } diff --git a/app/code/core/Mage/Downloadable/Model/Link/Purchased/Item.php b/app/code/core/Mage/Downloadable/Model/Link/Purchased/Item.php index bc264197d0..2eb25cb3cd 100644 --- a/app/code/core/Mage/Downloadable/Model/Link/Purchased/Item.php +++ b/app/code/core/Mage/Downloadable/Model/Link/Purchased/Item.php @@ -33,11 +33,12 @@ */ class Mage_Downloadable_Model_Link_Purchased_Item extends Mage_Core_Model_Abstract { - const XML_PATH_ORDER_STATUS = 'catalog/downloadable/order_status'; + const XML_PATH_ORDER_ITEM_STATUS = 'catalog/downloadable/order_item_status'; const LINK_STATUS_PENDING = 'pending'; const LINK_STATUS_AVAILABLE = 'available'; const LINK_STATUS_EXPIRED = 'expired'; + const LINK_STATUS_PENDING_PAYMENT = 'pending_payment'; /** * Enter description here... diff --git a/app/code/core/Mage/Downloadable/Model/Observer.php b/app/code/core/Mage/Downloadable/Model/Observer.php index 44e17eb96f..7b781db486 100644 --- a/app/code/core/Mage/Downloadable/Model/Observer.php +++ b/app/code/core/Mage/Downloadable/Model/Observer.php @@ -62,6 +62,9 @@ public function prepareProductSave($observer) public function saveDownloadableOrderItem($observer) { $orderItem = $observer->getEvent()->getItem(); + if (Mage::getModel('downloadable/link_purchased')->load($orderItem->getId(), 'order_item_id')->getId()) { + return $this; + } $product = Mage::getModel('catalog/product') ->setStoreId($orderItem->getOrder()->getStoreId()) ->load($orderItem->getProductId()); @@ -99,8 +102,10 @@ public function saveDownloadableOrderItem($observer) $links[$linkId], $linkPurchasedItem ); + $linkHash = strtr(base64_encode(microtime() . $linkPurchased->getId() . $orderItem->getId() . $product->getId()), '+/=', '-_,'); $numberOfDownloads = $links[$linkId]->getNumberOfDownloads()*$orderItem->getQtyOrdered(); - $linkPurchasedItem->setNumberOfDownloadsBought($numberOfDownloads) + $linkPurchasedItem->setLinkHash($linkHash) + ->setNumberOfDownloadsBought($numberOfDownloads) ->setStatus(Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_PENDING) ->setCreatedAt($orderItem->getCreatedAt()) ->setUpdatedAt($orderItem->getUpdatedAt()) @@ -124,8 +129,9 @@ public function setHasDownloadableProducts($observer) $session = Mage::getSingleton('checkout/session'); if (!$session->getHasDownloadableProducts()) { $order = $observer->getEvent()->getOrder(); - foreach ($order->getAllVisibleItems() as $item) { - if ($item->getProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE) { + foreach ($order->getAllItems() as $item) { + if ($item->getProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE + || $item->getProductOptionByCode('is_downloadable')) { $session->setHasDownloadableProducts(true); break; } @@ -145,25 +151,43 @@ public function setLinkStatus($observer) $order = $observer->getEvent()->getOrder(); /** @var $order Mage_Sales_Model_Order */ $status = ''; - $orderStatusToEnable = Mage::getStoreConfig(Mage_Downloadable_Model_Link_Purchased_Item::XML_PATH_ORDER_STATUS); + $orderItemsIds = array(); + $orderItemStatusToEnable = Mage::getStoreConfig(Mage_Downloadable_Model_Link_Purchased_Item::XML_PATH_ORDER_ITEM_STATUS); if ($order->getState() == Mage_Sales_Model_Order::STATE_HOLDED) { $status = Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_PENDING; } elseif ($order->getState() == Mage_Sales_Model_Order::STATE_CANCELED || $order->getState() == Mage_Sales_Model_Order::STATE_CLOSED) { $status = Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_EXPIRED; - } elseif ($order->getStatus() == $orderStatusToEnable) { - $status = Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_AVAILABLE; + } elseif ($order->getState() == Mage_Sales_Model_Order::STATE_PENDING_PAYMENT) { + $status = Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_PENDING_PAYMENT; + } else { + foreach ($order->getAllItems() as $item) { + if ($item->getProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE) { + if ($item->getStatusId() == $orderItemStatusToEnable) { + $orderItemsIds[] = $item->getId(); + } + } + } + if ($orderItemsIds) { + $status = Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_AVAILABLE; + } } - if ($status) { - $orderItemsIds = array(); + if (!$orderItemsIds && $status) { foreach ($order->getAllItems() as $item) { - $orderItemsIds[] = $item->getId(); + if ($item->getProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE) { + $orderItemsIds[] = $item->getId(); + } } + } + + if ($orderItemsIds) { $linkPurchased = Mage::getResourceModel('downloadable/link_purchased_item_collection') - ->addFieldToFilter('order_item_id', array('in'=>$orderItemsIds)); + ->addFieldToFilter('order_item_id', array('in'=>$orderItemsIds)); foreach ($linkPurchased as $link) { - $link->setStatus($status); - $link->save(); + if ($link->getStatus() != Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_EXPIRED) { + $link->setStatus($status) + ->save(); + } } } return $this; diff --git a/app/code/core/Mage/Downloadable/Model/Product/Type.php b/app/code/core/Mage/Downloadable/Model/Product/Type.php index c0d2378700..8365170df3 100644 --- a/app/code/core/Mage/Downloadable/Model/Product/Type.php +++ b/app/code/core/Mage/Downloadable/Model/Product/Type.php @@ -78,10 +78,23 @@ public function hasLinks() */ public function hasOptions() { - return true; + //return true; return $this->getProduct()->getLinksPurchasedSeparately() || parent::hasOptions(); } + /** + * Check if product has required options + * + * @return bool + */ + public function hasRequiredOptions() + { + if (parent::hasRequiredOptions() || $this->getProduct()->getLinksPurchasedSeparately()) { + return true; + } + return false; + } + /** * Check if product cannot be purchased with no links selected * @@ -137,7 +150,6 @@ public function save() if (isset($data['sample'])) { $_deleteItems = array(); foreach ($data['sample'] as $sampleItem) { -// Zend_Debug::dump($sampleItem);die(); if ($sampleItem['is_delete'] == '1') { if ($sampleItem['sample_id']) { $_deleteItems[] = $sampleItem['sample_id']; @@ -321,7 +333,32 @@ public function getOrderOptions() } $options = array_merge($options, array('links' => $linkOptions)); } + $options = array_merge($options, array( + 'is_downloadable' => true, + 'real_product_type' => self::TYPE_DOWNLOADABLE + )); return $options; } + + + /** + * Setting flag if dowenloadable product can be or not in complex product + * based on link can be purchased separately or not + * + */ + public function beforeSave() + { + parent::beforeSave(); + + $this->getProduct()->canAffectOptions(true); + + if ($this->getLinkSelectionRequired()) { + $this->getProduct()->setTypeHasOptions(true); + $this->getProduct()->setTypeHasRequiredOptions(true); + } else { + $this->getProduct()->setTypeHasOptions(false); + $this->getProduct()->setTypeHasRequiredOptions(false); + } + } } diff --git a/app/code/core/Mage/Downloadable/Model/System/Config/Source/Orderitemstatus.php b/app/code/core/Mage/Downloadable/Model/System/Config/Source/Orderitemstatus.php new file mode 100644 index 0000000000..7e1f44d5c7 --- /dev/null +++ b/app/code/core/Mage/Downloadable/Model/System/Config/Source/Orderitemstatus.php @@ -0,0 +1,49 @@ + + */ +class Mage_Downloadable_Model_System_Config_Source_Orderitemstatus +{ + public function toOptionArray() + { + return array( + array( + 'value' => Mage_Sales_Model_Order_Item::STATUS_PENDING, + 'label' => Mage::helper('downloadable')->__('Pending') + ), + array( + 'value' => Mage_Sales_Model_Order_Item::STATUS_INVOICED, + 'label' => Mage::helper('downloadable')->__('Invoiced') + ) + ); + } +} diff --git a/app/code/core/Mage/Downloadable/controllers/CustomerController.php b/app/code/core/Mage/Downloadable/controllers/CustomerController.php index cc024bc751..63289406bc 100644 --- a/app/code/core/Mage/Downloadable/controllers/CustomerController.php +++ b/app/code/core/Mage/Downloadable/controllers/CustomerController.php @@ -56,6 +56,9 @@ public function productsAction() { $this->loadLayout(); $this->_initLayoutMessages('customer/session'); + if ($block = $this->getLayout()->getBlock('downloadable_customer_products_list')) { + $block->setRefererUrl($this->_getRefererUrl()); + } $this->renderLayout(); } diff --git a/app/code/core/Mage/Downloadable/controllers/DownloadController.php b/app/code/core/Mage/Downloadable/controllers/DownloadController.php index 92b612fe3e..db9c5f1a45 100644 --- a/app/code/core/Mage/Downloadable/controllers/DownloadController.php +++ b/app/code/core/Mage/Downloadable/controllers/DownloadController.php @@ -97,6 +97,8 @@ public function sampleAction() $sampleId = $this->getRequest()->getParam('sample_id', 0); $sample = Mage::getModel('downloadable/sample')->load($sampleId); if ($sample->getId()) { + $resource = ''; + $resourceType = ''; if ($sample->getSampleType() == Mage_Downloadable_Helper_Download::LINK_TYPE_URL) { $resource = $sample->getSampleUrl(); $resourceType = Mage_Downloadable_Helper_Download::LINK_TYPE_URL; @@ -125,6 +127,8 @@ public function linkSampleAction() $linkId = $this->getRequest()->getParam('link_id', 0); $link = Mage::getModel('downloadable/link')->load($linkId); if ($link->getId()) { + $resource = ''; + $resourceType = ''; if ($link->getSampleType() == Mage_Downloadable_Helper_Download::LINK_TYPE_URL) { $resource = $link->getSampleUrl(); $resourceType = Mage_Downloadable_Helper_Download::LINK_TYPE_URL; @@ -149,10 +153,10 @@ public function linkSampleAction() public function linkAction() { $id = $this->getRequest()->getParam('id', 0); - $linkPurchasedItem = Mage::getModel('downloadable/link_purchased_item')->load($id); - if ( $linkPurchasedItem->getId() != $id ) { - $this->_getCustomerSession()->addNotice(Mage::helper('downloadable')->__("Requested link doesn't exist.")); - return $this->_redirect('*/customer/products'); + $linkPurchasedItem = Mage::getModel('downloadable/link_purchased_item')->load($id, 'link_hash'); + if (! $linkPurchasedItem->getId() ) { + $this->_getCustomerSession()->addNotice(Mage::helper('downloadable')->__("Requested link doesn't exist.")); + return $this->_redirect('*/customer/products'); } if (!Mage::helper('downloadable')->getIsShareable($linkPurchasedItem)) { $customerId = $this->_getCustomerSession()->getCustomerId(); @@ -180,6 +184,8 @@ public function linkAction() $downloadsLeft = $linkPurchasedItem->getNumberOfDownloadsBought() - $linkPurchasedItem->getNumberOfDownloadsUsed(); if ($linkPurchasedItem->getStatus() == Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_AVAILABLE && ($downloadsLeft || $linkPurchasedItem->getNumberOfDownloadsBought() == 0)) { + $resource = ''; + $resourceType = ''; if ($linkPurchasedItem->getLinkType() == Mage_Downloadable_Helper_Download::LINK_TYPE_URL) { $resource = $linkPurchasedItem->getLinkUrl(); $resourceType = Mage_Downloadable_Helper_Download::LINK_TYPE_URL; @@ -206,7 +212,7 @@ public function linkAction() ); } } elseif ($linkPurchasedItem->getStatus() == Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_EXPIRED) { - $this->_getCustomerSession()->addNotice(Mage::helper('downloadable')->__('Link is expired.')); + $this->_getCustomerSession()->addNotice(Mage::helper('downloadable')->__('Link has expired.')); } elseif ($linkPurchasedItem->getStatus() == Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_PENDING) { $this->_getCustomerSession()->addNotice(Mage::helper('downloadable')->__('Link is not available.')); } else { diff --git a/app/code/core/Mage/Downloadable/controllers/FileController.php b/app/code/core/Mage/Downloadable/controllers/FileController.php index a34d3ea49c..30464a1f81 100644 --- a/app/code/core/Mage/Downloadable/controllers/FileController.php +++ b/app/code/core/Mage/Downloadable/controllers/FileController.php @@ -18,21 +18,25 @@ * versions in the future. If you wish to customize Magento for your * needs please refer to http://www.magentocommerce.com for more information. * - * @category Mage - * @package Mage_Downloadable - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) - * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + * @category Mage + * @package Mage_Downloadable + * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ /** * Downloadable File upload controller * - * @category Mage - * @package Mage_Downloadable - * @author Magento Core Team + * @category Mage + * @package Mage_Downloadable + * @author Magento Core Team */ class Mage_Downloadable_FileController extends Mage_Adminhtml_Controller_Action { + + /** + * Upload file controller action + */ public function uploadAction() { $type = $this->getRequest()->getParam('type'); @@ -47,12 +51,9 @@ public function uploadAction() $result = array(); try { $uploader = new Varien_File_Uploader($type); -// $uploader->setAllowedExtensions(array('jpg','jpeg','gif','png')); $uploader->setAllowRenameFiles(true); $uploader->setFilesDispersion(true); $result = $uploader->save($tmpPath); -//file_put_contents('/home/ruslan.voitenko/dev/magento.1.x.x/upload2.txt', print_r($tmpPath, true)); -// $result['file'] = $result['file'] . '.tmp'; $result['cookie'] = array( 'name' => session_name(), 'value' => $this->_getSession()->getSessionId(), @@ -67,8 +68,14 @@ public function uploadAction() $this->getResponse()->setBody(Zend_Json::encode($result)); } + /** + * Check admin permissions for this controller + * + * @return boolean + */ protected function _isAllowed() { return Mage::getSingleton('admin/session')->isAllowed('catalog/products'); } -} \ No newline at end of file + +} diff --git a/app/code/core/Mage/Downloadable/controllers/Product/EditController.php b/app/code/core/Mage/Downloadable/controllers/Product/EditController.php index 96d0317c28..9170741552 100644 --- a/app/code/core/Mage/Downloadable/controllers/Product/EditController.php +++ b/app/code/core/Mage/Downloadable/controllers/Product/EditController.php @@ -58,4 +58,72 @@ public function formAction() ); } + /** + * Download process + * + * @param string $resource + * @param string $resourceType + */ + protected function _processDownload($resource, $resourceType) + { + $helper = Mage::helper('downloadable/download'); + /* @var $helper Mage_Downloadable_Helper_Download */ + + $helper->setResource($resource, $resourceType); + + $fileName = $helper->getFilename(); + $contentType = $helper->getContentType(); + + $this->getResponse() + ->setHttpResponseCode(200) + ->setHeader('Pragma', 'public', true) + ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) + ->setHeader('Content-type', $contentType, true); + + if ($fileSize = $helper->getFilesize()) { + $this->getResponse() + ->setHeader('Content-Length', $fileSize); + } + + if ($contentDisposition = $helper->getContentDisposition()) { + $this->getResponse() + ->setHeader('Content-Disposition', $contentDisposition . '; filename='.$fileName); + } + + $this->getResponse() + ->clearBody(); + $this->getResponse() + ->sendHeaders(); + + $helper->output(); + } + +/** + * Download link action + * + */ + public function linkAction() + { + $linkId = $this->getRequest()->getParam('id', 0); + $link = Mage::getModel('downloadable/link')->load($linkId); + if ($link->getId()) { + $resource = ''; + $resourceType = ''; + if ($link->getLinkType() == Mage_Downloadable_Helper_Download::LINK_TYPE_URL) { + $resource = $link->getLinkUrl(); + $resourceType = Mage_Downloadable_Helper_Download::LINK_TYPE_URL; + } elseif ($link->getLinkType() == Mage_Downloadable_Helper_Download::LINK_TYPE_FILE) { + $resource = Mage::helper('downloadable/file')->getFilePath( + Mage_Downloadable_Model_Link::getBasePath(), $link->getLinkFile() + ); + $resourceType = Mage_Downloadable_Helper_Download::LINK_TYPE_FILE; + } + try { + $this->_processDownload($resource, $resourceType); + } catch (Mage_Core_Exception $e) { + $this->_getCustomerSession()->addError(Mage::helper('downloadable')->__('Sorry, there was an error getting requested content')); + } + } + } + } diff --git a/app/code/core/Mage/Downloadable/etc/config.xml b/app/code/core/Mage/Downloadable/etc/config.xml index 77199e342e..4df3641f80 100644 --- a/app/code/core/Mage/Downloadable/etc/config.xml +++ b/app/code/core/Mage/Downloadable/etc/config.xml @@ -28,7 +28,7 @@ - 0.1.12 + 0.1.14 @@ -41,6 +41,7 @@ * + order_item_id product_name product_sku @@ -132,6 +133,21 @@ downloadable/product_price downloadable/catalogIndex_data_downloadable + + + + + + + + + + + + + + + @@ -824,7 +840,7 @@ - + downloadable.xml @@ -892,7 +908,7 @@ 0 - complete + 9 Samples Links 1 diff --git a/app/code/core/Mage/Downloadable/etc/system.xml b/app/code/core/Mage/Downloadable/etc/system.xml index eefce08478..ffd0f0dbad 100644 --- a/app/code/core/Mage/Downloadable/etc/system.xml +++ b/app/code/core/Mage/Downloadable/etc/system.xml @@ -37,15 +37,15 @@ 1 1 - - + + select - adminhtml/system_config_source_order_status + downloadable/system_config_source_orderitemstatus 100 1 1 0 - + text diff --git a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.12-0.1.13.php b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.12-0.1.13.php new file mode 100644 index 0000000000..588f6d87a8 --- /dev/null +++ b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.12-0.1.13.php @@ -0,0 +1,53 @@ +startSetup(); + +$installer->getConnection()->addColumn($installer->getTable('downloadable/link_purchased_item'), 'link_hash', "varchar(255) NOT NULL default '' AFTER `product_id`"); + +$installer->getConnection()->addKey($installer->getTable('downloadable/link_purchased_item'), 'DOWNLOADALBE_LINK_HASH', 'link_hash'); + +$select = $installer->getConnection()->select() + ->from($installer->getTable('downloadable/link_purchased_item'), array( + 'item_id', + 'purchased_id', + 'order_item_id', + 'product_id' + )); +$result = $installer->getConnection()->fetchAll($select); + +foreach ($result as $row) { + $installer->getConnection()->update( + $installer->getTable('downloadable/link_purchased_item'), + array('link_hash' => strtr(base64_encode(microtime() . $row['purchased_id'] . $row['order_item_id'] . $row['product_id']), '+/=', '-_,')), + $installer->getConnection()->quoteInto('item_id = ?', $row['item_id']) + ); +} + +$installer->endSetup(); diff --git a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.13-0.1.14.php b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.13-0.1.14.php new file mode 100644 index 0000000000..dd547caf2f --- /dev/null +++ b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.13-0.1.14.php @@ -0,0 +1,55 @@ +startSetup(); + +$installer->getConnection()->addColumn($installer->getTable('downloadable/link_purchased'), 'order_item_id', "int(10) unsigned NOT NULL default '0' AFTER `order_increment_id`"); + +$conn->addKey($installer->getTable('downloadable/link_purchased'), 'KEY_DOWNLOADABLE_ORDER_ITEM_ID', 'order_item_id'); + +$conn->addConstraint( + 'FK_DOWNLOADABLE_PURCHASED_ORDER_ITEM_ID', $installer->getTable('downloadable/link_purchased'), 'order_item_id', $installer->getTable('sales/order_item'), 'item_id' +); + +$select = $installer->getConnection()->select() + ->from($installer->getTable('downloadable/link_purchased_item'), array( + 'purchased_id', + 'order_item_id', + )); +$result = $installer->getConnection()->fetchAll($select); + +foreach ($result as $row) { + $installer->getConnection()->update( + $installer->getTable('downloadable/link_purchased'), + array('order_item_id' => $row['order_item_id']), + $installer->getConnection()->quoteInto('purchased_id = ?', $row['purchased_id']) + ); +} + +$installer->endSetup(); diff --git a/app/code/core/Mage/Eav/Model/Config.php b/app/code/core/Mage/Eav/Model/Config.php index 25763185c1..b2a3fefe4f 100644 --- a/app/code/core/Mage/Eav/Model/Config.php +++ b/app/code/core/Mage/Eav/Model/Config.php @@ -438,7 +438,7 @@ public function preloadAttributes($entityType, $attributes) if (empty($attributes)) { return $this; } - Varien_Profiler::start('EAV: '.__METHOD__); + Varien_Profiler::start('EAV: '.__METHOD__ . ':'.$entityTypeCode); $attributesInfo = Mage::getResourceModel('eav/entity_attribute_collection') ->setEntityTypeFilter($entityType->getId()) @@ -447,6 +447,7 @@ public function preloadAttributes($entityType, $attributes) ->getData(); if (!$attributesInfo) { + Varien_Profiler::stop('EAV: '.__METHOD__ . ':'.$entityTypeCode); return $this; } @@ -468,7 +469,7 @@ public function preloadAttributes($entityType, $attributes) $this->_attributeData[$entityTypeCode] = $attributesData; - Varien_Profiler::stop('EAV: '.__METHOD__); + Varien_Profiler::stop('EAV: '.__METHOD__ . ':'.$entityTypeCode); return $this; } } \ No newline at end of file diff --git a/app/code/core/Mage/Eav/Model/Entity/Setup.php b/app/code/core/Mage/Eav/Model/Entity/Setup.php index 6799175340..f1e63bc2e4 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Setup.php +++ b/app/code/core/Mage/Eav/Model/Entity/Setup.php @@ -347,6 +347,7 @@ public function addAttribute($entityTypeId, $code, array $attr) 'is_filterable' => isset($attr['filterable']) ? $attr['filterable'] : 0, 'is_comparable' => isset($attr['comparable']) ? $attr['comparable'] : 0, 'is_visible_on_front' => isset($attr['visible_on_front']) ? $attr['visible_on_front'] : 0, + 'is_html_allowed_on_front' => isset($attr['is_html_allowed_on_front']) ? $attr['is_html_allowed_on_front'] : 0, 'is_visible_in_advanced_search' => isset($attr['visible_in_advanced_search']) ? $attr['visible_in_advanced_search'] : 0, 'is_unique' => isset($attr['unique']) ? $attr['unique'] : 0, 'apply_to' => isset($attr['apply_to']) ? $attr['apply_to'] : '', diff --git a/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute.php b/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute.php index 3c86ced919..55d5277b5e 100644 --- a/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute.php +++ b/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute.php @@ -1,378 +1,388 @@ -_init('eav/attribute', 'attribute_id'); - $this->_uniqueFields = array( - array('field' => array('attribute_code','entity_type_id'), - 'title' => Mage::helper('eav')->__('Attribute with the same code') - )); - } - - protected function _loadTypeAttributes($entityTypeId) - { - if (!isset(self::$_entityAttributes[$entityTypeId])) { - $select = $this->_getReadAdapter()->select()->from($this->getMainTable()) - ->where('entity_type_id=?', $entityTypeId); - $data = $this->_getReadAdapter()->fetchAll($select); - foreach ($data as $row) { - self::$_entityAttributes[$entityTypeId][$row['attribute_code']] = $row; - } - } - return $this; - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @param int $entityTypeId - * @param string $code - * @return boolean - */ - public function loadByCode(Mage_Core_Model_Abstract $object, $entityTypeId, $code) - { - $select = $this->_getLoadSelect('attribute_code', $code, $object) - ->where('entity_type_id=?', $entityTypeId); - $data = $this->_getReadAdapter()->fetchRow($select); - - if ($data) { - $object->setData($data); - $this->_afterLoad($object); - return true; - } - return false; - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @return int - */ - private function _getMaxSortOrder(Mage_Core_Model_Abstract $object) - { - if( intval($object->getAttributeGroupId()) > 0 ) { - $read = $this->_getReadAdapter(); - $select = $read->select() - ->from($this->getTable('entity_attribute'), new Zend_Db_Expr("MAX(`sort_order`)")) - ->where("{$this->getTable('entity_attribute')}.attribute_set_id = ?", $object->getAttributeSetId()) - ->where("{$this->getTable('entity_attribute')}.attribute_group_id = ?", $object->getAttributeGroupId()); - $maxOrder = $read->fetchOne($select); - return $maxOrder; - } - - return 0; - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @return Mage_Eav_Model_Mysql4_Entity_Attribute - */ - public function deleteEntity(Mage_Core_Model_Abstract $object) - { - $write = $this->_getWriteAdapter(); - $condition = $write->quoteInto($this->getTable('entity_attribute').'.entity_attribute_id = ?', $object->getEntityAttributeId()); - /** - * Delete attribute values - */ - $select = $write->select() - ->from($this->getTable('entity_attribute')) - ->where($condition); - $data = $write->fetchRow($select); - if (!empty($data)) { - /** - * @todo !!!! need fix retrieving attribute entity, this realization is temprary - */ - $attribute = Mage::getModel('eav/entity_attribute') - ->load($data['attribute_id']) - ->setEntity(Mage::getSingleton('catalog/product')->getResource()); - - if ($this->isUsedBySuperProducts($attribute, $data['attribute_set_id'])) { - Mage::throwException(Mage::helper('eav')->__("Attribute '%s' used in configurable products.", $attribute->getAttributeCode())); - } - - if ($backendTable = $attribute->getBackend()->getTable()) { - $clearCondition = array( - $write->quoteInto('entity_type_id=?',$attribute->getEntityTypeId()), - $write->quoteInto('attribute_id=?',$attribute->getId()), - $write->quoteInto('entity_id IN ( - SELECT entity_id FROM '.$attribute->getEntity()->getEntityTable().' WHERE attribute_set_id=?)', - $data['attribute_set_id']) - ); - $write->delete($backendTable, $clearCondition); - } - } - - $write->delete($this->getTable('entity_attribute'), $condition); - return $this; - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @return Mage_Eav_Model_Mysql4_Entity_Attribute - */ - protected function _beforeSave(Mage_Core_Model_Abstract $object) - { - $frontendLabel = $object->getFrontendLabel(); - if (is_array($frontendLabel)) { - if (!isset($frontendLabel[0]) || is_null($frontendLabel[0]) || $frontendLabel[0]=='') { - Mage::throwException(Mage::helper('eav')->__('Frontend label is not defined')); - } - $object->setFrontendLabel($frontendLabel[0]); - - if ($object->getData('modulePrefix')) { - $str = $object->getData('modulePrefix') . Mage_Core_Model_Translate::SCOPE_SEPARATOR . $frontendLabel[0]; - } - else { - $str = $frontendLabel[0]; - } - Mage::getModel('core/translate_string') - ->setString($str) - ->setTranslate($frontendLabel[0]) - ->setStoreTranslations($frontendLabel) - ->save(); - } - $applyTo = $object->getApplyTo(); - - if (is_array($applyTo)) { - $object->setApplyTo(implode(',', $applyTo)); - } - - /** - * @todo need use default source model of entity type !!! - */ - if (!$object->getId()) { - if ($object->getFrontendInput()=='select') { - $object->setSourceModel('eav/entity_attribute_source_table'); - } - } - - return parent::_beforeSave($object); - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @return Mage_Eav_Model_Mysql4_Entity_Attribute - */ - protected function _afterSave(Mage_Core_Model_Abstract $object) - { - $this->saveInSetIncluding($object) - ->_saveOption($object); - return parent::_afterSave($object); - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @return Mage_Eav_Model_Mysql4_Entity_Attribute - */ - public function saveInSetIncluding(Mage_Core_Model_Abstract $object) - { - $attrId = $object->getId(); - $setId = (int) $object->getAttributeSetId(); - $groupId= (int) $object->getAttributeGroupId(); - - if ($setId && $groupId && $object->getEntityTypeId()) { - $write = $this->_getWriteAdapter(); - $table = $this->getTable('entity_attribute'); - - - $data = array( - 'entity_type_id' => $object->getEntityTypeId(), - 'attribute_set_id' => $setId, - 'attribute_group_id' => $groupId, - 'attribute_id' => $attrId, - 'sort_order' => (($object->getSortOrder()) ? $object->getSortOrder() : $this->_getMaxSortOrder($object) + 1), - ); - - $condition = "$table.attribute_id = '$attrId' - AND $table.attribute_set_id = '$setId'"; - $write->delete($table, $condition); - $write->insert($table, $data); - - } - return $this; - } - - /** - * Enter description here... - * - * @param Mage_Core_Model_Abstract $object - * @return Mage_Eav_Model_Mysql4_Entity_Attribute - */ - protected function _saveOption(Mage_Core_Model_Abstract $object) - { - $option = $object->getOption(); - if (is_array($option)) { - $write = $this->_getWriteAdapter(); - $optionTable = $this->getTable('attribute_option'); - $optionValueTable = $this->getTable('attribute_option_value'); - $stores = Mage::getModel('core/store') - ->getResourceCollection() - ->setLoadDefault(true) - ->load(); - - if (isset($option['value'])) { - $attributeDefaultValue = array(); - if (!is_array($object->getDefault())) { - $object->setDefault(array()); - } - - foreach ($option['value'] as $optionId => $values) { - $intOptionId = (int) $optionId; - if (!empty($option['delete'][$optionId])) { - if ($intOptionId) { - $condition = $write->quoteInto('option_id=?', $intOptionId); - $write->delete($optionTable, $condition); - } - - continue; - } - - if (!$intOptionId) { - $data = array( - 'attribute_id' => $object->getId(), - 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0, - ); - $write->insert($optionTable, $data); - $intOptionId = $write->lastInsertId(); - } - else { - $data = array( - 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0, - ); - $write->update($optionTable, $data, $write->quoteInto('option_id=?', $intOptionId)); - } - - if (in_array($optionId, $object->getDefault())) { - if ($object->getFrontendInput() == 'multiselect') { - $attributeDefaultValue[] = $intOptionId; - } else if ($object->getFrontendInput() == 'select') { - $attributeDefaultValue = array($intOptionId); - } - } - - - // Default value - if (!isset($values[0])) { - Mage::throwException(Mage::helper('eav')->__('Default option value is not defined')); - } - - $write->delete($optionValueTable, $write->quoteInto('option_id=?', $intOptionId)); - foreach ($stores as $store) { - if (isset($values[$store->getId()]) && (!empty($values[$store->getId()]) || $values[$store->getId()] == "0")) { - $data = array( - 'option_id' => $intOptionId, - 'store_id' => $store->getId(), - 'value' => $values[$store->getId()], - ); - $write->insert($optionValueTable, $data); - } - } - } - - $write->update($this->getMainTable(), array( - 'default_value' => implode(',', $attributeDefaultValue) - ), $write->quoteInto($this->getIdFieldName() . '=?', $object->getId())); - } - } - return $this; - } - - public function isUsedBySuperProducts(Mage_Core_Model_Abstract $object, $attributeSet=null) - { - $read = $this->_getReadAdapter(); - $attrTable = $this->getTable('catalog/product_super_attribute'); - $productTable = $this->getTable('catalog/product'); - $select = $read->select() - ->from(array('_main_table' => $attrTable), 'COUNT(*)') - ->join(array('_entity'=> $productTable), '_main_table.product_id = _entity.entity_id') - ->where("_main_table.attribute_id = ?", $object->getAttributeId()) - ->group('_main_table.attribute_id') - ->limit(1); - - if (!is_null($attributeSet)) { - $select->where('_entity.attribute_set_id = ?', $attributeSet); - } - $valueCount = $read->fetchOne($select); - return $valueCount; - } - - /** - * Return attribute id - * - * @param string $entityType - * @param string $code - * @return int - */ - public function getIdByCode($entityType, $code) - { - $select = $this->_getReadAdapter()->select() - ->from(array('a'=>$this->getTable('eav/attribute')), array('a.attribute_id')) - ->join(array('t'=>$this->getTable('eav/entity_type')), 'a.entity_type_id = t.entity_type_id', array()) - ->where('t.entity_type_code = ?', $entityType) - ->where('a.attribute_code = ?', $code); - - return $this->_getReadAdapter()->fetchOne($select); - } - - public function getAttributeCodesByFrontendType($type) - { - $select = $this->_getReadAdapter()->select(); - $select - ->from($this->getTable('eav/attribute'), 'attribute_code') - ->where('frontend_input = ?', $type); - - $result = $this->_getReadAdapter()->fetchCol($select); - - if ($result) { - return $result; - } else { - return array(); - } - } - -} +_init('eav/attribute', 'attribute_id'); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => array('attribute_code','entity_type_id'), + 'title' => Mage::helper('eav')->__('Attribute with the same code') + )); + return $this; + } + + protected function _loadTypeAttributes($entityTypeId) + { + if (!isset(self::$_entityAttributes[$entityTypeId])) { + $select = $this->_getReadAdapter()->select()->from($this->getMainTable()) + ->where('entity_type_id=?', $entityTypeId); + $data = $this->_getReadAdapter()->fetchAll($select); + foreach ($data as $row) { + self::$_entityAttributes[$entityTypeId][$row['attribute_code']] = $row; + } + } + return $this; + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @param int $entityTypeId + * @param string $code + * @return boolean + */ + public function loadByCode(Mage_Core_Model_Abstract $object, $entityTypeId, $code) + { + $select = $this->_getLoadSelect('attribute_code', $code, $object) + ->where('entity_type_id=?', $entityTypeId); + $data = $this->_getReadAdapter()->fetchRow($select); + + if ($data) { + $object->setData($data); + $this->_afterLoad($object); + return true; + } + return false; + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @return int + */ + private function _getMaxSortOrder(Mage_Core_Model_Abstract $object) + { + if( intval($object->getAttributeGroupId()) > 0 ) { + $read = $this->_getReadAdapter(); + $select = $read->select() + ->from($this->getTable('entity_attribute'), new Zend_Db_Expr("MAX(`sort_order`)")) + ->where("{$this->getTable('entity_attribute')}.attribute_set_id = ?", $object->getAttributeSetId()) + ->where("{$this->getTable('entity_attribute')}.attribute_group_id = ?", $object->getAttributeGroupId()); + $maxOrder = $read->fetchOne($select); + return $maxOrder; + } + + return 0; + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Eav_Model_Mysql4_Entity_Attribute + */ + public function deleteEntity(Mage_Core_Model_Abstract $object) + { + $write = $this->_getWriteAdapter(); + $condition = $write->quoteInto($this->getTable('entity_attribute').'.entity_attribute_id = ?', $object->getEntityAttributeId()); + /** + * Delete attribute values + */ + $select = $write->select() + ->from($this->getTable('entity_attribute')) + ->where($condition); + $data = $write->fetchRow($select); + if (!empty($data)) { + /** + * @todo !!!! need fix retrieving attribute entity, this realization is temprary + */ + $attribute = Mage::getModel('eav/entity_attribute') + ->load($data['attribute_id']) + ->setEntity(Mage::getSingleton('catalog/product')->getResource()); + + if ($this->isUsedBySuperProducts($attribute, $data['attribute_set_id'])) { + Mage::throwException(Mage::helper('eav')->__("Attribute '%s' used in configurable products.", $attribute->getAttributeCode())); + } + + if ($backendTable = $attribute->getBackend()->getTable()) { + $clearCondition = array( + $write->quoteInto('entity_type_id=?',$attribute->getEntityTypeId()), + $write->quoteInto('attribute_id=?',$attribute->getId()), + $write->quoteInto('entity_id IN ( + SELECT entity_id FROM '.$attribute->getEntity()->getEntityTable().' WHERE attribute_set_id=?)', + $data['attribute_set_id']) + ); + $write->delete($backendTable, $clearCondition); + } + } + + $write->delete($this->getTable('entity_attribute'), $condition); + return $this; + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Eav_Model_Mysql4_Entity_Attribute + */ + protected function _beforeSave(Mage_Core_Model_Abstract $object) + { + $frontendLabel = $object->getFrontendLabel(); + if (is_array($frontendLabel)) { + if (!isset($frontendLabel[0]) || is_null($frontendLabel[0]) || $frontendLabel[0]=='') { + Mage::throwException(Mage::helper('eav')->__('Frontend label is not defined')); + } + $object->setFrontendLabel($frontendLabel[0]); + + if ($object->getData('modulePrefix')) { + $str = $object->getData('modulePrefix') . Mage_Core_Model_Translate::SCOPE_SEPARATOR . $frontendLabel[0]; + } + else { + $str = $frontendLabel[0]; + } + Mage::getModel('core/translate_string') + ->setString($str) + ->setTranslate($frontendLabel[0]) + ->setStoreTranslations($frontendLabel) + ->save(); + } + $applyTo = $object->getApplyTo(); + + if (is_array($applyTo)) { + $object->setApplyTo(implode(',', $applyTo)); + } + + /** + * @todo need use default source model of entity type !!! + */ + if (!$object->getId()) { + if ($object->getFrontendInput()=='select') { + $object->setSourceModel('eav/entity_attribute_source_table'); + } + } + + return parent::_beforeSave($object); + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Eav_Model_Mysql4_Entity_Attribute + */ + protected function _afterSave(Mage_Core_Model_Abstract $object) + { + $this->saveInSetIncluding($object) + ->_saveOption($object); + return parent::_afterSave($object); + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Eav_Model_Mysql4_Entity_Attribute + */ + public function saveInSetIncluding(Mage_Core_Model_Abstract $object) + { + $attrId = $object->getId(); + $setId = (int) $object->getAttributeSetId(); + $groupId= (int) $object->getAttributeGroupId(); + + if ($setId && $groupId && $object->getEntityTypeId()) { + $write = $this->_getWriteAdapter(); + $table = $this->getTable('entity_attribute'); + + + $data = array( + 'entity_type_id' => $object->getEntityTypeId(), + 'attribute_set_id' => $setId, + 'attribute_group_id' => $groupId, + 'attribute_id' => $attrId, + 'sort_order' => (($object->getSortOrder()) ? $object->getSortOrder() : $this->_getMaxSortOrder($object) + 1), + ); + + $condition = "$table.attribute_id = '$attrId' + AND $table.attribute_set_id = '$setId'"; + $write->delete($table, $condition); + $write->insert($table, $data); + + } + return $this; + } + + /** + * Enter description here... + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Eav_Model_Mysql4_Entity_Attribute + */ + protected function _saveOption(Mage_Core_Model_Abstract $object) + { + $option = $object->getOption(); + if (is_array($option)) { + $write = $this->_getWriteAdapter(); + $optionTable = $this->getTable('attribute_option'); + $optionValueTable = $this->getTable('attribute_option_value'); + $stores = Mage::getModel('core/store') + ->getResourceCollection() + ->setLoadDefault(true) + ->load(); + + if (isset($option['value'])) { + $attributeDefaultValue = array(); + if (!is_array($object->getDefault())) { + $object->setDefault(array()); + } + + foreach ($option['value'] as $optionId => $values) { + $intOptionId = (int) $optionId; + if (!empty($option['delete'][$optionId])) { + if ($intOptionId) { + $condition = $write->quoteInto('option_id=?', $intOptionId); + $write->delete($optionTable, $condition); + } + + continue; + } + + if (!$intOptionId) { + $data = array( + 'attribute_id' => $object->getId(), + 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0, + ); + $write->insert($optionTable, $data); + $intOptionId = $write->lastInsertId(); + } + else { + $data = array( + 'sort_order' => isset($option['order'][$optionId]) ? $option['order'][$optionId] : 0, + ); + $write->update($optionTable, $data, $write->quoteInto('option_id=?', $intOptionId)); + } + + if (in_array($optionId, $object->getDefault())) { + if ($object->getFrontendInput() == 'multiselect') { + $attributeDefaultValue[] = $intOptionId; + } else if ($object->getFrontendInput() == 'select') { + $attributeDefaultValue = array($intOptionId); + } + } + + + // Default value + if (!isset($values[0])) { + Mage::throwException(Mage::helper('eav')->__('Default option value is not defined')); + } + + $write->delete($optionValueTable, $write->quoteInto('option_id=?', $intOptionId)); + foreach ($stores as $store) { + if (isset($values[$store->getId()]) && (!empty($values[$store->getId()]) || $values[$store->getId()] == "0")) { + $data = array( + 'option_id' => $intOptionId, + 'store_id' => $store->getId(), + 'value' => $values[$store->getId()], + ); + $write->insert($optionValueTable, $data); + } + } + } + + $write->update($this->getMainTable(), array( + 'default_value' => implode(',', $attributeDefaultValue) + ), $write->quoteInto($this->getIdFieldName() . '=?', $object->getId())); + } + } + return $this; + } + + public function isUsedBySuperProducts(Mage_Core_Model_Abstract $object, $attributeSet=null) + { + $read = $this->_getReadAdapter(); + $attrTable = $this->getTable('catalog/product_super_attribute'); + $productTable = $this->getTable('catalog/product'); + $select = $read->select() + ->from(array('_main_table' => $attrTable), 'COUNT(*)') + ->join(array('_entity'=> $productTable), '_main_table.product_id = _entity.entity_id') + ->where("_main_table.attribute_id = ?", $object->getAttributeId()) + ->group('_main_table.attribute_id') + ->limit(1); + + if (!is_null($attributeSet)) { + $select->where('_entity.attribute_set_id = ?', $attributeSet); + } + $valueCount = $read->fetchOne($select); + return $valueCount; + } + + /** + * Return attribute id + * + * @param string $entityType + * @param string $code + * @return int + */ + public function getIdByCode($entityType, $code) + { + $select = $this->_getReadAdapter()->select() + ->from(array('a'=>$this->getTable('eav/attribute')), array('a.attribute_id')) + ->join(array('t'=>$this->getTable('eav/entity_type')), 'a.entity_type_id = t.entity_type_id', array()) + ->where('t.entity_type_code = ?', $entityType) + ->where('a.attribute_code = ?', $code); + + return $this->_getReadAdapter()->fetchOne($select); + } + + public function getAttributeCodesByFrontendType($type) + { + $select = $this->_getReadAdapter()->select(); + $select + ->from($this->getTable('eav/attribute'), 'attribute_code') + ->where('frontend_input = ?', $type); + + $result = $this->_getReadAdapter()->fetchCol($select); + + if ($result) { + return $result; + } else { + return array(); + } + } + +} diff --git a/app/code/core/Mage/Eav/etc/config.xml b/app/code/core/Mage/Eav/etc/config.xml index 8a822241ff..d1537b1652 100644 --- a/app/code/core/Mage/Eav/etc/config.xml +++ b/app/code/core/Mage/Eav/etc/config.xml @@ -28,7 +28,7 @@ - 0.7.11 + 0.7.12 diff --git a/app/code/core/Mage/Eav/sql/eav_setup/mysql4-install-0.7.0.php b/app/code/core/Mage/Eav/sql/eav_setup/mysql4-install-0.7.0.php index ad0e1d2606..61876ab64c 100644 --- a/app/code/core/Mage/Eav/sql/eav_setup/mysql4-install-0.7.0.php +++ b/app/code/core/Mage/Eav/sql/eav_setup/mysql4-install-0.7.0.php @@ -140,10 +140,8 @@ UNIQUE KEY `attribute_group_id` (`attribute_group_id`,`attribute_id`), KEY `attribute_set_id_3` (`attribute_set_id`,`sort_order`), KEY `FK_EAV_ENTITY_ATTRIVUTE_ATTRIBUTE` (`attribute_id`), - CONSTRAINT `FK_eav_entity_attribute` FOREIGN KEY (`attribute_id`) REFERENCES `{$this->getTable('eav_attribute')}` (`attribute_id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `FK_eav_entity_attribute_group` FOREIGN KEY (`attribute_group_id`) REFERENCES `{$this->getTable('eav_attribute_group')}` (`attribute_group_id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `FK_EAV_ENTITY_ATTRIVUTE_ATTRIBUTE` FOREIGN KEY (`attribute_id`) REFERENCES `{$this->getTable('eav_attribute')}` (`attribute_id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `FK_EAV_ENTITY_ATTRIVUTE_GROUP` FOREIGN KEY (`attribute_group_id`) REFERENCES `{$this->getTable('eav_attribute_group')}` (`attribute_group_id`) ON DELETE CASCADE ON UPDATE CASCADE + CONSTRAINT `FK_EAV_ENTITY_ATTRIBUTE_ATTRIBUTE` FOREIGN KEY (`attribute_id`) REFERENCES `{$this->getTable('eav_attribute')}` (`attribute_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_EAV_ENTITY_ATTRIBUTE_GROUP` FOREIGN KEY (`attribute_group_id`) REFERENCES `{$this->getTable('eav_attribute_group')}` (`attribute_group_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS {$this->getTable('eav_entity_datetime')}; diff --git a/app/code/core/Mage/Eav/sql/eav_setup/mysql4-upgrade-0.7.11-0.7.12.php b/app/code/core/Mage/Eav/sql/eav_setup/mysql4-upgrade-0.7.11-0.7.12.php new file mode 100644 index 0000000000..a22c0c2f97 --- /dev/null +++ b/app/code/core/Mage/Eav/sql/eav_setup/mysql4-upgrade-0.7.11-0.7.12.php @@ -0,0 +1,32 @@ +startSetup(); +$installer->getConnection()->addColumn($installer->getTable('eav_attribute'), 'is_html_allowed_on_front', "tinyint(1) unsigned not null default '0' after `is_visible_on_front`"); +$installer->endSetup(); diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Items.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Items.php index e6a010e394..d6519ed4b2 100644 --- a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Items.php +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Items.php @@ -45,15 +45,7 @@ protected function _prepareLayout() { $this->setChild('item', $this->getLayout()->createBlock('googlebase/adminhtml_items_item')); $this->setChild('product', $this->getLayout()->createBlock('googlebase/adminhtml_items_product')); - if (!Mage::app()->isSingleStoreMode()) { - $_defaultStoreName = Mage::app()->getDefaultStoreView()->getName(); - $this->setChild('store_switcher', - $this->getLayout()->createBlock('adminhtml/store_switcher') - ->setDefaultStoreName($this->__('Default Store (%s)', $_defaultStoreName)) - ->setUseConfirm(false) - ->setSwitchUrl($this->getUrl('*/*/*', array('store'=>null))) - ); - } + $this->setChild('store_switcher', $this->getLayout()->createBlock('googlebase/adminhtml_store_switcher')); } public function getAddButtonHtml() diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Store/Switcher.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Store/Switcher.php new file mode 100644 index 0000000000..030dcf820a --- /dev/null +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Store/Switcher.php @@ -0,0 +1,44 @@ + + */ + +class Mage_GoogleBase_Block_Adminhtml_Store_Switcher extends Mage_Adminhtml_Block_Store_Switcher +{ + public function __construct() + { + parent::__construct(); + $this->setDefaultStoreName($this->__('Default Store (%s)', Mage::app()->getDefaultStoreView()->getName())) + ->setUseConfirm(false) + ->setSwitchUrl($this->getUrl('*/*/*', array('store'=>null))); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types.php index a1b788c9cf..056d173909 100644 --- a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types.php +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types.php @@ -42,4 +42,15 @@ public function __construct() $this->_headerText = Mage::helper('googlebase')->__('Manage Attribute Mapping'); parent::__construct(); } + +// public function getGridHtml() +// { +// $_storeSwitcherHtml = $this->getLayout()->createBlock('googlebase/adminhtml_store_switcher')->toHtml(); +// return $_storeSwitcherHtml . parent::getGridHtml(); +// } +// +// public function getCreateUrl() +// { +// return $this->getUrl('*/*/new', array('_current'=>true)); +// } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit.php index f593c8cfa6..80f1c1afe3 100644 --- a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit.php +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit.php @@ -53,16 +53,56 @@ public function __construct() var itemType = function() { return { updateAttributes: function() { - if ($("select_attribute_set").value != "" && $("select_itemtype").value != "") + if ($("select_attribute_set").value != "" && $("select_itemtype").value != "" && itemType.confirmChanges()) { - var blocksCount = Element.select($("attributes_details"), "div[id^=gbase_attribute_]").length; - if (blocksCount > 0 && confirm("'.$this->__('Current Mapping will be reloaded. Continue?').'") || blocksCount == 0) - { - var elements = [$("select_attribute_set"),$("select_itemtype")].flatten(); - $(\'save_button\').disabled = true; - new Ajax.Updater("attributes_details", "'.$this->getUrl('*/*/loadAttributes').'", {parameters:Form.serializeElements(elements), evalScripts:true, onComplete:function(){ $(\'save_button\').disabled = false; } }); - } + var elements = [$("select_attribute_set"),$("select_itemtype"),$("select_target_country")].flatten(); + $(\'save_button\').disabled = true; + new Ajax.Updater("attributes_details", "'.$this->getUrl('*/*/loadAttributes').'", + { + parameters:Form.serializeElements(elements), + evalScripts:true, + onComplete:function(){ $(\'save_button\').disabled = false; } + } + ); } + }, + + reloadItemTypes: function() { + if ($("select_target_country").value != "" && itemType.confirmChanges()) + { + var elements = [$("select_attribute_set"),$("select_itemtype"),$("select_target_country")].flatten(); + new Ajax.Updater("gbase_itemtype_select", "'.$this->getUrl('*/*/loadItemTypes').'", + { + parameters:Form.serializeElements(elements), + evalScripts:true, + onComplete:function(){ + $(\'save_button\').disabled = false; + Event.observe($("select_itemtype"), \'change\', itemType.updateAttributes); + } + } + ); + + new Ajax.Updater("attribute_set_select", "'.$this->getUrl('*/*/loadAttributeSets').'", + { + parameters:Form.serializeElements(elements), + evalScripts:true, + onComplete:function(){ + $(\'save_button\').disabled = false; + Event.observe($("select_attribute_set"), \'change\', itemType.updateAttributes); + } + } + ); + $("attributes_details").innerHTML = "' . $this->__('Please, select Attribute Set and Google Item Type to load attributes') . '"; + } + }, + + confirmChanges: function() { + var blocksCount = Element.select($("attributes_details"), "div[id^=gbase_attribute_]").length; + if (blocksCount > 0 && confirm("'.$this->__('Current Mapping will be reloaded. Continue?').'") || blocksCount == 0) + { + return true; + } + return false; } } }(); @@ -74,6 +114,9 @@ public function __construct() if ($("select_itemtype")) { Event.observe($("select_itemtype"), \'change\', itemType.updateAttributes); } + if ($("select_target_country")) { + Event.observe($("select_target_country"), \'change\', itemType.reloadItemTypes); + } }); '; } diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Attributes.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Attributes.php index 051badab02..0af324ee11 100644 --- a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Attributes.php +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Attributes.php @@ -76,7 +76,7 @@ public function getGbaseAttributesSelectHtml() $options = array('' => $this->__('Custom attribute, no mapping')); $attributes = Mage::getModel('googlebase/service_feed') - ->getAttributes($this->getGbaseItemtype()); + ->getAttributes($this->getGbaseItemtype(), $this->getTargetCountry()); foreach ($attributes as $attr) { $options[$attr->getId()] = $attr->getName(); } diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Form.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Form.php index 46a00a61a9..805abfef72 100644 --- a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Form.php +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Edit/Form.php @@ -38,44 +38,59 @@ protected function _prepareForm() { $form = new Varien_Data_Form(); - $itemType = Mage::registry('current_item_type'); + $itemType = $this->getItemType(); $fieldset = $form->addFieldset('base_fieldset', array( 'legend' => $this->__('Attribute Set and Item Type') )); - $attributeSelect = $fieldset->addField('select_attribute_set', 'select', array( + if ( !($targetCountry = $itemType->getTargetCountry()) ) { + $isoKeys = array_keys($this->_getCountriesArray()); + $targetCountry = isset($isoKeys[0]) ? $isoKeys[0] : null; + } + $countrySelect = $fieldset->addField('select_target_country', 'select', array( + 'label' => $this->__('Target Country'), + 'title' => $this->__('Target Country'), + 'name' => 'target_country', + 'required' => true, + 'options' => $this->_getCountriesArray(), + 'value' => $targetCountry, + )); + if ($itemType->getTargetCountry()) { + $countrySelect->setDisabled(true); + } + + $attributeSetsSelect = $this->getAttributeSetsSelectElement($targetCountry)->setValue($itemType->getAttributeSetId()); + if ($itemType->getAttributeSetId()) { + $attributeSetsSelect->setDisabled(true); + } + + $fieldset->addField('attribute_set', 'note', array( 'label' => $this->__('Attribute Set'), 'title' => $this->__('Attribute Set'), - 'name' => 'attribute_set_id', 'required' => true, - 'options' => $this->_getAttributeSetsArray(), - 'value' => $itemType->getAttributeSetId(), + 'text' => '
' . $attributeSetsSelect->toHtml() . '
', )); - if ($itemType->getAttributeSetId()) { - $attributeSelect->setValue($itemType->getAttributeSetId()) - ->setDisabled(true); + + $itemTypesSelect = $this->getItemTypesSelectElement($targetCountry)->setValue($itemType->getGbaseItemtype()); + if ($itemType->getGbaseItemtype()) { + $itemTypesSelect->setDisabled(true); } - $itemTypeSelect = $fieldset->addField('select_itemtype', 'select', array( + $fieldset->addField('itemtype', 'note', array( 'label' => $this->__('Google Base Item Type'), 'title' => $this->__('Google Base Item Type'), - 'name' => 'gbase_itemtype', 'required' => true, - 'options' => $this->_getGbaseItemTypesArray(), - 'value' => $itemType->getGbaseItemtype(), + 'text' => '
' . $itemTypesSelect->toHtml() . '
', )); - if ($itemType->getGbaseItemtype()) { - $itemTypeSelect->setValue($itemType->getGbaseItemtype()) - ->setDisabled(true); - } - $attributesBlock = $this->getLayout()->createBlock('googlebase/adminhtml_types_edit_attributes'); + $attributesBlock = $this->getLayout() + ->createBlock('googlebase/adminhtml_types_edit_attributes') + ->setTargetCountry($targetCountry); if ($itemType->getId()) { $attributesBlock->setAttributeSetId($itemType->getAttributeSetId()) ->setGbaseItemtype($itemType->getGbaseItemtype()) ->setAttributeSetSelected(true); - } $attributes = Mage::registry('attributes'); @@ -96,16 +111,50 @@ protected function _prepareForm() $this->setForm($form); } - protected function _getAttributeSetsArray() + public function getAttributeSetsSelectElement($targetCountry) + { + $field = new Varien_Data_Form_Element_Select(); + $field->setName('attribute_set_id') + ->setId('select_attribute_set') + ->setForm(new Varien_Data_Form()) + ->addClass('required-entry') + ->setValues($this->_getAttributeSetsArray($targetCountry)); + return $field; + } + + public function getItemTypesSelectElement($targetCountry) + { + $field = new Varien_Data_Form_Element_Select(); + $field->setName('gbase_itemtype') + ->setId('select_itemtype') + ->setForm(new Varien_Data_Form()) + ->addClass('required-entry') + ->setValues($this->_getGbaseItemTypesArray($targetCountry)); + return $field; + } + + protected function _getCountriesArray() + { + $_allowed = Mage::getSingleton('googlebase/config')->getAllowedCountries(); + $result = array(); + foreach ($_allowed as $iso => $info) { + $result[$iso] = $info['name']; + } + return $result; + } + + protected function _getAttributeSetsArray($targetCountry) { $entityType = Mage::getModel('catalog/product')->getResource()->getEntityType(); $collection = Mage::getResourceModel('eav/entity_attribute_set_collection') ->setEntityTypeFilter($entityType->getId()); $ids = array(); - $itemType = Mage::registry('current_item_type'); - if (!$itemType->getId()) { - $typesCollection = Mage::getResourceModel('googlebase/type_collection')->load(); + $itemType = $this->getItemType(); + if ( !($itemType instanceof Varien_Object && $itemType->getId()) ) { + $typesCollection = Mage::getResourceModel('googlebase/type_collection') + ->addCountryFilter($targetCountry) + ->load(); foreach ($typesCollection as $type) { $ids[] = $type->getAttributeSetId(); } @@ -119,9 +168,10 @@ protected function _getAttributeSetsArray() } return $result; } - protected function _getGbaseItemTypesArray() + + protected function _getGbaseItemTypesArray($targetCountry) { - $itemTypes = Mage::getModel('googlebase/service_feed')->getItemTypes(); + $itemTypes = Mage::getModel('googlebase/service_feed')->getItemTypes($targetCountry); $result = array('' => ''); foreach ($itemTypes as $type) { $result[$type->getId()] = $type->getName(); diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Grid.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Grid.php index 2ccb9908dd..07ceaf6e91 100644 --- a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Grid.php +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Grid.php @@ -57,6 +57,7 @@ protected function _prepareColumns() 'width' => '150px', 'index' => 'attribute_set_name', )); + $this->addColumn('gbase_itemtype', array( 'header' => $this->__('Google Base Item type'), @@ -64,6 +65,15 @@ protected function _prepareColumns() 'index' => 'gbase_itemtype', )); + $this->addColumn('target_country', + array( + 'header' => $this->__('Target Country'), + 'width' => '150px', + 'index' => 'target_country', + 'renderer' => 'googlebase/adminhtml_types_renderer_country', + 'filter' => false + )); + $this->addColumn('items_total', array( 'header' => Mage::helper('catalog')->__('Total Qty Base Items'), @@ -77,7 +87,7 @@ protected function _prepareColumns() public function getRowUrl($row) { - return $this->getUrl('*/*/edit', array('id'=>$row->getId())); + return $this->getUrl('*/*/edit', array('id'=>$row->getId(), '_current'=>true)); } public function getGridUrl() diff --git a/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Renderer/Country.php b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Renderer/Country.php new file mode 100644 index 0000000000..818101bcc1 --- /dev/null +++ b/app/code/core/Mage/GoogleBase/Block/Adminhtml/Types/Renderer/Country.php @@ -0,0 +1,49 @@ + + */ +class Mage_GoogleBase_Block_Adminhtml_Types_Renderer_Country + extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract +{ + /** + * Renders Google Base Item Id + * + * @param Varien_Object $row + * @return string + */ + public function render(Varien_Object $row) + { + $iso = $row->getData($this->getColumn()->getIndex()); + return Mage::getSingleton('googlebase/config')->getCountryInfo($iso, 'name'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/Model/Config.php b/app/code/core/Mage/GoogleBase/Model/Config.php new file mode 100644 index 0000000000..11e698aa46 --- /dev/null +++ b/app/code/core/Mage/GoogleBase/Model/Config.php @@ -0,0 +1,122 @@ + + */ +class Mage_GoogleBase_Model_Config extends Varien_Object +{ + /** + * Return config var + * + * @param string $key Var path key + * @param int $storeId Store View Id + * @return mixed + */ + public function getConfigData($key, $storeId = null) + { + if (!$this->hasData($key)) { + $value = Mage::getStoreConfig('google/googlebase/' . $key, $storeId); + $this->setData($key, $value); + } + return $this->getData($key); + } + + /** + * Google Account login + * + * @param int $storeId + * @return string + */ + public function getAccountLogin($storeId = null) + { + return $this->getConfigData('login', $storeId); + } + + /** + * Google Account password + * + * @param int $storeId + * @return string + */ + public function getAccountPassword($storeId = null) + { + return $this->getConfigData('password', $storeId); + } + + /** + * Google Account target country + * + * @param int $storeId + * @return string Two-letters country ISO code + */ + public function getTargetCountry($storeId = null) + { + return $this->getConfigData('target_country', $storeId); + } + + /** + * Default Item Type for country + * + * @param int $storeId + * @return string + */ + public function getDefaultItemType($storeId = null) + { + $country = $this->getTargetCountry($storeId); + return $this->getCountryInfo($country, 'default_item_type'); + } + + /** + * Google Base supported countries + * + * @param int $storeId + * @return array + */ + public function getAllowedCountries($storeId = null) + { + return $this->getConfigData('allowed_countries', $storeId); + } + + /** + * Country info such as name, locale, language etc. + * + * @param string $iso two-letters country ISO code + * @param string $field If specified, return value for field + * @param int $storeId + * @return array|string + */ + public function getCountryInfo($iso, $field = null, $storeId = null) + { + $countries = $this->getAllowedCountries($storeId); + $country = isset($countries[$iso]) ? $countries[$iso] : null; + return is_null($field) ? $country : ( isset($country[$field]) ? $country[$field] : null ); + } +} diff --git a/app/code/core/Mage/GoogleBase/Model/Item.php b/app/code/core/Mage/GoogleBase/Model/Item.php index a81ca21978..c61aff2990 100644 --- a/app/code/core/Mage/GoogleBase/Model/Item.php +++ b/app/code/core/Mage/GoogleBase/Model/Item.php @@ -52,6 +52,16 @@ public function getServiceItem() return Mage::getModel('googlebase/service_item')->setStoreId($this->getStoreId()); } + /** + * Target Country + * + * @return string Two-letters country ISO code + */ + public function getTargetCountry() + { + return Mage::getSingleton('googlebase/config')->getTargetCountry($this->getStoreId()); + } + /** * Save item to Google Base * @@ -277,7 +287,7 @@ protected function _getTypeModel() if (is_array($registry) && isset($registry[$attributeSetId])) { return $registry[$attributeSetId]; } - $model = Mage::getModel('googlebase/type')->loadByAttributeSetId($attributeSetId); + $model = Mage::getModel('googlebase/type')->loadByAttributeSetId($attributeSetId, $this->getTargetCountry()); $registry[$attributeSetId] = $model; Mage::unregister(self::TYPES_REGISTRY_KEY); Mage::register(self::TYPES_REGISTRY_KEY, $registry); @@ -340,7 +350,7 @@ protected function _getAttributesCollection() return $registry[$attributeSetId]; } $collection = Mage::getResourceModel('googlebase/attribute_collection') - ->addAttributeSetFilter($attributeSetId) + ->addAttributeSetFilter($attributeSetId, $this->getTargetCountry()) ->load(); $registry[$attributeSetId] = $collection; Mage::unregister(self::ATTRIBUTES_REGISTRY_KEY); diff --git a/app/code/core/Mage/GoogleBase/Model/Mysql4/Attribute/Collection.php b/app/code/core/Mage/GoogleBase/Model/Mysql4/Attribute/Collection.php index 6e82fac4ef..5bd5e5245e 100644 --- a/app/code/core/Mage/GoogleBase/Model/Mysql4/Attribute/Collection.php +++ b/app/code/core/Mage/GoogleBase/Model/Mysql4/Attribute/Collection.php @@ -44,12 +44,13 @@ protected function _construct() $this->_init('googlebase/attribute'); } - public function addAttributeSetFilter($attributeSetId) + public function addAttributeSetFilter($attributeSetId, $targetCountry) { if (!$this->getJoinAttributeSetFlag()) { return $this; } $this->getSelect()->where('attribute_set_id = ?', $attributeSetId); + $this->getSelect()->where('target_country = ?', $targetCountry); return $this; } @@ -77,7 +78,7 @@ protected function _joinAttributeSet() ->joinInner( array('types'=>$this->getTable('googlebase/types')), 'main_table.type_id=types.type_id', - array('attribute_set_id' => 'types.attribute_set_id')); + array('attribute_set_id' => 'types.attribute_set_id', 'target_country' => 'types.target_country')); return $this; } diff --git a/app/code/core/Mage/GoogleBase/Model/Mysql4/Type.php b/app/code/core/Mage/GoogleBase/Model/Mysql4/Type.php index 11b8581636..2a84c7ef7d 100644 --- a/app/code/core/Mage/GoogleBase/Model/Mysql4/Type.php +++ b/app/code/core/Mage/GoogleBase/Model/Mysql4/Type.php @@ -37,4 +37,21 @@ protected function _construct() { $this->_init('googlebase/types', 'type_id'); } + + /** + * Return Type ID by Attribute Set Id and target country + * + * @param int $attributeSetId Attribute Set + * @param string $targetCountry Two-letters country ISO code + * @return int + */ + public function getTypeIdByAttributeSetId($attributeSetId, $targetCountry) + { + $select = $this->_getReadAdapter()->select() + ->from($this->getMainTable(), 'type_id') + ->where('attribute_set_id=?', $attributeSetId) + ->where('target_country=?', $targetCountry); + + return $this->_getReadAdapter()->fetchOne($select); + } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/Model/Mysql4/Type/Collection.php b/app/code/core/Mage/GoogleBase/Model/Mysql4/Type/Collection.php index 2ad85d4636..0c7898448b 100644 --- a/app/code/core/Mage/GoogleBase/Model/Mysql4/Type/Collection.php +++ b/app/code/core/Mage/GoogleBase/Model/Mysql4/Type/Collection.php @@ -62,6 +62,18 @@ public function addItemsCount() return $this; } + /** + * Add country ISO filter to collection + * + * @param string $iso Two-letter country ISO code + * @return Mage_GoogleBase_Model_Mysql4_Type_Collection + */ + public function addCountryFilter($iso) + { + $this->getSelect()->where('target_country=?', $iso); + return $this; + } + /** * Join Attribute Set data * diff --git a/app/code/core/Mage/GoogleBase/Model/Service.php b/app/code/core/Mage/GoogleBase/Model/Service.php index b8d0d335f9..ebdc8542d7 100644 --- a/app/code/core/Mage/GoogleBase/Model/Service.php +++ b/app/code/core/Mage/GoogleBase/Model/Service.php @@ -40,8 +40,8 @@ class Mage_GoogleBase_Model_Service extends Varien_Object */ public function getClient($storeId = null, $loginToken = null, $loginCaptcha = null) { - $user = Mage::getStoreConfig('google/googlebase/login', $storeId); - $pass = Mage::getStoreConfig('google/googlebase/password', $storeId); + $user = $this->getConfig()->getAccountLogin($storeId); + $pass = $this->getConfig()->getAccountPassword($storeId); // Create an authenticated HTTP client $errorMsg = Mage::helper('googlebase')->__('Unable to connect to Google Base. Please, check Account settings in configuration.'); @@ -72,6 +72,26 @@ public function getService($storeId = null) return $this->_service; } + /** + * Retutn Google Base Anonymous Client Instance + * + * @return Zend_Gdata_Gbase + */ + public function getGuestService() + { + return new Zend_Gdata_Gbase(new Zend_Http_Client()); + } + + /** + * Google Base Config + * + * @return Mage_GoogleBase_Model_Config + */ + public function getConfig() + { + return Mage::getSingleton('googlebase/config'); + } + /** * Authorize Google Account * diff --git a/app/code/core/Mage/GoogleBase/Model/Service/Feed.php b/app/code/core/Mage/GoogleBase/Model/Service/Feed.php index 27bc423b63..3ceb94d0c5 100644 --- a/app/code/core/Mage/GoogleBase/Model/Service/Feed.php +++ b/app/code/core/Mage/GoogleBase/Model/Service/Feed.php @@ -86,15 +86,15 @@ public function getItemsStatsArray($storeId = null) /** * Returns Google Base recommended Item Types * + * @param string $targetCountry Two-letters country ISO code * @return array */ - public function getItemTypes($storeId = null) + public function getItemTypes($targetCountry) { - if (is_array($this->_itemTypes)) { - return $this->_itemTypes; - } - $location = self::ITEM_TYPES_LOCATION . '/' . Mage::app()->getLocale()->getLocale(); - $feed = $this->getFeed($location, $storeId); + $locale = Mage::getSingleton('googlebase/config')->getCountryInfo($targetCountry, 'locale'); + $location = self::ITEM_TYPES_LOCATION . '/' . $locale; + + $feed = $this->getGuestService()->getFeed($location); $itemTypes = array(); foreach ($feed->entries as $entry) { @@ -130,14 +130,15 @@ public function getItemTypes($storeId = null) * Returns Google Base Attributes * * @param string $type Google Base Item Type + * @param string $targetCountry Two-letters country ISO code * @return array */ - public function getAttributes($type, $storeId = null) + public function getAttributes($type, $targetCountry) { - $itemTypes = $this->getItemTypes($storeId); + $itemTypes = $this->getItemTypes($targetCountry); if (isset($itemTypes[$type]) && $itemTypes[$type] instanceof Varien_Object) { return $itemTypes[$type]->getAttributes(); } - Mage::throwException(Mage::helper('googlebase')->__('No such Item Type "%s" in Google Base to retrieve attributes', $type)); + return array(); } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/Model/Service/Item.php b/app/code/core/Mage/GoogleBase/Model/Service/Item.php index 729032097a..303363c912 100644 --- a/app/code/core/Mage/GoogleBase/Model/Service/Item.php +++ b/app/code/core/Mage/GoogleBase/Model/Service/Item.php @@ -280,9 +280,9 @@ protected function _setUniversalData() $this->_setAttribute('image_link', $object->getImageUrl(), 'url'); } - if ($country = Mage::getStoreConfig('google/googlebase/target_country', $this->getStoreId())) { - $this->_setAttribute('target_country', $country, 'text'); - } + $targetCountry = $this->getConfig()->getTargetCountry($this->getStoreId()); + $this->_setAttribute('target_country', $targetCountry, 'text'); + $this->_setAttribute('item_language', $this->getConfig()->getCountryInfo($targetCountry, 'language'), 'text'); return $this; } @@ -331,7 +331,7 @@ protected function _getAttributeValue($attribute) */ protected function _getItemType() { - return $this->getItemType() ? $this->getItemType() : self::DEFAULT_ITEM_TYPE; + return $this->getItemType() ? $this->getItemType() : $this->getConfig()->getDefaultItemType($this->getStoreId()); } /** diff --git a/app/code/core/Mage/GoogleBase/Model/Source/Country.php b/app/code/core/Mage/GoogleBase/Model/Source/Country.php index 585aa196ab..c999218b13 100644 --- a/app/code/core/Mage/GoogleBase/Model/Source/Country.php +++ b/app/code/core/Mage/GoogleBase/Model/Source/Country.php @@ -35,10 +35,11 @@ class Mage_GoogleBase_Model_Source_Country { public function toOptionArray() { - return array( - array('value' => 'US', 'label' => Mage::helper('googlebase')->__('United States')), - array('value' => 'GB', 'label' => Mage::helper('googlebase')->__('United Kingdom')), - array('value' => 'DE', 'label' => Mage::helper('googlebase')->__('Germany')), - ); + $_allowed = Mage::getSingleton('googlebase/config')->getAllowedCountries(); + $result = array(); + foreach ($_allowed as $iso => $info) { + $result[] = array('value' => $iso, 'label' => $info['name']); + } + return $result; } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/Model/Type.php b/app/code/core/Mage/GoogleBase/Model/Type.php index 50afe966a0..f44a7118ac 100644 --- a/app/code/core/Mage/GoogleBase/Model/Type.php +++ b/app/code/core/Mage/GoogleBase/Model/Type.php @@ -38,8 +38,16 @@ protected function _construct() $this->_init('googlebase/type'); } - public function loadByAttributeSetId($attributeSetId) + /** + * Load type model by Attribute Set Id + * + * @param int $attributeSetId Attribute Set + * @param string $targetCountry Two-letters country ISO code + * @return Mage_GoogleBase_Model_Type + */ + public function loadByAttributeSetId($attributeSetId, $targetCountry) { - return $this->load($attributeSetId, 'attribute_set_id'); + $typeId = $this->getResource()->getTypeIdByAttributeSetId($attributeSetId, $targetCountry); + return $this->load($typeId); } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleBase/controllers/TypesController.php b/app/code/core/Mage/GoogleBase/controllers/TypesController.php index 6101d449da..71a7d8bf0a 100644 --- a/app/code/core/Mage/GoogleBase/controllers/TypesController.php +++ b/app/code/core/Mage/GoogleBase/controllers/TypesController.php @@ -69,6 +69,7 @@ public function gridAction() $this->getLayout()->createBlock('googlebase/adminhtml_types_grid')->toHtml() ); } + public function newAction() { try { @@ -79,7 +80,7 @@ public function newAction() ->renderLayout(); } catch (Exception $e) { $this->_getSession()->addError($e->getMessage()); - $this->_redirect('*/*/index'); + $this->_redirect('*/*/index', array('store' => $this->_getStore()->getId())); } } @@ -93,7 +94,7 @@ public function editAction() if ($id) { $model->load($id); $collection = Mage::getResourceModel('googlebase/attribute_collection') - ->addAttributeSetFilter($model->getAttributeSetId()) + ->addTypeFilter($model->getTypeId()) ->load(); foreach ($collection as $attribute) { $result[] = $attribute->getData(); @@ -132,6 +133,7 @@ public function saveAction() } $typeModel->setAttributeSetId($this->getRequest()->getParam('attribute_set_id')) ->setGbaseItemtype($this->getRequest()->getParam('gbase_itemtype')) + ->setTargetCountry($this->getRequest()->getParam('target_country')) ->save(); @@ -154,7 +156,7 @@ public function saveAction() } catch (Exception $e) { Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } - $this->_redirect('*/*/index'); + $this->_redirect('*/*/index', array('store' => $this->_getStore()->getId())); } public function deleteAction () @@ -170,7 +172,7 @@ public function deleteAction () } catch (Exception $e) { $this->_getSession()->addError($e->getMessage()); } - $this->_redirect('*/*/index'); + $this->_redirect('*/*/index', array('store' => $this->_getStore()->getId())); } public function loadAttributesAction () @@ -180,6 +182,7 @@ public function loadAttributesAction () $this->getLayout()->createBlock('googlebase/adminhtml_types_edit_attributes') ->setAttributeSetId($this->getRequest()->getParam('attribute_set_id')) ->setGbaseItemtype($this->getRequest()->getParam('gbase_itemtype')) + ->setTargetCountry($this->getRequest()->getParam('target_country')) ->setAttributeSetSelected(true) ->toHtml() ); @@ -189,6 +192,43 @@ public function loadAttributesAction () } } + public function loadItemTypesAction() + { + try { + $this->getResponse()->setBody( + $this->getLayout()->getBlockSingleton('googlebase/adminhtml_types_edit_form') + ->getItemTypesSelectElement($this->getRequest()->getParam('target_country')) + ->toHtml() + ); + } catch (Exception $e) { + // just need to output text with error + $this->_getSession()->addError($e->getMessage()); + } + } + + protected function loadAttributeSetsAction() + { + try { + $this->getResponse()->setBody( + $this->getLayout()->getBlockSingleton('googlebase/adminhtml_types_edit_form') + ->getAttributeSetsSelectElement($this->getRequest()->getParam('target_country')) + ->toHtml() + ); + } catch (Exception $e) { + // just need to output text with error + $this->_getSession()->addError($e->getMessage()); + } + } + + public function _getStore() + { + $storeId = (int) $this->getRequest()->getParam('store', 0); + if ($storeId == 0) { + return Mage::app()->getDefaultStoreView(); + } + return Mage::app()->getStore($storeId); + } + protected function _isAllowed() { return Mage::getSingleton('admin/session')->isAllowed('catalog/googlebase/types'); diff --git a/app/code/core/Mage/GoogleBase/etc/config.xml b/app/code/core/Mage/GoogleBase/etc/config.xml index 0a7c143d22..b68be3e5c7 100644 --- a/app/code/core/Mage/GoogleBase/etc/config.xml +++ b/app/code/core/Mage/GoogleBase/etc/config.xml @@ -28,7 +28,7 @@ - 0.1.0 + 0.1.1 @@ -193,6 +193,26 @@ US + + + United States + EN + en_US + products + + + United Kingdom + EN + en_GB + products + + + Germany + DE + de_DE + produkte + + diff --git a/app/code/core/Mage/GoogleBase/sql/googlebase_setup/mysql4-upgrade-0.1.0-0.1.1.php b/app/code/core/Mage/GoogleBase/sql/googlebase_setup/mysql4-upgrade-0.1.0-0.1.1.php new file mode 100644 index 0000000000..0f2992f9af --- /dev/null +++ b/app/code/core/Mage/GoogleBase/sql/googlebase_setup/mysql4-upgrade-0.1.0-0.1.1.php @@ -0,0 +1,30 @@ +getConnection()->addColumn($this->getTable('googlebase/types'), 'target_country', "varchar(2) not null default 'US'"); diff --git a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php index 4d94f3762e..88c38c86b7 100644 --- a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php +++ b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php @@ -294,7 +294,12 @@ protected function _responseNewOrderNotification() $order = $convertQuote->toOrder($quote); - $convertQuote->addressToOrder($quote->getShippingAddress(), $order); + if ($quote->isVirtual()) { + $convertQuote->addressToOrder($quote->getBillingAddress(), $order); + } else { + $convertQuote->addressToOrder($quote->getShippingAddress(), $order); + } + $order->setExtOrderId($this->getGoogleOrderNumber()); $order->setExtCustomerId($this->getData('root/buyer-id/VALUE')); @@ -310,7 +315,9 @@ protected function _responseNewOrderNotification() } $order->setBillingAddress($convertQuote->addressToOrderAddress($quote->getBillingAddress())); - $order->setShippingAddress($convertQuote->addressToOrderAddress($quote->getShippingAddress())); + if (!$quote->isVirtual()) { + $order->setShippingAddress($convertQuote->addressToOrderAddress($quote->getShippingAddress())); + } #$order->setPayment($convertQuote->paymentToOrderPayment($quote->getPayment())); foreach ($quote->getAllItems() as $item) { @@ -323,7 +330,8 @@ protected function _responseNewOrderNotification() $emailAllowed = ($this->getData('root/buyer-marketing-preferences/email-allowed/VALUE')==='true'); - $order->setCustomerNote( + $order->addStatusToHistory( + $order->getStatus(), $this->__('Google Order Number: %s', ''.$this->getGoogleOrderNumber()).''. '
'. $this->__('Google Buyer Id: %s', ''.$this->getData('root/buyer-id/VALUE').''). @@ -331,6 +339,14 @@ protected function _responseNewOrderNotification() $this->__('Is Buyer Willing To Receive Marketing E-Mails: %s', '' . ($emailAllowed ? $this->__('Yes') : $this->__('No')) . '') ); +// $order->setCustomerNote( +// $this->__('Google Order Number: %s', ''.$this->getGoogleOrderNumber()).''. +// '
'. +// $this->__('Google Buyer Id: %s', ''.$this->getData('root/buyer-id/VALUE').''). +// '
'. +// $this->__('Is Buyer Willing To Receive Marketing E-Mails: %s', '' . ($emailAllowed ? $this->__('Yes') : $this->__('No')) . '') +// ); + #ob_start(array($this, 'log')); $order->place(); diff --git a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Checkout.php b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Checkout.php index 264c6afd44..f8431f6105 100644 --- a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Checkout.php +++ b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Checkout.php @@ -80,7 +80,6 @@ protected function _getItemsXml() } $taxClass = ($item->getTaxClassId() == 0 ? 'none' : $item->getTaxClassId()); $weight = (float) $item->getWeight(); - $digital = $item->getIsVirtual() ? 'true' : 'false'; $xml .= << @@ -91,7 +90,7 @@ protected function _getItemsXml() {$item->getQty()} {$taxClass} - {$this->_getDigitalContentXml($item)} + {$this->_getDigitalContentXml($item->getIsVirtual())} {$this->_getMerchantPrivateItemDataXml($item)} @@ -111,6 +110,7 @@ protected function _getItemsXml() 1 none + {$this->_getDigitalContentXml($this->getQuote()->isVirtual())} EOT; @@ -121,24 +121,36 @@ protected function _getItemsXml() return $xml; } - protected function _getDigitalContentXml($item) + protected function _getDigitalContentXml($isVirtual) { - return ''; - if (!$item->getIsVirtual()) { + if (!$isVirtual) { + return ''; + } + + $active = Mage::getStoreConfigFlag('google/checkout_shipping_virtual/active', $this->getQuote()->getStoreId()); + if (!$active) { return ''; } - $xml = 'true'; + + $schedule = Mage::getStoreConfig('google/checkout_shipping_virtual/schedule', $this->getQuote()->getStoreId()); + $method = Mage::getStoreConfig('google/checkout_shipping_virtual/method', $this->getQuote()->getStoreId()); + + $xml = "{$schedule}"; + + if ($method == 'email') { + $xml .= "true"; + } elseif ($method == 'key_url') { + } elseif ($method == 'description_based') { + } + + $xml = "{$xml}"; return $xml; } protected function _getMerchantPrivateItemDataXml($item) { - $xml = << - {$item->getId()} - -EOT; + $xml = "{$item->getId()}"; return $xml; } protected function _getMerchantPrivateDataXml() diff --git a/app/code/core/Mage/GoogleCheckout/Model/Source/Shipping/Virtual/Method.php b/app/code/core/Mage/GoogleCheckout/Model/Source/Shipping/Virtual/Method.php new file mode 100644 index 0000000000..0f581e3f6b --- /dev/null +++ b/app/code/core/Mage/GoogleCheckout/Model/Source/Shipping/Virtual/Method.php @@ -0,0 +1,39 @@ +'email', 'label'=>$hlp->__('Email delivery')), + // array('value'=>'key_url', 'label'=>$hlp->__('Key/URL delivery')), + // array('value'=>'description_based', 'label'=>$hlp->__('Description-based delivery')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/GoogleCheckout/Model/Source/Shipping/Virtual/Schedule.php b/app/code/core/Mage/GoogleCheckout/Model/Source/Shipping/Virtual/Schedule.php new file mode 100644 index 0000000000..fde3fceae0 --- /dev/null +++ b/app/code/core/Mage/GoogleCheckout/Model/Source/Shipping/Virtual/Schedule.php @@ -0,0 +1,38 @@ +'OPTIMISTIC', 'label'=>$hlp->__('Optimistic')), + array('value'=>'PESSIMISTIC', 'label'=>$hlp->__('Pessimistic')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/GoogleCheckout/etc/config.xml b/app/code/core/Mage/GoogleCheckout/etc/config.xml index 5619b7c621..a719f4cc42 100644 --- a/app/code/core/Mage/GoogleCheckout/etc/config.xml +++ b/app/code/core/Mage/GoogleCheckout/etc/config.xml @@ -210,6 +210,11 @@ Something like this is to be added to resolve bug #4890 0 0 + + 1 + OPTIMISTIC + email +
diff --git a/app/code/core/Mage/GoogleCheckout/etc/system.xml b/app/code/core/Mage/GoogleCheckout/etc/system.xml index ff120c0255..65d8d3bc82 100644 --- a/app/code/core/Mage/GoogleCheckout/etc/system.xml +++ b/app/code/core/Mage/GoogleCheckout/etc/system.xml @@ -424,6 +424,43 @@
+ + + text + 200 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + + select + googlecheckout/source_shipping_virtual_schedule + 2 + 1 + 1 + 0 + + + + select + googlecheckout/source_shipping_virtual_method + 3 + 1 + 1 + 0 + + + diff --git a/app/code/core/Mage/GoogleOptimizer/Block/Adminhtml/Catalog/Product/Edit/Tab/Googleoptimizer.php b/app/code/core/Mage/GoogleOptimizer/Block/Adminhtml/Catalog/Product/Edit/Tab/Googleoptimizer.php index 6de78ed970..f4b34245ee 100644 --- a/app/code/core/Mage/GoogleOptimizer/Block/Adminhtml/Catalog/Product/Edit/Tab/Googleoptimizer.php +++ b/app/code/core/Mage/GoogleOptimizer/Block/Adminhtml/Catalog/Product/Edit/Tab/Googleoptimizer.php @@ -209,7 +209,9 @@ public function getTabTitle() public function canShowTab() { - if (Mage::helper('googleoptimizer')->isOptimizerActive() && $this->getProduct()->getAttributeSetId()) { + if (Mage::helper('googleoptimizer')->isOptimizerActive($this->getProduct()->getStoreId()) + && $this->getProduct()->getAttributeSetId()) + { return true; } return false; diff --git a/app/code/core/Mage/GoogleOptimizer/Helper/Data.php b/app/code/core/Mage/GoogleOptimizer/Helper/Data.php index 09ecac43e6..6e68c2420f 100644 --- a/app/code/core/Mage/GoogleOptimizer/Helper/Data.php +++ b/app/code/core/Mage/GoogleOptimizer/Helper/Data.php @@ -69,7 +69,7 @@ public function productAttribute($callObject, $attributeHtml, $params) $attributeName = $params['attribute']; $product = $params['product']; - if (!$this->isOptimizerActive() + if (!$this->isOptimizerActive($product->getStoreId()) || !$product->getGoogleOptimizerScripts() || !$product->getGoogleOptimizerScripts()->getControlScript()) { return $attributeHtml; @@ -99,7 +99,7 @@ public function categoryAttribute($callObject, $attributeHtml, $params) $attributeName = $params['attribute']; $category = $params['category']; - if (!$this->isOptimizerActive() + if (!$this->isOptimizerActive($category->getStoreId()) || !$category->getGoogleOptimizerScripts() || !$category->getGoogleOptimizerScripts()->getControlScript()) { return $attributeHtml; diff --git a/app/code/core/Mage/GoogleOptimizer/Model/Code/Category.php b/app/code/core/Mage/GoogleOptimizer/Model/Code/Category.php index cb199883a6..899e5d3942 100644 --- a/app/code/core/Mage/GoogleOptimizer/Model/Code/Category.php +++ b/app/code/core/Mage/GoogleOptimizer/Model/Code/Category.php @@ -34,4 +34,28 @@ class Mage_GoogleOptimizer_Model_Code_Category extends Mage_GoogleOptimizer_Model_Code { protected $_entityType = 'category'; + + /** + * Removing scripts assigned to entity + * + * @param Varien_Object $entity + * @return Mage_Googleoptimizer_Model_Code + */ + public function deleteScripts($storeId) + { + $category = $this->getEntity(); + if ($category) { + /** + * We need check category children ids + */ + $ids = $category->getDeletedChildrenIds(); + if (is_array($ids)) { + $ids[] = $category->getId(); + } else { + $ids = array($category->getId()); + } + $this->setEntityIds($ids); + } + return parent::deleteScripts($storeId); + } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleOptimizer/Model/Mysql4/Code.php b/app/code/core/Mage/GoogleOptimizer/Model/Mysql4/Code.php index ef9c64d24c..a4a18ed1ed 100644 --- a/app/code/core/Mage/GoogleOptimizer/Model/Mysql4/Code.php +++ b/app/code/core/Mage/GoogleOptimizer/Model/Mysql4/Code.php @@ -93,8 +93,13 @@ public function deleteByEntityType($object, $store_id) { $write = $this->_getWriteAdapter(); if ($write) { - $where = $write->quoteInto($this->getMainTable().'.entity_id=?', $object->getEntity()->getId()) . - ' AND ' . $write->quoteInto($this->getMainTable().'.entity_type=?', $object->getEntityType()) . + $entityIds = $object->getEntityIds(); + if (!empty($entityIds)) { + $where = $write->quoteInto($this->getMainTable().'.entity_id IN (?)', $entityIds); + } else { + $where = $write->quoteInto($this->getMainTable().'.entity_id=?', $object->getEntity()->getId()); + } + $where.= ' AND ' . $write->quoteInto($this->getMainTable().'.entity_type=?', $object->getEntityType()) . ' AND ' . $write->quoteInto($this->getMainTable().'.store_id=?', $store_id); $write->delete($this->getMainTable(), $where); } diff --git a/app/code/core/Mage/GoogleOptimizer/Model/Observer.php b/app/code/core/Mage/GoogleOptimizer/Model/Observer.php index fa203b3571..926e29b68e 100644 --- a/app/code/core/Mage/GoogleOptimizer/Model/Observer.php +++ b/app/code/core/Mage/GoogleOptimizer/Model/Observer.php @@ -43,7 +43,7 @@ public function appendToProductGoogleOptimizerScripts($observer) { $product = $observer->getEvent()->getProduct(); - if (!Mage::helper('googleoptimizer')->isOptimizerActive()) { + if (!Mage::helper('googleoptimizer')->isOptimizerActive($product->getStoreId())) { return $this; } @@ -206,7 +206,7 @@ public function appendToCategoryGoogleOptimizerScripts($observer) { $category = $observer->getEvent()->getCategory(); - if (!Mage::helper('googleoptimizer')->isOptimizerActive()) { + if (!Mage::helper('googleoptimizer')->isOptimizerActive($category->getStoreId())) { return $this; } @@ -246,7 +246,7 @@ public function saveCategoryGoogleOptimizerScripts($observer) { $category = $observer->getEvent()->getCategory(); - if (!Mage::helper('googleoptimizer')->isOptimizerActive()) { + if (!Mage::helper('googleoptimizer')->isOptimizerActive($category->getStoreId())) { return $this; } @@ -260,10 +260,10 @@ public function saveCategoryGoogleOptimizerScripts($observer) } /** - * Delete Produt scripts after deleting product + * Delete category scripts after deleting category * - * @param Varien_Object $observer - * @return Mage_Googleoptimizer_Model_Observer + * @param Varien_Object $observer + * @return Mage_Googleoptimizer_Model_Observer */ public function deleteCategoryGoogleOptimizerScripts($observer) { diff --git a/app/code/core/Mage/Install/Model/Installer/Db.php b/app/code/core/Mage/Install/Model/Installer/Db.php index d0dfd56b01..ec2035a702 100644 --- a/app/code/core/Mage/Install/Model/Installer/Db.php +++ b/app/code/core/Mage/Install/Model/Installer/Db.php @@ -56,15 +56,24 @@ public function checkDatabase($data) try { $connection = Mage::getSingleton('core/resource')->createConnection('install', $this->_getConnenctionType(), $config); - $result = $connection->query($connection->quoteInto('SHOW VARIABLES LIKE ?', 'version')); - $row = $result->fetch(); - $version = $row['Value']; - preg_match("([0-9.]+)",$version,$toCompare); + $variables = $connection->fetchPairs("SHOW VARIABLES"); + + $version = isset($variables['version']) ? $variables['version'] : 'undefined'; + $match = array(); + if (preg_match("#^([0-9\.]+)#", $version, $match)) { + $version = $match[0]; + } $requiredVersion = (string)Mage::getSingleton('install/config')->getNode('check/mysql/version'); - if (version_compare(isset($toCompare[0])?$toCompare[0]:$version, $requiredVersion) == -1) { + // check MySQL Server version + if (version_compare($version, $requiredVersion) == -1) { Mage::throwException(Mage::helper('install')->__('Database server version does not match system requirements (required: %s, actual: %s)', $requiredVersion, $version)); } + + // check InnoDB support + if (!isset($variables['have_innodb']) || $variables['have_innodb'] != 'YES') { + Mage::throwException(Mage::helper('install')->__('Database server does not support InnoDB storage engine')); + } } catch (Exception $e){ $this->_getInstaller()->getDataModel()->addError($e->getMessage()); @@ -72,6 +81,11 @@ public function checkDatabase($data) } } + /** + * Retrieve Connection Type + * + * @return string + */ protected function _getConnenctionType() { return (string) Mage::getConfig()->getNode('global/resources/default_setup/connection/type'); diff --git a/app/code/core/Mage/Log/Model/Mysql4/Log.php b/app/code/core/Mage/Log/Model/Mysql4/Log.php index b335386aeb..a8a092a8f8 100644 --- a/app/code/core/Mage/Log/Model/Mysql4/Log.php +++ b/app/code/core/Mage/Log/Model/Mysql4/Log.php @@ -58,8 +58,8 @@ public function clean(Mage_Log_Model_Log $object) )); $this->_cleanVisitors($cleanTime); - $this->_cleanCustomers(); - $this->_cleanCustomers(); + $this->_cleanCustomers($cleanTime); + $this->_cleanUrls(); Mage::dispatchEvent('log_log_clean_after', array( 'log' => $object @@ -131,14 +131,16 @@ protected function _cleanVisitors($time) /** * Clean customer table * + * @param int $time * @return Mage_Log_Model_Mysql4_Log */ - protected function _cleanCustomers() + protected function _cleanCustomers($time) { // retrieve last active customer log id $row = $this->_getReadAdapter()->fetchRow( $this->_getReadAdapter()->select() ->from($this->getTable('log/customer'), 'log_id') + ->where('login_at < ?', gmdate('Y-m-d H:i:s', time() - $time)) ->order('log_id DESC') ->limit(1) ); @@ -149,15 +151,19 @@ protected function _cleanCustomers() $lastLogId = $row['log_id']; - $needLogIds = array(); + // Order by desc log_id before grouping (within-group aggregates query pattern) $select = $this->_getReadAdapter()->select() ->from( - $this->getTable('log/customer'), + array('log_customer_main' => $this->getTable('log/customer')), array('log_id')) - ->group('customer_id') - ->where('log_idorder('log_id DESC'); + ->joinLeft( + array('log_customer' => $this->getTable('log/customer')), + 'log_customer_main.customer_id = log_customer.customer_id AND log_customer_main.log_id < log_customer.log_id', + array()) + ->where('log_customer.customer_id IS NULL') + ->where('log_customer_main.log_id_getReadAdapter()->query($select); while ($row = $query->fetch()) { $needLogIds[$row['log_id']] = 1; @@ -174,17 +180,18 @@ protected function _cleanCustomers() ->where('log_idorder('log_id') ->limit(1000); + $query = $this->_getReadAdapter()->query($select); $count = 0; while ($row = $query->fetch()) { - $count ++; + $count++; $customerLogId = $row['log_id']; if (!isset($needLogIds[$row['log_id']])) { $visitorIds[] = $row['visitor_id']; } } - if (!$count || $customerLogId == $lastLogId) { + if (!$count) { break; } @@ -212,6 +219,16 @@ protected function _cleanCustomers() $this->getTable('log/visitor'), $this->_getWriteAdapter()->quoteInto('visitor_id IN(?)', $visitorIds) ); + + // remove customers from log/customer + $this->_getWriteAdapter()->delete( + $this->getTable('log/customer'), + $this->_getWriteAdapter()->quoteInto('visitor_id IN(?)', $visitorIds) + ); + } + + if ($customerLogId == $lastLogId) { + break; } } diff --git a/app/code/core/Mage/Newsletter/Model/Mysql4/Subscriber/Collection.php b/app/code/core/Mage/Newsletter/Model/Mysql4/Subscriber/Collection.php index 7aec1c24d0..548824478a 100644 --- a/app/code/core/Mage/Newsletter/Model/Mysql4/Subscriber/Collection.php +++ b/app/code/core/Mage/Newsletter/Model/Mysql4/Subscriber/Collection.php @@ -199,7 +199,7 @@ public function showStoreInfo() public function addFieldToFilter($field, $condition=null) { if(!is_null($condition)) { - $this->_select->having($this->_getConditionSql($field, $condition)); + $this->_select->where($this->_getConditionSql($this->_getFieldTableAlias($field), $condition)); $this->_countFilterPart[] = $this->_getConditionSql($this->_getFieldTableAlias($field), $condition); } return $this; diff --git a/app/code/core/Mage/Paygate/Model/Authorizenet.php b/app/code/core/Mage/Paygate/Model/Authorizenet.php index c450ef47f7..aadb02b109 100644 --- a/app/code/core/Mage/Paygate/Model/Authorizenet.php +++ b/app/code/core/Mage/Paygate/Model/Authorizenet.php @@ -285,7 +285,7 @@ protected function _buildRequest(Varien_Object $payment) ->setXCustId($billing->getCustomerId()) ->setXCustomerIp($order->getRemoteIp()) ->setXCustomerTaxId($billing->getTaxId()) - ->setXEmail($billing->getEmail()) + ->setXEmail($order->getCustomerEmail()) ->setXEmailCustomer($this->getConfigData('email_customer')) ->setXMerchantEmail($this->getConfigData('merchant_email')); } diff --git a/app/code/core/Mage/Poll/Model/Mysql4/Poll.php b/app/code/core/Mage/Poll/Model/Mysql4/Poll.php index 4697180237..2b7a1afe09 100644 --- a/app/code/core/Mage/Poll/Model/Mysql4/Poll.php +++ b/app/code/core/Mage/Poll/Model/Mysql4/Poll.php @@ -35,12 +35,20 @@ class Mage_Poll_Model_Mysql4_Poll extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('poll/poll', 'poll_id'); - $this->_uniqueFields = array( - array( - 'field' => 'poll_title', - 'title' => Mage::helper('poll')->__('Poll with the same question') - ) - ); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => 'poll_title', + 'title' => Mage::helper('poll')->__('Poll with the same question') + )); + return $this; } /** diff --git a/app/code/core/Mage/Poll/Model/Mysql4/Poll/Answer.php b/app/code/core/Mage/Poll/Model/Mysql4/Poll/Answer.php index be49d52664..2cbd707ee6 100644 --- a/app/code/core/Mage/Poll/Model/Mysql4/Poll/Answer.php +++ b/app/code/core/Mage/Poll/Model/Mysql4/Poll/Answer.php @@ -37,9 +37,20 @@ class Mage_Poll_Model_Mysql4_Poll_Answer extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('poll/poll_answer', 'answer_id'); - $this->_uniqueFields = array( - array('field' => array('answer_title', 'poll_id'), - 'title' => Mage::helper('poll')->__('Answer with the same title in this poll')) - ); } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => array('answer_title', 'poll_id'), + 'title' => Mage::helper('poll')->__('Answer with the same title in this poll') + )); + return $this; + } + } \ No newline at end of file diff --git a/app/code/core/Mage/Poll/Model/Poll.php b/app/code/core/Mage/Poll/Model/Poll.php index 3455639615..e7bd347702 100644 --- a/app/code/core/Mage/Poll/Model/Poll.php +++ b/app/code/core/Mage/Poll/Model/Poll.php @@ -43,6 +43,41 @@ protected function _construct() $this->_init('poll/poll'); } + /** + * Retrieve Cookie Object + * + * @return Mage_Core_Model_Cookie + */ + public function getCookie() + { + return Mage::app()->getCookie(); + } + + /** + * Get Cookie Name + * + * @param int $pollId + * @return string + */ + public function getCookieName($pollId = null) + { + return $this->_pollCookieDefaultName . $this->getPoolId($pollId); + } + + /** + * Retrieve defined or current Id + * + * @param int $pollId + * @return int + */ + public function getPoolId($pollId = null) + { + if (is_null($pollId)) { + $pollId = $this->getId(); + } + return $pollId; + } + /** * Check if validation by IP option is enabled in config * @@ -61,8 +96,8 @@ public function isValidationByIp() */ public function setVoted($pollId=null) { - $pollId = $pollId === null ? $this->getId() : $pollId; - Mage::getSingleton('core/cookie')->set($this->_pollCookieDefaultName . $pollId, $pollId); + $this->getCookie()->set($this->getCookieName($pollId), $this->getPoolId($pollId)); + return $this; } @@ -74,10 +109,10 @@ public function setVoted($pollId=null) */ public function isVoted($pollId = null) { - $pollId = $pollId === null ? $this->getId() : $pollId; + $pollId = $this->getPoolId($pollId); // check if it is in cookie - $cookie = Mage::getSingleton('core/cookie')->get($this->_pollCookieDefaultName . $pollId); + $cookie = $this->getCookie()->get($this->getCookieName($pollId)); if (false !== $cookie) { return true; } @@ -148,14 +183,14 @@ public function getVotedPollsIds() { $idsArray = array(); - // load from cookies - foreach ($_COOKIE as $cookieName => $cookieValue) { - $pattern = "/^" . $this->_pollCookieDefaultName . "([0-9]*?)$/"; - if (preg_match($pattern, $cookieName, $m)) { - if ($m[1] != Mage::getSingleton('core/session')->getJustVotedPoll()) { - $idsArray[$m[1]] = $m[1]; - } - } + foreach ($this->getCookie()->get() as $cookieName => $cookieValue) { + $pattern = '#^' . preg_quote($this->_pollCookieDefaultName, '#') . '(\d+)$#'; + $match = array(); + if (preg_match($pattern, $cookieName, $match)) { + if ($match[1] != Mage::getSingleton('core/session')->getJustVotedPoll()) { + $idsArray[$match[1]] = $match[1]; + } + } } // load from db for this ip diff --git a/app/code/core/Mage/Rating/Model/Mysql4/Rating.php b/app/code/core/Mage/Rating/Model/Mysql4/Rating.php index 39ffac305a..e66b42d179 100644 --- a/app/code/core/Mage/Rating/Model/Mysql4/Rating.php +++ b/app/code/core/Mage/Rating/Model/Mysql4/Rating.php @@ -37,7 +37,20 @@ class Mage_Rating_Model_Mysql4_Rating extends Mage_Core_Model_Mysql4_Abstract public function _construct() { $this->_init('rating/rating', 'rating_id'); - $this->_uniqueFields = array( array('field' => 'rating_code', 'title' => /* Mage::helper('rating')->__('Rating with the same title')*/ '' ) ); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => 'rating_code', + 'title' => /* Mage::helper('rating')->__('Rating with the same title')*/ '' + )); + return $this; } protected function _getLoadSelect($field, $value, $object) diff --git a/app/code/core/Mage/Rss/etc/config.xml b/app/code/core/Mage/Rss/etc/config.xml index 9831ea811b..0d9f537672 100644 --- a/app/code/core/Mage/Rss/etc/config.xml +++ b/app/code/core/Mage/Rss/etc/config.xml @@ -46,7 +46,7 @@
- + diff --git a/app/code/core/Mage/Sales/Block/Items/Abstract.php b/app/code/core/Mage/Sales/Block/Items/Abstract.php index 72018f5826..589dfa4a53 100644 --- a/app/code/core/Mage/Sales/Block/Items/Abstract.php +++ b/app/code/core/Mage/Sales/Block/Items/Abstract.php @@ -86,7 +86,8 @@ public function getItemRenderer($type) if (is_null($this->_itemRenders[$type]['renderer'])) { $this->_itemRenders[$type]['renderer'] = $this->getLayout() ->createBlock($this->_itemRenders[$type]['block']) - ->setTemplate($this->_itemRenders[$type]['template']); + ->setTemplate($this->_itemRenders[$type]['template']) + ->setRenderedBlock($this); } return $this->_itemRenders[$type]['renderer']; } diff --git a/app/code/core/Mage/Sales/Block/Order/Email/Items/Order/Grouped.php b/app/code/core/Mage/Sales/Block/Order/Email/Items/Order/Grouped.php new file mode 100644 index 0000000000..aad8884370 --- /dev/null +++ b/app/code/core/Mage/Sales/Block/Order/Email/Items/Order/Grouped.php @@ -0,0 +1,57 @@ + + */ +class Mage_Sales_Block_Order_Email_Items_Order_Grouped extends Mage_Sales_Block_Order_Email_Items_Order_Default +{ + /** + * Prepare item html + * + * This method uses renderer for real product type + * + * @return string + */ + protected function _toHtml() + { if ($this->getItem()->getOrderItem()) { + $item = $this->getItem()->getOrderItem(); + } else { + $item = $this->getItem(); + } + if ($productType = $item->getRealProductType()) { + $renderer = $this->getRenderedBlock()->getItemRenderer($productType); + $renderer->setItem($this->getItem()); + return $renderer->toHtml(); + } + return parent::_toHtml(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Block/Order/Item/Renderer/Grouped.php b/app/code/core/Mage/Sales/Block/Order/Item/Renderer/Grouped.php new file mode 100644 index 0000000000..2f26b3bd7b --- /dev/null +++ b/app/code/core/Mage/Sales/Block/Order/Item/Renderer/Grouped.php @@ -0,0 +1,57 @@ + + */ +class Mage_Sales_Block_Order_Item_Renderer_Grouped extends Mage_Sales_Block_Order_Item_Renderer_Default +{ + /** + * Prepare item html + * + * This method uses renderer for real product type + * + * @return string + */ + protected function _toHtml() + { + if ($this->getItem()->getOrderItem()) { + $item = $this->getItem()->getOrderItem(); + } else { + $item = $this->getItem(); + } + if ($productType = $item->getRealProductType()) { + $renderer = $this->getRenderedBlock()->getItemRenderer($productType); + $renderer->setItem($this->getItem()); + return $renderer->toHtml(); + } + return parent::_toHtml(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Abstract.php b/app/code/core/Mage/Sales/Model/Abstract.php new file mode 100644 index 0000000000..8e536e1e61 --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Abstract.php @@ -0,0 +1,70 @@ + + */ +abstract class Mage_Sales_Model_Abstract extends Mage_Core_Model_Abstract +{ + /** + * Get object store identifier + * + * @return int | string | Mage_Core_Model_Store + */ + abstract public function getStore(); + + /** + * Get object created at date affected current active store timezone + * + * @return Zend_Date + */ + public function getCreatedAtDate() + { + return Mage::app()->getLocale()->date( + $this->_getResource()->mktime(($this->getCreatedAt())), + null, + null, + true + ); + } + + /** + * Get object created at date affected with object store timezone + * + * @return Zend_Date + */ + public function getCreatedAtStoreDate() + { + return Mage::app()->getLocale()->storeDate( + $this->getStore(), + $this->_getResource()->mktime(($this->getCreatedAt())), + true + ); + } +} diff --git a/app/code/core/Mage/Sales/Model/Mysql4/Order/Collection.php b/app/code/core/Mage/Sales/Model/Mysql4/Order/Collection.php index 6427afd166..8165cb11b4 100644 --- a/app/code/core/Mage/Sales/Model/Mysql4/Order/Collection.php +++ b/app/code/core/Mage/Sales/Model/Mysql4/Order/Collection.php @@ -31,14 +31,22 @@ * @package Mage_Sales * @author Magento Core Team */ - class Mage_Sales_Model_Mysql4_Order_Collection extends Mage_Eav_Model_Entity_Collection_Abstract { + /** + * Initialize orders collection + * + */ protected function _construct() { $this->_init('sales/order'); } + /** + * Add order items count expression + * + * @return Mage_Sales_Model_Mysql4_Order_Collection + */ public function addItemCountExpr() { $orderTable = $this->getEntity()->getEntityTable(); @@ -51,4 +59,34 @@ public function addItemCountExpr() ->group('e.entity_id'); return $this; } + + /** + * Minimize usual count select + * + * @return Varien_Db_Select + */ + public function getSelectCountSql() + { + $countSelect = parent::getSelectCountSql(); + $countSelect->resetJoinLeft(); + return $countSelect; + } + + /** + * Retrive all ids for collection + * + * @return array + */ + public function getAllIds($limit=null, $offset=null) + { + $idsSelect = clone $this->getSelect(); + $idsSelect->reset(Zend_Db_Select::ORDER); + $idsSelect->reset(Zend_Db_Select::LIMIT_COUNT); + $idsSelect->reset(Zend_Db_Select::LIMIT_OFFSET); + $idsSelect->reset(Zend_Db_Select::COLUMNS); + $idsSelect->from(null, 'e.'.$this->getEntity()->getIdFieldName()); + $idsSelect->limit($limit, $offset); + $idsSelect->resetJoinLeft(); + return $this->getConnection()->fetchCol($idsSelect, $this->_bindParams); + } } diff --git a/app/code/core/Mage/Sales/Model/Mysql4/Setup.php b/app/code/core/Mage/Sales/Model/Mysql4/Setup.php index 3dfc223d08..9d911f7f45 100644 --- a/app/code/core/Mage/Sales/Model/Mysql4/Setup.php +++ b/app/code/core/Mage/Sales/Model/Mysql4/Setup.php @@ -53,7 +53,7 @@ public function addAttribute($entityTypeId, $code, array $attr) return $this; } - protected function _addFlatAttribute($table, $attribute, $data) + protected function _addFlatAttribute($table, $attribute, $attr) { $tableInfo = $this->getConnection()->describeTable($this->getTable($table)); if (isset($tableInfo[$attribute])) { diff --git a/app/code/core/Mage/Sales/Model/Order.php b/app/code/core/Mage/Sales/Model/Order.php index f880e89790..a46716c5a3 100644 --- a/app/code/core/Mage/Sales/Model/Order.php +++ b/app/code/core/Mage/Sales/Model/Order.php @@ -36,7 +36,7 @@ * * @author Magento Core Team */ -class Mage_Sales_Model_Order extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order extends Mage_Sales_Model_Abstract { /** * XML configuration paths @@ -1275,19 +1275,15 @@ public function addRelatedObject(Mage_Core_Model_Abstract $object) return $this; } - public function getCreatedAtFormated($format) - { - return Mage::getBlockSingleton('core/text')->formatDate($this->getCreatedAt(), $format); - } - /** - * Retrieve created at store date object + * Get formated order created date in store timezone * - * @return Zend_Date + * @param string $format date format type (short|medium|long|full) + * @return string */ - public function getCreatedAtDate() + public function getCreatedAtFormated($format) { - return Mage::app()->getLocale()->storeDate($this->getStore(), $this->_getResource()->mktime(($this->getCreatedAt())), true); + return Mage::helper('core')->formatDate($this->getCreatedAtStoreDate(), $format); } public function getEmailCustomerNote() @@ -1342,7 +1338,10 @@ protected function _checkState() $this->setState(self::STATE_COMPLETE, true); } } - else { + /** + * Order can be closed just in case when we have refunded amount + */ + elseif(floatval($this->getTotalRefunded())) { if ($this->getState() !== self::STATE_CLOSED) { $this->setState(self::STATE_CLOSED, true); } diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo.php index 3228da8e7c..06a58ffbe5 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Creditmemo extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Creditmemo extends Mage_Sales_Model_Abstract { const STATE_OPEN = 1; const STATE_REFUNDED = 2; diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Comment.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Comment.php index 6aa0e59c69..cd11bab2a2 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Comment.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Comment.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Creditmemo_Comment extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Creditmemo_Comment extends Mage_Sales_Model_Abstract { /** * Creditmemo instance @@ -63,4 +63,17 @@ public function getCreditmemo() { return $this->_creditmemo; } + + /** + * Get store object + * + * @return Mage_Core_Model_Store + */ + public function getStore() + { + if ($this->getCreditmemo()) { + return $this->getCreditmemo()->getStore(); + } + return Mage::app()->getStore(); + } } diff --git a/app/code/core/Mage/Sales/Model/Order/Invoice.php b/app/code/core/Mage/Sales/Model/Order/Invoice.php index a77afcca06..c307ce1579 100644 --- a/app/code/core/Mage/Sales/Model/Order/Invoice.php +++ b/app/code/core/Mage/Sales/Model/Order/Invoice.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Invoice extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Invoice extends Mage_Sales_Model_Abstract { /** * Invoice states diff --git a/app/code/core/Mage/Sales/Model/Order/Invoice/Comment.php b/app/code/core/Mage/Sales/Model/Order/Invoice/Comment.php index ca736d2709..79c3cda031 100644 --- a/app/code/core/Mage/Sales/Model/Order/Invoice/Comment.php +++ b/app/code/core/Mage/Sales/Model/Order/Invoice/Comment.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Invoice_Comment extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Invoice_Comment extends Mage_Sales_Model_Abstract { /** * Invoice instance @@ -63,4 +63,17 @@ public function getInvoice() { return $this->_invoice; } + + /** + * Get store object + * + * @return Mage_Core_Model_Store + */ + public function getStore() + { + if ($this->getInvoice()) { + return $this->getInvoice()->getStore(); + } + return Mage::app()->getStore(); + } } diff --git a/app/code/core/Mage/Sales/Model/Order/Item.php b/app/code/core/Mage/Sales/Model/Order/Item.php index 6a05aee571..a0bb4b8268 100644 --- a/app/code/core/Mage/Sales/Model/Order/Item.php +++ b/app/code/core/Mage/Sales/Model/Order/Item.php @@ -396,6 +396,19 @@ public function getProductOptionByCode($code=null) return null; } + /** + * Return real product type of item or NULL if item is not composite + * + * @return string | null + */ + public function getRealProductType() + { + if ($productType = $this->getProductOptionByCode('real_product_type')) { + return $productType; + } + return null; + } + /** * Adds child item to this item * diff --git a/app/code/core/Mage/Sales/Model/Order/Pdf/Abstract.php b/app/code/core/Mage/Sales/Model/Order/Pdf/Abstract.php index 1c2958d305..de0621e9b9 100644 --- a/app/code/core/Mage/Sales/Model/Order/Pdf/Abstract.php +++ b/app/code/core/Mage/Sales/Model/Order/Pdf/Abstract.php @@ -180,7 +180,7 @@ protected function insertOrder(&$page, $order, $putOrderId = true) $page->drawText(Mage::helper('sales')->__('Order # ').$order->getRealOrderId(), 35, 770, 'UTF-8'); } //$page->drawText(Mage::helper('sales')->__('Order Date: ') . date( 'D M j Y', strtotime( $order->getCreatedAt() ) ), 35, 760, 'UTF-8'); - $page->drawText(Mage::helper('sales')->__('Order Date: ') . Mage::helper('core')->formatDate($order->getCreatedAt(), 'medium', false), 35, 760, 'UTF-8'); + $page->drawText(Mage::helper('sales')->__('Order Date: ') . Mage::helper('core')->formatDate($order->getCreatedAtStoreDate(), 'medium', false), 35, 760, 'UTF-8'); $page->setFillColor(new Zend_Pdf_Color_Rgb(0.93, 0.92, 0.92)); $page->setLineColor(new Zend_Pdf_Color_GrayScale(0.5)); @@ -294,7 +294,7 @@ protected function insertOrder(&$page, $order, $putOrderId = true) $yShipments = $this->y; - $totalShippingChargesText = "(" . Mage::helper('sales')->__('Total Shipping Charges') . " " . $order->formatPriceTxt($order->getBaseShippingAmount()) . ")"; + $totalShippingChargesText = "(" . Mage::helper('sales')->__('Total Shipping Charges') . " " . $order->formatPriceTxt($order->getShippingAmount()) . ")"; $page->drawText($totalShippingChargesText, 285, $yShipments-7, 'UTF-8'); $yShipments -=10; @@ -491,6 +491,19 @@ protected function _getRenderer($type) return $this->_renderers[$type]['renderer']; } + /** + * Public method of protected @see _getRenderer() + * + * Retrieve renderer model + * + * @param string $type + * @return Mage_Sales_Model_Order_Pdf_Items_Abstract + */ + public function getRenderer($type) + { + return $this->_getRenderer($type); + } + protected function _drawItem(Varien_Object $item, Zend_Pdf_Page $page, Mage_Sales_Model_Order $order) { $type = $item->getOrderItem()->getProductType(); @@ -499,6 +512,7 @@ protected function _drawItem(Varien_Object $item, Zend_Pdf_Page $page, Mage_Sale $renderer->setItem($item); $renderer->setPdf($this); $renderer->setPage($page); + $renderer->setRenderedModel($this); $renderer->draw(); } diff --git a/app/code/core/Mage/Sales/Model/Order/Pdf/Items/Creditmemo/Grouped.php b/app/code/core/Mage/Sales/Model/Order/Pdf/Items/Creditmemo/Grouped.php new file mode 100644 index 0000000000..a862512d9b --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Order/Pdf/Items/Creditmemo/Grouped.php @@ -0,0 +1,48 @@ + + */ +class Mage_Sales_Model_Order_Pdf_Items_Creditmemo_Grouped extends Mage_Sales_Model_Order_Pdf_Items_Creditmemo_Default +{ + public function draw() + { + $type = $this->getItem()->getOrderItem()->getRealProductType(); + $renderer = $this->getRenderedModel()->getRenderer($type); + $renderer->setOrder($this->getOrder()); + $renderer->setItem($this->getItem()); + $renderer->setPdf($this->getPdf()); + $renderer->setPage($this->getPage()); + + $renderer->draw(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Order/Pdf/Items/Invoice/Grouped.php b/app/code/core/Mage/Sales/Model/Order/Pdf/Items/Invoice/Grouped.php new file mode 100644 index 0000000000..d9098daca7 --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Order/Pdf/Items/Invoice/Grouped.php @@ -0,0 +1,48 @@ + + */ +class Mage_Sales_Model_Order_Pdf_Items_Invoice_Grouped extends Mage_Sales_Model_Order_Pdf_Items_Invoice_Default +{ + public function draw() + { + $type = $this->getItem()->getOrderItem()->getRealProductType(); + $renderer = $this->getRenderedModel()->getRenderer($type); + $renderer->setOrder($this->getOrder()); + $renderer->setItem($this->getItem()); + $renderer->setPdf($this->getPdf()); + $renderer->setPage($this->getPage()); + + $renderer->draw(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Order/Shipment.php b/app/code/core/Mage/Sales/Model/Order/Shipment.php index 11dc6c57dc..3a1c473841 100644 --- a/app/code/core/Mage/Sales/Model/Order/Shipment.php +++ b/app/code/core/Mage/Sales/Model/Order/Shipment.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Shipment extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Shipment extends Mage_Sales_Model_Abstract { const STATUS_NEW = 1; diff --git a/app/code/core/Mage/Sales/Model/Order/Shipment/Comment.php b/app/code/core/Mage/Sales/Model/Order/Shipment/Comment.php index 4849fac0d6..784650101d 100644 --- a/app/code/core/Mage/Sales/Model/Order/Shipment/Comment.php +++ b/app/code/core/Mage/Sales/Model/Order/Shipment/Comment.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Shipment_Comment extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Shipment_Comment extends Mage_Sales_Model_Abstract { /** * Shipment instance @@ -63,4 +63,17 @@ public function getShipment() { return $this->_shipment; } + + /** + * Get store object + * + * @return Mage_Core_Model_Store + */ + public function getStore() + { + if ($this->getShipment()) { + return $this->getShipment()->getStore(); + } + return Mage::app()->getStore(); + } } diff --git a/app/code/core/Mage/Sales/Model/Order/Shipment/Track.php b/app/code/core/Mage/Sales/Model/Order/Shipment/Track.php index f779298f7d..d8a772f925 100644 --- a/app/code/core/Mage/Sales/Model/Order/Shipment/Track.php +++ b/app/code/core/Mage/Sales/Model/Order/Shipment/Track.php @@ -24,7 +24,7 @@ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -class Mage_Sales_Model_Order_Shipment_Track extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Shipment_Track extends Mage_Sales_Model_Abstract { const CUSTOM_CARRIER_CODE = 'custom'; protected $_shipment = null; @@ -87,4 +87,17 @@ public function getNumberDetail() return $trackingInfo; } + + /** + * Get store object + * + * @return Mage_Core_Model_Store + */ + public function getStore() + { + if ($this->getShipment()) { + return $this->getShipment()->getStore(); + } + return Mage::app()->getStore(); + } } \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Order/Status/History.php b/app/code/core/Mage/Sales/Model/Order/Status/History.php index 631acd6e3e..7e2f9a9339 100644 --- a/app/code/core/Mage/Sales/Model/Order/Status/History.php +++ b/app/code/core/Mage/Sales/Model/Order/Status/History.php @@ -25,7 +25,7 @@ */ -class Mage_Sales_Model_Order_Status_History extends Mage_Core_Model_Abstract +class Mage_Sales_Model_Order_Status_History extends Mage_Sales_Model_Abstract { /** * Order instance @@ -74,7 +74,18 @@ public function getStatusLabel() if($this->getOrder()) { return $this->getOrder()->getConfig()->getStatusLabel($this->getStatus()); } - } + /** + * Get store object + * + * @return unknown + */ + public function getStore() + { + if ($this->getOrder()) { + return $this->getOrder()->getStore(); + } + return Mage::app()->getStore(); + } } diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Tax.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Tax.php index 8147db579f..69a4027ce1 100644 --- a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Tax.php +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Tax.php @@ -126,17 +126,15 @@ public function collect(Mage_Sales_Model_Quote_Address $address) $shippingTaxClass = Mage::getStoreConfig(Mage_Tax_Model_Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS, $store); + + $shippingTax = 0; + $shippingBaseTax = 0; + if ($shippingTaxClass) { if ($rate = $taxCalculationModel->getRate($request->setProductClassId($shippingTaxClass))) { if (!Mage::helper('tax')->shippingPriceIncludesTax()) { $shippingTax = $address->getShippingAmount() * $rate/100; $shippingBaseTax= $address->getBaseShippingAmount() * $rate/100; - - $shippingTax = $store->roundPrice($shippingTax); - $shippingBaseTax= $store->roundPrice($shippingBaseTax); - - $address->setShippingTaxAmount($shippingTax); - $address->setBaseShippingTaxAmount($shippingBaseTax); } else { $shippingTax = $address->getShippingTaxAmount(); $shippingBaseTax= $address->getBaseShippingTaxAmount(); @@ -158,6 +156,11 @@ public function collect(Mage_Sales_Model_Quote_Address $address) } } + if (!Mage::helper('tax')->shippingPriceIncludesTax()) { + $address->setShippingTaxAmount($shippingTax); + $address->setBaseShippingTaxAmount($shippingBaseTax); + } + $address->setGrandTotal($address->getGrandTotal() + $address->getTaxAmount()); $address->setBaseGrandTotal($address->getBaseGrandTotal() + $address->getBaseTaxAmount()); return $this; diff --git a/app/code/core/Mage/Sales/Model/Quote/Item.php b/app/code/core/Mage/Sales/Model/Quote/Item.php index 0fe565831d..79ed2afa22 100644 --- a/app/code/core/Mage/Sales/Model/Quote/Item.php +++ b/app/code/core/Mage/Sales/Model/Quote/Item.php @@ -302,6 +302,16 @@ public function getProductType() return $this->_getData('product_type'); } + /** + * Return real product type of item + * + * @return unknown + */ + public function getRealProductType() + { + return $this->_getData('product_type'); + } + public function toArray(array $arrAttributes=array()) { $data = parent::toArray($arrAttributes); diff --git a/app/code/core/Mage/Sales/etc/config.xml b/app/code/core/Mage/Sales/etc/config.xml index 2724dd960b..663ef4020f 100644 --- a/app/code/core/Mage/Sales/etc/config.xml +++ b/app/code/core/Mage/Sales/etc/config.xml @@ -28,7 +28,7 @@ - 0.9.29 + 0.9.31 @@ -632,12 +632,14 @@ sales/order_pdf_items_invoice_default + sales/order_pdf_items_invoice_grouped sales/order_pdf_items_shipment_default sales/order_pdf_items_creditmemo_default + sales/order_pdf_items_creditmemo_grouped diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.29-0.9.30.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.29-0.9.30.php new file mode 100644 index 0000000000..2ad7e089e2 --- /dev/null +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.29-0.9.30.php @@ -0,0 +1,45 @@ +getTable('sales/order'); +$tableOrderEntity = $this->getTable('sales/order_entity'); + +$installer->getConnection()->addKey("{$tableOrder}_datetime", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrder}_decimal", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrder}_int", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrder}_text", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrder}_varchar", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); + +$installer->getConnection()->addKey("{$tableOrderEntity}_datetime", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrderEntity}_decimal", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrderEntity}_int", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrderEntity}_text", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); +$installer->getConnection()->addKey("{$tableOrderEntity}_varchar", 'UNQ_ENTITY_ATTRIBUTE_TYPE', array('entity_id', 'attribute_id', 'entity_type_id'), 'unique'); + +$installer->getConnection()->addKey($tableOrderEntity, 'IDX_SALES_ORDER_ENTITY_PARENT', 'parent_id'); diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.30-0.9.31.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.30-0.9.31.php new file mode 100644 index 0000000000..29874e9da1 --- /dev/null +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.30-0.9.31.php @@ -0,0 +1,42 @@ +getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'base_tax_before_discount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'tax_before_discount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'base_tax_before_discount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'tax_before_discount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'original_custom_price', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'subtotal', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'base_subtotal', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'subtotal_with_discount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'base_subtotal_with_discount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'is_changed', 'int(10) unsigned'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'global_currency_code', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'base_to_global_rate', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote'), 'base_to_quote_rate', 'decimal(12,4)'); diff --git a/app/code/core/Mage/Sendfriend/Model/Sendfriend.php b/app/code/core/Mage/Sendfriend/Model/Sendfriend.php index 6bfad0e376..ff3a980464 100644 --- a/app/code/core/Mage/Sendfriend/Model/Sendfriend.php +++ b/app/code/core/Mage/Sendfriend/Model/Sendfriend.php @@ -239,10 +239,15 @@ private function _getSendToFriendCheckType() return max(0, (int) Mage::getStoreConfig('sendfriend/email/check_by')); } + /** + * Retrieve amount by cookie + * @return int + */ private function _amountByCookies() { $newTimes = array(); - $oldTimes = Mage::getSingleton('core/cookie')->get($this->_cookieName); + $oldTimes = Mage::app()->getCookie() + ->get($this->_cookieName); if ($oldTimes){ $oldTimes = explode(',', $oldTimes); foreach ($oldTimes as $time){ @@ -254,12 +259,17 @@ private function _amountByCookies() $amount = count($newTimes); $newTimes[] = time(); - Mage::getSingleton('core/cookie') + Mage::app()->getCookie() ->set($this->_cookieName, implode(',', $newTimes), $this->_period); return $amount; } + /** + * Retrieve amount by IP address + * + * @return int + */ private function _amountByIp() { $this->_deleteLogsBefore(time() - $this->_period); @@ -272,12 +282,23 @@ private function _amountByIp() return $amount; } + /** + * Delete Before Log + * + * @param int $time + * @return Mage_Sendfriend_Model_Sendfriend + */ private function _deleteLogsBefore($time) { $this->_getResource()->deleteLogsBefore($time); return $this; } + /** + * Check and register object + * + * @return Mage_Sendfriend_Model_Sendfriend + */ public function register() { if (!Mage::registry('send_to_friend_model')) { diff --git a/app/code/core/Mage/Shipping/Block/Tracking/Popup.php b/app/code/core/Mage/Shipping/Block/Tracking/Popup.php index 452fc39f4b..41b7c80d16 100644 --- a/app/code/core/Mage/Shipping/Block/Tracking/Popup.php +++ b/app/code/core/Mage/Shipping/Block/Tracking/Popup.php @@ -169,7 +169,7 @@ public function formatDeliveryDateTime($date,$time) return Mage::app()->getLocale()->date(strtotime($date.' '.$time),Zend_Date::TIMESTAMP)->toString('MM/dd/YYYY hh:mm a'); } - /* + /* * change date format to mm/dd/Y */ public function formatDeliveryDate($date) @@ -177,11 +177,14 @@ public function formatDeliveryDate($date) return Mage::app()->getLocale()->date(strtotime($date),Zend_Date::TIMESTAMP)->toString('MM/dd/YYYY'); } - /* + /* * change date format to mm/dd/Y */ - public function formatDeliveryTime($time) + public function formatDeliveryTime($time, $date = null) { + if (!empty($date)) { + $time = $date.' '.$time; + } return Mage::app()->getLocale()->date(strtotime($time),Zend_Date::TIMESTAMP)->toString('hh:mm a'); } diff --git a/app/code/core/Mage/Shipping/Model/Config.php b/app/code/core/Mage/Shipping/Model/Config.php index 77cc4705bd..aba41e063b 100644 --- a/app/code/core/Mage/Shipping/Model/Config.php +++ b/app/code/core/Mage/Shipping/Model/Config.php @@ -41,7 +41,7 @@ public function getActiveCarriers($store=null) $config = Mage::getStoreConfig('carriers', $store); foreach ($config as $code => $carrierConfig) { if (Mage::getStoreConfigFlag('carriers/'.$code.'/active', $store)) { - $carriers[$code] = $this->_getCarrier($code, $carrierConfig); + $carriers[$code] = $this->_getCarrier($code, $carrierConfig, $store); } } return $carriers; @@ -58,7 +58,7 @@ public function getAllCarriers($store=null) $carriers = array(); $config = Mage::getStoreConfig('carriers', $store); foreach ($config as $code => $carrierConfig) { - $carriers[$code] = $this->_getCarrier($code, $carrierConfig); + $carriers[$code] = $this->_getCarrier($code, $carrierConfig, $store); } return $carriers; } diff --git a/app/code/core/Mage/Tag/Model/Mysql4/Tag.php b/app/code/core/Mage/Tag/Model/Mysql4/Tag.php index f0c048cd41..ca093b4f58 100644 --- a/app/code/core/Mage/Tag/Model/Mysql4/Tag.php +++ b/app/code/core/Mage/Tag/Model/Mysql4/Tag.php @@ -37,7 +37,20 @@ class Mage_Tag_Model_Mysql4_Tag extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('tag/tag', 'tag_id'); - $this->_uniqueFields = array( array('field' => 'name', 'title' => Mage::helper('tag')->__('Tag') ) ); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => 'name', + 'title' => Mage::helper('tag')->__('Tag') + )); + return $this; } public function loadByName($model, $name) diff --git a/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rate.php b/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rate.php index bd45d5b186..250623371d 100644 --- a/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rate.php +++ b/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rate.php @@ -33,18 +33,25 @@ */ class Mage_Tax_Model_Mysql4_Calculation_Rate extends Mage_Core_Model_Mysql4_Abstract { - protected $_uniqueFields = array( - array( - 'field' => array('code'), - 'title' => 'Code', - ), - ); - protected function _construct() { $this->_init('tax/tax_calculation_rate', 'tax_calculation_rate_id'); } + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => array('code'), + 'title' => Mage::helper('tax')->__('Code'), + )); + return $this; + } + public function deleteAllRates() { $this->_getWriteAdapter()->delete($this->getMainTable()); diff --git a/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rule.php b/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rule.php index ea266002d6..42ab02fa8c 100644 --- a/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rule.php +++ b/app/code/core/Mage/Tax/Model/Mysql4/Calculation/Rule.php @@ -33,15 +33,22 @@ */ class Mage_Tax_Model_Mysql4_Calculation_Rule extends Mage_Core_Model_Mysql4_Abstract { - protected $_uniqueFields = array( - array( - 'field' => array('code'), - 'title' => 'Code', - ), - ); - protected function _construct() { $this->_init('tax/tax_calculation_rule', 'tax_calculation_rule_id'); } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => array('code'), + 'title' => Mage::helper('tax')->__('Code'), + )); + return $this; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Tax/Model/Mysql4/Class.php b/app/code/core/Mage/Tax/Model/Mysql4/Class.php index 57311d0b07..636aaeb83e 100644 --- a/app/code/core/Mage/Tax/Model/Mysql4/Class.php +++ b/app/code/core/Mage/Tax/Model/Mysql4/Class.php @@ -37,9 +37,19 @@ class Mage_Tax_Model_Mysql4_Class extends Mage_Core_Model_Mysql4_Abstract public function _construct() { $this->_init('tax/tax_class', 'class_id'); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { $this->_uniqueFields = array(array( 'field' => array('class_type', 'class_name'), 'title' => Mage::helper('tax')->__('Error while saving this tax class. Class with the same name '), )); + return $this; } } \ No newline at end of file diff --git a/app/code/core/Mage/Tax/Model/Mysql4/Rate.php b/app/code/core/Mage/Tax/Model/Mysql4/Rate.php index 0f11175842..2449b997f4 100644 --- a/app/code/core/Mage/Tax/Model/Mysql4/Rate.php +++ b/app/code/core/Mage/Tax/Model/Mysql4/Rate.php @@ -34,18 +34,25 @@ class Mage_Tax_Model_Mysql4_Rate extends Mage_Core_Model_Mysql4_Abstract { - protected $_uniqueFields = array( - array( - 'field' => array('tax_country_id', 'tax_region_id', 'tax_postcode'), - 'title' => 'Country/Region/Postal code combination', - ), - ); - protected function _construct() { $this->_init('tax/tax_rate', 'tax_rate_id'); } + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { + $this->_uniqueFields = array(array( + 'field' => array('tax_country_id', 'tax_region_id', 'tax_postcode'), + 'title' => Mage::helper('tax')->__('Country/Region/Postal code combination'), + )); + return $this; + } + // public function loadWithAttributes($rateId = 0) // { // $select = Mage::getModel('tax/rate')->getCollection() diff --git a/app/code/core/Mage/Tax/Model/Mysql4/Rule.php b/app/code/core/Mage/Tax/Model/Mysql4/Rule.php index 89b07d9ed3..ce8b14dfca 100644 --- a/app/code/core/Mage/Tax/Model/Mysql4/Rule.php +++ b/app/code/core/Mage/Tax/Model/Mysql4/Rule.php @@ -36,9 +36,19 @@ class Mage_Tax_Model_Mysql4_Rule extends Mage_Core_Model_Mysql4_Abstract protected function _construct() { $this->_init('tax/tax_rule', 'tax_rule_id'); + } + + /** + * Initialize unique fields + * + * @return Mage_Core_Model_Mysql4_Abstract + */ + protected function _initUniqueFields() + { $this->_uniqueFields = array(array( 'field' => array('tax_customer_class_id', 'tax_product_class_id', 'tax_rate_type_id'), 'title' => Mage::helper('tax')->__('Error while saving this tax rule. This product tax class, customer tax class and tax rate combination'), )); + return $this; } } \ No newline at end of file diff --git a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php index e88d47e17e..fed3b4d576 100644 --- a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php +++ b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php @@ -105,6 +105,16 @@ public function setRequest(Mage_Shipping_Model_Rate_Request $request) $r->setOrigCountry(Mage::getModel('directory/country')->load($origCountry)->getIso2Code()); + if ($request->getOrigRegionCode()) { + $origRegionCode = $request->getOrigRegionCode(); + } else { + $origRegionCode = Mage::getStoreConfig('shipping/origin/region_id', $this->getStore()); + if (is_numeric($origRegionCode)) { + $origRegionCode = Mage::getModel('directory/region')->load($origRegionCode)->getCode(); + } + } + $r->setOrigRegionCode($origRegionCode); + if ($request->getOrigPostcode()) { $r->setOrigPostal($request->getOrigPostcode()); } else { @@ -131,6 +141,8 @@ public function setRequest(Mage_Shipping_Model_Rate_Request $request) $r->setDestCountry(Mage::getModel('directory/country')->load($destCountry)->getIso2Code()); + $r->setDestRegionCode($request->getDestRegionCode()); + if ($request->getDestPostcode()) { $r->setDestPostal($request->getDestPostcode()); } else { @@ -496,8 +508,10 @@ protected function _getXmlQuotes() '14_origCountry' => $r->getOrigCountry(), '15_origPostal' => $r->getOrigPostal(), 'origCity' => $r->getOrigCity(), + 'origRegionCode' => $r->getOrigRegionCode(), '19_destPostal' => $r->getDestPostal(), '22_destCountry' => $r->getDestCountry(), + 'destRegionCode' => $r->getDestRegionCode(), '23_weight' => $r->getWeight(), '47_rate_chart' => $r->getPickup(), '48_container' => $r->getContainer(), @@ -530,18 +544,26 @@ protected function _getXmlQuotes() {$serviceDescription} +XMLRequest; + + if ($this->getConfigFlag('negotiated_active') && ($shipper = $this->getConfigData('shipper_number')) ) { + $xmlRequest .= "{$shipper}"; + } + +$xmlRequest .= <<< XMLRequest
{$params['origCity']} {$params['15_origPostal']} {$params['14_origCountry']} + {$params['origRegionCode']}
-
{$params['19_destPostal']} {$params['22_destCountry']} {$params['49_residential']} + {$params['destRegionCode']} XMLRequest; $xmlRequest .= ($params['49_residential']==='01' ? "{$params['49_residential']}" : ''); @@ -555,6 +577,7 @@ protected function _getXmlQuotes()
{$params['15_origPostal']} {$params['14_origCountry']} + {$params['origRegionCode']}
@@ -565,10 +588,16 @@ protected function _getXmlQuotes() {$params['23_weight']} +XMLRequest; + if ($this->getConfigFlag('negotiated_active')) { + $xmlRequest .= ""; + } +$xmlRequest .= <<< XMLRequest XMLRequest; + try { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); diff --git a/app/code/core/Mage/Usa/etc/config.xml b/app/code/core/Mage/Usa/etc/config.xml index d3c4694d48..3ee75f858f 100644 --- a/app/code/core/Mage/Usa/etc/config.xml +++ b/app/code/core/Mage/Usa/etc/config.xml @@ -171,6 +171,7 @@ 150 F O + 0 diff --git a/app/code/core/Mage/Usa/etc/system.xml b/app/code/core/Mage/Usa/etc/system.xml index f836f7fb0f..60a855749b 100644 --- a/app/code/core/Mage/Usa/etc/system.xml +++ b/app/code/core/Mage/Usa/etc/system.xml @@ -757,6 +757,24 @@ 1 1 + + + select + adminhtml/system_config_source_yesno + 4 + 1 + 1 + 1 + + + + text + 5 + 1 + 1 + 1 + Required for negotiated rates; 6-character UPS + select diff --git a/app/code/core/Mage/Weee/etc/config.xml b/app/code/core/Mage/Weee/etc/config.xml index 91b55e6044..6003479da8 100644 --- a/app/code/core/Mage/Weee/etc/config.xml +++ b/app/code/core/Mage/Weee/etc/config.xml @@ -28,7 +28,7 @@ - 0.12 + 0.13 diff --git a/app/code/core/Mage/Weee/etc/system.xml b/app/code/core/Mage/Weee/etc/system.xml index 14e1f6945d..f9f9f6f6f8 100644 --- a/app/code/core/Mage/Weee/etc/system.xml +++ b/app/code/core/Mage/Weee/etc/system.xml @@ -42,7 +42,7 @@ adminhtml/system_config_source_yesno 1 1 - 0 + 1 0 diff --git a/app/code/core/Mage/Weee/sql/weee_setup/mysql4-upgrade-0.12-0.13.php b/app/code/core/Mage/Weee/sql/weee_setup/mysql4-upgrade-0.12-0.13.php new file mode 100644 index 0000000000..a33cfad4f6 --- /dev/null +++ b/app/code/core/Mage/Weee/sql/weee_setup/mysql4-upgrade-0.12-0.13.php @@ -0,0 +1,45 @@ +getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'weee_tax_applied_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'weee_tax_applied_row_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'weee_tax_applied_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'weee_tax_applied_row_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'base_weee_tax_applied_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'base_weee_tax_applied_row_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'base_weee_tax_applied_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'base_weee_tax_applied_row_amount', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'weee_tax_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'weee_tax_row_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'base_weee_tax_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/quote_item'), 'base_weee_tax_row_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'weee_tax_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'weee_tax_row_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'base_weee_tax_disposition', 'decimal(12,4)'); +$installer->getConnection()->modifyColumn($installer->getTable('sales/order_item'), 'base_weee_tax_row_disposition', 'decimal(12,4)'); diff --git a/app/design/adminhtml/default/default/layout/sales.xml b/app/design/adminhtml/default/default/layout/sales.xml index 69d707374e..707f92771b 100644 --- a/app/design/adminhtml/default/default/layout/sales.xml +++ b/app/design/adminhtml/default/default/layout/sales.xml @@ -40,6 +40,7 @@ defaultadminhtml/sales_order_view_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped @@ -71,6 +72,7 @@ defaultadminhtml/sales_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped @@ -86,6 +88,7 @@ defaultadminhtml/sales_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped @@ -110,6 +113,7 @@ defaultadminhtml/sales_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped invoice @@ -183,6 +187,7 @@ defaultadminhtml/sales_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped @@ -197,6 +202,7 @@ defaultadminhtml/sales_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped @@ -219,6 +225,7 @@ defaultadminhtml/sales_items_renderer_default qtyadminhtml/sales_items_column_qty nameadminhtml/sales_items_column_name + nameadminhtml/sales_items_column_name_groupedgrouped creditmemo diff --git a/app/design/adminhtml/default/default/template/catalog/product/attribute/js.phtml b/app/design/adminhtml/default/default/template/catalog/product/attribute/js.phtml index 066955b01c..a5318a4f76 100644 --- a/app/design/adminhtml/default/default/template/catalog/product/attribute/js.phtml +++ b/app/design/adminhtml/default/default/template/catalog/product/attribute/js.phtml @@ -84,6 +84,19 @@ function bindAttributeInputType() $('is_filterable_in_search').disabled = true; } } + + if ($('frontend_input') && ($('frontend_input').value=='text' || $('frontend_input').value=='textarea')) { + if($('is_html_allowed_on_front')){ + $('is_html_allowed_on_front').disabled = false; + } + } + else { + if($('is_html_allowed_on_front')){ + $('is_html_allowed_on_front').selectedIndex=0; + $('is_html_allowed_on_front').disabled = true; + } + } + switchIsFilterable(); } diff --git a/app/design/adminhtml/default/default/template/catalog/product/tab/inventory.phtml b/app/design/adminhtml/default/default/template/catalog/product/tab/inventory.phtml index 630f01f150..483cf799a7 100644 --- a/app/design/adminhtml/default/default/template/catalog/product/tab/inventory.phtml +++ b/app/design/adminhtml/default/default/template/catalog/product/tab/inventory.phtml @@ -127,8 +127,10 @@ __('[GLOBAL]') ?> @@ -136,6 +138,10 @@ @@ -48,8 +48,8 @@