diff --git a/STATUS.txt b/STATUS.txt deleted file mode 100644 index da8753d8a6..0000000000 --- a/STATUS.txt +++ /dev/null @@ -1,235 +0,0 @@ -This file tracks the status of Magento in the 1.2.x branch. - -FIXED BUGS: - - #6341: Duble subject line in e-mail - #7162: Wrong div class used in 'mini compared products box - #7400: In order view (admin panel) statuses history is different in tabs 'Information' and 'Comments History' - #7423: Cart Rules - Condition Limit Error - #7472: Invoices Report - #7612: Products are displayed in categories they are not included in - #7636: Catalog Product Attribute -> Catalog Input Type -> Dropdown - #8020: Custom catalog attributes created as "price" not displaying correctly - #8159: One page checkout :: doesn't work when inline translation was enabled - #8366: Not possible to translate tax identifier for two store views - #8411: (assign users to role) - #8413: Mass Delete through product update screen does not work - #8439: The checkout-process-page has no pagetitle - #8452: Layout Update Problem (Mage_Core_Model_Config) - #8492: Custom Options percentage Price Type: price rounded off unwantedly - #8503: Bundle products and decimal places in options quantity - #8523: Analytics does not show revenue with configurable products - #8573: lifetime sale incorrect when refunding order with discount code applied - #8595: Many numeric-only searches in Admin/Autocomplete - #8657: Spelling mistake in error msg - "Selected currency code () is not compatabile with PayPal" - #8674: User doesn't subscribed to newsletter. - #8726: Mage_Core_Model_Resource_Setup::getAllAttributeSetIds() belongs in another class - #8762: Filter settings on Sales - Orders not saved - #8770: [MAC, Safari] Unable to create new order in the admin with new customer creating & #8771: [MAC, Safari] Unable to create new order in the admin - #8784: Send product to a friend not under store configuration - #8803: Remove "Yes, No, Any" dropdown filter on Google Base Items - #8893: Trace error after clicking button "Search" - #8896: Applying unable coupons in the empty shopping cart - #8898: Free Shipping: 'For matching items only' cart rule doesn't work with UPS XML type method - #8903: SQL error when sorting Online Customers by Type attribute - #8925: Locale settings not applied after installation - #8928: Unable to create rule for Not logged in customer. - #8943: Import is not working in Safari - #8966: Google Base API - Mass Insert / Update -> CAPTCHA challenge issued - #8968: Category save process is very slow for big catalog - #8974: Content type validation - #8977: Google Analytics reports wrong URL when URL has no query string OR store resides in subfolder... - #8980: Shopping cart discount error - #8992: Mage_Sitemap / Varien_Io_File Bug - Destination not writeable error - #8993: 'No Data Found' for Web Site - #8995: Search does not return correct URL key for additional store view (e.g. 2nd language) - #9109: Sales Report disregards currency rates - #9135: Advanced search by price doesn't work at all - #9139: Admin: menu items not readable when images are disabled - #9166: Unable to add image to all types of product - #9167: AJAX will freeze during 'quick simple product creation' - #9171: Product's table doesn't hide after select products for adding SKU - #9172: Free Shipping: 'For matching items only' cart rule doesn't work with DHL type method - #9173: Calendar's button doesn't work in rules - #9195: [admin - reports - sales - tax] - no data - #9207: Field country is not marked as required - #9208: Unable to add bundle items to bundle product - #9228: cart error after product delete - #9248: On admin page, search function does not work - #9250: Import doesn't work - #9278: Peruvian Nuevo Sol Shown as PES (Peruvian Sol) - #9279: Catalog price rules do not apply when attribute selected has Catalog Input Type for Store Owner ='Multiple Select' - #9296: Customer DOB saved not correctly - #9324: Bad counting products - #9326: Verification in "Contact Us" - #9348: Sales Tax Reports Give False Results (No Results) - #9354: Date Format - #9358: Unable add products to Google Base after filtering - #9359: Google Analytics Revenue Not Working - #9375: Fatal error: Call to a member function getItemsCollection() in Address.php - #9389: SQL-error while creating custom option forproduct - #9390: In "Advanced Search" all text is not displayed - #9392: The field is too broad insert text - #9440: Error subscription to newsletter - #9452: Email Reply Address - #9468: The same group name in an attribute set can be created but it can NOT be saved - #9470: Gift message is not moved over on an order edit - #9479: Reorder / shipping method price doesn't displayed in the "Order Totals" area - #9481: Broken design of the Expiration Date field for CC payment method - #9492: Dataflow export doesn't recognize absolute path - #9493: Buggy Sorting in Manage Tax Rates - Mantis - #9505: Missing echo statement in blank/default/template/shipping/tracking/popup.phtml - #9517: Impossible to set custom admin theme in configuration - #9536: User permissions - Manage Attributes setup - #9540: Google Base Adding SID to urls in multi domain environmnet - #9544: Zero tax total folding/unfolding - #9556: Fatal error during Store View creating - #9566: table rate shipping uploading csv broken - #9596: Store-specific payment method name not in order email - #9611: Not correct message, when you remove the product. - #9619: On front-end Serch is not working - #9622: Unable to add Configurable product to the shopping cart - #9639: Design crushing and SQL-error in admin Dashboard - #9649: Fatal error after reordering orders that contains gift message - #9651: Total revenu always 0 on Google Analytics - #9657: Email to a friend -> User can send more than 5 emails in an hour - #9659: Email to a Friend -> "Add Recipient" button doesn't work - #9660: newsletter subscribers: "Select All" working as select visible - #9665: Export action on Manage Customers and Newsletter Subscribers exports only visible page content - #9683: Orders: Function "Move to shopping cart" does not work. - #9684: Orders: Action "Hold" leads to fatal error. - #9685: No tax data in Reports - Sales - Tax - #9688: Incorrect update qty of the bundle product in the shopping cart - #9695: Manage Attribute Sets: Does not create new group - #9696: Unable to assign existing products to any web sites - #9697: Enable/Disable status works incorrect - #9699: Blank theme :: Pay Pal's Review page :: button doesn't work + products names doesn't shows - #9700: Search synonym does not work - #9707: Customers->Manage Customers->*user* - orders with DP doesn't displayed - #9708: Manage Attribute Sets: Error when creating and deleting groups - #9711: Google checkout are returning Comments History with HTML tags - #9712: Parameter "Maximum Package Weight ", leads to error 404. - #9716: Creating of Bundle Product -> SQLSTATE error - #9717: "Shipment email not sent" status does not change when you send a letter. - #9738: Downloadable product + Tax Rules = error 404 - #9742: Notice on products sitemap - #9743: Wrong count of related products, upsells and crosssells in admin - #9760: Free Shipping : 'For matching items only' incorrect works with USPS + FedEx method - - Fixed Bundle with custom options final price calculation - Fixed CatalogSearch events - Fixed EAV attribute save bug - Fixed EAV use joinAttribute method in conditions addAttributeToFilter method - Fixed MySQL 4.1 bug with NO_AUTO_VALUE_ON_ZERO mode and ALTER TABLE command - Fixed Online customers - Fixed REQUEST_URI for GA - Fixed Varieb_Db_Select::resetJoinLeft (find tables in conditions) - Fixed Varien_Data_Collection_Db::getSelectCountSql - Fixed _joinAttributeToSelect for abstract catalog collection (using attribute in select result and filter condition) - Fixed adding product to shopping cart - Fixed admin notification window block ACL - Fixed autobinding unescaped SQL variables - Fixed bug with categories tree interface after switching store - Fixed catalog price rule validation (case if rule is wrong: need to remove existing generated price changes) - Fixed cleanup call in catalog index - Fixed command line installation with index.php - Fixed customer and product grid performance in admin - Fixed cybersource payment for virtual order - Fixed generate xml string for config cache (0 value was ignored) - Fixed getting table names during installation - Fixed refreshing catalog rewrites after save catalog configuration and url resource model for getting static attributes value - Fixed reindex prices after saving catalog configuration section - Fixed reorder of grouped products - Fixed tax data joining (join tax classes just for special cases) - Fixed tierprice rendering for different product types - Fixed translations in permissions titles - Fixed PayPalUk Redirect url wrong type - Fixed create unique index in install - Fixed catalog product index - Fixed session validate and re-validate sent cookie - Added customer group id for price condition in advanced search, replaced 'or' on 'in' for price layered navigation condition - Fixed Google Checkout displaying of button when it is disabled - Fixed PayPal css/html - Fixed USPS Express Mail free shipping - Fixed applying catalog price rule with empty from date - Fixed catalog search in multistore setup - Fixed checkout crosssell block: when there are no products in cart, the filter was working incorrect - Fixed displaying of shipping name in orders history (if there are only downloadable or virtual products in orders) - Fixed displaying of uploaded files; changed redirecting after log in action while trying to download is not shearable link - Fixed downloadable products renderers for multishipping checkout - Fixed fixed amount product taxes - Fixed foreign key upgrade in core DB upgrade 0.8.10-0.8.11 - Fixed getting massaction_key from POST - Fixed issues with applying coupon codes - Fixed js error in manage rules interface - Fixed downlodable products admin interface in IE - Updated Flex-Javascript bridge, fixed IE/Safari issues - Added applying frontend translation when sending emails from admin - Fixed recreating new instances of Flex-Ajax bridges on hide/display flash objects - Fixed category selection in URL-rewrites management form - Fixed fatal error in downloader - Fixed command line installer - Fixed fixed amount product taxes for products without discounts - Fixed hiding of flex uploader after removing row of downloadbale sample|link - Fixed FPT editing for bundle products - Fixed generatting filenames for create custom extensions interface in Windows - Fixed calling final step of dataflow import - Fixed bundle js for calculating children with FPT - Fixed grid to select products SKU in price rules in IE6/7 - Fixed duplicate diplaying prices in bundle product price ranges - Fixed displaying category image in blank theme - Fixed cross sells issues on frontend - Fixed serialized data checking when reading FPT data from order item - Fixed checking order status for downloadable products to become available - Fixed #9757: Paypal Payment Pro Direct not visible - - -CHANGES: - - Upgraded ZF library to version 1.7.2 (r13445 from http://framework.zend.com/svn/framework/standard/trunk/library/Zend) - Added downlodable product type - Implented fulltext search - Added layred navigation to search results - Added option to disable google checkout for selected products - Added fixed amount product taxes - Moved information about product visibility to catalog_product_category_index - Changed Mage::app()->isInstalled to Mage::isInstalled to prevent db upgrades - Removed session ID encryption - Changed catalogindex price to store prices per website - Changed modules dependencies: Mage_Shipment and Mage_Payment now depend on Mage_Catalog, Mage_GoogleCheckout depends on Mage_Payment, Mage_CatalogInventory doesn't depend on Mage_Sales - Added "Add Field with URL" option for product export - Added configuration field to include some additional output on the HTML page before head closing tag - Added Spanish - Costa Rica locale - Extended Mage_Adminhtml_Controller_Action->_prepareDownloadResponse() to avoid copy-pasting in admin controllers - Removed adding ?ft parameter in admin to prevent the issues it caused - Made category URL suffix to be .html by default - Added setting body class on tag for admin pages (similar to frontend - full action name separated by dashes) - Added local.xml.additional file to list/explain possible additional configuration nodes/values for local.xml - Added currency rates into order info box in backend - Added ability to display form buttons on bottom in admin container widget, fixed button levels - Added ability to use memcached to store sessions and to be used as cache backend - Added additional permissions to WS api, fixed bugs with custom permissions - Added option "Disable Guest Checkout if cart contains downloadable items" - Removed store view name from page titles on catalog pages - Commented out required_once in lib/Zend - Added permanent redirect option for URL Rewrites - Added ability to display custom child blocks on success page - Added collecting results of non-asynchronous event handlers in adminhtml/events.js - Added confirmation dialog before switching from 'Images' and 'Downloadable Information' tabs in product edit form to prevent losing selected files - Added firing 'tabChangeBefore' event before switching tab in adminhtml/tabs.js (event handler can return 'cannotchange' to prevent switching) - Rewritten URL Rewrite Management interface - Added checking of file size before uploading - Added downloadable order items pdf renderers - Added downloadable products templates to blank, iphone, modern theme - Added flex uploader source code - Added support of allowed tags into Mage_Core_Helper_Abstract->htmlEscape() method - Added unquoting autobinded quoted values in SQL adapter - Added validation for selected, but not uploaded files - Updated downloader version to 1.2.0 - Changed app/etc/local.xml* license to AFL - Added autoguessing mime type of a file if mimemagic extension is not installed - - -NOTES: - - Please check the SVN log if you are looking for the changes that are not listed here. diff --git a/app/Mage.php b/app/Mage.php index 9b39268796..6702f50c6a 100644 --- a/app/Mage.php +++ b/app/Mage.php @@ -31,8 +31,8 @@ Mage::register('original_include_path', get_include_path()); if (defined('COMPILER_INCLUDE_PATH')) { - $app_path = COMPILER_INCLUDE_PATH; - set_include_path($app_path . PS . Mage::registry('original_include_path')); + $appPath = COMPILER_INCLUDE_PATH; + set_include_path($appPath . PS . Mage::registry('original_include_path')); include_once "Mage_Core_functions.php"; include_once "Varien_Autoload.php"; } else { @@ -44,8 +44,8 @@ $paths[] = BP . DS . 'app' . DS . 'code' . DS . 'core'; $paths[] = BP . DS . 'lib'; - $app_path = implode(PS, $paths); - set_include_path($app_path . PS . Mage::registry('original_include_path')); + $appPath = implode(PS, $paths); + set_include_path($appPath . PS . Mage::registry('original_include_path')); include_once "Mage/Core/functions.php"; include_once "Varien/Autoload.php"; } @@ -57,13 +57,21 @@ * * @author Magento Core Team */ -final class Mage { +final class Mage +{ /** * Registry collection * * @var array */ - static private $_registry = array(); + static private $_registry = array(); + + /** + * Application root absolute path + * + * @var string + */ + static private $_appRoot; /** * Application model @@ -72,19 +80,63 @@ final class Mage { */ static private $_app; - static private $_useCache = array(); + /** + * Config Model + * + * @var Mage_Core_Model_Config + */ + static private $_config; + + /** + * Event Collection Object + * + * @var Varien_Event_Collection + */ + static private $_events; + /** + * Object cache instance + * + * @var Varien_Object_Cache + */ static private $_objects; - static private $_isDownloader = false; + /** + * Is downloader flag + * + * @var bool + */ + static private $_isDownloader = false; - static private $_isDeveloperMode = false; + /** + * Is developer mode flag + * + * @var bool + */ + static private $_isDeveloperMode = false; - public static $headersSentThrowsException = true; + /** + * Is allow throw Exception about headers already sent + * + * @var bool + */ + public static $headersSentThrowsException = true; + /** + * Is installed flag + * + * @var bool + */ + static private $_isInstalled; + + /** + * Retrieve current Magento version + * + * @return string + */ public static function getVersion() { - return '1.3.2.4'; + return '1.4.0.0-alpha1'; } /** @@ -93,10 +145,11 @@ public static function getVersion() */ public static function reset() { - self::$_registry = array(); - self::$_app = null; - self::$_useCache = array(); - self::$_objects = null; + self::$_registry = array(); + self::$_app = null; + self::$_config = null; + self::$_events = null; + self::$_objects = null; self::$_isDownloader = false; self::$_isDeveloperMode = false; // do not reset $headersSentThrowsException @@ -108,22 +161,28 @@ public static function reset() * @param string $key * @param mixed $value * @param bool $graceful + * @throws Mage_Core_Exception */ public static function register($key, $value, $graceful = false) { - if(isset(self::$_registry[$key])) { + if (isset(self::$_registry[$key])) { if ($graceful) { return; } - Mage::throwException('Mage registry key "'.$key.'" already exists'); + self::throwException('Mage registry key "'.$key.'" already exists'); } self::$_registry[$key] = $value; } + /** + * Unregister a variable from register by key + * + * @param string $key + */ public static function unregister($key) { if (isset(self::$_registry[$key])) { - if (is_object(self::$_registry[$key]) && (method_exists(self::$_registry[$key],'__destruct'))) { + if (is_object(self::$_registry[$key]) && (method_exists(self::$_registry[$key], '__destruct'))) { self::$_registry[$key]->__destruct(); } unset(self::$_registry[$key]); @@ -148,13 +207,15 @@ public static function registry($key) * Set application root absolute path * * @param string $appRoot + * @throws Mage_Core_Exception */ - public static function setRoot($appRoot='') + public static function setRoot($appRoot = '') { - if (self::registry('appRoot')) { + if (self::$_appRoot) { return ; } - if (''===$appRoot) { + + if ('' === $appRoot) { // automagically find application root by dirname of Mage.php $appRoot = dirname(__FILE__); } @@ -162,21 +223,30 @@ public static function setRoot($appRoot='') $appRoot = realpath($appRoot); if (is_dir($appRoot) and is_readable($appRoot)) { - Mage::register('appRoot', $appRoot); + self::$_appRoot = $appRoot; } else { - Mage::throwException($appRoot.' is not a directory or not readable by this user'); + self::throwException($appRoot . ' is not a directory or not readable by this user'); } } /** - * Get application root absolute path + * Retrieve application root absolute path * * @return string */ - public static function getRoot() { - return Mage::registry('appRoot'); + return self::$_appRoot; + } + + /** + * Retrieve Events Collection + * + * @return Varien_Event_Collection $collection + */ + public static function getEvents() + { + return self::$_events; } /** @@ -185,7 +255,7 @@ public static function getRoot() * @param string $key optional, if specified will load this key * @return Varien_Object_Cache */ - public static function objects($key=null) + public static function objects($key = null) { if (!self::$_objects) { self::$_objects = new Varien_Object_Cache; @@ -200,26 +270,48 @@ public static function objects($key=null) /** * Retrieve application root absolute path * + * @param string $type * @return string */ - public static function getBaseDir($type='base') + public static function getBaseDir($type = 'base') { - return Mage::getConfig()->getOptions()->getDir($type); + return self::getConfig()->getOptions()->getDir($type); } + /** + * Retrieve module absolute path by directory type + * + * @param string $type + * @param string $moduleName + * @return string + */ public static function getModuleDir($type, $moduleName) { - return Mage::getConfig()->getModuleDir($type, $moduleName); + return self::getConfig()->getModuleDir($type, $moduleName); } - public static function getStoreConfig($path, $id=null) + /** + * Retrieve config value for store by path + * + * @param string $path + * @param mixed $store + * @return mixed + */ + public static function getStoreConfig($path, $store = null) { - return self::app()->getStore($id)->getConfig($path); + return self::app()->getStore($store)->getConfig($path); } - public static function getStoreConfigFlag($path, $id=null) + /** + * Retrieve config flag for store by path + * + * @param string $path + * @param mixed $store + * @return bool + */ + public static function getStoreConfigFlag($path, $store = null) { - $flag = strtolower(Mage::getStoreConfig($path, $id)); + $flag = strtolower(self::getStoreConfig($path, $store)); if (!empty($flag) && 'false'!==$flag && '0'!==$flag) { return true; } else { @@ -233,9 +325,9 @@ public static function getStoreConfigFlag($path, $id=null) * @param string $type * @return string */ - public static function getBaseUrl($type=Mage_Core_Model_Store::URL_TYPE_LINK, $secure=null) + public static function getBaseUrl($type = Mage_Core_Model_Store::URL_TYPE_LINK, $secure = null) { - return Mage::app()->getStore()->getBaseUrl($type, $secure); + return self::app()->getStore()->getBaseUrl($type, $secure); } /** @@ -245,9 +337,9 @@ public static function getBaseUrl($type=Mage_Core_Model_Store::URL_TYPE_LINK, $s * @param array $params * @return string */ - public static function getUrl($route='', $params=array()) + public static function getUrl($route = '', $params = array()) { - return Mage::getModel('core/url')->getUrl($route, $params); + return self::getModel('core/url')->getUrl($route, $params); } /** @@ -257,17 +349,17 @@ public static function getUrl($route='', $params=array()) */ public static function getDesign() { - return Mage::getSingleton('core/design_package'); + return self::getSingleton('core/design_package'); } /** - * Get a config object + * Retrieve a config instance * * @return Mage_Core_Model_Config */ public static function getConfig() { - return Mage::registry('config'); + return self::$_config; } /** @@ -278,14 +370,14 @@ public static function getConfig() * @param array $arguments * @param string $observerName */ - public static function addObserver($eventName, $callback, $data=array(), $observerName='', $observerClass='') + public static function addObserver($eventName, $callback, $data = array(), $observerName = '', $observerClass = '') { - if ($observerClass=='') { + if ($observerClass == '') { $observerClass = 'Varien_Event_Observer'; } $observer = new $observerClass(); $observer->setName($observerName)->addData($data)->setEventName($eventName)->setCallback($callback); - return Mage::registry('events')->addObserver($observer); + return self::getEvents()->addObserver($observer); } /** @@ -296,12 +388,13 @@ public static function addObserver($eventName, $callback, $data=array(), $observ * * @param string $name * @param array $args + * @return Mage_Core_Model_App */ - public static function dispatchEvent($name, array $data=array()) + public static function dispatchEvent($name, array $data = array()) { Varien_Profiler::start('DISPATCH EVENT:'.$name); - $result = Mage::app()->dispatchEvent($name, $data); - #$result = Mage::registry('events')->dispatch($name, $data); + $result = self::app()->dispatchEvent($name, $data); + #$result = self::registry('events')->dispatch($name, $data); Varien_Profiler::stop('DISPATCH EVENT:'.$name); return $result; } @@ -314,9 +407,9 @@ public static function dispatchEvent($name, array $data=array()) * @param array $arguments * @return Mage_Core_Model_Abstract */ - public static function getModel($modelClass='', $arguments=array()) + public static function getModel($modelClass = '', $arguments = array()) { - return Mage::getConfig()->getModelInstance($modelClass, $arguments); + return self::getConfig()->getModelInstance($modelClass, $arguments); } /** @@ -329,10 +422,10 @@ public static function getModel($modelClass='', $arguments=array()) public static function getSingleton($modelClass='', array $arguments=array()) { $registryKey = '_singleton/'.$modelClass; - if (!Mage::registry($registryKey)) { - Mage::register($registryKey, Mage::getModel($modelClass, $arguments)); + if (!self::registry($registryKey)) { + self::register($registryKey, self::getModel($modelClass, $arguments)); } - return Mage::registry($registryKey); + return self::registry($registryKey); } /** @@ -342,9 +435,23 @@ public static function getSingleton($modelClass='', array $arguments=array()) * @param array $arguments * @return Object */ - public static function getResourceModel($modelClass, $arguments=array()) + public static function getResourceModel($modelClass, $arguments = array()) { - return Mage::getConfig()->getResourceModelInstance($modelClass, $arguments); + return self::getConfig()->getResourceModelInstance($modelClass, $arguments); + } + + /** + * Retrieve Controller instance by ClassName + * + * @param string $class + * @param Mage_Core_Controller_Request_Http $request + * @param Mage_Core_Controller_Response_Http $response + * @param array $invokeArgs + * @return Mage_Core_Controller_Front_Action + */ + public static function getControllerInstance($class, $request, $response, array $invokeArgs = array()) + { + return new $class($request, $response, $invokeArgs); } /** @@ -354,36 +461,45 @@ public static function getResourceModel($modelClass, $arguments=array()) * @param array $arguments * @return object */ - public static function getResourceSingleton($modelClass='', array $arguments=array()) + public static function getResourceSingleton($modelClass = '', array $arguments = array()) { $registryKey = '_resource_singleton/'.$modelClass; - if (!Mage::registry($registryKey)) { - Mage::register($registryKey, Mage::getResourceModel($modelClass, $arguments)); + if (!self::registry($registryKey)) { + self::register($registryKey, self::getResourceModel($modelClass, $arguments)); } - return Mage::registry($registryKey); + return self::registry($registryKey); } /** - * Deprecated, use Mage::helper() + * Deprecated, use self::helper() * * @param string $type * @return object */ public static function getBlockSingleton($type) { - $action = Mage::app()->getFrontController()->getAction(); + $action = self::app()->getFrontController()->getAction(); return $action ? $action->getLayout()->getBlockSingleton($type) : false; } /** * Retrieve helper object * - * @param helper name $name - * @return Mage_Core_Helper_Abstract + * @param string $name the helper name + * @return Mage_Core_Helper_Abstract */ public static function helper($name) { - return Mage::app()->getHelper($name); + if (strpos($name, '/') === false) { + $name .= '/data'; + } + + $registryKey = '_helper/' . $name; + if (!self::registry($registryKey)) { + $helperClass = self::getConfig()->getHelperClassName($name); + self::register($registryKey, new $helperClass); + } + return self::registry($registryKey); } /** @@ -392,16 +508,23 @@ public static function helper($name) * @param string $module * @param string $message * @param integer $code + * @return Mage_Core_Exception */ - public static function exception($module='Mage_Core', $message='', $code=0) + public static function exception($module = 'Mage_Core', $message = '', $code = 0) { $className = $module.'_Exception'; return new $className($message, $code); } - public static function throwException($message, $messageStorage=null) + /** + * Throw Exception + * + * @param string $message + * @param string $messageStorage + */ + public static function throwException($message, $messageStorage = null) { - if ($messageStorage && ($storage = Mage::getSingleton($messageStorage))) { + if ($messageStorage && ($storage = self::getSingleton($messageStorage))) { $storage->addError($message); } throw new Mage_Core_Exception($message); @@ -410,29 +533,29 @@ public static function throwException($message, $messageStorage=null) /** * Initialize and retrieve application * - * @param string $code - * @param string $type - * @param string|array $options - * @return Mage_Core_Model_App + * @param string $code + * @param string $type + * @param string|array $options + * @return Mage_Core_Model_App */ - public static function app($code = '', $type = 'store', $options=array()) + public static function app($code = '', $type = 'store', $options = array()) { if (null === self::$_app) { - Varien_Profiler::start('mage::app::construct'); + Varien_Profiler::start('self::app::construct'); self::$_app = new Mage_Core_Model_App(); - Varien_Profiler::stop('mage::app::construct'); + Varien_Profiler::stop('self::app::construct'); - Mage::setRoot(); - Mage::register('events', new Varien_Event_Collection()); + self::setRoot(); + self::$_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('self::app::register_config'); + self::$_config = new Mage_Core_Model_Config(); + Varien_Profiler::stop('self::app::register_config'); - Varien_Profiler::start('mage::app::init'); + Varien_Profiler::start('self::app::init'); self::$_app->init($code, $type, $options); - Varien_Profiler::stop('mage::app::init'); + Varien_Profiler::stop('self::app::init'); self::$_app->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS); } @@ -451,26 +574,25 @@ public static function run($code = '', $type = 'store', $options=array()) try { Varien_Profiler::start('mage'); - Varien_Profiler::start('mage::app'); + Varien_Profiler::start('self::app'); self::app($code, $type, $options); - Varien_Profiler::stop('mage::app'); + Varien_Profiler::stop('self::app'); - Varien_Profiler::start('mage::dispatch'); + Varien_Profiler::start('self::dispatch'); self::app()->getFrontController()->dispatch(); - Varien_Profiler::stop('mage::dispatch'); + Varien_Profiler::stop('self::dispatch'); Varien_Profiler::stop('mage'); } catch (Mage_Core_Model_Session_Exception $e) { - header('Location: ' . Mage::getBaseUrl()); + header('Location: ' . self::getBaseUrl()); die(); } catch (Mage_Core_Model_Store_Exception $e) { $baseUrl = self::getScriptSystemUrl('404'); if (!headers_sent()) { header('Location: ' . rtrim($baseUrl, '/').'/404/'); - } - else { + } else { print ''; @@ -486,8 +608,7 @@ public static function run($code = '', $type = 'store', $options=array()) self::dispatchEvent('mage_run_exception', array('exception' => $e)); if (!headers_sent()) { header('Location:'.self::getUrl('install')); - } - else { + } else { self::printException($e); } } @@ -505,8 +626,7 @@ public static function run($code = '', $type = 'store', $options=array()) */ public static function isInstalled($options = array()) { - $isInstalled = self::registry('_is_installed'); - if ($isInstalled === null) { + if (self::$_isInstalled === null) { self::setRoot(); if (is_string($options)) { @@ -520,18 +640,17 @@ public static function isInstalled($options = array()) } $localConfigFile = self::getRoot() . DS . $etcDir . DS . 'local.xml'; - $isInstalled = false; + self::$_isInstalled = false; if (is_readable($localConfigFile)) { $localConfig = simplexml_load_file($localConfigFile); date_default_timezone_set('UTC'); if (($date = $localConfig->global->install->date) && strtotime($date)) { - $isInstalled = true; + self::$_isInstalled = true; } } - self::register('_is_installed', $isInstalled); } - return $isInstalled; + return self::$_isInstalled; } /** @@ -541,12 +660,12 @@ public static function isInstalled($options = array()) * @param integer $level * @param string $file */ - public static function log($message, $level=null, $file = '') + public static function log($message, $level = null, $file = '') { if (!self::getConfig()) { return; } - if (!Mage::getStoreConfig('dev/log/active')) { + if (!self::getStoreConfig('dev/log/active')) { return; } @@ -554,21 +673,20 @@ public static function log($message, $level=null, $file = '') $level = is_null($level) ? Zend_Log::DEBUG : $level; if (empty($file)) { - $file = Mage::getStoreConfig('dev/log/file'); + $file = self::getStoreConfig('dev/log/file'); $file = empty($file) ? 'system.log' : $file; } try { if (!isset($loggers[$file])) { - $logFile = Mage::getBaseDir('var').DS.'log'.DS.$file; - $logDir = Mage::getBaseDir('var').DS.'log'; + $logFile = self::getBaseDir('var') . DS . 'log' . DS . $file; - if (!is_dir(Mage::getBaseDir('var').DS.'log')) { - mkdir(Mage::getBaseDir('var').DS.'log', 0777); + if (!is_dir(self::getBaseDir('var').DS.'log')) { + mkdir(self::getBaseDir('var').DS.'log', 0777); } if (!file_exists($logFile)) { - file_put_contents($logFile,''); + file_put_contents($logFile, ''); chmod($logFile, 0777); } @@ -585,18 +703,22 @@ public static function log($message, $level=null, $file = '') $loggers[$file]->log($message, $level); } - catch (Exception $e){ - + catch (Exception $e) { } } + /** + * Write exception to log + * + * @param Exception $e + */ public static function logException(Exception $e) { if (!self::getConfig()) { return; } - $file = Mage::getStoreConfig('dev/log/exception_file'); - self::log("\n".(string)$e, Zend_Log::ERR, $file); + $file = self::getStoreConfig('dev/log/exception_file'); + self::log("\n" . $e->__toString(), Zend_Log::ERR, $file); } /** @@ -638,10 +760,9 @@ public static function printException(Exception $e, $extra = '') print $e->getMessage() . "\n\n"; print $e->getTraceAsString(); print ''; - } - else { + } else { self::getConfig()->createDirIfNotExists(self::getBaseDir('var') . DS . 'report'); - $reportId = intval(microtime(true) * rand(100, 1000)); + $reportId = abs(intval(microtime(true) * rand(100, 1000))); $reportFile = self::getBaseDir('var') . DS . 'report' . DS . $reportId; $reportData = array( !empty($extra) ? $extra . "\n\n" : '' . $e->getMessage(), @@ -656,7 +777,8 @@ public static function printException(Exception $e, $extra = '') try { $storeCode = self::app()->getStore()->getCode(); } - catch (Exception $e) {} + catch (Exception $e) { + } $baseUrl = self::getScriptSystemUrl('report', true); $reportUrl = rtrim($baseUrl, '/') . '/report/?id=' @@ -664,8 +786,7 @@ public static function printException(Exception $e, $extra = '') if (!headers_sent()) { header('Location: ' . $reportUrl); - } - else { + } else { print ''; @@ -693,8 +814,8 @@ public static function getScriptSystemUrl($folder, $exitIfNot = false) $baseUrl = str_replace(DS, '/', $runDirUrl); } else { $runDirUrlArray = explode('/', $runDirUrl); - $runDirArray = explode('/', $runDir); - $count = count($runDirArray); + $runDirArray = explode('/', $runDir); + $count = count($runDirArray); for ($i=0; $i < $count; $i++) { array_pop($runDirUrlArray); @@ -706,7 +827,7 @@ public static function getScriptSystemUrl($folder, $exitIfNot = false) if (is_dir($_runDir.$folder)) { $_runDirUrl = implode('/', $runDirUrlArray); - $baseUrl = str_replace(DS, '/', $_runDirUrl); + $baseUrl = str_replace(DS, '/', $_runDirUrl); break; } } @@ -725,7 +846,12 @@ public static function getScriptSystemUrl($folder, $exitIfNot = false) return $baseUrl; } - public static function setIsDownloader($flag=true) + /** + * Set is downloader flag + * + * @param bool $flag + */ + public static function setIsDownloader($flag = true) { self::$_isDownloader = $flag; } diff --git a/app/code/core/Mage/Admin/Model/User.php b/app/code/core/Mage/Admin/Model/User.php index 1c7d85ae06..aee3ccb533 100644 --- a/app/code/core/Mage/Admin/Model/User.php +++ b/app/code/core/Mage/Admin/Model/User.php @@ -36,6 +36,7 @@ class Mage_Admin_Model_User extends Mage_Core_Model_Abstract const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'admin/emails/forgot_email_template'; const XML_PATH_FORGOT_EMAIL_IDENTITY = 'admin/emails/forgot_email_identity'; const XML_PATH_STARTUP_PAGE = 'admin/startup/page'; + const MIN_PASSWORD_LENGTH = 7; protected $_eventPrefix = 'admin_user'; @@ -371,7 +372,7 @@ public function getStartupPageUrl() $startupPage = Mage::getStoreConfig(self::XML_PATH_STARTUP_PAGE); $aclResource = 'admin/' . $startupPage; if (Mage::getSingleton('admin/session')->isAllowed($aclResource)) { - $nodePath = 'adminhtml/menu/' . join('/children/', split('/', $startupPage)) . '/action'; + $nodePath = 'adminhtml/menu/' . join('/children/', explode('/', $startupPage)) . '/action'; if ($url = Mage::getConfig()->getNode($nodePath)) { return $url; } @@ -379,4 +380,54 @@ public function getStartupPageUrl() return $this->findFirstAvailableMenu(); } + /** + * Validate user attribute values. + * Returns TRUE or array of errors. + * + * @return mixed + */ + public function validate() + { + $errors = array(); + + if (!Zend_Validate::is($this->getUsername(), 'NotEmpty')) { + $errors[] = Mage::helper('adminhtml')->__('User Name is required field.'); + } + + if (!Zend_Validate::is($this->getFirstname(), 'NotEmpty')) { + $errors[] = Mage::helper('adminhtml')->__('First Name is required field.'); + } + + if (!Zend_Validate::is($this->getLastname(), 'NotEmpty')) { + $errors[] = Mage::helper('adminhtml')->__('Last Name is required field.'); + } + + if (!Zend_Validate::is($this->getEmail(), 'EmailAddress')) { + $errors[] = Mage::helper('adminhtml')->__('Please enter a valid email.'); + } + + if ($this->hasNewPassword()) { + if (Mage::helper('core/string')->strlen($this->getNewPassword()) < self::MIN_PASSWORD_LENGTH) { + $errors[] = Mage::helper('adminhtml')->__('Password must be at least of %d characters.', self::MIN_PASSWORD_LENGTH); + } + + if (!preg_match('/[a-z]/iu', $this->getNewPassword()) || !preg_match('/[0-9]/u', $this->getNewPassword())) { + $errors[] = Mage::helper('adminhtml')->__('Password must include both numeric and alphabetic characters.'); + } + + if ($this->hasPasswordConfirmation() && $this->getNewPassword() != $this->getPasswordConfirmation()) { + $errors[] = Mage::helper('adminhtml')->__('Password confirmation must be same as password.'); + } + } + + if ($this->userExists()) { + $errors[] = Mage::helper('adminhtml')->__('User with the same User Name or Email aleady exists'); + } + + if (empty($errors)) { + return true; + } + return $errors; + } + } diff --git a/app/code/core/Mage/Admin/etc/config.xml b/app/code/core/Mage/Admin/etc/config.xml index b3ff73d82a..2a7e371ce3 100644 --- a/app/code/core/Mage/Admin/etc/config.xml +++ b/app/code/core/Mage/Admin/etc/config.xml @@ -28,7 +28,7 @@ - 0.7.1 + 0.7.2 diff --git a/app/code/core/Mage/Admin/sql/admin_setup/mysql4-install-0.7.0.php b/app/code/core/Mage/Admin/sql/admin_setup/mysql4-install-0.7.0.php index 00302615f2..5c5462f388 100644 --- a/app/code/core/Mage/Admin/sql/admin_setup/mysql4-install-0.7.0.php +++ b/app/code/core/Mage/Admin/sql/admin_setup/mysql4-install-0.7.0.php @@ -88,9 +88,6 @@ `is_active` tinyint(1) NOT NULL default '1', PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Users'; - -insert into {$this->getTable('admin_user')}(`user_id`,`firstname`,`lastname`,`email`,`username`,`password`,`created`,`modified`,`logdate`,`lognum`,`reload_acl_flag`,`is_active`) values (1,'admin','admin','admin@your_domain.com','admin','4297f44b13955235245b2497399d7a93',NOW(),NOW(),NOW(),1,0,1); - - "); +"); $installer->endSetup(); diff --git a/app/code/core/Mage/Admin/sql/admin_setup/mysql4-upgrade-0.7.1-0.7.2.php b/app/code/core/Mage/Admin/sql/admin_setup/mysql4-upgrade-0.7.1-0.7.2.php new file mode 100644 index 0000000000..cf3c7151dc --- /dev/null +++ b/app/code/core/Mage/Admin/sql/admin_setup/mysql4-upgrade-0.7.1-0.7.2.php @@ -0,0 +1,46 @@ +startSetup(); + +$tableAdmins = $installer->getTable('admin/user'); + +// delete admin username duplicates +$duplicatedUsers = $installer->getConnection()->fetchPairs(" +SELECT user_id, username FROM {$tableAdmins} GROUP by username HAVING COUNT(user_id) > 1 +"); +$installer->run("DELETE FROM {$tableAdmins} WHERE username " + . $installer->getConnection()->quoteInto('IN (?) ', array_values($duplicatedUsers)) + . 'AND user_id ' . $installer->getConnection()->quoteInto('NOT IN (?) ', array_keys($duplicatedUsers)) +); + +// add unique key to username field +$installer->getConnection()->addKey($tableAdmins, 'UNQ_ADMIN_USER_USERNAME', 'username', 'unique'); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Adminhtml/Block/Api/Role/Grid/User.php b/app/code/core/Mage/Adminhtml/Block/Api/Role/Grid/User.php index e4ae7ae1ae..268bb661e6 100644 --- a/app/code/core/Mage/Adminhtml/Block/Api/Role/Grid/User.php +++ b/app/code/core/Mage/Adminhtml/Block/Api/Role/Grid/User.php @@ -165,7 +165,7 @@ protected function _getUsers($json=false) if ( $json ) { $jsonUsers = Array(); foreach($users as $usrid) $jsonUsers[$usrid] = 0; - return Zend_Json::encode((object)$jsonUsers); + return Mage::helper('core')->jsonEncode((object)$jsonUsers); } else { return array_values($users); } diff --git a/app/code/core/Mage/Adminhtml/Block/Api/Tab/Rolesedit.php b/app/code/core/Mage/Adminhtml/Block/Api/Tab/Rolesedit.php index da2bcbd835..522717b75e 100644 --- a/app/code/core/Mage/Adminhtml/Block/Api/Tab/Rolesedit.php +++ b/app/code/core/Mage/Adminhtml/Block/Api/Tab/Rolesedit.php @@ -63,7 +63,7 @@ public function getResTreeJson() $rootArray = $this->_getNodeJson($resources,1); - $json = Zend_Json::encode(isset($rootArray['children']) ? $rootArray['children'] : array()); + $json = Mage::helper('core')->jsonEncode(isset($rootArray['children']) ? $rootArray['children'] : array()); return $json; } diff --git a/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Main.php b/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Main.php index b02f24424c..54f186078e 100644 --- a/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Main.php +++ b/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Main.php @@ -94,7 +94,7 @@ protected function _prepareForm() 'label' => Mage::helper('adminhtml')->__('New Api Key'), 'id' => 'new_pass', 'title' => Mage::helper('adminhtml')->__('New Api Key'), - 'class' => 'input-text validate-password', + 'class' => 'input-text validate-admin-password', )); $fieldset->addField('confirmation', 'password', array( @@ -110,7 +110,7 @@ protected function _prepareForm() 'label' => Mage::helper('adminhtml')->__('Api Key'), 'id' => 'customer_pass', 'title' => Mage::helper('adminhtml')->__('Api Key'), - 'class' => 'input-text required-entry validate-password', + 'class' => 'input-text required-entry validate-admin-password', 'required' => true, )); $fieldset->addField('confirmation', 'password', array( diff --git a/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Roles.php b/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Roles.php index cfd1335730..9e1e5f65a1 100644 --- a/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Roles.php +++ b/app/code/core/Mage/Adminhtml/Block/Api/User/Edit/Tab/Roles.php @@ -110,7 +110,7 @@ protected function _getSelectedRoles($json=false) if ($json) { $jsonRoles = Array(); foreach($uRoles as $urid) $jsonRoles[$urid] = 0; - return Zend_Json::encode((object)$jsonRoles); + return Mage::helper('core')->jsonEncode((object)$jsonRoles); } else { return $uRoles; } diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Abstract.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Abstract.php index 40e90c125f..51c985d3e8 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Abstract.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Abstract.php @@ -39,6 +39,11 @@ public function __construct() parent::__construct(); } + /** + * Retrieve current category instance + * + * @return Mage_Catalog_Model_Category + */ public function getCategory() { return Mage::registry('category'); diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit/Form.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit/Form.php index 008d765080..c79ba06c52 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit/Form.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Edit/Form.php @@ -75,11 +75,12 @@ protected function _prepareLayout() } if (!$this->getCategory()->isReadonly()) { + $resetPath = $this->getCategory()->getId() ? '*/*/edit' : '*/*/add'; $this->setChild('reset_button', $this->getLayout()->createBlock('adminhtml/widget_button') ->setData(array( 'label' => Mage::helper('catalog')->__('Reset'), - 'onclick' => "categoryReset('".$this->getUrl('*/*/edit', array('_current'=>true))."',true)" + 'onclick' => "categoryReset('".$this->getUrl($resetPath, array('_current'=>true))."',true)" )) ); } @@ -186,11 +187,24 @@ public function getDeleteUrl(array $args = array()) return $this->getUrl('*/*/delete', $params); } + /** + * Return URL for refresh input element 'path' in form + * + * @param array $args + * @return string + */ + public function getRefreshPathUrl(array $args = array()) + { + $params = array('_current'=>true); + $params = array_merge($params, $args); + return $this->getUrl('*/*/refreshPath', $params); + } + public function getProductsJson() { $products = $this->getCategory()->getProductsPosition(); if (!empty($products)) { - return Zend_Json::encode($products); + return Mage::helper('core')->jsonEncode($products); } return '{}'; } diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Available.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Available.php index 24d0c40ce1..879bd77150 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Available.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Available.php @@ -1,68 +1,67 @@ - - */ -class Mage_Adminhtml_Block_Catalog_Category_Helper_Sortby_Available - extends Varien_Data_Form_Element_Multiselect -{ -/** - * Retrieve Element HTML fragment - * - * @return string - */ - public function getElementHtml() - { - $disabled = false; - if (!$this->getValue()) { - $this->setData('disabled', 'disabled'); - $disabled = true; - } - $html = parent::getElementHtml(); - - $htmlId = 'use_config_' . $this->getHtmlId(); - $html .= 'getReadonly()) { - $html .= ' disabled="disabled"'; - } - - $html .= 'onclick="toggleValueElements(this, this.parentNode);" class="checkbox" type="checkbox">'; - - $html .= ' '; - $html .= ''; - - return $html; - } -} + + */ +class Mage_Adminhtml_Block_Catalog_Category_Helper_Sortby_Available + extends Varien_Data_Form_Element_Multiselect +{ +/** + * Retrieve Element HTML fragment + * + * @return string + */ + public function getElementHtml() + { + $disabled = false; + if (!$this->getValue()) { + $this->setData('disabled', 'disabled'); + $disabled = true; + } + $html = parent::getElementHtml(); + $htmlId = 'use_config_' . $this->getHtmlId(); + $html .= 'getReadonly()) { + $html .= ' disabled="disabled"'; + } + + $html .= 'onclick="toggleValueElements(this, this.parentNode);" class="checkbox" type="checkbox">'; + + $html .= ' '; + $html .= ''; + + return $html; + } +} diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Default.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Default.php index 9ed1bc3e84..6d5d74e9c9 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Default.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Helper/Sortby/Default.php @@ -1,68 +1,67 @@ - - */ -class Mage_Adminhtml_Block_Catalog_Category_Helper_Sortby_Default - extends Varien_Data_Form_Element_Select -{ - /** - * Retrieve Element HTML fragment - * - * @return string - */ - public function getElementHtml() - { - $disabled = false; - if (!$this->getValue()) { - $this->setData('disabled', 'disabled'); - $disabled = true; - } - $html = parent::getElementHtml(); - - $htmlId = 'use_config_' . $this->getHtmlId(); - $html .= 'getReadonly()) { - $html .= ' disabled="disabled"'; - } - $html .= 'onclick="toggleValueElements(this, this.parentNode);" class="checkbox" type="checkbox">'; - - - - $html .= ' '; - $html .= ''; - - return $html; - } -} + + */ +class Mage_Adminhtml_Block_Catalog_Category_Helper_Sortby_Default + extends Varien_Data_Form_Element_Select +{ + /** + * Retrieve Element HTML fragment + * + * @return string + */ + public function getElementHtml() + { + $disabled = false; + if (!$this->getValue()) { + $this->setData('disabled', 'disabled'); + $disabled = true; + } + $html = parent::getElementHtml(); + $htmlId = 'use_config_' . $this->getHtmlId(); + $html .= 'getReadonly()) { + $html .= ' disabled="disabled"'; + } + $html .= 'onclick="toggleValueElements(this, this.parentNode);" class="checkbox" type="checkbox">'; + + + + $html .= ' '; + $html .= ''; + + return $html; + } +} diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tree.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tree.php index 24d482278f..993c5bbc67 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tree.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tree.php @@ -174,7 +174,7 @@ public function getTree($parenNodeCategory=null) public function getTreeJson($parenNodeCategory=null) { $rootArray = $this->_getNodeJson($this->getRoot($parenNodeCategory)); - $json = Zend_Json::encode(isset($rootArray['children']) ? $rootArray['children'] : array()); + $json = Mage::helper('core')->jsonEncode(isset($rootArray['children']) ? $rootArray['children'] : array()); return $json; } @@ -190,7 +190,9 @@ public function getBreadcrumbsJavascript($path, $javascriptVarName) if (empty($path)) { return ''; } - $categories = Mage::getResourceSingleton('catalog/category_tree')->loadBreadcrumbsArray($path); + + $categories = Mage::getResourceSingleton('catalog/category_tree') + ->setStoreId($this->getStore()->getId())->loadBreadcrumbsArray($path); if (empty($categories)) { return ''; } @@ -199,7 +201,7 @@ public function getBreadcrumbsJavascript($path, $javascriptVarName) } return ''; } diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit.php index 67e7171cf5..8de5e13945 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Edit.php @@ -53,18 +53,19 @@ public function __construct() 'level' => -1 ) ); + } else { + $this->_addButton( + 'save_and_edit_button', + array( + 'label' => Mage::helper('catalog')->__('Save And Continue Edit'), + 'onclick' => 'saveAndContinueEdit()', + 'class' => 'save' + ), + 100 + ); } $this->_updateButton('save', 'label', Mage::helper('catalog')->__('Save Attribute')); - $this->_addButton( - 'save_and_edit_button', - array( - 'label' => Mage::helper('catalog')->__('Save And Continue Edit'), - 'onclick' => 'saveAndContinueEdit()', - 'class' => 'save' - ), - 100 - ); if (! Mage::registry('entity_attribute')->getIsUserDefined()) { $this->_removeButton('delete'); 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 06ea1114b2..8526e7d60b 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 @@ -46,22 +46,23 @@ protected function _prepareForm() )); $disableAttributeFields = array( - 'sku' => array( + 'sku' => array( 'is_global', 'is_unique', ), - 'url_key' => array( + 'url_key' => array( 'is_unique', ), - 'status' => array( - 'is_configurable' - ) - ); - - $rewriteAttributeValue = array( - 'status' => array( - 'is_configurable' => 0 - ) + 'status' => array( + 'is_configurable', + 'is_filterable', + 'is_filterable_in_search' + ), + 'visibility' => array( + 'is_configurable', + 'is_filterable', + 'is_filterable_in_search' + ), ); $fieldset = $form->addFieldset('base_fieldset', @@ -141,16 +142,19 @@ protected function _prepareForm() 'value' => 'price', 'label' => Mage::helper('catalog')->__('Price') ), - array( - 'value' => 'gallery', - 'label' => Mage::helper('catalog')->__('Gallery') - ), array( 'value' => 'media_image', 'label' => Mage::helper('catalog')->__('Media Image') ), ); + if ($model->getFrontendInput() == 'gallery') { + $inputTypes[] = array( + 'value' => 'gallery', + 'label' => Mage::helper('catalog')->__('Gallery') + ); + } + $response = new Varien_Object(); $response->setTypes(array()); Mage::dispatchEvent('adminhtml_product_attribute_types', array('response'=>$response)); @@ -385,6 +389,7 @@ protected function _prepareForm() if (isset($disableAttributeFields[$model->getAttributeCode()])) { foreach ($disableAttributeFields[$model->getAttributeCode()] as $field) { $form->getElement($field)->setDisabled(1); + $form->getElement($field)->setReadonly(1); } } } @@ -394,12 +399,6 @@ protected function _prepareForm() $form->addValues($model->getData()); - if ($model->getId() && isset($rewriteAttributeValue[$model->getAttributeCode()])) { - foreach ($rewriteAttributeValue[$model->getAttributeCode()] as $field => $value) { - $form->getElement($field)->setValue($value); - } - } - $form->getElement('apply_to')->setSize(5); if ($applyTo = $model->getApplyTo()) { diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/New/Product/Created.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/New/Product/Created.php index f84cde7fd0..d621842598 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/New/Product/Created.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/New/Product/Created.php @@ -84,6 +84,6 @@ public function getAttributesBlockJson() $this->getRequest()->getParam('tab') => $this->getChildHtml('attributes') ); - return Zend_Json::encode($result); + return Mage::helper('core')->jsonEncode($result); } } // Class Mage_Adminhtml_Block_Catalog_Product_Attribute_New_Product_Created End \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php index a754e0a7e9..bca0c3bbcb 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main.php @@ -18,25 +18,35 @@ * 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) 2009 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 + * Adminhtml Catalog Attribute Set Main Block + * + * @category Mage + * @package Mage_Adminhtml * @author Magento Core Team */ - class Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main extends Mage_Adminhtml_Block_Template { + /** + * Initialize template + * + */ protected function _construct() { $this->setTemplate('catalog/product/attribute/set/main.phtml'); } + /** + * Prepare Global Layout + * + * @return Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main + */ protected function _prepareLayout() { $setId = $this->_getSetId(); @@ -50,203 +60,258 @@ protected function _prepareLayout() ); $this->setChild('delete_group_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Delete Selected Group'), - 'onclick' => 'editSet.submit();', - 'class' => 'delete' + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('Delete Selected Group'), + 'onclick' => 'editSet.submit();', + 'class' => 'delete' ))); $this->setChild('add_group_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Add New'), - 'onclick' => 'editSet.addGroup();', - 'class' => 'add' + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('Add New'), + 'onclick' => 'editSet.addGroup();', + 'class' => 'add' ))); $this->setChild('back_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Back'), - 'onclick' => 'setLocation(\''.$this->getUrl('*/*/').'\')', - 'class' => 'back' + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('Back'), + 'onclick' => 'setLocation(\''.$this->getUrl('*/*/').'\')', + 'class' => 'back' ))); $this->setChild('reset_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Reset'), - 'onclick' => 'window.location.reload()' + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('Reset'), + 'onclick' => 'window.location.reload()' ))); $this->setChild('save_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Save Attribute Set'), - 'onclick' => 'editSet.save();', - 'class' => 'save' + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('Save Attribute Set'), + 'onclick' => 'editSet.save();', + 'class' => 'save' ))); $this->setChild('delete_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Delete Attribute Set'), - 'onclick' => 'deleteConfirm(\''. $this->jsQuoteEscape(Mage::helper('catalog')->__('All products of this set will be deleted! Are you sure you want to delete this attribute set?')) . '\', \'' . $this->getUrl('*/*/delete', array('id' => $setId)) . '\')', - 'class' => 'delete' + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('Delete Attribute Set'), + 'onclick' => 'deleteConfirm(\''. $this->jsQuoteEscape(Mage::helper('catalog')->__('All products of this set will be deleted! Are you sure you want to delete this attribute set?')) . '\', \'' . $this->getUrl('*/*/delete', array('id' => $setId)) . '\')', + 'class' => 'delete' ))); $this->setChild('rename_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('New Set Name'), - 'onclick' => 'editSet.rename()' - )) - ); + $this->getLayout()->createBlock('adminhtml/widget_button')->setData(array( + 'label' => Mage::helper('catalog')->__('New Set Name'), + 'onclick' => 'editSet.rename()' + ))); + return parent::_prepareLayout(); } + /** + * Retrieve Attribute Set Group Tree HTML + * + * @return string + */ public function getGroupTreeHtml() { return $this->getChildHtml('group_tree'); } + /** + * Retrieve Attribute Set Edit Form HTML + * + * @return string + */ public function getSetFormHtml() { return $this->getChildHtml('edit_set_form'); } + /** + * Retrieve Block Header Text + * + * @return string + */ protected function _getHeader() { - return Mage::helper('catalog')->__("Edit Attribute Set '%s'", $this->_getSetData()->getAttributeSetName()); + return Mage::helper('catalog')->__("Edit Attribute Set '%s'", $this->_getAttributeSet()->getAttributeSetName()); } + /** + * Retrieve Attribute Set Save URL + * + * @return string + */ public function getMoveUrl() { return $this->getUrl('*/catalog_product_set/save', array('id' => $this->_getSetId())); } + /** + * Retrieve Attribute Set Group Save URL + * + * @return string + */ public function getGroupUrl() { return $this->getUrl('*/catalog_product_group/save', array('id' => $this->_getSetId())); } + /** + * Retrieve Attribute Set Group Tree as JSON format + * + * @return string + */ public function getGroupTreeJson() { + $items = array(); $setId = $this->_getSetId(); + /* @var $groups Mage_Eav_Model_Mysql4_Entity_Attribute_Group_Collection */ $groups = Mage::getModel('eav/entity_attribute_group') - ->getResourceCollection() - ->setAttributeSetFilter($setId) - ->load(); + ->getResourceCollection() + ->setAttributeSetFilter($setId) + ->load(); - $items = array(); - foreach( $groups as $node ) { + $configurable = Mage::getResourceModel('catalog/product_type_configurable_attribute') + ->getUsedAttributes($setId); + + /* @var $node Mage_Eav_Model_Entity_Attribute_Group */ + foreach ($groups as $node) { $item = array(); - $item['text']= $node->getAttributeGroupName(); - $item['id'] = $node->getAttributeGroupId(); - $item['cls'] = 'folder'; - $item['allowDrop'] = true; - $item['allowDrag'] = true; + $item['text'] = $node->getAttributeGroupName(); + $item['id'] = $node->getAttributeGroupId(); + $item['cls'] = 'folder'; + $item['allowDrop'] = true; + $item['allowDrag'] = true; $nodeChildren = Mage::getModel('eav/entity_attribute') - ->getResourceCollection() - ->setAttributeGroupFilter($node->getAttributeGroupId()) - ->addVisibleFilter() - /** - * TODO: issue #5126 - * @see Mage_Eav_Model_Mysql4_Entity_Attribute_Collection - */ - ->checkConfigurableProducts() - ->load(); - - if ( $nodeChildren->getSize() > 0 ) { + ->getResourceCollection() + ->setAttributeGroupFilter($node->getId()) + ->addVisibleFilter() + ->checkConfigurableProducts() + ->load(); + + if ($nodeChildren->getSize() > 0) { $item['children'] = array(); - foreach( $nodeChildren->getItems() as $child ) { - $tmpArr = array(); - $tmpArr['text'] = $child->getAttributeCode(); - $tmpArr['id'] = $child->getAttributeId(); - $tmpArr['cls'] = ( $child->getIsUserDefined() == 0 ) ? 'system-leaf' : 'leaf'; - $tmpArr['allowDrop'] = false; - $tmpArr['allowDrag'] = true; - $tmpArr['leaf'] = true; - $tmpArr['is_user_defined'] = $child->getIsUserDefined(); - // TODO: issue #5126. Template already has reuqired changes - $tmpArr['is_used_in_configurable'] = false; // (bool)$child->getIsUsedInConfigurable(); // TODO: issue #5126 - $tmpArr['entity_id'] = $child->getEntityAttributeId(); - - $item['children'][] = $tmpArr; + foreach ($nodeChildren->getItems() as $child) { + /* @var $child Mage_Eav_Model_Entity_Attribute */ + $attr = array( + 'text' => $child->getAttributeCode(), + 'id' => $child->getAttributeId(), + 'cls' => (!$child->getIsUserDefined()) ? 'system-leaf' : 'leaf', + 'allowDrop' => false, + 'allowDrag' => true, + 'leaf' => true, + 'is_user_defined' => $child->getIsUserDefined(), + 'is_configurable' => (int)in_array($child->getAttributeId(), $configurable), + 'entity_id' => $child->getEntityId() + ); + + $item['children'][] = $attr; } } $items[] = $item; } - return Zend_Json::encode($items); + return Mage::helper('core')->jsonEncode($items); } + /** + * Retrieve Unused in Attribute Set Attribute Tree as JSON + * + * @return string + */ public function getAttributeTreeJson() { + $items = array(); $setId = $this->_getSetId(); - $attributesIdsObj = Mage::getModel('eav/entity_attribute') - ->getResourceCollection() - ->setAttributeSetFilter($setId) - ->load(); + $collection = Mage::getModel('eav/entity_attribute') + ->getResourceCollection() + ->setAttributeSetFilter($setId) + ->load(); + $attributesIds = array('0'); - foreach( $attributesIdsObj->getItems() as $item ) { + /* @var $item Mage_Eav_Model_Entity_Attribute */ + foreach ($collection->getItems() as $item) { $attributesIds[] = $item->getAttributeId(); } - $attributes = Mage::getModel('eav/entity_attribute') - ->getResourceCollection() - ->setEntityTypeFilter(Mage::registry('entityType')) - ->setAttributesExcludeFilter($attributesIds) - ->addVisibleFilter() - ->load(); - $items = array(); - foreach( $attributes as $node ) { - $item = array(); - $item['text']= $node->getAttributeCode(); - $item['id'] = $node->getAttributeId(); - $item['cls'] = 'leaf'; - $item['allowDrop'] = false; - $item['allowDrag'] = true; - $item['leaf'] = true; - $item['is_user_defined'] = $node->getIsUserDefined(); - $item['is_used_in_configurable'] = false; + $attributes = Mage::getModel('eav/entity_attribute') + ->getResourceCollection() + ->setEntityTypeFilter(Mage::registry('entityType')) + ->setAttributesExcludeFilter($attributesIds) + ->addVisibleFilter() + ->load(); + + foreach ($attributes as $child) { + $attr = array( + 'text' => $child->getAttributeCode(), + 'id' => $child->getAttributeId(), + 'cls' => 'leaf', + 'allowDrop' => false, + 'allowDrag' => true, + 'leaf' => true, + 'is_user_defined' => $child->getIsUserDefined(), + 'is_configurable' => false, + 'entity_id' => $child->getEntityId() + ); - $items[] = $item; + $items[] = $attr; } - if( count($items) == 0 ) { + if (count($items) == 0) { $items[] = array( - 'text' => Mage::helper('catalog')->__('Empty'), - 'id' => 'empty', - 'cls' => 'folder', + 'text' => Mage::helper('catalog')->__('Empty'), + 'id' => 'empty', + 'cls' => 'folder', 'allowDrop' => false, 'allowDrag' => false, ); } - return Zend_Json::encode($items); + return Mage::helper('core')->jsonEncode($items); } + /** + * Retrieve Back Button HTML + * + * @return string + */ public function getBackButtonHtml() { return $this->getChildHtml('back_button'); } + /** + * Retrieve Reset Button HTML + * + * @return string + */ public function getResetButtonHtml() { return $this->getChildHtml('reset_button'); } + /** + * Retrieve Save Button HTML + * + * @return string + */ public function getSaveButtonHtml() { return $this->getChildHtml('save_button'); } + /** + * Retrieve Delete Button HTML + * + * @return string + */ public function getDeleteButtonHtml() { if ($this->getIsCurrentSetDefault()) { @@ -255,26 +320,61 @@ public function getDeleteButtonHtml() return $this->getChildHtml('delete_button'); } + /** + * Retrieve Delete Group Button HTML + * + * @return string + */ public function getDeleteGroupButton() { return $this->getChildHtml('delete_group_button'); } + /** + * Retrieve Add New Group Button HTML + * + * @return string + */ public function getAddGroupButton() { return $this->getChildHtml('add_group_button'); } + /** + * Retrieve Rename Button HTML + * + * @return string + */ public function getRenameButton() { return $this->getChildHtml('rename_button'); } + /** + * Retrieve current Attribute Set object + * + * @return Mage_Eav_Model_Entity_Attribute_Set + */ + protected function _getAttributeSet() + { + return Mage::registry('current_attribute_set'); + } + + /** + * Retrieve current attribute set Id + * + * @return int + */ protected function _getSetId() { - return Mage::registry('current_attribute_set')->getId(); + return $this->_getAttributeSet()->getId(); } + /** + * Check Current Attribute Set is a default + * + * @return bool + */ public function getIsCurrentSetDefault() { $isDefault = $this->getData('is_current_set_default'); @@ -288,14 +388,25 @@ public function getIsCurrentSetDefault() return $isDefault; } + /** + * Retrieve current Attribute Set object + * + * @deprecated use _getAttributeSet + * @return Mage_Eav_Model_Entity_Attribute_Set + */ protected function _getSetData() { - return Mage::getModel('eav/entity_attribute_set')->load( $this->_getSetId() ); + return $this->_getAttributeSet(); } + /** + * Prepare HTML + * + * @return string + */ protected function _toHtml() { Mage::dispatchEvent('adminhtml_catalog_product_attribute_set_main_html_before', array('block' => $this)); return parent::_toHtml(); } -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Created.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Created.php index 606f30e16d..318412ac26 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Created.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Created.php @@ -1,152 +1,152 @@ - - */ -class Mage_Adminhtml_Block_Catalog_Product_Created extends Mage_Adminhtml_Block_Widget -{ - protected $_configurableProduct; - protected $_product; - - public function __construct() - { - parent::__construct(); - $this->setTemplate('catalog/product/created.phtml'); - } - - - protected function _prepareLayout() - { - $this->setChild( - 'close_button', - $this->getLayout()->createBlock('adminhtml/widget_button') - ->setData(array( - 'label' => Mage::helper('catalog')->__('Close Window'), - 'onclick' => 'addProduct(true)' - )) - ); - } - - - public function getCloseButtonHtml() - { - return $this->getChildHtml('close_button'); - } - - public function getProductId() - { - return (int) $this->getRequest()->getParam('id'); - } - - /** - * Indentifies edit mode of popup - * - * @return boolean - */ - public function isEdit() - { - return (bool) $this->getRequest()->getParam('edit'); - } - - /** - * Retrive serialized json with configurable attributes values of simple - * - * @return string - */ - public function getAttributesJson() - { - $result = array(); - foreach ($this->getAttributes() as $attribute) { - $value = $this->getProduct()->getAttributeText($attribute->getAttributeCode()); - - $result[] = array( - 'label' => $value, - 'value_index' => $this->getProduct()->getData($attribute->getAttributeCode()), - 'attribute_id' => $attribute->getId() - ); - } - - return Zend_Json::encode($result); - } - - public function getAttributes() - { - if ($this->getConfigurableProduct()->getId()) { - return $this->getConfigurableProduct()->getTypeInstance(true)->getUsedProductAttributes($this->getConfigurableProduct()); - } - - $attributes = array(); - - $attributesIds = $this->getRequest()->getParam('required'); - if ($attributesIds) { - $attributesIds = explode(',', $attributesIds); - foreach ($attributesIds as $attributeId) { - $attribute = $this->getProduct()->getTypeInstance(true)->getAttributeById($attributeId, $this->getProduct()); - if (!$attribute) { - continue; - } - $attributes[] = $attribute; - } - } - - return $attributes; - } - - /** - * Retrive configurable product for created/edited simple - * - * @return Mage_Catalog_Model_Product - */ - public function getConfigurableProduct() - { - if (is_null($this->_configurableProduct)) { - $this->_configurableProduct = Mage::getModel('catalog/product') - ->setStore(0) - ->load($this->getRequest()->getParam('product')); - } - return $this->_configurableProduct; - } - - /** - * Retrive product - * - * @return Mage_Catalog_Model_Product - */ - public function getProduct() - { - if (is_null($this->_product)) { - $this->_product = Mage::getModel('catalog/product') - ->setStore(0) - ->load($this->getRequest()->getParam('id')); - } - return $this->_product; - } + + */ +class Mage_Adminhtml_Block_Catalog_Product_Created extends Mage_Adminhtml_Block_Widget +{ + protected $_configurableProduct; + protected $_product; + + public function __construct() + { + parent::__construct(); + $this->setTemplate('catalog/product/created.phtml'); + } + + + protected function _prepareLayout() + { + $this->setChild( + 'close_button', + $this->getLayout()->createBlock('adminhtml/widget_button') + ->setData(array( + 'label' => Mage::helper('catalog')->__('Close Window'), + 'onclick' => 'addProduct(true)' + )) + ); + } + + + public function getCloseButtonHtml() + { + return $this->getChildHtml('close_button'); + } + + public function getProductId() + { + return (int) $this->getRequest()->getParam('id'); + } + + /** + * Indentifies edit mode of popup + * + * @return boolean + */ + public function isEdit() + { + return (bool) $this->getRequest()->getParam('edit'); + } + + /** + * Retrive serialized json with configurable attributes values of simple + * + * @return string + */ + public function getAttributesJson() + { + $result = array(); + foreach ($this->getAttributes() as $attribute) { + $value = $this->getProduct()->getAttributeText($attribute->getAttributeCode()); + + $result[] = array( + 'label' => $value, + 'value_index' => $this->getProduct()->getData($attribute->getAttributeCode()), + 'attribute_id' => $attribute->getId() + ); + } + + return Mage::helper('core')->jsonEncode($result); + } + + public function getAttributes() + { + if ($this->getConfigurableProduct()->getId()) { + return $this->getConfigurableProduct()->getTypeInstance(true)->getUsedProductAttributes($this->getConfigurableProduct()); + } + + $attributes = array(); + + $attributesIds = $this->getRequest()->getParam('required'); + if ($attributesIds) { + $attributesIds = explode(',', $attributesIds); + foreach ($attributesIds as $attributeId) { + $attribute = $this->getProduct()->getTypeInstance(true)->getAttributeById($attributeId, $this->getProduct()); + if (!$attribute) { + continue; + } + $attributes[] = $attribute; + } + } + + return $attributes; + } + + /** + * Retrive configurable product for created/edited simple + * + * @return Mage_Catalog_Model_Product + */ + public function getConfigurableProduct() + { + if (is_null($this->_configurableProduct)) { + $this->_configurableProduct = Mage::getModel('catalog/product') + ->setStore(0) + ->load($this->getRequest()->getParam('product')); + } + return $this->_configurableProduct; + } + + /** + * Retrive product + * + * @return Mage_Catalog_Model_Product + */ + public function getProduct() + { + if (is_null($this->_product)) { + $this->_product = Mage::getModel('catalog/product') + ->setStore(0) + ->load($this->getRequest()->getParam('id')); + } + return $this->_product; + } } // Class Mage_Adminhtml_Block_Catalog_Product_Created End \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Attributes/Create.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Attributes/Create.php index bb71e28d03..ec7caee367 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Attributes/Create.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Attributes/Create.php @@ -90,7 +90,7 @@ protected function _toHtml() $html = parent::_toHtml(); $html .= Mage::helper('adminhtml/js')->getScript( "var {$this->getJsObjectName()} = new Product.Attributes('{$this->getId()}');\n" - . "{$this->getJsObjectName()}.setConfig(" . Zend_Json::encode($this->getConfig()->getData()) . ");\n" + . "{$this->getJsObjectName()}.setConfig(" . Mage::helper('core')->jsonEncode($this->getConfig()->getData()) . ");\n" ); return $html; diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Categories.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Categories.php index 009f8efcc8..4d99e52969 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Categories.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Categories.php @@ -192,7 +192,7 @@ public function getCategoryChildrenJson($categoryId) $children[] = $this->_getNodeJson($child); } - return Zend_Json::encode($children); + return Mage::helper('core')->jsonEncode($children); } public function getLoadTreeUrl($expanded=null) diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config.php index 29f90a807c..43bdf22d78 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config.php @@ -126,7 +126,7 @@ public function getAttributesJson() if(!$attributes) { return '[]'; } - return Zend_Json::encode($attributes); + return Mage::helper('core')->jsonEncode($attributes); } /** @@ -145,7 +145,7 @@ public function getLinksJson() foreach ($products as $product) { $data[$product->getId()] = $this->getConfigurableSettings($product); } - return Zend_Json::encode($data); + return Mage::helper('core')->jsonEncode($data); } /** diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config/Grid/Renderer/Checkbox.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config/Grid/Renderer/Checkbox.php index ab1c58aff6..49d14c62a2 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config/Grid/Renderer/Checkbox.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Tab/Super/Config/Grid/Renderer/Checkbox.php @@ -67,6 +67,6 @@ public function getAttributesJson(Varien_Object $row) $result[] = $item; } - return Zend_Json::encode($result); + return Mage::helper('core')->jsonEncode($result); } }// Class Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Super_Config_Grid_Renderer_Checkbox END \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Helper/Form/Gallery/Content.php b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Helper/Form/Gallery/Content.php index 83b36bc35c..6bd38b042a 100644 --- a/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Helper/Form/Gallery/Content.php +++ b/app/code/core/Mage/Adminhtml/Block/Catalog/Product/Helper/Form/Gallery/Content.php @@ -104,7 +104,7 @@ public function getImagesJson() $image['url'] = Mage::getSingleton('catalog/product_media_config') ->getMediaUrl($image['file']); } - return Zend_Json::encode($value['images']); + return Mage::helper('core')->jsonEncode($value['images']); } } return '[]'; @@ -119,7 +119,7 @@ public function getImagesValuesJson() $attribute->getAttributeCode() ); } - return Zend_Json::encode($values); + return Mage::helper('core')->jsonEncode($values); } /** @@ -164,7 +164,7 @@ public function getMediaAttributes() public function getImageTypesJson() { - return Zend_Json::encode($this->getImageTypes()); + return Mage::helper('core')->jsonEncode($this->getImageTypes()); } } diff --git a/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Renderer/Region.php b/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Renderer/Region.php index bdffd13d39..586c120073 100644 --- a/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Renderer/Region.php +++ b/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Renderer/Region.php @@ -42,6 +42,7 @@ public function render(Varien_Data_Form_Element_Abstract $element) } $regionId = $element->getForm()->getElement('region_id')->getValue(); + $postcode = $element->getForm()->getElement('postcode'); $html = ''; $element->setClass('input-text'); @@ -54,9 +55,23 @@ public function render(Varien_Data_Form_Element_Abstract $element) $html.= ''; $html.= ''; $html.= ''; $html.= ''."\n"; return $html; diff --git a/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Tab/Account.php b/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Tab/Account.php index 2d17235dcd..bcde34a2d9 100644 --- a/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Tab/Account.php +++ b/app/code/core/Mage/Adminhtml/Block/Customer/Edit/Tab/Account.php @@ -132,16 +132,24 @@ public function initForm() 'name' => 'sendemail', 'id' => 'sendemail', )); + if (!Mage::app()->isSingleStoreMode()) { + $fieldset->addField('store_id', 'select', array( + 'label' => $this->helper('customer')->__('Send From'), + 'name' => 'store_id', + 'values' => Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm() + )); + } } - // make sendemail disabled, if website_id has empty value - if ($sendemail = $form->getElement('sendemail')) { + // make sendemail and store_id disabled, if website_id has empty value + if ($sendemail = $form->getElement('store_id')) { $prefix = $form->getHtmlIdPrefix(); $sendemail->setAfterElementHtml( ' '; + echo ''; } echo ''; //print $this->getUrl('*/*/batchFinish', array('id' => $batchModel->getId())); diff --git a/app/code/core/Mage/Adminhtml/Block/Tax/Rule/Grid.php b/app/code/core/Mage/Adminhtml/Block/Tax/Rule/Grid.php index 4ec551f2b1..c10efb02a5 100644 --- a/app/code/core/Mage/Adminhtml/Block/Tax/Rule/Grid.php +++ b/app/code/core/Mage/Adminhtml/Block/Tax/Rule/Grid.php @@ -90,6 +90,7 @@ protected function _prepareColumns() 'index' => 'customer_tax_classes', 'filter_index' => 'ctc.customer_tax_class_id', 'type' => 'options', + 'show_missing_option_values' => true, 'options' => Mage::getModel('tax/class')->getCollection()->setClassTypeFilter('CUSTOMER')->toOptionHash(), ) ); @@ -102,6 +103,7 @@ protected function _prepareColumns() 'index' => 'product_tax_classes', 'filter_index' => 'ptc.product_tax_class_id', 'type' => 'options', + 'show_missing_option_values' => true, 'options' => Mage::getModel('tax/class')->getCollection()->setClassTypeFilter('PRODUCT')->toOptionHash(), ) ); @@ -114,6 +116,7 @@ protected function _prepareColumns() 'index' => 'tax_rates', 'filter_index' => 'rate.tax_calculation_rate_id', 'type' => 'options', + 'show_missing_option_values' => true, 'options' => Mage::getModel('tax/calculation_rate')->getCollection()->toOptionHash(), ) ); diff --git a/app/code/core/Mage/Adminhtml/Block/Urlrewrite.php b/app/code/core/Mage/Adminhtml/Block/Urlrewrite.php index f26a6b94ef..eeaf3cb50a 100644 --- a/app/code/core/Mage/Adminhtml/Block/Urlrewrite.php +++ b/app/code/core/Mage/Adminhtml/Block/Urlrewrite.php @@ -46,8 +46,8 @@ class Mage_Adminhtml_Block_Urlrewrite extends Mage_Adminhtml_Block_Widget_Grid_C */ public function __construct() { - $this->_headerText = Mage::helper('adminhtml')->__('Url Rewrite Management'); - $this->_addButtonLabel = Mage::helper('adminhtml')->__('Add Urlrewrite'); + $this->_headerText = Mage::helper('adminhtml')->__('URL Rewrite Management'); + $this->_addButtonLabel = Mage::helper('adminhtml')->__('Add URL Rewrite'); parent::__construct(); } diff --git a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Category/Tree.php b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Category/Tree.php index b6548bb19a..163b2955fa 100644 --- a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Category/Tree.php +++ b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Category/Tree.php @@ -67,7 +67,7 @@ public function getTreeArray($parentId = null, $asJson = false, $recursionLevel $result = $this->_getNodesArray($this->getRoot(null, $recursionLevel)); } if ($asJson) { - return Zend_Json::encode($result); + return Mage::helper('core')->jsonEncode($result); } return $result; } diff --git a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit.php b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit.php index 141b7e0e84..a76a9ca295 100644 --- a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit.php +++ b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit.php @@ -87,15 +87,15 @@ protected function _prepareLayout() ); } - $this->_headerText = Mage::helper('adminhtml')->__('Add New Urlrewrite'); + $this->_headerText = Mage::helper('adminhtml')->__('Add New URL Rewrite'); // edit form for existing urlrewrite if ($this->getUrlrewriteId()) { - $this->_headerText = Mage::helper('adminhtml')->__('Edit Urlrewrite'); + $this->_headerText = Mage::helper('adminhtml')->__('Edit URL Rewrite'); $this->_setFormChild(); } elseif ($this->getProductId()) { - $this->_headerText = Mage::helper('adminhtml')->__('Add Urlrewrite for a Product'); + $this->_headerText = Mage::helper('adminhtml')->__('Add URL Rewrite for a Product'); // edit form for product with or without category if ($this->getCategoryId() || !$this->isMode('category')) { @@ -119,7 +119,7 @@ protected function _prepareLayout() } // edit form for category elseif ($this->getCategoryId()) { - $this->_headerText = Mage::helper('adminhtml')->__('Add Urlrewrite for a Category'); + $this->_headerText = Mage::helper('adminhtml')->__('Add URL Rewrite for a Category'); $this->_setFormChild(); } // modes selector and products/categories selectors, as well as edit form for custom urlrewrite diff --git a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit/Form.php b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit/Form.php index fe982f7daa..eabf76556c 100644 --- a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit/Form.php +++ b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Edit/Form.php @@ -75,7 +75,7 @@ protected function _prepareForm() } $fieldset = $form->addFieldset('base_fieldset', array( - 'legend' => Mage::helper('adminhtml')->__('Urlrewrite Information') + 'legend' => Mage::helper('adminhtml')->__('URL Rewrite Information') )); $fieldset->addField('is_system', 'select', array( diff --git a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Selector.php b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Selector.php index f0b1e58b5b..c328c1266a 100644 --- a/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Selector.php +++ b/app/code/core/Mage/Adminhtml/Block/Urlrewrite/Selector.php @@ -72,7 +72,7 @@ public function getModes() */ public function getSelectorLabel() { - return Mage::helper('adminhtml')->__('Create Urlrewrite:'); + return Mage::helper('adminhtml')->__('Create URL Rewrite:'); } /** diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Button.php b/app/code/core/Mage/Adminhtml/Block/Widget/Button.php index a54c0b1879..772cc66581 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Button.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Button.php @@ -61,7 +61,7 @@ protected function _toHtml() . ' onclick="'.$this->getOnClick().'"' . ' style="'.$this->getStyle() .'"' . ($this->getValue()?' value="'.$this->getValue() . '"':'') - . ($this->getDisabled() ? 'disabled="disabled"' : '') + . ($this->getDisabled() ? ' disabled="disabled"' : '') . '>' .$this->getLabel().''.$this->getAfterHtml(); return $html; diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php index b37c891ce8..92051b521d 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php @@ -274,6 +274,38 @@ public function addColumn($columnId, $column) return $this; } + /** + * Add column to grid after specified column. + * + * @param string $columnId + * @param array|Varien_Object $column + * @param string $after + * @return Mage_Adminhtml_Block_Widget_Grid + */ + public function addColumnAfter($columnId, $column, $after) + { + $this->addColumn($columnId, $column); + // Moving grid column + $keys = array_keys($this->_columns); + $values = array_values($this->_columns); + $positionCurrent = array_search($columnId, $keys); + + if (array_search($after, $keys) === false || $positionCurrent === false) { + return $this; + } + + $key = array_splice($keys, $positionCurrent, 1); + $value = array_splice($values, $positionCurrent, 1); + $positionTarget = array_search($after, $keys) + 1; + array_splice($keys, $positionTarget, 0, $key); + array_splice($values, $positionTarget, 0, $value); + + $this->_columns = array_combine($keys, $values); + end($this->_columns); + $this->_lastColumnId = key($this->_columns); + return $this; + } + public function getLastColumnId() { return $this->_lastColumnId; diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php index bbcf9a3b10..f690ece8ca 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column.php @@ -125,7 +125,21 @@ public function getHeaderHtmlProperty() */ public function getRowField(Varien_Object $row) { - return $this->getRenderer()->render($row); + $renderedValue = $this->getRenderer()->render($row); + + /* + * if column has determined callback for framing call + * it before give away rendered value + * + * callback_function($renderedValue, $row, $column, $isExport) + * should return new version of rendered value + */ + $frameCallback = $this->getFrameCallback(); + if (is_array($frameCallback)) { + $renderedValue = call_user_func($frameCallback, $renderedValue, $row, $this, false); + } + + return $renderedValue; } /** @@ -136,7 +150,21 @@ public function getRowField(Varien_Object $row) */ public function getRowFieldExport(Varien_Object $row) { - return $this->getRenderer()->renderExport($row); + $renderedValue = $this->getRenderer()->renderExport($row); + + /* + * if column has determined callback for framing call + * it before give away rendered value + * + * callback_function($renderedValue, $row, $column, $isExport) + * should return new version of rendered value + */ + $frameCallback = $this->getFrameCallback(); + if (is_array($frameCallback)) { + $renderedValue = call_user_func($frameCallback, $renderedValue, $row, $this, true); + } + + return $renderedValue; } public function setRenderer($renderer) @@ -328,4 +356,4 @@ public function getExportHeader() } return $this->getHeader(); } -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Action.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Action.php index e27a37e669..1865727c36 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Action.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Action.php @@ -82,7 +82,7 @@ protected function _toOptionHtml($action, Varien_Object $row) $actionCaption = ''; $this->_transformActionData($action, $actionCaption, $row); - $htmlAttibutes = array('value'=>$this->htmlEscape(Zend_Json::encode($action))); + $htmlAttibutes = array('value'=>$this->htmlEscape(Mage::helper('core')->jsonEncode($action))); $actionAttributes->setData($htmlAttibutes); return ''; } diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Checkbox.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Checkbox.php index 23c6649b4d..bceadb0f60 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Checkbox.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Checkbox.php @@ -59,18 +59,29 @@ public function render(Varien_Object $row) else { $checked = ($value === $this->getColumn()->getValue()) ? ' checked="checked"' : ''; } - if ($this->getNoObjectId() || $this->getColumn()->getUseIndex()){ - //WTF: why getNoObjectId??? - $v = $value; - } else { + + $disabledValues = $this->getColumn()->getDisabledValues(); + if (is_array($disabledValues)) { + $disabled = in_array($value, $disabledValues) ? ' disabled="disabled"' : ''; + } + else { + $disabled = ($value === $this->getColumn()->getDisabledValue()) ? ' disabled="disabled"' : ''; + } + + $this->setDisabled($disabled); + + if ($this->getNoObjectId() || $this->getColumn()->getUseIndex()){ + $v = $value; + } else { $v = ($row->getId() != "") ? $row->getId():$value; - } + } + return $this->_getCheckboxHtml($v, $checked); } protected function _getCheckboxHtml($value, $checked) { - return ''; + return 'getDisabled().'/>'; } public function renderHeader() @@ -81,8 +92,13 @@ public function renderHeader() $checked = ''; if ($filter = $this->getColumn()->getFilter()) { - $checked = $filter->getValue() ? 'checked="checked"' : ''; + $checked = $filter->getValue() ? ' checked="checked"' : ''; + } + + $disabled = ''; + if ($this->getColumn()->getDisabled()) { + $disabled = ' disabled="disabled"'; } - return ''; + return ''; } } diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Massaction/Abstract.php b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Massaction/Abstract.php index d84e8d7ae7..5e505849b7 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Massaction/Abstract.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Grid/Massaction/Abstract.php @@ -114,7 +114,7 @@ public function getItemsJson() $result[$itemId] = $item->toArray(); } - return Zend_Json::encode($result); + return Mage::helper('core')->jsonEncode($result); } /** @@ -187,7 +187,7 @@ public function getSelectedJson() if($selected = $this->getRequest()->getParam($this->getFormFieldNameInternal())) { $selected = explode(',', $selected); return join(',', $selected); -// return Zend_Json::encode($selected); +// return Mage::helper('core')->jsonEncode($selected); } else { return ''; // return '[]'; @@ -235,7 +235,7 @@ public function getGridIdsJson() if(!empty($gridIds)) { return join(",", $gridIds); - //return Zend_Json::encode($gridIds); + //return Mage::helper('core')->jsonEncode($gridIds); } return ''; //return '[]'; diff --git a/app/code/core/Mage/Adminhtml/Block/Widget/Tabs.php b/app/code/core/Mage/Adminhtml/Block/Widget/Tabs.php index 38044813b2..b7b92ca7d8 100644 --- a/app/code/core/Mage/Adminhtml/Block/Widget/Tabs.php +++ b/app/code/core/Mage/Adminhtml/Block/Widget/Tabs.php @@ -345,7 +345,7 @@ public function getAllShadowTabs($asJson = true) } } if ($asJson) { - return Zend_Json::encode($result); + return Mage::helper('core')->jsonEncode($result); } return $result; } diff --git a/app/code/core/Mage/Adminhtml/Controller/Action.php b/app/code/core/Mage/Adminhtml/Controller/Action.php index 322582f8df..0b96c50644 100644 --- a/app/code/core/Mage/Adminhtml/Controller/Action.php +++ b/app/code/core/Mage/Adminhtml/Controller/Action.php @@ -144,7 +144,7 @@ public function preDispatch() $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( + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode(array( 'error' => true, 'message' => $_keyErrorMsg ))); @@ -192,7 +192,7 @@ protected function _checkUrlSettings() 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'))) + $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; } @@ -213,7 +213,7 @@ protected function _checkUrlSettings() 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) + $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; } diff --git a/app/code/core/Mage/Adminhtml/Helper/Dashboard/Data.php b/app/code/core/Mage/Adminhtml/Helper/Dashboard/Data.php index b025f803e0..efa04da104 100644 --- a/app/code/core/Mage/Adminhtml/Helper/Dashboard/Data.php +++ b/app/code/core/Mage/Adminhtml/Helper/Dashboard/Data.php @@ -56,7 +56,7 @@ public function getDatePeriods() return array( '24h'=>$this->__('Last 24 hours'), '7d'=>$this->__('Last 7 days'), - '1m'=>$this->__('Last Month'), + '1m'=>$this->__('Month-To-Date'), '1y'=>$this->__('YTD'), '2y'=>$this->__('2YTD') ); diff --git a/app/code/core/Mage/Adminhtml/Helper/Sales.php b/app/code/core/Mage/Adminhtml/Helper/Sales.php new file mode 100644 index 0000000000..37db7facbc --- /dev/null +++ b/app/code/core/Mage/Adminhtml/Helper/Sales.php @@ -0,0 +1,87 @@ +') + { + return $this->displayPrices( + $dataObject, + $dataObject->getData('base_'.$code), + $dataObject->getData($code), + $strong, + $separator + ); + } + + /** + * Get "double" prices html (block with base and place currency) + * + * @param Varien_Object $dataObject + * @param float $basePrice + * @param float $price + * @param bool $strong + * @param string $separator + * @return string + */ + public function displayPrices($dataObject, $basePrice, $price, $strong = false, $separator = '
') + { + $order = false; + if ($dataObject instanceof Mage_Sales_Model_Order) { + $order = $dataObject; + } else { + $order = $dataObject->getOrder(); + } + + if ($order && $order->isCurrencyDifferent()) { + $res = ''; + $res.= $order->formatBasePrice($basePrice); + $res.= ''.$separator; + $res.= '['.$order->formatPrice($price).']'; + } elseif ($order) { + $res = $order->formatPrice($price); + if ($strong) { + $res = ''.$res.''; + } + } else { + $res = Mage::app()->getStore()->formatPrice($price); + if ($strong) { + $res = ''.$res.''; + } + } + return $res; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/Model/Observer.php b/app/code/core/Mage/Adminhtml/Model/Observer.php index df4a9b41e8..4633ac3471 100644 --- a/app/code/core/Mage/Adminhtml/Model/Observer.php +++ b/app/code/core/Mage/Adminhtml/Model/Observer.php @@ -60,7 +60,7 @@ public function massactionPrepareKey() { $request = Mage::app()->getFrontController()->getRequest(); if ($key = $request->getPost('massaction_prepare_key')) { - $value = is_array($request->getPost($key)) ? $request->getPost($key) : split(',', $request->getPost($key)); + $value = is_array($request->getPost($key)) ? $request->getPost($key) : explode(',', $request->getPost($key)); $request->setPost($key, $value ? $value : null); } return $this; 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 aab7bd5e4c..e3e654c568 100644 --- a/app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php +++ b/app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php @@ -420,8 +420,6 @@ public function moveQuoteItem($item, $moveTo, $qty) $product->unsSkipCheckRequiredOption(); $newItem->checkData(); $newItem->setQty($qty); - $this->getQuote()->collectTotals() - ->save(); break; case 'cart': if (($cart = $this->getCustomerCart()) && is_null($item->getOptionByCode('additional_options'))) { @@ -1123,7 +1121,7 @@ protected function _validate() { $customerId = $this->getSession()->getCustomerId(); if (is_null($customerId)) { - Mage::throwException(Mage::helper('adminhtml')->__('Please select a custmer')); + Mage::throwException(Mage::helper('adminhtml')->__('Please select a customer')); } if (!$this->getSession()->getStore()->getId()) { diff --git a/app/code/core/Mage/Adminhtml/Model/Search/Catalog.php b/app/code/core/Mage/Adminhtml/Model/Search/Catalog.php index 5b57b35d57..6070efa804 100644 --- a/app/code/core/Mage/Adminhtml/Model/Search/Catalog.php +++ b/app/code/core/Mage/Adminhtml/Model/Search/Catalog.php @@ -36,7 +36,7 @@ public function load() return $this; } - $collection = Mage::helper('catalogSearch')->getQuery()->getResultCollection() + $collection = Mage::helper('catalogsearch')->getQuery()->getResultCollection() ->addAttributeToSelect('name') ->addAttributeToSelect('description') ->addSearchFilter($this->getQuery()) diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Abstract.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Abstract.php index 1abd9e1602..75415d4c19 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Abstract.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Abstract.php @@ -44,7 +44,7 @@ abstract class Mage_Adminhtml_Model_System_Config_Backend_Currency_Abstract exte protected function _getAllowedCurrencies() { if ($this->getData('groups/options/fields/allow/inherit')) { - return split(',', Mage::getConfig()->getNode('currency/options/allow', $this->getScope(), $this->getScopeId())); + return explode(',', Mage::getConfig()->getNode('currency/options/allow', $this->getScope(), $this->getScopeId())); } return $this->getData('groups/options/fields/allow/value'); } @@ -56,7 +56,7 @@ protected function _getAllowedCurrencies() */ protected function _getInstalledCurrencies() { - return split(',', Mage::getStoreConfig('system/currency/installed')); + return explode(',', Mage::getStoreConfig('system/currency/installed')); } /** diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Cron.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Cron.php index cf26d3a998..9358402af3 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Cron.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Currency/Cron.php @@ -41,7 +41,7 @@ protected function _afterSave() $enabled = $this->getData('groups/import/fields/enabled/value'); $service = $this->getData('groups/import/fields/service/value'); $time = $this->getData('groups/import/fields/time/value'); - $frequncy = $this->getData('groups/import/fields/frequncy/value'); + $frequency = $this->getData('groups/import/fields/frequency/value'); $errorEmail = $this->getData('groups/import/fields/error_email/value'); $frequencyDaily = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_DAILY; @@ -53,9 +53,9 @@ protected function _afterSave() $cronExprArray = array( intval($time[1]), # Minute intval($time[0]), # Hour - ($frequncy == $frequencyMonthly) ? '1' : '*', # Day of the Month + ($frequency == $frequencyMonthly) ? '1' : '*', # Day of the Month '*', # Month of the Year - ($frequncy == $frequencyWeekly) ? '1' : '*', # Day of the Week + ($frequency == $frequencyWeekly) ? '1' : '*', # Day of the Week ); $cronExprString = join(' ', $cronExprArray); diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Sender.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Sender.php index 8e8e368af8..eb129ad66f 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Sender.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Sender.php @@ -37,8 +37,8 @@ class Mage_Adminhtml_Model_System_Config_Backend_Email_Sender extends Mage_Core_ protected function _beforeSave() { $value = $this->getValue(); - if (!preg_match("/^[A-Za-z0-9_\s]+$/", $value)) { - Mage::throwException(Mage::helper('adminhtml')->__('Invalid sender name "%s"', $value)); + if (!preg_match("/^[\S ]+$/", $value)) { + Mage::throwException(Mage::helper('adminhtml')->__('Invalid sender name "%s". Please use only visible characters and spaces.', $value)); } return $this; } diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Locale.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Locale.php index fe48b7aa43..3cc42063e3 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Locale.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Locale.php @@ -46,7 +46,7 @@ protected function _afterSave() ->getCollection() ->addPathFilter('currency/options'); - $values = split(',', $this->getValue()); + $values = explode(',', $this->getValue()); $exceptions = array(); foreach ($collection as $data) { $match = false; diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Checktype.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Checktype.php index 2a0240bce5..e3b27215b9 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Checktype.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Checktype.php @@ -18,20 +18,38 @@ * 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) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ +/** + * Send to a Friend Limit sending by Source + * + * @category Mage + * @package Mage_Adminhtml + * @author Magento Core Team + */ class Mage_Adminhtml_Model_System_Config_Source_Checktype { + /** + * Retrieve Check Type Option array + * + * @return array + */ public function toOptionArray() { return array( - array('value'=>0, 'label'=>Mage::helper('adminhtml')->__('IP Address')), - array('value'=>1, 'label'=>Mage::helper('adminhtml')->__('Cookie (Unsafe)')), + array( + 'value' => Mage_Sendfriend_Helper_Data::CHECK_IP, + 'label' => Mage::helper('adminhtml')->__('IP Address') + ), + array( + 'value' => Mage_Sendfriend_Helper_Data::CHECK_COOKIE, + 'label' => Mage::helper('adminhtml')->__('Cookie (Unsafe)') + ), ); } -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Watermark/Position.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Watermark/Position.php index db04ee4d7e..82e1d7c7f1 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Watermark/Position.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Watermark/Position.php @@ -49,6 +49,7 @@ public function toOptionArray() array('value' => 'top-right', 'label' => Mage::helper('catalog')->__('Top/Right')), array('value' => 'bottom-left', 'label' => Mage::helper('catalog')->__('Bottom/Left')), array('value' => 'bottom-right', 'label' => Mage::helper('catalog')->__('Bottom/Right')), + array('value' => 'center', 'label' => Mage::helper('catalog')->__('Center')), ); } diff --git a/app/code/core/Mage/Adminhtml/Model/Url.php b/app/code/core/Mage/Adminhtml/Model/Url.php index c474df6183..2915ee133f 100644 --- a/app/code/core/Mage/Adminhtml/Model/Url.php +++ b/app/code/core/Mage/Adminhtml/Model/Url.php @@ -69,8 +69,13 @@ public function setRouteParams(array $data, $unsetOldParams=true) */ public function getUrl($routePath=null, $routeParams=null) { - $result = parent::getUrl($routePath, $routeParams); + $cacheSecretKey = false; + if (is_array($routeParams) && isset($routeParams['_cache_secret_key'])) { + unset($routeParams['_cache_secret_key']); + $cacheSecretKey = true; + } + $result = parent::getUrl($routePath, $routeParams); if (!$this->useSecretKey()) { return $result; } @@ -78,7 +83,13 @@ public function getUrl($routePath=null, $routeParams=null) $_route = $this->getRouteName() ? $this->getRouteName() : '*'; $_controller = $this->getControllerName() ? $this->getControllerName() : $this->getDefaultControllerName(); $_action = $this->getActionName() ? $this->getActionName() : $this->getDefaultActionName(); - $secret = array(self::SECRET_KEY_PARAM_NAME => $this->getSecretKey($_controller, $_action)); + + if ($cacheSecretKey) { + $secret = array(self::SECRET_KEY_PARAM_NAME => "\${$_controller}/{$_action}\$"); + } + else { + $secret = array(self::SECRET_KEY_PARAM_NAME => $this->getSecretKey($_controller, $_action)); + } if (is_array($routeParams)) { $routeParams = array_merge($secret, $routeParams); } else { @@ -87,6 +98,7 @@ public function getUrl($routePath=null, $routeParams=null) if (is_array($this->getRouteParams())) { $routeParams = array_merge($this->getRouteParams(), $routeParams); } + return parent::getUrl("{$_route}/{$_controller}/{$_action}", $routeParams); } diff --git a/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php b/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php index 73ec707e44..b208cf163a 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/CategoryController.php @@ -246,6 +246,15 @@ public function saveAction() } } + /** + * Process "Use Config Settings" checkboxes + */ + if ($useConfig = $this->getRequest()->getPost('use_config')) { + foreach ($useConfig as $attributeCode) { + $category->setData($attributeCode, null); + } + } + $category->setAttributeSetId($category->getDefaultAttributeSetId()); if (isset($data['category_products']) && @@ -370,7 +379,7 @@ public function treeAction() $block = $this->getLayout()->createBlock('adminhtml/catalog_category_tree'); $root = $block->getRoot(); - $this->getResponse()->setBody(Zend_Json::encode(array( + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode(array( 'data' => $block->getTree(), 'parameters' => array( 'text' => $block->buildNodeName($root), @@ -384,8 +393,24 @@ public function treeAction() )))); } + /** + * Build response for refresh input element 'path' in form + */ + public function refreshPathAction() + { + if ($id = (int) $this->getRequest()->getParam('id')) { + $category = Mage::getModel('catalog/category')->load($id); + $this->getResponse()->setBody( + Mage::helper('core')->jsonEncode(array( + 'id' => $id, + 'path' => $category->getPath(), + )) + ); + } + } + protected function _isAllowed() { - return Mage::getSingleton('admin/session')->isAllowed('catalog/categories'); + return Mage::getSingleton('admin/session')->isAllowed('catalog/categories'); } } 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 d677c4e2a4..5a41353317 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php @@ -158,6 +158,12 @@ public function saveAction() if (!isset($data['is_configurable'])) { $data['is_configurable'] = 0; } + if (!isset($data['is_filterable'])) { + $data['is_filterable'] = 0; + } + if (!isset($data['is_filterable_in_search'])) { + $data['is_filterable_in_search'] = 0; + } if (is_null($model->getIsUserDefined()) || $model->getIsUserDefined() != 0) { $data['backend_type'] = $model->getBackendTypeByInput($data['frontend_input']); diff --git a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/GalleryController.php b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/GalleryController.php index 362853bd20..21525943af 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/GalleryController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/GalleryController.php @@ -58,7 +58,7 @@ public function uploadAction() $result = array('error'=>$e->getMessage(), 'errorcode'=>$e->getCode()); } - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } protected function _isAllowed() diff --git a/app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php b/app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php index 7fb0d9ad2e..aaeec49e77 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php @@ -515,10 +515,10 @@ protected function _initProductSave() * Initialize data for configurable product */ if (($data = $this->getRequest()->getPost('configurable_products_data')) && !$product->getConfigurableReadonly()) { - $product->setConfigurableProductsData(Zend_Json::decode($data)); + $product->setConfigurableProductsData(Mage::helper('core')->jsonDecode($data)); } if (($data = $this->getRequest()->getPost('configurable_attributes_data')) && !$product->getConfigurableReadonly()) { - $product->setConfigurableAttributesData(Zend_Json::decode($data)); + $product->setConfigurableAttributesData(Mage::helper('core')->jsonDecode($data)); } $product->setCanSaveConfigurableAttributes((bool)$this->getRequest()->getPost('affect_configurable_product_attributes') && !$product->getConfigurableReadonly()); @@ -893,7 +893,7 @@ public function quickCreateAction() ); } - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } protected function _isAllowed() diff --git a/app/code/core/Mage/Adminhtml/controllers/CustomerController.php b/app/code/core/Mage/Adminhtml/controllers/CustomerController.php index 9fd89d8fc1..ede6002828 100644 --- a/app/code/core/Mage/Adminhtml/controllers/CustomerController.php +++ b/app/code/core/Mage/Adminhtml/controllers/CustomerController.php @@ -203,15 +203,15 @@ public function saveAction() ); $customer->save(); - // send welcome email if ($customer->getWebsiteId() && $customer->hasData('sendemail')) { + $store_id = $customer->getStoreId(); if ($isNewCustomer) { - $customer->sendNewAccountEmail(); + $customer->sendNewAccountEmail('registered', '', $store_id); } // confirm not confirmed customer elseif ((!$customer->getConfirmation())) { - $customer->sendNewAccountEmail('confirmed'); + $customer->sendNewAccountEmail('confirmed', '', $store_id); } } diff --git a/app/code/core/Mage/Adminhtml/controllers/IndexController.php b/app/code/core/Mage/Adminhtml/controllers/IndexController.php index 88d9ab2124..e455fbec04 100644 --- a/app/code/core/Mage/Adminhtml/controllers/IndexController.php +++ b/app/code/core/Mage/Adminhtml/controllers/IndexController.php @@ -146,7 +146,7 @@ public function deniedJsonAction() protected function _getDeniedJson() { - return Zend_Json::encode( + return Mage::helper('core')->jsonEncode( array( 'ajaxExpired' => 1, 'ajaxRedirect' => $this->getUrl('*/index/login') diff --git a/app/code/core/Mage/Adminhtml/controllers/JsonController.php b/app/code/core/Mage/Adminhtml/controllers/JsonController.php index 3085026309..dcc5882875 100644 --- a/app/code/core/Mage/Adminhtml/controllers/JsonController.php +++ b/app/code/core/Mage/Adminhtml/controllers/JsonController.php @@ -47,6 +47,6 @@ public function countryRegionAction() } } - $this->getResponse()->setBody(Zend_Json::encode($arrRes)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($arrRes)); } } \ No newline at end of file diff --git a/app/code/core/Mage/Adminhtml/controllers/Media/UploaderController.php b/app/code/core/Mage/Adminhtml/controllers/Media/UploaderController.php index a8675a8e9d..c00ea66383 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Media/UploaderController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Media/UploaderController.php @@ -36,7 +36,7 @@ class Mage_Adminhtml_Media_UploaderController extends Mage_Adminhtml_Controller_ public function uploadAction() { - $this->getResponse()->setBody(Zend_Json::encode($_REQUEST)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($_REQUEST)); } public function indexAction() diff --git a/app/code/core/Mage/Adminhtml/controllers/Newsletter/TemplateController.php b/app/code/core/Mage/Adminhtml/controllers/Newsletter/TemplateController.php index 05e1e7c858..4fde237585 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Newsletter/TemplateController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Newsletter/TemplateController.php @@ -116,6 +116,9 @@ public function editAction () public function saveAction () { $request = $this->getRequest(); + if (!$request->isPost()) { + $this->getResponse()->setRedirect($this->getUrl('*/newsletter_template')); + } $template = Mage::getModel('newsletter/template'); if ($id = (int)$request->getParam('id')) { diff --git a/app/code/core/Mage/Adminhtml/controllers/Permissions/UserController.php b/app/code/core/Mage/Adminhtml/controllers/Permissions/UserController.php index 569da7ed70..7968d8b88a 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Permissions/UserController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Permissions/UserController.php @@ -84,6 +84,17 @@ public function saveAction() if ($data = $this->getRequest()->getPost()) { $model = Mage::getModel('admin/user'); $model->setData($data); + + $result = $model->validate(); + if (is_array($result)) { + Mage::getSingleton('adminhtml/session')->setUserData($data); + foreach ($result as $message) { + Mage::getSingleton('adminhtml/session')->addError($message); + } + $this->_redirect('*/*/edit', array('_current' => true)); + return $this; + } + try { $model->save(); if ( $uRoles = $this->getRequest()->getParam('roles', false) ) { @@ -105,7 +116,7 @@ public function saveAction() Mage::getSingleton('adminhtml/session')->setUserData(false); $this->_redirect('*/*/edit', array('user_id' => $model->getUserId())); return; - } catch (Exception $e) { + } catch (Mage_Core_Exception $e) { Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); Mage::getSingleton('adminhtml/session')->setUserData($data); $this->_redirect('*/*/edit', array('user_id' => $model->getUserId())); diff --git a/app/code/core/Mage/Adminhtml/controllers/Promo/QuoteController.php b/app/code/core/Mage/Adminhtml/controllers/Promo/QuoteController.php index cf1d0477c5..a159239baa 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Promo/QuoteController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Promo/QuoteController.php @@ -101,11 +101,12 @@ public function editAction() public function saveAction() { - if ($data = $this->getRequest()->getPost()) { + $data = $this->getRequest()->getPost(); + if ($data) { try { $model = Mage::getModel('salesrule/rule'); - - if ($id = $this->getRequest()->getParam('rule_id')) { + $id = $this->getRequest()->getParam('rule_id'); + if ($id) { $model->load($id); if ($id != $model->getId()) { Mage::throwException(Mage::helper('salesrule')->__('Wrong rule specified.')); @@ -121,14 +122,14 @@ public function saveAction() $data['actions'] = $data['rule']['actions']; } unset($data['rule']); - $model->loadPost($data); - Mage::getSingleton('adminhtml/session')->setPageData($model->getData()); + $session = Mage::getSingleton('adminhtml/session'); + $session->setPageData($model->getData()); $model->save(); - Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('salesrule')->__('Rule was successfully saved')); - Mage::getSingleton('adminhtml/session')->setPageData(false); + $session->addSuccess(Mage::helper('salesrule')->__('Rule was successfully saved')); + $session->setPageData(false); $this->_redirect('*/*/'); return; } catch (Exception $e) { 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 c8e00a5807..0c6f31d22f 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreateController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreateController.php @@ -175,7 +175,7 @@ protected function _processData() * Adding products to quote from special grid and */ if ($data = $this->getRequest()->getPost('add_products')) { - $this->_getOrderCreateModel()->addProducts(Zend_Json::decode($data)); + $this->_getOrderCreateModel()->addProducts(Mage::helper('core')->jsonDecode($data)); } /** @@ -236,7 +236,7 @@ protected function _processData() * Importing gift message allow items from specific product grid */ if ($data = $this->getRequest()->getPost('add_products')) { - $this->_getGiftmessageSaveModel()->importAllowQuoteItemsFromProducts(Zend_Json::decode($data)); + $this->_getGiftmessageSaveModel()->importAllowQuoteItemsFromProducts(Mage::helper('core')->jsonDecode($data)); } /** diff --git a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreditmemoController.php b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreditmemoController.php index 0389e8dd32..605c6130e5 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreditmemoController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/CreditmemoController.php @@ -190,14 +190,9 @@ protected function _initCreditmemo($update = false) } if (isset($data['shipping_amount'])) { - $creditmemo->setShippingAmount($data['shipping_amount']); + $creditmemo->setBaseShippingAmount((float)$data['shipping_amount']); } elseif ($invoice) { - $creditmemo->setShippingAmount($invoice->getShippingAmount()); - } - else { - $creditmemo->setShippingAmount( - $order->getBaseShippingAmount()-$order->getBaseShippingRefunded() - ); + $creditmemo->setBaseShippingAmount($invoice->getBaseShippingAmount()); } if (isset($data['adjustment_positive'])) { @@ -295,14 +290,14 @@ public function updateQtyAction() 'error' => true, 'message' => $e->getMessage() ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } catch (Exception $e) { $response = array( 'error' => true, 'message' => $this->__('Can not update item qty') ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } @@ -435,14 +430,14 @@ public function addCommentAction() 'error' => true, 'message' => $e->getMessage() ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } catch (Exception $e) { $response = array( 'error' => true, 'message' => $this->__('Can not add new comment.') ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } diff --git a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/InvoiceController.php b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/InvoiceController.php index 5350dc1741..e699c31544 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/InvoiceController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/InvoiceController.php @@ -271,14 +271,14 @@ public function updateQtyAction() 'error' => true, 'message' => $e->getMessage() ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } catch (Exception $e) { $response = array( 'error' => true, 'message' => $this->__('Can not update item qty') ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } @@ -453,14 +453,14 @@ public function addCommentAction() 'error' => true, 'message' => $e->getMessage() ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } catch (Exception $e) { $response = array( 'error' => true, 'message' => $this->__('Can not add new comment.') ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } diff --git a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/ShipmentController.php b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/ShipmentController.php index b147784b6f..efdb56b9a6 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/Order/ShipmentController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/Order/ShipmentController.php @@ -288,7 +288,7 @@ public function addTrackAction() ); } if (is_array($response)) { - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } @@ -327,7 +327,7 @@ public function removeTrackAction() ); } if (is_array($response)) { - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } @@ -368,7 +368,7 @@ public function viewTrackAction() } else { if (is_array($response)) { - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); @@ -399,14 +399,14 @@ public function addCommentAction() 'error' => true, 'message' => $e->getMessage() ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } catch (Exception $e) { $response = array( 'error' => true, 'message' => $this->__('Can not add new comment.') ); - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); } $this->getResponse()->setBody($response); } diff --git a/app/code/core/Mage/Adminhtml/controllers/Sales/OrderController.php b/app/code/core/Mage/Adminhtml/controllers/Sales/OrderController.php index a5b8131838..79e8b926b0 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Sales/OrderController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Sales/OrderController.php @@ -217,7 +217,7 @@ public function addCommentAction() ); } if (is_array($response)) { - $response = Zend_Json::encode($response); + $response = Mage::helper('core')->jsonEncode($response); $this->getResponse()->setBody($response); } } diff --git a/app/code/core/Mage/Adminhtml/controllers/System/AccountController.php b/app/code/core/Mage/Adminhtml/controllers/System/AccountController.php index 00cd23b06a..43ec1f6313 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/AccountController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/AccountController.php @@ -42,6 +42,9 @@ public function indexAction() $this->renderLayout(); } + /** + * Saving edited user information + */ public function saveAction() { $userId = Mage::getSingleton('admin/session')->getUser()->getId(); @@ -53,36 +56,33 @@ public function saveAction() ->setFirstname($this->getRequest()->getParam('firstname', false)) ->setLastname($this->getRequest()->getParam('lastname', false)) ->setEmail(strtolower($this->getRequest()->getParam('email', false))); - if ( $this->getRequest()->getParam('password', false) ) { - $user->setPassword($this->getRequest()->getParam('password', false)); + if ( $this->getRequest()->getParam('new_password', false) ) { + $user->setNewPassword($this->getRequest()->getParam('new_password', false)); } - try { - try { - $_isValid = Zend_Validate::is($user->getUsername(), 'NotEmpty') - && Zend_Validate::is($user->getFirstname(), 'NotEmpty') - && Zend_Validate::is($user->getLastname(), 'NotEmpty') - && Zend_Validate::is($user->getEmail(), 'EmailAddress'); + if ($this->getRequest()->getParam('password_confirmation', false)) { + $user->setPasswordConfirmation($this->getRequest()->getParam('password_confirmation', false)); + } - if (!$_isValid) { - Mage::throwException(Mage::helper('adminhtml')->__('Error while saving account. Please check all required fields')); - } - if ($user->userExists()) { - Mage::throwException(Mage::helper('adminhtml')->__('User with the same User Name or Email aleady exists')); - } - $user->save(); - Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Account successfully saved')); - } - catch (Mage_Core_Exception $e) { - throw $e; - } - catch (Exception $e) { - throw new Exception(Mage::helper('adminhtml')->__('Error while saving account. Please try again later')); + $result = $user->validate(); + if (is_array($result)) { + foreach($result as $error) { + Mage::getSingleton('adminhtml/session')->addError($error); } + $this->getResponse()->setRedirect($this->getUrl("*/*/")); + return; } - catch (Exception $e) { + + try { + $user->save(); + Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Account successfully saved')); + } + catch (Mage_Core_Exception $e) { Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); } + catch (Exception $e) { + Mage::getSingleton('adminhtml/session')->addError(Mage::helper('adminhtml')->__('Error while saving account. Please try again later')); + } $this->getResponse()->setRedirect($this->getUrl("*/*/")); } diff --git a/app/code/core/Mage/Adminhtml/controllers/System/ConfigController.php b/app/code/core/Mage/Adminhtml/controllers/System/ConfigController.php index 959e271762..348e98a94d 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/ConfigController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/ConfigController.php @@ -151,7 +151,7 @@ public function saveAction() $session->addSuccess(Mage::helper('adminhtml')->__('Configuration successfully saved')); } catch (Mage_Core_Exception $e) { - foreach(split("\n", $e->getMessage()) as $message) { + foreach(explode("\n", $e->getMessage()) as $message) { $session->addError($message); } } 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 dc01be85f5..bfcbe3bc73 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/Convert/ProfileController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/Convert/ProfileController.php @@ -256,7 +256,7 @@ public function batchRunAction() 'savedRows' => $saved, 'errors' => $errors ); - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } @@ -278,7 +278,7 @@ public function batchFinishAction() $result['error'] = Mage::helper('adminhtml')->__('Error while finished process. Please refresh cache'); } $batchModel->delete(); - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } } diff --git a/app/code/core/Mage/Adminhtml/controllers/System/Email/TemplateController.php b/app/code/core/Mage/Adminhtml/controllers/System/Email/TemplateController.php index 83733c1149..c3822d0f27 100644 --- a/app/code/core/Mage/Adminhtml/controllers/System/Email/TemplateController.php +++ b/app/code/core/Mage/Adminhtml/controllers/System/Email/TemplateController.php @@ -143,7 +143,7 @@ public function defaultTemplateAction() $template->loadDefault($this->getRequest()->getParam('code'), $this->getRequest()->getParam('locale')); - $this->getResponse()->setBody(Zend_Json::encode($template->getData())); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($template->getData())); } protected function _isAllowed() diff --git a/app/code/core/Mage/Adminhtml/controllers/Tax/ClassController.php b/app/code/core/Mage/Adminhtml/controllers/Tax/ClassController.php index f91050ffdd..8ec767ae1e 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Tax/ClassController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Tax/ClassController.php @@ -66,7 +66,9 @@ public function saveAction() } $this->_redirectReferer(); + return; } + $this->getResponse()->setRedirect($this->getUrl('*/tax_class')); } /** diff --git a/app/code/core/Mage/Adminhtml/controllers/Tax/RateController.php b/app/code/core/Mage/Adminhtml/controllers/Tax/RateController.php index c3311f5a54..44d978e1bb 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Tax/RateController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Tax/RateController.php @@ -97,7 +97,9 @@ public function saveAction() } $this->_redirectReferer(); + return; } + $this->getResponse()->setRedirect($this->getUrl('*/tax_rate')); } /** diff --git a/app/code/core/Mage/Adminhtml/controllers/Tax/RuleController.php b/app/code/core/Mage/Adminhtml/controllers/Tax/RuleController.php index 075ce23f2b..6e641a7d03 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Tax/RuleController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Tax/RuleController.php @@ -95,7 +95,9 @@ public function saveAction() Mage::getSingleton('adminhtml/session')->setRuleData($postData); $this->_redirectReferer(); + return; } + $this->getResponse()->setRedirect($this->getUrl('*/tax_rule')); } public function deleteAction() diff --git a/app/code/core/Mage/Adminhtml/controllers/UrlrewriteController.php b/app/code/core/Mage/Adminhtml/controllers/UrlrewriteController.php index 3ead699a4c..90e17e0b51 100644 --- a/app/code/core/Mage/Adminhtml/controllers/UrlrewriteController.php +++ b/app/code/core/Mage/Adminhtml/controllers/UrlrewriteController.php @@ -151,7 +151,7 @@ public function saveAction() // save and redirect $model->save(); Mage::getSingleton('adminhtml/session')->addSuccess( - Mage::helper('adminhtml')->__('Urlrewrite has been successfully saved') + Mage::helper('adminhtml')->__('URL Rewrite has been successfully saved') ); $this->_redirect('*/*/'); return; @@ -179,7 +179,7 @@ public function deleteAction() try { Mage::registry('current_urlrewrite')->delete(); Mage::getSingleton('adminhtml/session')->addSuccess( - Mage::helper('adminhtml')->__('Urlrewrite has been successfully deleted') + Mage::helper('adminhtml')->__('URL Rewrite has been successfully deleted') ); } catch (Exception $e) { diff --git a/app/code/core/Mage/AmazonPayments/Block/Asp/Form.php b/app/code/core/Mage/AmazonPayments/Block/Asp/Form.php index 4f0e2badd0..c616c5b0ad 100644 --- a/app/code/core/Mage/AmazonPayments/Block/Asp/Form.php +++ b/app/code/core/Mage/AmazonPayments/Block/Asp/Form.php @@ -41,4 +41,15 @@ protected function _construct() $this->setTemplate('amazonpayments/asp/form.phtml'); parent::_construct(); } + + /** + * Return method description message + * + * @return string + */ + public function getDescriptionMessage() + { + return $this->getMethod()->getChoiceMethodDescription(); + } + } diff --git a/app/code/core/Mage/AmazonPayments/Block/Asp/Redirect.php b/app/code/core/Mage/AmazonPayments/Block/Asp/Redirect.php index 74f19f342a..c6109f714c 100644 --- a/app/code/core/Mage/AmazonPayments/Block/Asp/Redirect.php +++ b/app/code/core/Mage/AmazonPayments/Block/Asp/Redirect.php @@ -33,6 +33,17 @@ */ class Mage_AmazonPayments_Block_Asp_Redirect extends Mage_Core_Block_Template { + + /** + * Return Amazon Simple Pay payment model + * + * @return Mage_AmazonPayments_Model_Payment_Asp + */ + public function getPayment() + { + return Mage::registry('amazonpayments_payment_asp'); + } + /** * Return Amazon Simple Pay payment url * @@ -40,7 +51,7 @@ class Mage_AmazonPayments_Block_Asp_Redirect extends Mage_Core_Block_Template */ public function getRedirectUrl() { - return Mage::registry('amazonpayments_payment_asp')->getPayRedirectUrl(); + return $this->getPayment()->getPayRedirectUrl(); } /** @@ -50,6 +61,17 @@ public function getRedirectUrl() */ public function getRedirectParams() { - return Mage::registry('amazonpayments_payment_asp')->getPayRedirectParams(); + return $this->getPayment()->getPayRedirectParams(); + } + + /** + * Return redirect message + * + * @return string + */ + public function getRedirectMessage() + { + return $this->getPayment()->getRedirectMessage(); } + } diff --git a/app/code/core/Mage/AmazonPayments/Model/Payment/Asp.php b/app/code/core/Mage/AmazonPayments/Model/Payment/Asp.php index cc409cc7f4..9ab650d7c0 100644 --- a/app/code/core/Mage/AmazonPayments/Model/Payment/Asp.php +++ b/app/code/core/Mage/AmazonPayments/Model/Payment/Asp.php @@ -183,7 +183,27 @@ public function getPayRedirectUrl() { return $this->getApi()->getPayUrl(); } + + /** + * Return choice method description + * + * @return string + */ + public function getChoiceMethodDescription() + { + return $this->getConfig('choice_method_description'); + } + /** + * Return redirect message + * + * @return string + */ + public function getRedirectMessage() + { + return $this->getConfig('redirect_message'); + } + /** * Return pay params for current order * diff --git a/app/code/core/Mage/AmazonPayments/Model/Payment/Cba.php b/app/code/core/Mage/AmazonPayments/Model/Payment/Cba.php index 5a6afc39cc..c8ec4a4b61 100644 --- a/app/code/core/Mage/AmazonPayments/Model/Payment/Cba.php +++ b/app/code/core/Mage/AmazonPayments/Model/Payment/Cba.php @@ -446,6 +446,8 @@ protected function _createNewOrder(array $newOrderDetails) ->setCustomerGroupId(Mage_Customer_Model_Group::NOT_LOGGED_IN_ID); } + $order->save(); + $quote->setIsActive(false); $quote->save(); diff --git a/app/code/core/Mage/AmazonPayments/etc/config.xml b/app/code/core/Mage/AmazonPayments/etc/config.xml index f31edee80a..a92a921461 100644 --- a/app/code/core/Mage/AmazonPayments/etc/config.xml +++ b/app/code/core/Mage/AmazonPayments/etc/config.xml @@ -210,6 +210,8 @@ Something like this is to be added to resolve bug #4890 https://authorize.payments.amazon.com/pba/paypipeline https://authorize.payments-sandbox.amazon.com/pba/paypipeline + Your billing address will be ignored and you will be redirected to Amazon Simple Pay website. + You will be redirected to Amazon Simple Pay in a few seconds. MAGENTO ASP PAYMENT MAGENTO ASP REFUND MAGENTO ASP CANCEL diff --git a/app/code/core/Mage/AmazonPayments/etc/system.xml b/app/code/core/Mage/AmazonPayments/etc/system.xml index 7b3e835b67..bd91ba946b 100644 --- a/app/code/core/Mage/AmazonPayments/etc/system.xml +++ b/app/code/core/Mage/AmazonPayments/etc/system.xml @@ -36,12 +36,12 @@


Signing up with Checkout by Amazon

To configure Checkout by Amazon™ you will need to enter your Checkout by Amazon™ Merchant ID, Access Key ID, and Secret Access Key.

-

If you do not already have a Checkout by Amazon™ account, click here to create one now. Sign-Up

+

If you do not already have a Checkout by Amazon™ account, click here to create one now. Sign-Up

To locate your Merchant ID, sign in to your Seller Central Checkout by Amazon™ account and click Settings > Checkout Pipeline Settings.

To locate your Access Key ID and Secret Access Key, sign in to your Seller Central Checkout by Amazon™ account and click Integration > AWS Key. Click the link to read the Amazon Web Services Customer Agreement, and then click the check box, if you are setting up a new Access Key ID.

To enable XML Order Reports click Settings > Checkout Pipeline Settings, and then clicking Edit under the Order Report Settings section. Select Order Report Type as XML to get XML Order Reports using SOAP APIs. Configure your downloads for hourly.

For additional information on setting up your Checkout by Amazon account, Click Here - FAQ.

-


Signup for Checkout by Amazon

+


Signup for Checkout by Amazon

]]> @@ -334,10 +334,26 @@ 0 + + + text + 30 + 1 + 1 + 0 + + + + text + 31 + 1 + 1 + 0 + text - 31 + 32 1 1 0 @@ -345,7 +361,7 @@ text - 32 + 33 1 1 0 @@ -353,7 +369,7 @@ text - 31 + 34 1 1 0 @@ -361,7 +377,7 @@ text - 33 + 35 1 1 0 @@ -369,7 +385,7 @@ text - 34 + 36 1 1 0 @@ -378,7 +394,7 @@ select adminhtml/system_config_source_email_identity - 35 + 37 1 1 0 @@ -387,7 +403,7 @@ select adminhtml/system_config_source_email_template - 36 + 38 1 1 0 @@ -396,7 +412,7 @@ select adminhtml/system_config_source_yesno - 37 + 39 1 1 0 @@ -405,7 +421,7 @@ select adminhtml/system_config_source_yesno - 38 + 40 1 1 0 @@ -414,7 +430,7 @@ select adminhtml/system_config_source_yesno - 39 + 41 1 1 0 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 ce69f229de..8d260b0648 100644 --- a/app/code/core/Mage/Api/Model/Server/Handler/Abstract.php +++ b/app/code/core/Mage/Api/Model/Server/Handler/Abstract.php @@ -37,10 +37,10 @@ abstract class Mage_Api_Model_Server_Handler_Abstract public function __construct() { - set_error_handler(array(get_class($this), 'handlePhpError'), E_ALL); + set_error_handler(array($this, 'handlePhpError'), E_ALL); } - static public function handlePhpError($errorCode, $errorMessage, $errorFile) + public function handlePhpError($errorCode, $errorMessage, $errorFile) { Mage::log($errorMessage . $errorFile); if (in_array($errorCode, array(E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR))) { diff --git a/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php index bd61f04b20..e4914cd01a 100644 --- a/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -123,7 +123,7 @@ public function getJsonConfig() 'specialPrice' => $this->getProduct()->getSpecialPrice() ); - return Zend_Json::encode($config); + return Mage::helper('core')->jsonEncode($config); } public function addRenderer($type, $block) diff --git a/app/code/core/Mage/Bundle/Model/Product/Type.php b/app/code/core/Mage/Bundle/Model/Product/Type.php index 68d584a964..eac0c050db 100644 --- a/app/code/core/Mage/Bundle/Model/Product/Type.php +++ b/app/code/core/Mage/Bundle/Model/Product/Type.php @@ -826,4 +826,33 @@ public function getSearchableData($product = null) return $searchData; } + + /** + * Check if product can be bought + * + * @param Mage_Catalog_Model_Product $product + * @return Mage_Bundle_Model_Product_Type + * @throws Mage_Core_Exception + */ + public function checkProductBuyState($product = null) + { + parent::checkProductBuyState($product); + $product = $this->getProduct($product); + $productOptionIds = $this->getOptionsIds($product); + $productSelections = $this->getSelectionsCollection($productOptionIds, $product); + + $selectionIds = $product->getCustomOption('bundle_selection_ids'); + $selectionIds = unserialize($selectionIds->getValue()); + foreach ($selectionIds as $selectionId) { + /* @var $selection Mage_Bundle_Model_Selection */ + $selection = $productSelections->getItemById($selectionId); + if (!$selection || !$selection->isSalable()) { + Mage::throwException( + Mage::helper('bundle')->__('Selected required options not available.') + ); + } + } + + return $this; + } } diff --git a/app/code/core/Mage/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php b/app/code/core/Mage/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php index a49b0fc468..2881f1d68d 100644 --- a/app/code/core/Mage/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php +++ b/app/code/core/Mage/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php @@ -109,7 +109,7 @@ public function draw() } $line[] = array( 'text' => $text, - 'feed' => 240 + 'feed' => 255 ); } diff --git a/app/code/core/Mage/Bundle/etc/config.xml b/app/code/core/Mage/Bundle/etc/config.xml index 1e5f4ae24f..61bbe2c904 100644 --- a/app/code/core/Mage/Bundle/etc/config.xml +++ b/app/code/core/Mage/Bundle/etc/config.xml @@ -28,7 +28,7 @@ - 0.1.7 + 0.1.8 diff --git a/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-install-0.1.0.php index c5fbde2262..8e39db93f3 100644 --- a/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-install-0.1.0.php +++ b/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-install-0.1.0.php @@ -159,7 +159,7 @@ $fieldList = array('price','special_price','special_from_date','special_to_date', 'minimal_price','cost','tier_price','weight','tax_class_id'); foreach ($fieldList as $field) { - $applyTo = split(',', $installer->getAttribute('catalog_product', $field, 'apply_to')); + $applyTo = explode(',', $installer->getAttribute('catalog_product', $field, 'apply_to')); if (!in_array('bundle', $applyTo)) { $applyTo[] = 'bundle'; $installer->updateAttribute('catalog_product', $field, 'apply_to', join(',', $applyTo)); diff --git a/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-upgrade-0.1.7-0.1.8.php b/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-upgrade-0.1.7-0.1.8.php new file mode 100644 index 0000000000..270e8d6716 --- /dev/null +++ b/app/code/core/Mage/Bundle/sql/bundle_setup/mysql4-upgrade-0.1.7-0.1.8.php @@ -0,0 +1,34 @@ +startSetup(); +$installer->getConnection()->addKey($installer->getTable('bundle/option_value'), 'UNQ_OPTION_STORE', + array('option_id', 'store_id'), 'unique'); +$installer->endSetup(); diff --git a/app/code/core/Mage/Catalog/Block/Layer/State.php b/app/code/core/Mage/Catalog/Block/Layer/State.php index 6cfe6acaf7..7cc72dcc82 100644 --- a/app/code/core/Mage/Catalog/Block/Layer/State.php +++ b/app/code/core/Mage/Catalog/Block/Layer/State.php @@ -68,9 +68,10 @@ public function getClearUrl() foreach ($this->getActiveFilters() as $item) { $filterState[$item->getFilter()->getRequestVar()] = $item->getFilter()->getCleanValue(); } - $params['_current'] = true; + $params['_current'] = true; $params['_use_rewrite'] = true; - $params['_query'] = $filterState; + $params['_query'] = $filterState; + $params['_escape'] = true; return Mage::getUrl('*/*/*', $params); } diff --git a/app/code/core/Mage/Catalog/Block/Layer/View.php b/app/code/core/Mage/Catalog/Block/Layer/View.php index 7733ae4ef5..a03966f30a 100644 --- a/app/code/core/Mage/Catalog/Block/Layer/View.php +++ b/app/code/core/Mage/Catalog/Block/Layer/View.php @@ -33,6 +33,16 @@ */ class Mage_Catalog_Block_Layer_View extends Mage_Core_Block_Template { + /** + * Get attribute filter block name + * + * @return string + */ + protected function _getAttributeFilterBlockName() + { + return 'catalog/layer_filter_attribute'; + } + /** * Prepare child blocks * @@ -52,7 +62,7 @@ protected function _prepareLayout() $filterableAttributes = $this->_getFilterableAttributes(); foreach ($filterableAttributes as $attribute) { - $filterBlockName = 'catalog/layer_filter_attribute'; + $filterBlockName = $this->_getAttributeFilterBlockName(); if ($attribute->getFrontendInput() == 'price') { $filterBlockName = 'catalog/layer_filter_price'; } diff --git a/app/code/core/Mage/Catalog/Block/Product/Abstract.php b/app/code/core/Mage/Catalog/Block/Product/Abstract.php index f537988b36..a5e6581d21 100644 --- a/app/code/core/Mage/Catalog/Block/Product/Abstract.php +++ b/app/code/core/Mage/Catalog/Block/Product/Abstract.php @@ -20,26 +20,26 @@ * * @category Mage * @package Mage_Catalog - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ /** - * Product abstract block + * Catalog Product Abstract Block * * @category Mage * @package Mage_Catalog - * @author Magento Core Team + * @author Magento Core Team */ abstract class Mage_Catalog_Block_Product_Abstract extends Mage_Core_Block_Template { - private $_priceBlock = array(); - private $_priceBlockDefaultTemplate = 'catalog/product/price.phtml'; - private $_tierPriceDefaultTemplate = 'catalog/product/view/tierprices.phtml'; - private $_priceBlockTypes = array(); + protected $_priceBlock = array(); + protected $_priceBlockDefaultTemplate = 'catalog/product/price.phtml'; + protected $_tierPriceDefaultTemplate = 'catalog/product/view/tierprices.phtml'; + protected $_priceBlockTypes = array(); - private $_reviewsHelperBlock; + protected $_reviewsHelperBlock; /** * Retrieve url for add product to cart @@ -52,9 +52,15 @@ abstract class Mage_Catalog_Block_Product_Abstract extends Mage_Core_Block_Templ public function getAddToCartUrl($product, $additional = array()) { if ($product->getTypeInstance(true)->hasRequiredOptions($product)) { - $url = $product->getProductUrl(); - $link = (strpos($url, '?') !== false) ? '&' : '?'; - return $url . $link . 'options=cart'; + if (!isset($additional['_escape'])) { + $additional['_escape'] = true; + } + if (!isset($additional['_query'])) { + $additional['_query'] = array(); + } + $additional['_query']['options'] = 'cart'; + + return $this->getProductUrl($product, $additional); } return $this->helper('checkout/cart')->getAddUrl($product, $additional); } @@ -67,11 +73,11 @@ public function getAddToCartUrl($product, $additional = array()) */ public function getAddToWishlistUrl($product) { - return $this->getUrl('wishlist/index/add',array('product'=>$product->getId())); + return $this->helper('wishlist')->getAddUrl($product); } /** - * Enter description here... + * Retrieve Add Product to Compare Products List URL * * @param Mage_Catalog_Model_Product $product * @return string @@ -299,4 +305,42 @@ public function getImageLabel($product=null, $mediaAttributeCode='image') return $label; } + /** + * Retrieve Product URL using UrlDataObject + * + * @param Mage_Catalog_Model_Product $product + * @param array $additional the route params + * @return string + */ + public function getProductUrl($product, $additional = array()) + { + if ($this->hasProductUrl($product)) { + if (!isset($additional['_escape'])) { + $additional['_escape'] = true; + } + return $product->getUrlModel()->getUrl($product, $additional); + } + + return '#'; + } + + /** + * Check Product has URL + * + * @param Mage_Catalog_Model_Product $product + * @return bool + */ + public function hasProductUrl($product) + { + if ($product->getVisibleInSiteVisibilities()) { + return true; + } + if ($product->hasUrlDataObject()) { + if (in_array($product->hasUrlDataObject()->getVisibility(), $product->getVisibleInSiteVisibilities())) { + return true; + } + } + + return false; + } } diff --git a/app/code/core/Mage/Catalog/Block/Product/Compare/Abstract.php b/app/code/core/Mage/Catalog/Block/Product/Compare/Abstract.php new file mode 100644 index 0000000000..f527a65e6e --- /dev/null +++ b/app/code/core/Mage/Catalog/Block/Product/Compare/Abstract.php @@ -0,0 +1,57 @@ + + */ +abstract class Mage_Catalog_Block_Product_Compare_Abstract extends Mage_Catalog_Block_Product_Abstract +{ + /** + * Retrieve Product Compare Helper + * + * @return Mage_Catalog_Helper_Product_Compare + */ + protected function _getHelper() + { + return Mage::helper('catalog/product_compare'); + } + + /** + * Retrieve Remove Item from Compare List URL + * + * @param Mage_Catalog_Model_Product $item + * @return string + */ + public function getRemoveUrl($item) + { + return $this->_getHelper()->getRemoveUrl($item); + } +} diff --git a/app/code/core/Mage/Catalog/Block/Product/Compare/List.php b/app/code/core/Mage/Catalog/Block/Product/Compare/List.php index c3fd2d63cc..bf67135156 100644 --- a/app/code/core/Mage/Catalog/Block/Product/Compare/List.php +++ b/app/code/core/Mage/Catalog/Block/Product/Compare/List.php @@ -32,7 +32,7 @@ * @package Mage_Catalog * @author Magento Core Team */ -class Mage_Catalog_Block_Product_Compare_List extends Mage_Catalog_Block_Product_Abstract +class Mage_Catalog_Block_Product_Compare_List extends Mage_Catalog_Block_Product_Compare_Abstract { /** * Product Compare items collection @@ -55,7 +55,8 @@ class Mage_Catalog_Block_Product_Compare_List extends Mage_Catalog_Block_Product */ protected function _prepareLayout() { - if ($headBlock = $this->getLayout()->getBlock('head')) { + $headBlock = $this->getLayout()->getBlock('head'); + if ($headBlock) { $headBlock->setTitle(Mage::helper('catalog')->__('Compare Products List') . ' - ' . $headBlock->getDefaultTitle()); } return parent::_prepareLayout(); @@ -70,17 +71,16 @@ public function getItems() { if (is_null($this->_items)) { Mage::helper('catalog/product_compare')->setAllowUsedFlat(false); + $this->_items = Mage::getResourceModel('catalog/product_compare_item_collection') ->useProductItem(true) ->setStoreId(Mage::app()->getStore()->getId()); if (Mage::getSingleton('customer/session')->isLoggedIn()) { - $this->_items - ->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId()); + $this->_items->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId()); } else { - $this->_items - ->setVisitorId(Mage::getSingleton('log/visitor')->getId()); + $this->_items->setVisitorId(Mage::getSingleton('log/visitor')->getId()); } $this->_items diff --git a/app/code/core/Mage/Catalog/Block/Product/Compare/Sidebar.php b/app/code/core/Mage/Catalog/Block/Product/Compare/Sidebar.php index 7e5b9c2c2f..8f21ad1802 100644 --- a/app/code/core/Mage/Catalog/Block/Product/Compare/Sidebar.php +++ b/app/code/core/Mage/Catalog/Block/Product/Compare/Sidebar.php @@ -20,42 +20,56 @@ * * @category Mage * @package Mage_Catalog - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ /** - * Catalog comapare sidebar block + * Catalog Comapare Products Sidebar Block * * @category Mage * @package Mage_Catalog - * @author Magento Core Team + * @author Magento Core Team */ - class Mage_Catalog_Block_Product_Compare_Sidebar extends Mage_Catalog_Block_Product_Abstract - { - protected function _construct() - { - $this->setId('compare'); - } - - public function getItems() - { - return $this->helper('catalog/product_compare')->getItemCollection(); - } +class Mage_Catalog_Block_Product_Compare_Sidebar extends Mage_Catalog_Block_Product_Compare_Abstract +{ + /** + * Initialize block + * + */ + protected function _construct() + { + $this->setId('compare'); + } - public function getRemoveUrl($item) + /** + * Retrieve Compare Products Collection + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item_Collection + */ + public function getItems() { - return $this->helper('catalog/product_compare')->getRemoveUrl($item); + return $this->_getHelper()->getItemCollection(); } + /** + * Retrieve Clean Compared Items URL + * + * @return string + */ public function getClearUrl() { - return $this->helper('catalog/product_compare')->getClearListUrl(); + return $this->_getHelper()->getClearListUrl(); } - public function getCompareUrl() - { - return $this->helper('catalog/product_compare')->getListUrl(); - } - } + /** + * Retrieve Full Compare page URL + * + * @return string + */ + public function getCompareUrl() + { + return $this->_getHelper()->getListUrl(); + } +} diff --git a/app/code/core/Mage/Catalog/Block/Product/View.php b/app/code/core/Mage/Catalog/Block/Product/View.php index 3e3f9840ad..b4e9b8da3f 100644 --- a/app/code/core/Mage/Catalog/Block/Product/View.php +++ b/app/code/core/Mage/Catalog/Block/Product/View.php @@ -144,7 +144,7 @@ public function getJsonConfig() } } - return Zend_Json::encode($config); + return Mage::helper('core')->jsonEncode($config); } /** diff --git a/app/code/core/Mage/Catalog/Block/Product/View/Options.php b/app/code/core/Mage/Catalog/Block/Product/View/Options.php index fa985048eb..318e843ef9 100644 --- a/app/code/core/Mage/Catalog/Block/Product/View/Options.php +++ b/app/code/core/Mage/Catalog/Block/Product/View/Options.php @@ -155,7 +155,7 @@ public function getJsonConfig() $config[$option->getId()] = $priceValue; } - return Zend_Json::encode($config); + return Mage::helper('core')->jsonEncode($config); } /** 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 a89413d081..2e66aaf4fc 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 @@ -171,7 +171,7 @@ public function getJsonConfig() 'taxConfig' => $taxConfig, ); - return Zend_Json::encode($config); + return Mage::helper('core')->jsonEncode($config); } /** diff --git a/app/code/core/Mage/Catalog/Helper/Category/Flat.php b/app/code/core/Mage/Catalog/Helper/Category/Flat.php index 1995bd37b6..9feea72b27 100644 --- a/app/code/core/Mage/Catalog/Helper/Category/Flat.php +++ b/app/code/core/Mage/Catalog/Helper/Category/Flat.php @@ -61,4 +61,14 @@ public function isRebuilt() { return Mage::getResourceSingleton('catalog/category_flat')->isRebuilt(); } + + /** + * Back Flat compatibility: check is built and enabled flat + * + * @return bool + */ + public function isBuilt() + { + return $this->isEnabled(true); + } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Helper/Image.php b/app/code/core/Mage/Catalog/Helper/Image.php index a100b40b32..dbdcdd558c 100644 --- a/app/code/core/Mage/Catalog/Helper/Image.php +++ b/app/code/core/Mage/Catalog/Helper/Image.php @@ -39,6 +39,7 @@ class Mage_Catalog_Helper_Image extends Mage_Core_Helper_Abstract protected $_watermark; protected $_watermarkPosition; protected $_watermarkSize; + protected $_watermarkImageOpacity; protected $_product; protected $_imageFile; protected $_placeholder; @@ -233,12 +234,14 @@ public function __toString() if( $this->_scheduleWatermark ) { $this->_getModel() ->setWatermarkPosition( $this->getWatermarkPosition() ) + ->setWatermarkImageOpacity( $this->getWatermarkImageOpacity() ) ->setWatermarkSize($this->parseSize($this->getWatermarkSize())) ->setWatermark($this->getWatermark(), $this->getWatermarkPosition()); } else { if( $watermark = Mage::getStoreConfig("design/watermark/{$this->_getModel()->getDestinationSubdir()}_image") ) { $this->_getModel() ->setWatermarkPosition( $this->getWatermarkPosition() ) + ->setWatermarkImageOpacity( $this->getWatermarkImageOpacity() ) ->setWatermarkSize($this->parseSize($this->getWatermarkSize())) ->setWatermark($watermark, $this->getWatermarkPosition()); } @@ -325,6 +328,26 @@ protected function getWatermarkSize() } } + public function setWatermarkImageOpacity($imageOpacity) + { + $this->_watermarkImageOpacity = $imageOpacity; + return $this; + } + + protected function getWatermarkImageOpacity() + { + if( $this->_watermarkImageOpacity ) { + return $this->_watermarkImageOpacity; + } + + if ($imageOpacity = Mage::getStoreConfig("design/watermark/{$this->_getModel()->getDestinationSubdir()}_imageOpacity")) + { + return $imageOpacity; + } + + return $this->_getModel()->getWatermarkImageOpacity(); + } + protected function setProduct($product) { $this->_product = $product; diff --git a/app/code/core/Mage/Catalog/Helper/Product/Compare.php b/app/code/core/Mage/Catalog/Helper/Product/Compare.php index e169a66cb6..1fc5dec88d 100644 --- a/app/code/core/Mage/Catalog/Helper/Product/Compare.php +++ b/app/code/core/Mage/Catalog/Helper/Product/Compare.php @@ -55,6 +55,16 @@ class Mage_Catalog_Helper_Product_Compare extends Mage_Core_Helper_Url */ protected $_allowUsedFlat = true; + /** + * Retrieve Catalog Session instance + * + * @return Mage_Catalog_Model_Session + */ + protected function _getSession() + { + return Mage::getSingleton('catalog/session'); + } + /** * Retrieve compare list url * @@ -166,7 +176,7 @@ public function getClearListUrl() /** * Retrieve compare list items collection * - * @return + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item_Collection */ public function getItemCollection() { @@ -182,7 +192,8 @@ public function getItemCollection() $this->_itemCollection->setVisitorId(Mage::getSingleton('log/visitor')->getId()); } - Mage::getSingleton('catalog/product_visibility')->addVisibleInSiteFilterToCollection($this->_itemCollection); + Mage::getSingleton('catalog/product_visibility') + ->addVisibleInSiteFilterToCollection($this->_itemCollection); $this->_itemCollection->addAttributeToSelect('name') ->addUrlRewrite() @@ -195,30 +206,34 @@ public function getItemCollection() /** * Calculate cache product compare collection * + * @param bool $logout * @return Mage_Catalog_Helper_Product_Compare */ - public function calculate() + public function calculate($logout = false) { - if (!Mage::getSingleton('customer/session')->isLoggedIn() - and !Mage::getSingleton('log/visitor')->hasCatalogCompareItemsCount() - ) { - Mage::getSingleton('log/visitor')->setCatalogCompareItemsCount(0); - return $this; - } - - $itemCollection = Mage::getResourceModel('catalog/product_compare_item_collection'); - /* @var $itemCollection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item_Collection */ - $itemCollection->setStoreId(Mage::app()->getStore()->getId()); - if (Mage::getSingleton('customer/session')->isLoggedIn()) { - $itemCollection->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId()); + // first visit + if (!$this->_getSession()->hasCatalogCompareItemsCount()) { + $count = 0; } else { - $itemCollection->setVisitorId(Mage::getSingleton('log/visitor')->getId()); + /* @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item_Collection */ + $collection = Mage::getResourceModel('catalog/product_compare_item_collection') + ->useProductItem(true); + if (!$logout && Mage::getSingleton('customer/session')->isLoggedIn()) { + $collection->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId()); + } + else { + $collection->setVisitorId(Mage::getSingleton('log/visitor')->getId()); + } + + Mage::getSingleton('catalog/product_visibility') + ->addVisibleInSiteFilterToCollection($collection); + + $count = $collection->getSize(); } - Mage::getSingleton('catalog/product_visibility') - ->addVisibleInSiteFilterToCollection($itemCollection); - Mage::getSingleton('log/visitor')->setCatalogCompareItemsCount($itemCollection->getSize()); + $this->_getSession()->setCatalogCompareItemsCount($count); + return $this; } @@ -229,10 +244,11 @@ public function calculate() */ public function getItemCount() { - if (is_null(Mage::getSingleton('log/visitor')->getCatalogCompareItemsCount())) { + if (!$this->_getSession()->hasCatalogCompareItemsCount()) { $this->calculate(); } - return Mage::getSingleton('log/visitor')->getCatalogCompareItemsCount(); + + return $this->_getSession()->getCatalogCompareItemsCount(); } /** diff --git a/app/code/core/Mage/Catalog/Model/Category.php b/app/code/core/Mage/Catalog/Model/Category.php index 1e31b2a3d3..912433ad74 100644 --- a/app/code/core/Mage/Catalog/Model/Category.php +++ b/app/code/core/Mage/Catalog/Model/Category.php @@ -262,7 +262,30 @@ public function getStoreIds() if ($storeIds = $this->getData('store_ids')) { return $storeIds; } - $storeIds = $this->getResource()->getStoreIds($this); + + if (!$this->getId()) { + return array(); + } + + $nodes = array(); + foreach ($this->getPathIds() as $id) { + $nodes[] = $id; + } + + $storeIds = array(); + $storeCollection = Mage::getModel('core/store')->getCollection()->loadByCategoryIds($nodes); + foreach ($storeCollection as $store) { + $storeIds[$store->getId()] = $store->getId(); + } + + $entityStoreId = $this->getStoreId(); + if (!in_array($entityStoreId, $storeIds)) { + array_unshift($storeIds, $entityStoreId); + } + if (!in_array(0, $storeIds)) { + array_unshift($storeIds, 0); + } + $this->setData('store_ids', $storeIds); return $storeIds; } @@ -762,7 +785,7 @@ public function getAvailableSortBy() return array(); } if ($available && !is_array($available)) { - $available = split(',', $available); + $available = explode(',', $available); } return $available; } diff --git a/app/code/core/Mage/Catalog/Model/Category/Attribute/Backend/Sortby.php b/app/code/core/Mage/Catalog/Model/Category/Attribute/Backend/Sortby.php index d453a83a5f..dbf258a42e 100644 --- a/app/code/core/Mage/Catalog/Model/Category/Attribute/Backend/Sortby.php +++ b/app/code/core/Mage/Catalog/Model/Category/Attribute/Backend/Sortby.php @@ -51,7 +51,7 @@ public function validate($object) if ($attributeCode == 'default_sort_by') { if ($available = $object->getData('available_sort_by')) { if (!is_array($available)) { - $available = split(',', $available); + $available = explode(',', $available); } if (!in_array($object->getData($attributeCode), $available)) { Mage::throwException(Mage::helper('eav')->__('Default Product Listing Sort by not exists on Available Product Listing Sort by')); @@ -85,7 +85,7 @@ public function afterLoad($object) { if ($attributeCode == 'available_sort_by') { $data = $object->getData($attributeCode); if ($data) { - $object->setData($attributeCode, split(',', $data)); + $object->setData($attributeCode, explode(',', $data)); } } return $this; diff --git a/app/code/core/Mage/Catalog/Model/Convert/Adapter/Product.php b/app/code/core/Mage/Catalog/Model/Convert/Adapter/Product.php index 16df3ceaa7..b79d1182b2 100644 --- a/app/code/core/Mage/Catalog/Model/Convert/Adapter/Product.php +++ b/app/code/core/Mage/Catalog/Model/Convert/Adapter/Product.php @@ -599,7 +599,7 @@ public function saveRow(array $importData) if (!is_array($websiteIds)) { $websiteIds = array(); } - $websiteCodes = split(',', $importData['websites']); + $websiteCodes = explode(',', $importData['websites']); foreach ($websiteCodes as $websiteCode) { try { $website = Mage::app()->getWebsite(trim($websiteCode)); @@ -630,7 +630,7 @@ public function saveRow(array $importData) $setValue = $value; if ($attribute->getFrontendInput() == 'multiselect') { - $value = split(self::MULTI_DELIMITER, $value); + $value = explode(self::MULTI_DELIMITER, $value); $isArray = true; $setValue = array(); } diff --git a/app/code/core/Mage/Catalog/Model/Convert/Parser/Product.php b/app/code/core/Mage/Catalog/Model/Convert/Parser/Product.php index bd4121057f..d4e472a2bf 100644 --- a/app/code/core/Mage/Catalog/Model/Convert/Parser/Product.php +++ b/app/code/core/Mage/Catalog/Model/Convert/Parser/Product.php @@ -403,6 +403,7 @@ public function unparse() 'websites' => '', 'attribute_set' => $this->getAttributeSetName($product->getEntityTypeId(), $product->getAttributeSetId()), 'type' => $product->getTypeId(), + 'category_ids' => join(',', $product->getCategoryIds()) ); if ($this->getStore()->getCode() == Mage_Core_Model_Store::ADMIN_CODE) { diff --git a/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php b/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php index 1f913c3a3a..0bed8286ac 100644 --- a/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php +++ b/app/code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php @@ -82,6 +82,17 @@ public function apply(Zend_Controller_Request_Abstract $request, $filterBlock) return $this; } + /** + * Check whether specified attribute can be used in LN + * + * @param Mage_Catalog_Model_Resource_Eav_Attribute $attribute + * @return bool + */ + protected function _getIsFilterableAttribute($attribute) + { + return $attribute->getIsFilterable(); + } + /** * Get data array for building attribute filter items * @@ -109,7 +120,7 @@ protected function _getItemsData() } if (Mage::helper('core/string')->strlen($option['value'])) { // Check filter type - if ($attribute->getIsFilterable() == self::OPTIONS_ONLY_WITH_RESULTS) { + if ($this->_getIsFilterableAttribute($attribute) == self::OPTIONS_ONLY_WITH_RESULTS) { if (!empty($optionsCount[$option['value']])) { $data[] = array( 'label' => $option['label'], 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 cbe35ace1c..07a1cda11a 100644 --- a/app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php +++ b/app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php @@ -74,6 +74,7 @@ public function getRemoveUrl() $params['_current'] = true; $params['_use_rewrite'] = true; $params['_query'] = $query; + $params['_escape'] = true; return Mage::getUrl('*/*/*', $params); } diff --git a/app/code/core/Mage/Catalog/Model/Layer/Filter/Price.php b/app/code/core/Mage/Catalog/Model/Layer/Filter/Price.php index be07d989c4..9b91a8c5eb 100644 --- a/app/code/core/Mage/Catalog/Model/Layer/Filter/Price.php +++ b/app/code/core/Mage/Catalog/Model/Layer/Filter/Price.php @@ -133,10 +133,11 @@ protected function _getCacheKey() $key = $this->getLayer()->getStateKey() . '_PRICES_GRP_' . Mage::getSingleton('customer/session')->getCustomerGroupId() . '_CURR_' . Mage::app()->getStore()->getCurrentCurrencyCode() + . '_ATTR_' . $this->getAttributeModel()->getAttributeCode() . '_LOC_' ; $taxReq = Mage::getSingleton('tax/calculation')->getRateRequest(false, false, false); - $key.= $taxReq->__toString(array(), '_'); + $key.= implode('_', $taxReq->getData()); return $key; } diff --git a/app/code/core/Mage/Catalog/Model/Product.php b/app/code/core/Mage/Catalog/Model/Product.php index 8a107c438a..dabf92d06f 100644 --- a/app/code/core/Mage/Catalog/Model/Product.php +++ b/app/code/core/Mage/Catalog/Model/Product.php @@ -290,40 +290,38 @@ public function getCategory() return $category; } + /** + * Set assigned category IDs array to product + * + * @param array|string $ids + * @return Mage_Catalog_Model_Product + */ public function setCategoryIds($ids) { if (is_string($ids)) { $ids = explode(',', $ids); - } elseif (!is_array($ids)) { + } + elseif (!is_array($ids)) { Mage::throwException(Mage::helper('catalog')->__('Invalid category IDs')); } - foreach ($ids as $i=>$v) { + foreach ($ids as $i => $v) { if (empty($v)) { unset($ids[$i]); } } + $this->setData('category_ids', $ids); return $this; } + /** + * Retrieve assigned category Ids + * + * @return array + */ public function getCategoryIds() { - if ($this->hasData('category_ids')) { - $ids = $this->_getData('category_ids'); - if (!is_array($ids)) { - $wasLocked = false; - if ($this->isLockedAttribute('category_ids')) { - $this->unlockAttribute('category_ids'); - $wasLocked = true; - } - - $ids = !empty($ids) ? explode(',', $ids) : array(); - $this->setData('category_ids', $ids); - if ($wasLocked) { - $this->lockAttribute('category_ids'); - } - } - } else { + if (!$this->hasData('category_ids')) { $wasLocked = false; if ($this->isLockedAttribute('category_ids')) { $this->unlockAttribute('category_ids'); @@ -334,6 +332,7 @@ public function getCategoryIds() $this->lockAttribute('category_ids'); } } + return $this->_getData('category_ids'); } @@ -344,7 +343,7 @@ public function getCategoryIds() */ public function getCategoryCollection() { - return $this->getResource()->getCategoryCollection($this); + return $this->_getResource()->getCategoryCollection($this); } /** @@ -531,6 +530,16 @@ protected function _afterLoad() return $this; } + /** + * Retrieve resource instance wrapper + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product + */ + protected function _getResource() + { + return parent::_getResource(); + } + /** * Clear cache related with product id * @@ -1060,16 +1069,41 @@ public function getVisibleInCatalogStatuses() return Mage::getSingleton('catalog/product_status')->getVisibleStatusIds(); } + /** + * Retrieve visible statuses + * + * @return array + */ + public function getVisibleStatuses() + { + return Mage::getSingleton('catalog/product_status')->getVisibleStatusIds(); + } + + /** + * Check Product visilbe in catalog + * + * @return bool + */ public function isVisibleInCatalog() { return in_array($this->getStatus(), $this->getVisibleInCatalogStatuses()); } + /** + * Retrieve visible in site visibilities + * + * @return array + */ public function getVisibleInSiteVisibilities() { return Mage::getSingleton('catalog/product_visibility')->getVisibleInSiteIds(); } + /** + * Check Product visible in site + * + * @return bool + */ public function isVisibleInSiteVisibility() { return in_array($this->getVisibility(), $this->getVisibleInSiteVisibilities()); @@ -1161,16 +1195,27 @@ public function getCustomDesignDate() } /** - * Get product url + * Retrieve Product URL * * @param bool $useSid * @return string */ - public function getProductUrl($useSid = true) + public function getProductUrl($useSid = null) { return $this->getUrlModel()->getProductUrl($this, $useSid); } + /** + * Retrieve URL in current store + * + * @param array $params the route params + * @return string + */ + public function getUrlInStore($params = array()) + { + return $this->getUrlModel()->getUrlInStore($this, $params); + } + public function formatUrlKey($str) { return $this->getUrlModel()->formatUrlKey($str); @@ -1431,26 +1476,16 @@ public function canBeShowInCategory($categoryId) return $this->_getResource()->canBeShowInCategory($this, $categoryId); } - + /** + * Retrieve category ids where product is available + * + * @return array + */ 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'); + return $this->_getResource()->getAvailableInCategories($this); } - /** * Retrieve default attribute set id * diff --git a/app/code/core/Mage/Catalog/Model/Product/Api.php b/app/code/core/Mage/Catalog/Model/Product/Api.php index 4b58a2a6e5..a29ab2b7e6 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Api.php +++ b/app/code/core/Mage/Catalog/Model/Product/Api.php @@ -183,7 +183,7 @@ public function create($type, $set, $sku, $productData) * @param string|int $store * @return boolean */ - public function update($productId, $productData = array(), $store = null) + public function update($productId, $productData, $store = null) { $product = $this->_getProduct($productId, $store); @@ -248,6 +248,10 @@ protected function _prepareDataForSave ($product, $productData) $product->setWebsiteIds($productData['websites']); } + if (Mage::app()->isSingleStoreMode()) { + $product->setWebsiteIds(array(Mage::app()->getStore(true)->getWebsite()->getId())); + } + if (isset($productData['stock_data']) && is_array($productData['stock_data'])) { $product->setStockData($productData['stock_data']); } diff --git a/app/code/core/Mage/Catalog/Model/Product/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Api/V2.php index 28c818f2a2..d153de5916 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Api/V2.php +++ b/app/code/core/Mage/Catalog/Model/Product/Api/V2.php @@ -34,7 +34,7 @@ class Mage_Catalog_Model_Product_Api_V2 extends Mage_Catalog_Model_Product_Api { - /** + /** * Retrieve list of products with basic info (id, sku, type, set, name) * * @param array $filters @@ -79,14 +79,14 @@ public function items($filters = null, $store = null) $result = array(); foreach ($collection as $product) { -// $result[] = $product->getData(); - $result[] = array( // Basic product data + $result[] = array( 'product_id' => $product->getId(), 'sku' => $product->getSku(), 'name' => $product->getName(), 'set' => $product->getAttributeSetId(), 'type' => $product->getTypeId(), - 'category_ids' => $product->getCategoryIds() + 'category_ids' => $product->getCategoryIds(), + 'website_ids' => $product->getWebsiteIds() ); } @@ -179,6 +179,7 @@ public function create($type, $set, $sku, $productData) } unset($productData->additional_attributes); } + foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { $_attrCode = $attribute->getAttributeCode(); if ($this->_isAllowedAttribute($attribute) @@ -287,8 +288,16 @@ protected function _prepareDataForSave ($product, $productData) $product->setWebsiteIds($productData->websites); } - if (property_exists($productData, 'stock_data') && is_array($productData->stock_data)) { - $product->setStockData($productData->stock_data); + if (Mage::app()->isSingleStoreMode()) { + $product->setWebsiteIds(array(Mage::app()->getStore(true)->getWebsite()->getId())); + } + + if (property_exists($productData, 'stock_data')) { + $_stockData = array(); + foreach ($productData->stock_data as $key => $value) { + $_stockData[$key] = $value; + } + $product->setStockData($_stockData); } } diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php index 790253d798..08e5d41bdd 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php @@ -78,7 +78,7 @@ public function beforeSave($object) } if(!is_array($value['images']) && strlen($value['images']) > 0) { - $value['images'] = Zend_Json::decode($value['images']); + $value['images'] = Mage::helper('core')->jsonDecode($value['images']); } if (!is_array($value['images'])) { diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Tierprice.php index e1274eb12e..3be9195005 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -225,6 +225,16 @@ public function afterSave($object) continue; } + if (intval($tierPrice['website_id']) > 0 && + (!is_array($object->getWebsiteIds()) || !in_array($tierPrice['website_id'], $object->getWebsiteIds()))) { + continue; + } + + if ($object->getStoreId() && + Mage::app()->getStore($object->getStoreId())->getWebsiteId() != $tierPrice['website_id']) { + continue; + } + $useForAllGroups = $tierPrice['cust_group'] == Mage_Customer_Model_Group::CUST_GROUP_ALL; $customerGroupId = !$useForAllGroups ? $tierPrice['cust_group'] : 0; $priceKey = join('-', array( diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api.php index 049f3fd74e..5fd156bf21 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api.php @@ -90,6 +90,10 @@ public function update($productId, $tierPrices) } } + if (intval($tierPrice['website']) > 0 && !in_array($tierPrice['website'], $product->getWebsiteIds())) { + $this->_fault('data_invalid', Mage::helper('catalog')->__('Invalid tier prices. Product is not associated to the requested website.')); + } + if (!isset($tierPrice['customer_group_id'])) { $tierPrice['customer_group_id'] = 'all'; } @@ -104,10 +108,8 @@ public function update($productId, $tierPrices) 'price_qty' => $tierPrice['qty'], 'price' => $tierPrice['price'] ); - } - try { if (is_array($errors = $product->validate())) { $this->_fault('data_invalid', implode("\n", $errors)); diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api/V2.php index dd9bf3d9af..31ad005979 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api/V2.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Tierprice/Api/V2.php @@ -42,7 +42,6 @@ class Mage_Catalog_Model_Product_Attribute_Tierprice_Api_V2 extends Mage_Catalog */ public function update($productId, $tierPrices) { - Mage::log($tierPrices); $product = $this->_initProduct($productId); if (!is_array($tierPrices)) { $this->_fault('data_invalid', Mage::helper('catalog')->__('Invalid Tier Prices')); @@ -67,6 +66,10 @@ public function update($productId, $tierPrices) } } + if (intval($tierPrice->website) > 0 && !in_array($tierPrice->website, $product->getWebsiteIds())) { + $this->_fault('data_invalid', Mage::helper('catalog')->__('Invalid tier prices. Product is not associated to the requested website.')); + } + if (!isset($tierPrice->customer_group_id)) { $tierPrice->customer_group_id = 'all'; } diff --git a/app/code/core/Mage/Catalog/Model/Product/Compare/Item.php b/app/code/core/Mage/Catalog/Model/Product/Compare/Item.php index d059dbc47e..9be4bb2a3c 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Compare/Item.php +++ b/app/code/core/Mage/Catalog/Model/Product/Compare/Item.php @@ -26,7 +26,7 @@ /** - * Catalog compare item model + * Catalog Compare Item Model * * @category Mage * @package Mage_Catalog @@ -43,6 +43,31 @@ protected function _construct() $this->_init('catalog/product_compare_item'); } + /** + * Retrieve Resource instance + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item + */ + protected function _getResource() + { + return parent::_getResource(); + } + + /** + * Set current store before save + * + * @return Mage_Catalog_Model_Product_Compare_Item + */ + protected function _beforeSave() + { + parent::_beforeSave(); + if (!$this->hasStoreId()) { + $this->setStoreId(Mage::app()->getStore()->getId()); + } + + return $this; + } + /** * Add customer data from customer object * @@ -52,7 +77,6 @@ protected function _construct() public function addCustomerData(Mage_Customer_Model_Customer $customer) { $this->setCustomerId($customer->getId()); - $this->setVisitorId(0); return $this; } @@ -91,7 +115,7 @@ public function addProductData($product) if ($product instanceof Mage_Catalog_Model_Product) { $this->setProductId($product->getId()); } - elseif(intval($product)) { + else if(intval($product)) { $this->setProductId(intval($product)); } @@ -120,29 +144,7 @@ public function getDataForSave() */ public function bindCustomerLogin() { - $customer = Mage::getSingleton('customer/session')->getCustomer(); - $visitorItemCollection = Mage::getResourceModel('catalog/product_compare_item_collection') - ->setObject('catalog/product_compare_item') - ->setVisitorId(Mage::getSingleton('log/visitor')->getId()) - ->load(); - - $customerItemCollection = $this->getResourceCollection() - ->setCustomerId($customer->getId()) - ->useProductItem(true) - ->load(); - - $customerProductIds = $customerItemCollection->getProductIds(); - - foreach ($visitorItemCollection as $item) { - if (in_array($item->getProductId(), $customerProductIds)) { - $item->delete(); - } - else { - $item->setCustomerId($customer->getId()) - ->setVisitorId(0) - ->save(); - } - } + $this->_getResource()->updateCustomerFromVisitor($this); Mage::helper('catalog/product_compare')->calculate(); return $this; @@ -156,7 +158,9 @@ public function bindCustomerLogin() */ public function bindCustomerLogout(Varien_Event_Observer $observer) { - Mage::getSingleton('log/visitor')->setCatalogCompareItemsCount(0); + $this->_getResource()->purgeVisitorByCustomer($this); + + Mage::helper('catalog/product_compare')->calculate(true); return $this; } @@ -170,4 +174,32 @@ public function clean() $this->_getResource()->clean($this); return $this; } + + /** + * Retrieve Customer Id if loggined + * + * @return int + */ + public function getCustomerId() + { + if (!$this->hasData('customer_id')) { + $customerId = Mage::getSingleton('customer/session')->getCustomerId(); + $this->setData('customer_id', $customerId); + } + return $this->getData('customer_id'); + } + + /** + * Retrieve Visitor Id + * + * @return int + */ + public function getVisitorId() + { + if (!$this->hasData('visitor_id')) { + $visitorId = Mage::getSingleton('log/visitor')->getId(); + $this->setData('visitor_id', $visitorId); + } + return $this->getData('visitor_id'); + } } diff --git a/app/code/core/Mage/Catalog/Model/Product/Compare/List.php b/app/code/core/Mage/Catalog/Model/Product/Compare/List.php index 071397aaaf..63c446b5bf 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Compare/List.php +++ b/app/code/core/Mage/Catalog/Model/Product/Compare/List.php @@ -20,33 +20,46 @@ * * @category Mage * @package Mage_Catalog - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ /** - * Product compare list + * Product Compare List Model * * @category Mage * @package Mage_Catalog - * @author Magento Core Team + * @author Magento Core Team */ class Mage_Catalog_Model_Product_Compare_List extends Varien_Object { + /** + * Add product to Compare List + * + * @param int|Mage_Catalog_Model_Product $product + * @return Mage_Catalog_Model_Product_Compare_List + */ public function addProduct($product) { + /* @var $item Mage_Catalog_Model_Product_Compare_Item */ $item = Mage::getModel('catalog/product_compare_item'); $this->_addVisitorToItem($item); - $item->loadByProduct($product); if (!$item->getId()) { $item->addProductData($product); $item->save(); } + return $this; } + /** + * Add products to compare list + * + * @param array $productIds + * @return Mage_Catalog_Model_Product_Compare_List + */ public function addProducts($productIds) { if (is_array($productIds)) { @@ -57,29 +70,62 @@ public function addProducts($productIds) return $this; } + /** + * Retrieve Compare Items Collection + * + * @return product_compare_item_collection + */ public function getItemCollection() { return Mage::getResourceModel('catalog/product_compare_item_collection'); } - public function removeProduct() + /** + * Remove product from compare list + * + * @param int|Mage_Catalog_Model_Product $product + * @return Mage_Catalog_Model_Product_Compare_List + */ + public function removeProduct($product) { + /* @var $item Mage_Catalog_Model_Product_Compare_Item */ + $item = Mage::getModel('catalog/product_compare_item'); + $this->_addVisitorToItem($item); + $item->loadByProduct($product); + if ($item->getId()) { + $item->delete(); + } + + return $this; } + /** + * Add visitor and customer data to compare item + * + * @param Mage_Catalog_Model_Product_Compare_Item $item + * @return Mage_Catalog_Model_Product_Compare_List + */ protected function _addVisitorToItem($item) { + $item->addVisitorId(Mage::getSingleton('log/visitor')->getId()); if (Mage::getSingleton('customer/session')->isLoggedIn()) { $item->addCustomerData(Mage::getSingleton('customer/session')->getCustomer()); } - else { - $item->addVisitorId(Mage::getSingleton('log/visitor')->getId()); - } + return $this; } + /** + * Check has compare items by visitor/customer + * + * @param int $customerId + * @param int $visitorId + * @return bool + */ public function hasItems($customerId, $visitorId) { - return Mage::getResourceSingleton('catalog/product_compare_item')->getCount($customerId, $visitorId); + return Mage::getResourceSingleton('catalog/product_compare_item') + ->getCount($customerId, $visitorId); } } \ No newline at end of file diff --git a/app/code/core/Mage/Catalog/Model/Product/Flat/Indexer.php b/app/code/core/Mage/Catalog/Model/Product/Flat/Indexer.php index b3328e6a0c..dee437f7b3 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Flat/Indexer.php +++ b/app/code/core/Mage/Catalog/Model/Product/Flat/Indexer.php @@ -70,13 +70,14 @@ public function rebuild($store = null) * * @param string $attributeCode * @param int $store + * @param int|array $productIds * @return Mage_Catalog_Model_Product_Flat_Indexer */ - public function updateAttribute($attributeCode, $store = null) + public function updateAttribute($attributeCode, $store = null, $productIds = null) { if (is_null($store)) { foreach (Mage::app()->getStores() as $store) { - $this->updateAttribute($attributeCode, $store->getId()); + $this->updateAttribute($attributeCode, $store->getId(), $productIds); } return $this; @@ -84,8 +85,8 @@ public function updateAttribute($attributeCode, $store = null) $this->_getResource()->prepareFlatTable($store); $attribute = $this->_getResource()->getAttribute($attributeCode); - $this->_getResource()->updateAttribute($attribute, $store); - $this->_getResource()->updateChildrenDataFromParent($store); + $this->_getResource()->updateAttribute($attribute, $store, $productIds); + $this->_getResource()->updateChildrenDataFromParent($store, $productIds); return $this; } diff --git a/app/code/core/Mage/Catalog/Model/Product/Flat/Observer.php b/app/code/core/Mage/Catalog/Model/Product/Flat/Observer.php index 4659757830..c359fd32d5 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Flat/Observer.php +++ b/app/code/core/Mage/Catalog/Model/Product/Flat/Observer.php @@ -289,16 +289,12 @@ public function customerGroupSaveAfter(Varien_Event_Observer $observer) /** * Update category ids in flat * + * @deprecated 1.3.2.2 * @param Varien_Event_Observer $observer * @return Mage_Catalog_Model_Product_Flat_Observer */ public function catalogCategoryChangeProducts(Varien_Event_Observer $observer) { - if (!$this->_getHelper()->isBuilt()) { - return $this; - } - $this->_getIndexer()->updateAttribute('category_ids'); - return $this; } } diff --git a/app/code/core/Mage/Catalog/Model/Product/Image.php b/app/code/core/Mage/Catalog/Model/Product/Image.php index 2db9c23f61..0cb8af56ca 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Image.php +++ b/app/code/core/Mage/Catalog/Model/Product/Image.php @@ -50,6 +50,7 @@ class Mage_Catalog_Model_Product_Image extends Mage_Core_Model_Abstract protected $_watermarkPosition; protected $_watermarkWidth; protected $_watermarkHeigth; + protected $_watermarkImageOpacity = 70; /** * @return Mage_Catalog_Model_Product_Image @@ -375,7 +376,7 @@ public function setAngle($angle) /** * @return Mage_Catalog_Model_Product_Image */ - public function setWatermark($file, $position=null, $size=null, $width=null, $heigth=null) + public function setWatermark($file, $position=null, $size=null, $width=null, $heigth=null, $imageOpacity=null) { $filename = false; @@ -399,10 +400,11 @@ public function setWatermark($file, $position=null, $size=null, $width=null, $he $filename = $baseDir . $file; } } - + if( $filename ) { $this->getImageProcessor() ->setWatermarkPosition( ($position) ? $position : $this->getWatermarkPosition() ) + ->setWatermarkImageOpacity( ($imageOpacity) ? $imageOpacity : $this->getWatermarkImageOpacity() ) ->setWatermarkWidth( ($width) ? $width : $this->getWatermarkWidth() ) ->setWatermarkHeigth( ($heigth) ? $heigth : $this->getWatermarkHeigth() ) ->watermark($filename); @@ -471,6 +473,17 @@ public function getWatermarkPosition() return $this->_watermarkPosition; } + public function setWatermarkImageOpacity($imageOpacity) + { + $this->_watermarkImageOpacity = $imageOpacity; + return $this; + } + + public function getWatermarkImageOpacity() + { + return $this->_watermarkImageOpacity; + } + /** * @return Mage_Catalog_Model_Product_Image */ diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php b/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php index 990193ee52..588eb1f7bf 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php @@ -176,7 +176,7 @@ public function validateUserValue($values) 'target' => $fileFullPath, 'overwrite' => true )); - if (!$upload->receive()) { + if (!$upload->receive($file)) { $this->setIsValid(false); Mage::throwException(Mage::helper('catalog')->__("File upload failed")); } @@ -216,7 +216,7 @@ public function validateUserValue($values) $option->getTitle() ); } elseif ($errorCode == Zend_Validate_File_ImageSize::WIDTH_TOO_BIG - || $errorCode == Zend_Validate_File_ImageSize::WIDTH_TOO_BIG) + || $errorCode == Zend_Validate_File_ImageSize::HEIGHT_TOO_BIG) { $errors[] = Mage::helper('catalog')->__("Maximum allowed image size for '%s' is %sx%s px.", $option->getTitle(), diff --git a/app/code/core/Mage/Catalog/Model/Product/Type.php b/app/code/core/Mage/Catalog/Model/Product/Type.php index c7a075090b..dfcd0cdf34 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type.php @@ -153,7 +153,16 @@ static public function getOptionText($optionId) static public function getTypes() { if (is_null(self::$_types)) { - self::$_types = Mage::getConfig()->getNode('global/catalog/product/type')->asArray(); + $productTypes = Mage::getConfig()->getNode('global/catalog/product/type')->asArray(); + foreach ($productTypes as $productKey => $productConfig) { + $moduleName = 'catalog'; + if (isset($productConfig['@']['module'])) { + $moduleName = $productConfig['@']['module']; + } + $translatedLabel = Mage::helper($moduleName)->__($productConfig['label']); + $productTypes[$productKey]['label'] = $translatedLabel; + } + self::$_types = $productTypes; } return self::$_types; 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 78f70bfd76..5c5fa48e4a 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type/Grouped.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type/Grouped.php @@ -265,6 +265,7 @@ public function save($product = null) */ public function prepareForCart(Varien_Object $buyRequest, $product = null) { + $product = $this->getProduct($product); $productsInfo = $buyRequest->getSuperGroup(); if (!empty($productsInfo) && is_array($productsInfo)) { $products = array(); @@ -286,12 +287,12 @@ public function prepareForCart(Varien_Object $buyRequest, $product = null) } $_result[0]->setCartQty($qty); - $_result[0]->addCustomOption('product_type', self::TYPE_CODE, $this->getProduct($product)); + $_result[0]->addCustomOption('product_type', self::TYPE_CODE, $product); $_result[0]->addCustomOption('info_buyRequest', serialize(array( 'super_product_config' => array( 'product_type' => self::TYPE_CODE, - 'product_id' => $this->getProduct($product)->getId() + 'product_id' => $product->getId() ) )) ); diff --git a/app/code/core/Mage/Catalog/Model/Product/Url.php b/app/code/core/Mage/Catalog/Model/Product/Url.php index 87824aa3cc..aa655ed568 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Url.php +++ b/app/code/core/Mage/Catalog/Model/Product/Url.php @@ -20,26 +20,41 @@ * * @category Mage * @package Mage_Catalog - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + /** * Product Url model * - * @author Magento Core Team + * @category Mage + * @package Mage_Catalog + * @author Magento Core Team */ - class Mage_Catalog_Model_Product_Url extends Varien_Object { + const CACHE_TAG = 'url_rewrite'; + + /** + * Static URL instance + * + * @var Mage_Core_Model_Url + */ protected static $_url; - protected static $_urlRewrite; - const CACHE_TAG = 'url_rewrite'; + /** + * Static URL Rewrite Instance + * + * @var Mage_Core_Model_Url_Rewrite + */ + protected static $_urlRewrite; /** - * @return Mage_Core_Model_Url - */ + * Retrieve URL Instance + * + * @return Mage_Core_Model_Url + */ public function getUrlInstance() { if (!self::$_url) { @@ -49,8 +64,10 @@ public function getUrlInstance() } /** - * @return Mage_Core_Model_Url_Rewrite - */ + * Retrieve URL Rewrite Instance + * + * @return Mage_Core_Model_Url_Rewrite + */ public function getUrlRewrite() { if (!self::$_urlRewrite) { @@ -61,6 +78,7 @@ public function getUrlRewrite() /** * 'no_selection' shouldn't be a valid image attribute value + * * @param string $image * @return string */ @@ -73,10 +91,23 @@ protected function _validImage($image) } /** - * Get product url + * Retrieve URL in current store + * + * @param Mage_Catalog_Model_Product $product + * @param array $params the URL route params + * @return string + */ + public function getUrlInStore(Mage_Catalog_Model_Product $product, $params = array()) + { + $params['_store_to_url'] = true; + return $this->getUrl($product, $params); + } + + /** + * Retrieve Product URL * * @param Mage_Catalog_Model_Product $product - * @param bool $useSid + * @param bool $useSid forced SID mode * @return string */ public function getProductUrl($product, $useSid = null) @@ -85,68 +116,20 @@ public function getProductUrl($product, $useSid = null) $useSid = Mage::app()->getUseSessionInUrl(); } - $categoryId = $product->getCategoryId() && !$product->getDoNotUseCategoryId() ? $product->getCategoryId() : 0; - - $cacheUrlKey = sprintf('url_%d_%d', $categoryId, $useSid); - $url = $product->getData($cacheUrlKey); - - if (is_null($url)) { - if ($product->getStoreId()) { - $this->getUrlInstance()->setStore($product->getStoreId()); - } - - // auto add SID to URL - $originalSid = $this->getUrlInstance()->getUseSession(); - if ($originalSid != $useSid) { - $this->getUrlInstance()->setUseSession($useSid); - } - - if ($product->hasData('request_path') && $product->getRequestPath() != '') { - $this->setData($cacheUrlKey, $this->getUrlInstance()->getDirectUrl($product->getRequestPath())); - $this->getUrlInstance()->setUseSession($originalSid); - return $this->getData($cacheUrlKey); - } - - Varien_Profiler::start('REWRITE: '.__METHOD__); - - $rewrite = $this->getUrlRewrite(); - if ($product->getStoreId()) { - $rewrite->setStoreId($product->getStoreId()); - } - else { - $rewrite->setStoreId(Mage::app()->getStore()->getId()); - } - - $idPath = 'product/'.$product->getId(); - if ($product->getCategoryId() && !$product->getDoNotUseCategoryId() && Mage::getStoreConfig('catalog/seo/product_use_categories')) { - $idPath .= '/'.$product->getCategoryId(); - } - - $rewrite->loadByIdPath($idPath); -//echo $this->getUrlInstance()->getBaseUrl(); - if ($rewrite->getId()) { - $this->setData($cacheUrlKey, $this->getUrlInstance()->getDirectUrl($rewrite->getRequestPath())); - Varien_Profiler::stop('REWRITE: '.__METHOD__); - $this->getUrlInstance()->setUseSession($originalSid); - return $this->getData($cacheUrlKey); - } - - Varien_Profiler::stop('REWRITE: '.__METHOD__); - Varien_Profiler::start('REGULAR: '.__METHOD__); - - $url = $this->getUrlInstance()->getUrl('catalog/product/view', array( - 'id' => $product->getId(), - 's' => $product->getUrlKey(), - 'category' => $product->getCategoryId() - )); - - $this->getUrlInstance()->setUseSession($originalSid); - - Varien_Profiler::stop('REGULAR: '.__METHOD__); + $params = array(); + if (!$useSid) { + $params['_nosid'] = true; } - return $url; + + return $this->getUrl($product, $params); } + /** + * Format Key for URL + * + * @param string $str + * @return string + */ public function formatUrlKey($str) { $urlKey = preg_replace('#[^0-9a-z]+#i', '-', Mage::helper('catalog/product_url')->format($str)); @@ -178,4 +161,70 @@ public function getUrlPath($product, $category=null) return Mage::helper('catalog/category')->getCategoryUrlPath($category->getUrlPath()) . '/' . $path; } -} \ No newline at end of file + + /** + * Retrieve Product URL using UrlDataObject + * + * @param Mage_Catalog_Model_Product $product + * @param array $params + * @return string + */ + public function getUrl(Mage_Catalog_Model_Product $product, $params = array()) + { + $routePath = ''; + $routeParams = $params; + + $storeId = $product->getStoreId(); + $categoryId = $product->getCategoryId() && !$product->getDoNotUseCategoryId() + ? $product->getCategoryId() : null; + + if ($product->hasUrlDataObject()) { + $requestPath = $product->getUrlDataObject()->getUrlRewrite(); + $routeParams['_store'] = $product->getUrlDataObject()->getStoreId(); + } + else { + $requestPath = $product->getRequestPath(); + if (empty($requestPath)) { + $idPath = sprintf('product/%d', $product->getEntityId()); + if ($categoryId) { + $idPath = sprintf('%s/%d', $idPath, $categoryId); + } + $rewrite = $this->getUrlRewrite(); + $rewrite->setStoreId($storeId) + ->loadByIdPath($idPath); + if ($rewrite->getId()) { + $requestPath = $rewrite->getRequestPath(); + $product->setRequestPath($requestPath); + } + } + } + + if (isset($routeParams['_store'])) { + $storeId = Mage::app()->getStore($routeParams['_store'])->getId(); + } + + if ($storeId != Mage::app()->getStore()->getId()) { + $routeParams['_store_to_url'] = true; + } + + if (!empty($requestPath)) { + $routeParams['_direct'] = $requestPath; + } + else { + $routePath = 'catalog/product/view'; + $routeParams['id'] = $product->getId(); + $routeParams['s'] = $product->getUrlKey(); + if ($categoryId) { + $routeParams['category'] = $categoryId; + } + } + + // reset cached URL instance GET query params + if (!isset($routeParams['_query'])) { + $routeParams['_query'] = array(); + } + + return $this->getUrlInstance()->setStore($storeId) + ->getUrl($routePath, $routeParams); + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Visibility.php b/app/code/core/Mage/Catalog/Model/Product/Visibility.php index 1ccf4191ac..0a21ef168a 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Visibility.php +++ b/app/code/core/Mage/Catalog/Model/Product/Visibility.php @@ -192,7 +192,13 @@ static public function getOptionText($optionId) */ public function getFlatColums() { - return array(); + return array($this->getAttribute()->getAttributeCode() => array( + 'type' => 'tinyint', + 'unsigned' => true, + 'is_null' => true, + 'default' => null, + 'extra' => null + )); } /** @@ -214,7 +220,8 @@ public function getFlatIndexes() */ public function getFlatUpdateSelect($store) { - return null; + return Mage::getResourceSingleton('eav/entity_attribute') + ->getFlatUpdateSelect($this->getAttribute(), $store); } /** diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Abstract.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Abstract.php index 8f63389d80..dabb857bcb 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Abstract.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Abstract.php @@ -152,17 +152,20 @@ protected function _insertAttribute($object, $attribute, $value) 'value' => $this->_prepareValueForSave($value, $attribute), 'store_id' => $this->getDefaultStoreId() ); + $fields = array(); - $values = array(); + $bind = array(); foreach ($row as $k => $v) { - $fields[] = $this->_getWriteAdapter()->quoteIdentifier('?', $k); - $values[] = $this->_getWriteAdapter()->quoteInto('?', $v); + $fields[] = $this->_getWriteAdapter()->quoteIdentifier($k); + $bind[':' . $k] = $v; } + $sql = sprintf('INSERT IGNORE INTO %s (%s) VALUES(%s)', $this->_getWriteAdapter()->quoteIdentifier($attribute->getBackend()->getTable()), - join(',', array_keys($row)), - join(',', $values)); - $this->_getWriteAdapter()->query($sql); + implode(',', $fields), + implode(',', array_keys($bind))); + + $this->_getWriteAdapter()->query($sql, $bind); if (!$lastId = $this->_getWriteAdapter()->lastInsertId()) { $select = $this->_getReadAdapter()->select() ->from($attribute->getBackend()->getTable(), 'value_id') 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 98672da76b..5d7b029516 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 @@ -267,10 +267,13 @@ protected function _saveCategoryProducts($category) /** * new category-product relationships + * */ $products = $category->getPostedProducts(); + /** * Example re-save category + * */ if (is_null($products)) { return $this; @@ -278,38 +281,35 @@ protected function _saveCategoryProducts($category) /** * old category-product relationships + * */ $oldProducts = $category->getProductsPosition(); $insert = array_diff_key($products, $oldProducts); $delete = array_diff_key($oldProducts, $products); + /** * Find product ids which are presented in both arrays + * */ $update = array_intersect_key($products, $oldProducts); + /** * Use for update just products with changed position + * */ $update = array_diff_assoc($update, $oldProducts); - $productTable = $this->getTable('catalog/product'); - $productUpdateSql = sprintf('UPDATE `%s` AS `e` SET `category_ids`=(SELECT - GROUP_CONCAT(`category_id`) FROM `%s` AS `cp` WHERE `cp`.`product_id`=`e`.`entity_id`) - WHERE `e`.`entity_id` IN(?)', $productTable, $this->_categoryProductTable); - /** * Delete products from category * */ if (!empty($delete)) { - $deleteIds = array_keys($delete); - $this->_getWriteAdapter()->delete($this->_categoryProductTable, - $this->_getWriteAdapter()->quoteInto('product_id in(?)', $deleteIds) . - $this->_getWriteAdapter()->quoteInto(' AND category_id=?', $category->getId()) - ); - - $sql = $this->_getWriteAdapter()->quoteInto($productUpdateSql, $deleteIds); - $this->_getWriteAdapter()->query($sql); + $cond = join(' AND ', array( + $this->_getWriteAdapter()->quoteInto('product_id IN(?)', array_keys($delete)), + $this->_getWriteAdapter()->quoteInto('category_id=?', $category->getId()) + )); + $this->_getWriteAdapter()->delete($this->_categoryProductTable, $cond); } /** @@ -317,20 +317,17 @@ protected function _saveCategoryProducts($category) * */ if (!empty($insert)) { - $insertSql = array(); - foreach ($insert as $k => $v) { - $insertSql[] = '('.(int)$category->getId().','.(int)$k.','.(int)$v.')'; + $data = array(); + foreach ($insert as $productId => $position) { + $data[] = array( + 'category_id' => $category->getId(), + 'product_id' => (int)$productId, + 'position' => (int)$position + ); } - $sql = sprintf( - 'INSERT INTO `%s` (`category_id`,`product_id`,`position`) VALUES%s', - $this->_categoryProductTable, - join(',', $insertSql) - ); - $this->_getWriteAdapter()->query($sql); - $insertIds = array_keys($insert); - $sql = $this->_getWriteAdapter()->quoteInto($productUpdateSql, $insertIds); - $this->_getWriteAdapter()->query($sql); + $this->_getWriteAdapter() + ->insertMultiple($this->_categoryProductTable, $data); } /** @@ -338,14 +335,13 @@ protected function _saveCategoryProducts($category) * */ if (!empty($update)) { - foreach ($update as $k => $v) { - $cond = array( + foreach ($update as $productId => $position) { + $where = join(' AND ', array( $this->_getWriteAdapter()->quoteInto('category_id=?', (int)$category->getId()), - $this->_getWriteAdapter()->quoteInto('product_id=?', (int)$k) - ); - $where = join(' AND ', $cond); + $this->_getWriteAdapter()->quoteInto('product_id=?', (int)$productId) + )); $bind = array( - 'position' => (int)$v + 'position' => (int)$position ); $this->_getWriteAdapter()->update($this->_categoryProductTable, $bind, $where); } @@ -371,6 +367,7 @@ protected function _saveCategoryProducts($category) /** * Get store identifiers where category is presented * + * @deprecated after 1.3.2.2 moved to model * @param Mage_Catalog_Model_Category $category * @return array */ @@ -380,13 +377,9 @@ public function getStoreIds($category) return array(); } - $nodePath = $this->_getTree() - ->getNodeById($category->getId()) - ->getPath(); - $nodes = array(); - foreach ($nodePath as $node) { - $nodes[] = $node->getId(); + foreach ($category->getPathIds() as $id) { + $nodes[] = $id; } $stores = array(); diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Collection.php index ae6976d6c7..5a81d4fac9 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Collection.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Collection.php @@ -168,18 +168,14 @@ public function getProductStoreId() } /** - * Load collection + * Enter description here... * - * @param bool $printQuery - * @param bool $logQuery + * @param boolean $printQuery + * @param boolean $logQuery * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Collection */ public function load($printQuery = false, $logQuery = false) { - if ($this->isLoaded()) { - return $this; - } - if ($this->_loadWithProductCount) { $this->addAttributeToSelect('all_children'); $this->addAttributeToSelect('is_anchor'); @@ -196,6 +192,8 @@ public function load($printQuery = false, $logQuery = false) /** * Load categories product count + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Collection */ protected function _loadProductCount() { diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Flat.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Flat.php index d2ecb36c67..ac91194822 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Flat.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Flat.php @@ -40,6 +40,12 @@ class Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Flat extends Mage_Core_Mod protected $_nodes = array(); + protected $_columns = null; + + protected $_columnsSql = null; + + protected $_attributeCodes = null; + /** * Inactive categories ids * @@ -175,9 +181,9 @@ protected function _loadNodes($parentNode = null, $recursionLevel = 0, $storeId $startLevel = $parentNode->getLevel(); } elseif (is_numeric($parentNode)) { $selectParent = $_conn->select() - ->from($this->getMainStoreTable()) + ->from($this->getMainStoreTable($storeId)) ->where('entity_id = ?', $parentNode) - ->where('store_id = ?', '0'); + ->where('store_id = ?', $storeId); if ($parentNode = $_conn->fetchRow($selectParent)) { $parentPath = $parentNode['path']; $startLevel = $parentNode['level']; @@ -265,7 +271,7 @@ public function getNodes($parentId, $recursionLevel = 0, $storeId = 0) { if (!$this->_loaded) { $selectParent = $this->_getReadAdapter()->select() - ->from($this->getMainStoreTable()) + ->from($this->getMainStoreTable($storeId)) ->where('entity_id = ?', $parentId); if ($parentNode = $this->_getReadAdapter()->fetchRow($selectParent)) { $parentNode['id'] = $parentNode['entity_id']; @@ -307,7 +313,7 @@ public function getCategories($parent, $recursionLevel = 0, $sorted=false, $asCo { if ($asCollection) { $parentPath = $this->_getReadAdapter()->fetchOne(new Zend_Db_Expr(" - SELECT path FROM {$this->getMainStoreTable()} WHERE entity_id = {$parent} + SELECT path FROM {$this->getMainStoreTable($this->getStoreId())} WHERE entity_id = {$parent} ")); $collection = Mage::getModel('catalog/category')->getCollection() ->addNameToResult() @@ -358,7 +364,7 @@ public function isRebuilt() { if ($this->_isRebuilt === null) { $select = $this->_getReadAdapter()->select() - ->from($this->getMainStoreTable($this->getStoreId()), 'entity_id') + ->from($this->getMainStoreTable(Mage::app()->getDefaultStoreView()->getId()), 'entity_id') ->limit(1); try { $this->_isRebuilt = (bool) $this->_getReadAdapter()->fetchOne($select); @@ -399,38 +405,73 @@ protected function _getTableSqlSchema($storeId = 0) * * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Flat */ - public function rebuild() + public function rebuild($stores = null) { - $_conn = $this->_getWriteAdapter(); - if ($this->getUseStoreTables()) { - $stores = array(); - $selectStores = $_conn->select() - ->from($this->getTable('core/store'), 'store_id'); - $stores = array(); - foreach ($_conn->fetchAll($selectStores) as $store) { - $stores[] = $store['store_id']; - } - $this->_createTable($stores); - } else { - $this->_createTable(0); + if ($stores === null) { + $stores = Mage::app()->getStores(); } - $select = $_conn->select() - ->from($this->getTable('catalog/category'), 'entity_id'); - $_categories = $_conn->fetchAll($select); - foreach ($_categories as $_category) { - foreach ($stores as $store) { - $_tmpCategory = Mage::getModel('catalog/category') - ->setStoreId($store) - ->load($_category['entity_id']); - if ($_tmpCategory->getId()) { - $this->_synchronize($_tmpCategory, 'insert'); + + if (!is_array($stores)) { + $stores = array($stores); + } + + $rootId = Mage_Catalog_Model_Category::TREE_ROOT_ID; + $categories = array(); + $categoriesIds = array(); + /* @var $store Mage_Core_Model_Store */ + foreach ($stores as $store) { + $this->_createTable($store->getId()); + + if (!isset($categories[$store->getRootCategoryId()])) { + $select = $this->_getWriteAdapter()->select() + ->from($this->getTable('catalog/category')) + ->where('path = ?', (string)$rootId) + ->orWhere('path = ?', "{$rootId}/{$store->getRootCategoryId()}") + ->orWhere('path LIKE ?', "{$rootId}/{$store->getRootCategoryId()}/%"); + $categories[$store->getRootCategoryId()] = $this->_getWriteAdapter()->fetchAll($select); + $categoriesIds[$store->getRootCategoryId()] = array(); + foreach ($categories[$store->getRootCategoryId()] as $category) { + $categoriesIds[$store->getRootCategoryId()][] = $category['entity_id']; + } + } + $categoriesIdsChunks = array_chunk($categoriesIds[$store->getRootCategoryId()], 500); + foreach ($categoriesIdsChunks as $categoriesIdsChunk) { + $attributesData = $this->_getAttributeValues($categoriesIdsChunk, $store->getId()); + $data = array(); + foreach ($categories[$store->getRootCategoryId()] as $category) { + if (!isset($attributesData[$category['entity_id']])) { + continue; + } + $category['store_id'] = $store->getId(); + $data[] = $this->_prepareValuesToInsert( + array_merge($category, $attributesData[$category['entity_id']]) + ); } + $this->_getWriteAdapter()->insertMultiple($this->getMainStoreTable($store->getId()), $data); } } - $_tmpCategory = null; return $this; } + /** + * Prepare array of column and columnValue pairs + * + * @param array $data + * @return array + */ + protected function _prepareValuesToInsert($data) + { + $values = array(); + foreach (array_keys($this->_columns) as $key => $column) { + if (isset($data[$column])) { + $values[$column] = $data[$column]; + } else { + $values[$column] = ''; + } + } + return $values; + } + /** * Create Flate Table(s) * @@ -445,63 +486,225 @@ public function createTable($stores) /** * Creating table and adding attributes as fields to table * - * @param array|integer $stores + * @param array|integer $store * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Flat */ - protected function _createTable($stores) + protected function _createTable($store) { - if (!is_array($stores)) { - $stores = (int) $stores; + $this->_getWriteAdapter()->query("DROP TABLE IF EXISTS `{$this->getMainStoreTable($store)}`;"); + $_tableSql = "CREATE TABLE `{$this->getMainStoreTable($store)}` (\n"; + if ($this->_columnsSql === null || $this->_columnsSql === null) { + $this->_columns = array_merge($this->_getStaticColumns(), $this->_getEavColumns()); + foreach ($this->_columns as $columnName => $columnData) { + $this->_columnsSql .= '`' . $columnName . '` ' . $columnData['type']; + $this->_columnsSql .= $columnData['is_unsigned'] ? ' unsigned' : ''; + $this->_columnsSql .= ($columnData['is_null'] ? '' : ' not null'); + $this->_columnsSql .= ($columnData['default'] === false ? '' : ' default \'' . $columnData['default'] . '\''); + $this->_columnsSql .= ",\n"; + } } - $_conn = $this->_getWriteAdapter(); - if ($this->getUseStoreTables() && is_array($stores)) { - foreach ($stores as $store) { - $_conn->query("DROP TABLE IF EXISTS `{$this->getMainStoreTable($store)}`"); - $_conn->query($this->_getTableSqlSchema($store)); + $_tableSql .= $this->_columnsSql; + $_tableSql .= "KEY `CATEGORY_FLAT_CATEGORY_ID` (`entity_id`), + KEY `CATEGORY_FLAT_STORE_ID` (`store_id`), + KEY `path` (`path`), + KEY `IDX_LEVEL` (`level`), + CONSTRAINT `FK_CATEGORY_FLAT_CATEGORY_ID_STORE_{$store}` FOREIGN KEY (`entity_id`) + REFERENCES `{$this->getTable('catalog/category')}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_CATEGORY_FLAT_STORE_ID_STORE_{$store}` FOREIGN KEY (`store_id`) + REFERENCES `{$this->getTable('core/store')}` (`store_id`) ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8"; + $this->_getWriteAdapter()->query($_tableSql); + return $this; + } + + /** + * Return array of static columns + * + * @return array + */ + protected function _getStaticColumns() + { + $columns = array(); + $columnsToSkip = array('entity_type_id', 'attribute_set_id'); + $describe = $this->_getWriteAdapter()->describeTable($this->getTable('catalog/category')); + foreach ($describe as $column) { + if (in_array($column['COLUMN_NAME'], $columnsToSkip)) { + continue; } - } else { - $_conn->query("DROP TABLE IF EXISTS `{$this->getMainStoreTable($stores)}`"); - $_conn->query($this->_getTableSqlSchema($stores)); - } - $selectAttribute = $_conn->select() - ->from($this->getTable('eav/entity_type'), array()) - ->join( - $this->getTable('eav/attribute'), - $this->getTable('eav/attribute').'.entity_type_id = '.$this->getTable('eav/entity_type').'.entity_type_id', - $this->getTable('eav/attribute').'.*' - ) - ->where($this->getTable('eav/entity_type').'.entity_type_code=?', 'catalog_category'); - $resultAttribute = $_conn->fetchAll($selectAttribute); - foreach ($resultAttribute as $attribute) { - $type = ''; + $_type = ''; + $_is_unsigned = ''; + switch ($column['DATA_TYPE']) { + case 'smallint': + case 'int': + $_type = $column['DATA_TYPE'] . '(11)'; + $_is_unsigned = (bool)$column['UNSIGNED']; + break; + case 'varchar': + $_type = $column['DATA_TYPE'] . '(' . $column['LENGTH'] . ')'; + $_is_unsigned = null; + break; + case 'datetime': + $_type = $column['DATA_TYPE']; + $_is_unsigned = null; + break; + case 'decimal': + $_type = $columns['DATA_TYPE'] . '(' . $column['PRECISION'] . ',' . $column['SCALE'] . ')'; + $_is_unsigned = null; + break; + } + $columns[$column['COLUMN_NAME']] = array( + 'type' => $_type, + 'is_unsigned' => $_is_unsigned, + 'is_null' => $column['NULLABLE'], + 'default' => ($column['DEFAULT'] === null ? false : $column['DEFAULT']) + ); + } + $columns['store_id'] = array( + 'type' => 'smallint(5)', + 'is_unsigned' => true, + 'is_null' => false, + 'default' => '0' + ); + return $columns; + } + + /** + * Return array of eav columns, skip attribute with static type + * + * @return array + */ + protected function _getEavColumns() + { + $columns = array(); + $attributes = $this->_getAttributes(); + foreach ($attributes as $attribute) { + if ($attribute['backend_type'] == 'static') { + continue; + } + $columns[$attribute['attribute_code']] = array(); switch ($attribute['backend_type']) { case 'varchar': - $type = 'varchar(255) not null default \'\''; + $columns[$attribute['attribute_code']] = array( + 'type' => 'varchar(255)', + 'is_unsigned' => null, + 'is_null' => false, + 'default' => '' + ); break; case 'int': - $type = 'int(10) not null default \'0\''; + $columns[$attribute['attribute_code']] = array( + 'type' => 'int(10)', + 'is_unsigned' => null, + 'is_null' => false, + 'default' => '0' + ); break; case 'text': - $type = 'text'; + $columns[$attribute['attribute_code']] = array( + 'type' => 'text', + 'is_unsigned' => null, + 'is_null' => true, + 'default' => null + ); break; case 'datetime': - $type = 'datetime not null default \'0000-00-00 00:00:00\''; + $columns[$attribute['attribute_code']] = array( + 'type' => 'datetime', + 'is_unsigned' => null, + 'is_null' => false, + 'default' => '0000-00-00 00:00:00' + ); break; case 'decimal': - $type = 'decimal(10,2) not null default \'0.00\''; + $columns[$attribute['attribute_code']] = array( + 'type' => 'decimal(12,4)', + 'is_unsigned' => null, + 'is_null' => false, + 'default' => '0.0000' + ); break; } - if ($type) { - if ($this->getUseStoreTables() && is_array($stores)) { - foreach ($stores as $store) { - $_conn->addColumn($this->getMainStoreTable($store), $attribute['attribute_code'], $type); - } - } else { - $_conn->addColumn($this->getMainStoreTable($stores), $attribute['attribute_code'], $type); - } + } + return $columns; + } + + /** + * Return array of attribute codes for entity type 'catalog_category' + * + * @return array + */ + protected function _getAttributes() + { + if ($this->_attributeCodes === null) { + $select = $this->_getWriteAdapter()->select() + ->from($this->getTable('eav/entity_type'), array()) + ->join( + $this->getTable('eav/attribute'), + $this->getTable('eav/attribute').'.entity_type_id = '.$this->getTable('eav/entity_type').'.entity_type_id', + $this->getTable('eav/attribute').'.*' + ) + ->where($this->getTable('eav/entity_type').'.entity_type_code=?', 'catalog_category'); + $this->_attributeCodes = array(); + foreach ($this->_getWriteAdapter()->fetchAll($select) as $attribute) { + $this->_attributeCodes[$attribute['attribute_id']] = $attribute; } } - return $this; + return $this->_attributeCodes; + } + + /** + * Return attribute values for given entities and store + * + * @param array $entityIds + * @param integer $store_id + * @return array + */ + protected function _getAttributeValues($entityIds, $store_id) + { + if (!is_array($entityIds)) { + $entityIds = array($entityIds); + } + $values = array(); + + foreach ($entityIds as $entityId) { + $values[$entityId] = array(); + } + $attributes = $this->_getAttributes(); + $attributesType = array( + 'varchar', + 'int', + 'decimal', + 'text', + 'datetime' + ); + foreach ($attributesType as $type) { + foreach ($this->_getAttributeTypeValues($type, $entityIds, $store_id) as $row) { + $values[$row['entity_id']][$attributes[$row['attribute_id']]['attribute_code']] = $row['value']; + } + } + return $values; + } + + /** + * Return attribute values for given entities and store of specific attribute type + * + * @param string $type + * @param array $entityIds + * @param integer $store_id + * @return array + */ + protected function _getAttributeTypeValues($type, $entityIds, $store_id) + { + $select = $this->_getWriteAdapter()->select() + ->from(array('default' => $this->getTable('catalog/category') . '_' . $type), array('entity_id', 'attribute_id')) + ->joinLeft( + array('store' => $this->getTable('catalog/category') . '_' . $type), + '`store`.entity_id = `default`.entity_id AND `store`.attribute_id = `default`.attribute_id AND `store`.store_id = ' . $store_id, + array('value' => new Zend_Db_Expr('IFNULL(`store`.value, `default`.value)')) + ) + ->where('`default`.entity_id IN (?)', $entityIds) + ->where('`default`.store_id = ?', 0); + return $this->_getWriteAdapter()->fetchAll($select); } /** @@ -547,7 +750,7 @@ protected function _synchronize($category, $action = null) if (is_null($action)) { $select = $this->_getWriteAdapter()->select() ->from($this->getMainStoreTable($category->getStoreId()), 'entity_id') - ->where('entity_id = ?', $category->getId()); + ->where('entity_id = ?', $category->getEntityId()); if ($result = $this->_getWriteAdapter()->fetchOne($select)) { $action = 'update'; } else { @@ -560,7 +763,7 @@ protected function _synchronize($category, $action = null) $this->_getWriteAdapter()->update( $this->getMainStoreTable($category->getStoreId()), $this->_prepareDataForAllFields($category), - $this->_getWriteAdapter()->quoteInto('entity_id = ?', $category->getId()) + $this->_getWriteAdapter()->quoteInto('entity_id = ?', $category->getEntityId()) ); } elseif ($action == 'insert') { // insert @@ -625,7 +828,7 @@ public function synchronize($category = null, $storeIds = array()) } $stores = $this->_getWriteAdapter()->fetchAll(" SELECT - s.store_id, s.website_id, c.path AS root_path + s.store_id, s.website_id, c.path AS root_path, sg.root_category_id FROM {$this->getTable('core/store')} AS s, {$this->getTable('core/store_group')} AS sg, @@ -635,36 +838,30 @@ public function synchronize($category = null, $storeIds = array()) AND c.entity_id=sg.root_category_id {$storesCondition} "); + $storesObjects = array(); foreach ($stores as $store) { - $select = $this->_getWriteAdapter()->select() - ->from($this->getTable('catalog/category'), 'entity_id') - ->where('path LIKE ?', "{$store['root_path']}/%") - ->orWhere('path = ?', $store['root_path']); - $_categories = $this->_getWriteAdapter()->fetchAll($select); - if (!$this->_getWriteAdapter()->showTableStatus($this->getMainStoreTable($store['store_id']))) { - $this->_createTable($store['store_id']); - } - $this->_getWriteAdapter()->delete( - $this->getMainStoreTable($store['store_id']), - $this->_getWriteAdapter()->quoteInto('store_id = ?', $store['store_id']) - ); - foreach ($_categories as $_category) { - $_tmpCategory = Mage::getModel('catalog/category') - ->setStoreId($store['store_id']) - ->load($_category['entity_id']); - $this->_synchronize($_tmpCategory, 'insert'); - } +// if ($this->_getWriteAdapter()->showTableStatus($this->getMainStoreTable($store['store_id']))) { +// $this->_getWriteAdapter()->truncate($this->getMainStoreTable($store['store_id'])); +// } + $_store = new Varien_Object(); + $_store->setData('id', $store['store_id']) + ->setData('root_category_id', $store['root_category_id']); + $storesObjects[] = $_store; } - $_tmpCategory = null; + $this->rebuild($storesObjects); } elseif ($category instanceof Mage_Catalog_Model_Category) { + $categoriesIds = array($category->getId()); foreach ($category->getStoreIds() as $storeId) { - $_tmpCategory = Mage::getModel('catalog/category') - ->setStoreId($storeId) - ->load($category->getId()); - $_tmpCategory->setStoreId($storeId); - $this->_synchronize($_tmpCategory); + if ($storeId == 0) { + continue; + } + $attributesData = $this->_getAttributeValues($categoriesIds, $storeId); + $data = new Varien_Object(); + $data->setData($category->getData()) + ->addData($attributesData[$category->getId()]) + ->setData('store_id', $storeId); + $this->_synchronize($data); } - $_tmpCategory = null; } return $this; } @@ -918,7 +1115,7 @@ public function getAllChildren($category) public function checkId($id) { $select = $this->_getReadAdapter()->select() - ->from($this->getMainStoreTable(), 'entity_id') + ->from($this->getMainStoreTable($this->getStoreId()), 'entity_id') ->where('entity_id=?', $id); return $this->_getReadAdapter()->fetchOne($select); } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Tree.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Tree.php index 7c4e3a785c..dc504829cd 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Tree.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Category/Tree.php @@ -58,6 +58,13 @@ class Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Tree extends Varien_Data_T */ protected $_inactiveCategoryIds = null; + /** + * store id + * + * @var integer + */ + protected $_storeId = null; + /** * Enter description here... * @@ -78,6 +85,31 @@ public function __construct() ); } + /** + * Set store id + * + * @param integer $storeId + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Tree + */ + public function setStoreId($storeId) + { + $this->_storeId = (int) $storeId; + return $this; + } + + /** + * Return store id + * + * @return integer + */ + public function getStoreId() + { + if ($this->_storeId === null) { + $this->_storeId = Mage::app()->getStore()->getId(); + } + return $this->_storeId; + } + /** * Enter description here... * @@ -506,13 +538,21 @@ protected function _createCollectionDataSelect($sorted = true, $optionalAttribut $attribute = Mage::getResourceSingleton('catalog/category')->getAttribute($attributeCode); // join non-static attribute table if (!$attribute->getBackend()->isStatic()) { - $tableAs = "_$attributeCode"; + $defaultTableAs = "default_$attributeCode"; + $storeTableAs = "store_$attributeCode"; $select->joinLeft( - array($tableAs => $attribute->getBackend()->getTable()), + array($defaultTableAs => $attribute->getBackend()->getTable()), sprintf('`%1$s`.entity_id=e.entity_id AND `%1$s`.attribute_id=%2$d AND `%1$s`.entity_type_id=e.entity_type_id AND `%1$s`.store_id=%3$d', - $tableAs, $attribute->getData('attribute_id'), Mage_Core_Model_App::ADMIN_STORE_ID + $defaultTableAs, $attribute->getData('attribute_id'), Mage_Core_Model_App::ADMIN_STORE_ID ), array($attributeCode => 'value') + ) + ->joinLeft( + array($storeTableAs => $attribute->getBackend()->getTable()), + sprintf('`%1$s`.entity_id=e.entity_id AND `%1$s`.attribute_id=%2$d AND `%1$s`.entity_type_id=e.entity_type_id AND `%1$s`.store_id=%3$d', + $storeTableAs, $attribute->getData('attribute_id'), $this->getStoreId() + ), + array($attributeCode => new Zend_Db_Expr("IFNULL(`{$storeTableAs}`.value, `$defaultTableAs`.value)")) ); } } 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 1160709652..859be61e43 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 @@ -80,15 +80,15 @@ public function getWebsiteIds($product) /** * Retrieve product category identifiers * - * @param $product - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product + * @param Mage_Catalog_Model_Product $product + * @return array */ public function getCategoryIds($product) { - $select = $this->_getWriteAdapter()->select() + $select = $this->_getReadAdapter()->select() ->from($this->_productCategoryTable, 'category_id') ->where('product_id=?', $product->getId()); - return $this->_getWriteAdapter()->fetchCol($select); + return $this->_getReadAdapter()->fetchCol($select); } /** @@ -111,15 +111,15 @@ public function getIdBySku($sku) protected function _beforeSave(Varien_Object $object) { if (!$object->getId() && $object->getSku()) { - $object->setId($this->getIdBySku($object->getSku())); + $object->setId($this->getIdBySku($object->getSku())); } $categoryIds = $object->getCategoryIds(); if ($categoryIds) { $categoryIds = Mage::getModel('catalog/category')->verifyIds($categoryIds); + $object->setCategoryIds($categoryIds); } - $object->setData('category_ids', implode(',', $categoryIds)); return parent::_beforeSave($object); } @@ -191,15 +191,14 @@ protected function _saveWebsiteIds($product) /** * Save product category relations * - * @param Mage_Catalog_Model_Product $product - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product + * @param Mage_Catalog_Model_Product $product + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product */ protected function _saveCategories(Varien_Object $object) { $categoryIds = $object->getCategoryIds(); - $oldCategoryIds = $object->getOrigData('category_ids'); - $oldCategoryIds = !empty($oldCategoryIds) ? explode(',', $oldCategoryIds) : array(); + $oldCategoryIds = $this->getCategoryIds($object); $object->setIsChangedCategories(false); @@ -208,23 +207,28 @@ protected function _saveCategories(Varien_Object $object) $write = $this->_getWriteAdapter(); if (!empty($insert)) { - $insertSql = array(); - foreach ($insert as $v) { - if (!empty($v)) { - $insertSql[] = '('.(int)$v.','.$object->getId().',0)'; + $data = array(); + foreach ($insert as $categoryId) { + if (empty($categoryId)) { + continue; } - }; - if ($insertSql) { - $write->query("insert into {$this->_productCategoryTable} - (category_id, product_id, position) values ".join(',', $insertSql)); + $data[] = array( + 'category_id' => (int)$categoryId, + 'product_id' => $object->getId(), + 'position' => 1 + ); + } + if ($data) { + $write->insertMultiple($this->_productCategoryTable, $data); } } if (!empty($delete)) { - $write->delete($this->_productCategoryTable, - $write->quoteInto('product_id=?', $object->getId()) - .' and '.$write->quoteInto('category_id in (?)', $delete) - ); + $where = join(' AND ', array( + $write->quoteInto('product_id=?', $object->getId()), + $write->quoteInto('category_id IN(?)', $delete) + )); + $write->delete($this->_productCategoryTable, $where); } if (!empty($insert) || !empty($delete)) { @@ -411,6 +415,20 @@ public function getCategoryCollection($product) return $collection; } + /** + * Retrieve category ids where product is available + * + * @param Mage_Catalog_Model_Product $object + * @return array + */ + public function getAvailableInCategories($object) + { + $select = $this->_getReadAdapter()->select() + ->from($this->getTable('catalog/category_product_index'), array('category_id')) + ->where('product_id=?', $object->getEntityId()); + return $this->_getReadAdapter()->fetchCol($select); + } + /** * Get default attribute source model * 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 efdc4e91f5..74341c80eb 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 @@ -101,11 +101,12 @@ class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection * Product limitation filters * * Allowed filters - * store_id - * category_id - * category_is_anchor - * visibility - * website_ids + * store_id int; + * category_id int; + * category_is_anchor int; + * visibility array|int; + * website_ids array|int; + * store_table string; * * @var array */ @@ -288,7 +289,7 @@ public function _loadAttributes($printQuery = false, $logQuery = false) * If $attribute=='*' select all attributes * * @param array|string|integer|Mage_Core_Model_Config_Element $attribute - * @return Mage_Eav_Model_Entity_Collection_Abstract + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */ public function addAttributeToSelect($attribute, $joinType = false) { @@ -351,12 +352,51 @@ protected function _afterLoad() if ($this->_addFinalPrice) { $this->_addFinalPrice(); } + + $this->_prepareUrlDataObject(); + if (count($this) > 0) { Mage::dispatchEvent('catalog_product_collection_load_after', array('collection'=>$this)); } return $this; } + /** + * Prepare Url Data object + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection + */ + protected function _prepareUrlDataObject() + { + if (!$this->hasFlag('url_data_object')) { + return $this; + } + + $objects = array(); + /* @var $item Mage_Catalog_Model_Product */ + foreach ($this->_items as $item) { + if ($this->getFlag('do_not_use_category_id')) { + $item->setDoNotUseCategoryId(true); + } + if (!$item->isVisibleInSiteVisibility() && $item->getItemStoreId()) { + $objects[$item->getEntityId()] = $item->getItemStoreId(); + } + } + + if ($objects) { + $objects = Mage::getResourceSingleton('catalog/url') + ->getRewriteByProductStore($objects); + foreach ($this->_items as $item) { + if (isset($objects[$item->getEntityId()])) { + $object = new Varien_Object($objects[$item->getEntityId()]); + $item->setUrlDataObject($object); + } + } + } + + return $this; + } + /** * Add collection filters by identifiers * @@ -1176,6 +1216,9 @@ public function addAttributeToSort($attribute, $dir='asc') if ($column) { $this->getSelect()->order("e.{$column} {$dir}"); } + else if (isset($this->_joinFields[$attribute])) { + $this->getSelect()->order($this->_getAttributeFieldName($attribute).' '.$dir); + } return $this; } else { @@ -1270,6 +1313,75 @@ protected function _productLimitationJoinWebsite() return $this; } + /** + * Join additional (alternative) store visibility filter + * + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection + */ + protected function _productLimitationJoinStore() + { + $filters = $this->_productLimitationFilters; + if (!isset($filters['store_table'])) { + return $this; + } + + $hasColumn = false; + foreach ($this->getSelect()->getPart(Zend_Db_Select::COLUMNS) as $columnEntry) { + list(,,$alias) = $columnEntry; + if ($alias == 'visibility') { + $hasColumn = true; + } + } + if (!$hasColumn) { + $this->getSelect()->columns('visibility', 'cat_index'); + } + + $fromPart = $this->getSelect()->getPart(Zend_Db_Select::FROM); + if (!isset($fromPart['store_index'])) { + $this->getSelect()->joinLeft( + array('store_index' => $this->getTable('core/store')), + 'store_index.store_id='.$filters['store_table'].'.store_id', + array() + ); + } + if (!isset($fromPart['store_group_index'])) { + $this->getSelect()->joinLeft( + array('store_group_index' => $this->getTable('core/store_group')), + 'store_index.group_id=store_group_index.group_id', + array() + ); + } + if (!isset($fromPart['store_cat_index'])) { + $this->getSelect()->joinLeft( + array('store_cat_index' => $this->getTable('catalog/category_product_index')), + join(' AND ', array( + 'store_cat_index.product_id=e.entity_id', + 'store_cat_index.store_id='.$filters['store_table'].'.store_id', + 'store_cat_index.category_id=store_group_index.root_category_id' + )), + array('store_visibility' => 'visibility') + ); + } + $whereCond = join(' OR ', array( + $this->getConnection()->quoteInto('cat_index.visibility IN(?)', $filters['visibility']), + $this->getConnection()->quoteInto('store_cat_index.visibility IN(?)', $filters['visibility']) + )); + + $wherePart = $this->getSelect()->getPart(Zend_Db_Select::WHERE); + $hasCond = false; + foreach ($wherePart as $cond) { + if ($cond == '('.$whereCond.')') { + $hasCond = true; + } + } + + if (!$hasCond) { + $this->getSelect()->where($whereCond); + } + + return $this; + } + /** * Apply limitation filters to collection * @@ -1294,7 +1406,7 @@ protected function _applyProductLimitations() 'cat_index.product_id=e.entity_id', $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id']) ); - if (isset($filters['visibility'])) { + if (isset($filters['visibility']) && !isset($filters['store_table'])) { $conditions[] = $this->getConnection() ->quoteInto('cat_index.visibility IN(?)', $filters['visibility']); } @@ -1319,6 +1431,8 @@ protected function _applyProductLimitations() ); } + $this->_productLimitationJoinStore(); + Mage::dispatchEvent('catalog_product_collection_apply_limitations_after', array( 'collection' => $this )); diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item.php index d591c3d767..396d5b74f5 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item.php @@ -57,15 +57,18 @@ public function loadByProduct(Mage_Catalog_Model_Product_Compare_Item $object, $ $productId = $product->getId(); } else { - $productId = (int) $product; + $productId = (int)$product; } $select = $read->select()->from($this->getMainTable()) - ->where('product_id=?', $productId) - ->where('visitor_id=?', $object->getVisitorId()); + ->where('product_id=?', $productId); + if ($object->getCustomerId()) { $select->where('customer_id=?', $object->getCustomerId()); } + else { + $select->where('visitor_id=?', $object->getVisitorId()); + } $data = $read->fetchRow($select); @@ -99,21 +102,20 @@ public function getCount($customerId, $visitorId) /** * Clean compare table * - * @param Mage_Catalog_Model_Product_Compare_Item $object * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item */ - public function clean(Mage_Catalog_Model_Product_Compare_Item $object) + public function clean() { while (true) { $select = $this->_getReadAdapter()->select() ->from(array('compare_table' => $this->getMainTable()), array('catalog_compare_item_id')) ->joinLeft( array('visitor_table' => $this->getTable('log/visitor')), - '`visitor_table`.`visitor_id`=`compare_table`.`visitor_id`', + '`visitor_table`.`visitor_id`=`compare_table`.`visitor_id` AND `compare_table`.`customer_id` IS NULL', array()) - ->where('compare_table.visitor_id>?',0) + ->where('compare_table.visitor_id>?', 0) ->where('`visitor_table`.`visitor_id` IS NULL') - ->limit(1000); + ->limit(100); $itemIds = $this->_getReadAdapter()->fetchCol($select); if (!$itemIds) { @@ -128,4 +130,95 @@ public function clean(Mage_Catalog_Model_Product_Compare_Item $object) return $this; } + + /** + * Purge visitor data after customer logout + * + * @param Mage_Catalog_Model_Product_Compare_Item $object + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item + */ + public function purgeVisitorByCustomer($object) + { + if (!$object->getCustomerId()) { + return $this; + } + + $where = $this->_getWriteAdapter()->quoteInto('customer_id=?', $object->getCustomerId()); + $bind = array( + 'visitor_id' => 0, + ); + + $this->_getWriteAdapter()->update($this->getMainTable(), $bind, $where); + + return $this; + } + + /** + * Update (Merge) customer data from visitor + * + * After Login process + * + * @param Mage_Catalog_Model_Product_Compare_Item $object + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Compare_Item + */ + public function updateCustomerFromVisitor($object) + { + if (!$object->getCustomerId()) { + return $this; + } + + // collect visitor compared items + $select = $this->_getWriteAdapter()->select() + ->from($this->getMainTable()) + ->where('visitor_id=?', $object->getVisitorId()); + $visitor = $this->_getWriteAdapter()->fetchAll($select); + + // collect customer compared items + $select = $this->_getWriteAdapter()->select() + ->from($this->getMainTable()) + ->where('customer_id=?', $object->getCustomerId()) + ->where('visitor_id<>?', $object->getVisitorId()); + $customer = $this->_getWriteAdapter()->fetchAll($select); + + $products = array(); + $delete = array(); + $update = array(); + foreach ($visitor as $row) { + $products[$row['product_id']] = array( + 'store_id' => $row['store_id'], + 'customer_id' => $object->getCustomerId(), + 'visitor_id' => $object->getVisitorId(), + 'product_id' => $row['product_id'] + ); + $update[$row[$this->getIdFieldName()]] = $row['product_id']; + } + + foreach ($customer as $row) { + if (isset($products[$row['product_id']])) { + $delete[] = $row[$this->getIdFieldName()]; + } + else { + $products[$row['product_id']] = array( + 'store_id' => $row['store_id'], + 'customer_id' => $object->getCustomerId(), + 'visitor_id' => $object->getVisitorId(), + 'product_id' => $row['product_id'] + ); + } + } + + if ($delete) { + $this->_getWriteAdapter()->delete($this->getMainTable(), + $this->_getWriteAdapter()->quoteInto($this->getIdFieldName() . ' IN(?)', $delete)); + } + if ($update) { + foreach ($update as $itemId => $productId) { + $bind = $products[$productId]; + $this->_getWriteAdapter()->update($this->getMainTable(), $bind, + $this->_getWriteAdapter()->quoteInto($this->getIdFieldName() . '=?', $itemId)); + } + } + + return $this; + } } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item/Collection.php index 9e4f024dc8..de3f5e1b8d 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item/Collection.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Compare/Item/Collection.php @@ -138,18 +138,20 @@ public function getConditionForJoin() */ public function _addJoinToSelect() { - $this->joinField( - 'catalog_compare_item_id', - 'catalog/compare_item', - 'catalog_compare_item_id', + $this->joinTable( + array('t_compare' => 'catalog/compare_item'), 'product_id=entity_id', + array( + 'product_id' => 'product_id', + 'customer_id' => 'customer_id', + 'visitor_id' => 'visitor_id', + 'item_store_id' => 'store_id', + ), $this->getConditionForJoin() ); - $this->joinTable( - 'catalog/compare_item', - 'catalog_compare_item_id=catalog_compare_item_id', - array('product_id', 'customer_id', 'visitor_id')); - $this->addStoreFilter(); + + $this->_productLimitationFilters['store_table'] = 't_compare'; + return $this; } @@ -274,6 +276,10 @@ public function loadComparableAttributes() public function useProductItem() { $this->setObject('catalog/product'); + + $this->setFlag('url_data_object', true); + $this->setFlag('do_not_use_category_id', true); + return $this; } @@ -312,6 +318,8 @@ public function clear() $this->getConnection()->delete($this->getTable('catalog/compare_item'), $where); + Mage::dispatchEvent('catalog_product_compare_item_collection_clear'); + return $this; } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat.php index b90eb06955..6620eda4c2 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat.php @@ -53,7 +53,17 @@ protected function _construct() } /** - * Set store scope for resource model + * Retrieve store for resource model + * + * @return int + */ + public function getStoreId() + { + return $this->_storeId; + } + + /** + * Set store for resource model * * @param mixed $store * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Flat @@ -72,8 +82,8 @@ public function setStoreId($store) */ public function getFlatTableName($store = null) { - if (!is_numeric($store)) { - $store = Mage::app()->getStore($store)->getId(); + if (is_null($store)) { + $store = $this->getStoreId(); } return $this->getTable('catalog/product_flat') . '_' . $store; } @@ -193,4 +203,14 @@ public function getAttribute($attribute) return Mage::getSingleton('catalog/config') ->getAttribute('catalog_product', $attribute); } + + /** + * Retrieve main resource table name + * + * @return string + */ + public function getMainTable() + { + return $this->getFlatTableName($this->getStoreId()); + } } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat/Indexer.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat/Indexer.php index 1ad39934ec..075503c0fc 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat/Indexer.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Flat/Indexer.php @@ -390,6 +390,109 @@ public function getFlatIndexes() return $this->_indexes; } + /** + * Compare Flat style with Describe style columns + * + * If column a different - return false + * + * @param array $column + * @param array $describe + * @return bool + */ + protected function _compareColumnProperties($column, $describe) + { + $type = $column['type']; + $length = null; + $precision = null; + $scale = null; + + $matches = array(); + if (preg_match('/^((?:var)?char)\((\d+)\)/', $type, $matches)) { + $type = $matches[1]; + $length = $matches[2]; + } else if (preg_match('/^decimal\((\d+),(\d+)\)/', $type, $matches)) { + $type = 'decimal'; + $precision = $matches[1]; + $scale = $matches[2]; + } else if (preg_match('/^float\((\d+),(\d+)\)/', $type, $matches)) { + $type = 'float'; + $precision = $matches[1]; + $scale = $matches[2]; + } else if (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)?/', $type, $matches)) { + $type = $matches[1]; + } + + return ($describe['DATA_TYPE'] == $type) + && ($describe['DEFAULT'] == $column['default']) + && ((bool)$describe['NULLABLE'] == (bool)$column['is_null']) + && ((bool)$describe['UNSIGNED'] == (bool)$column['unsigned']) + && ($describe['LENGTH'] == $length) + && ($describe['SCALE'] == $scale) + && ($describe['PRECISION'] == $precision); + } + + /** + * Retrieve column definition fragment + * + * Example: `field_name` smallint(5) unsigned NOT NULL default '0' + * + * @param string $fieldName + * @param array $fieldProp + * @return string + */ + protected function _sqlColunmDefinition($fieldName, $fieldProp) + { + $fieldNameQuote = $this->_getWriteAdapter()->quoteIdentifier($fieldName); + return "{$fieldNameQuote} {$fieldProp['type']}" + . ($fieldProp['unsigned'] ? ' UNSIGNED' : '') + . ($fieldProp['extra'] ? ' ' . $fieldProp['extra'] : '') + . ($fieldProp['is_null'] === false ? ' NOT NULL' : '') + . ($fieldProp['default'] === null ? ' DEFAULT NULL' : $this->_getReadAdapter() + ->quoteInto(' DEFAULT ?', $fieldProp['default'])); + } + + /** + * Retrieve index definition fragment + * + * Example: INDEX `IDX_NAME` (`field_id`) + * + * @param string $indexName + * @param array $indexProp + * @return string + */ + protected function _sqlIndexDefinition($indexName, $indexProp) + { + $fields = $indexProp['fields']; + if (is_array($fields)) { + $fieldSql = array(); + foreach ($fields as $field) { + $fieldSql[] = $this->_getReadAdapter()->quoteIdentifier($field); + } + $fieldSql = join(',', $fieldSql); + } + else { + $fieldSql = $this->_getReadAdapter()->quoteIdentifier($fields); + } + + $indexNameQuote = $this->_getReadAdapter()->quoteIdentifier($indexName); + switch (strtolower($indexProp['type'])) { + case 'primary': + $condition = 'PRIMARY KEY'; + break; + case 'unique': + $condition = 'UNIQUE ' . $indexNameQuote; + break; + case 'fulltext': + $condition = 'FULLTEXT ' . $indexNameQuote; + break; + default: + $condition = 'INDEX ' . $indexNameQuote; + break; + } + + return sprintf('%s (%s)', $condition, $fieldSql); + } + /** * Prepare flat table for store * @@ -414,45 +517,12 @@ public function prepareFlatTable($store) if (!$this->_getWriteAdapter()->fetchRow($tableExistsSql)) { $sql = "CREATE TABLE {$tableNameQuote} (\n"; foreach ($columns as $field => $fieldProp) { - $fieldNameQuote = $this->_getWriteAdapter()->quoteIdentifier($field); - $sql .= " {$fieldNameQuote} {$fieldProp['type']}"; - $sql .= ($fieldProp['unsigned'] ? ' UNSIGNED' : ''); - $sql .= ($fieldProp['extra'] ? ' ' . $fieldProp['extra'] : ''); - $sql .= ($fieldProp['is_null'] === false ? ' NOT NULL' : ''); - $sql .= ($fieldProp['default'] === null - ? ' DEFAULT NULL' - : $this->_getReadAdapter()->quoteInto(' DEFAULT ?', $fieldProp['default'])); - $sql .= ",\n"; + $sql .= sprintf(" %s,\n", + $this->_sqlColunmDefinition($field, $fieldProp)); } foreach ($indexes as $indexName => $indexProp) { - $fields = $indexProp['fields']; - if (is_array($fields)) { - $fieldSql = array(); - foreach ($fields as $field) { - $fieldSql[] = $this->_getReadAdapter()->quoteIdentifier($field); - } - $fieldSql = join(',', $fieldSql); - } - else { - $fieldSql = $this->_getReadAdapter()->quoteIdentifier($fields); - } - - switch (strtolower($indexProp['type'])) { - case 'primary': - $condition = 'PRIMARY KEY'; - break; - case 'unique': - $condition = 'UNIQUE ' . $this->_getReadAdapter()->quoteIdentifier($indexName); - break; - case 'fulltext': - $condition = 'FULLTEXT ' . $this->_getReadAdapter()->quoteIdentifier($indexName); - break; - default: - $condition = 'KEY ' . $this->_getReadAdapter()->quoteIdentifier($indexName); - break; - } - - $sql .= " {$condition} (" . $fieldSql . "),\n"; + $sql .= sprintf(" %s,\n", + $this->_sqlIndexDefinition($indexName, $indexProp)); } $sql .= " CONSTRAINT `FK_CATALOG_PRODUCT_FLAT_{$store}_ENTITY` FOREIGN KEY (`entity_id`)" @@ -470,6 +540,7 @@ public function prepareFlatTable($store) $addColumns = array_diff_key($columns, $describe); $dropColumns = array_diff_key($describe, $columns); + $modifyColumns = array(); $addIndexes = array_diff_key($indexes, $indexList); $dropIndexes = array_diff_key($indexList, $indexes); @@ -492,6 +563,13 @@ public function prepareFlatTable($store) ); } + foreach ($columns as $field => $fieldProp) { + if (isset($describe[$field]) + && !$this->_compareColumnProperties($fieldProp, $describe[$field])) { + $modifyColumns[$field] = $fieldProp; + } + } + foreach ($indexList as $indexName => $indexProp) { if (isset($indexes[$indexName]) && ($indexes[$indexName]['type'] != $indexProp['type'])) { $dropIndexes[$indexName] = $indexProp; @@ -499,13 +577,14 @@ public function prepareFlatTable($store) } } - if ($addColumns or $dropColumns or $addIndexes or $dropIndexes) { + if ($addColumns or $dropColumns or $modifyColumns or $addIndexes or $dropIndexes) { $sql = "ALTER TABLE {$tableNameQuote}"; // drop columns foreach ($dropColumns as $columnName => $columnProp) { $columnNameQuote = $this->_getWriteAdapter()->quoteIdentifier($columnName); $sql .= " DROP COLUMN {$columnNameQuote},"; } + // drop indexes foreach ($dropIndexes as $indexName => $indexProp) { if ($indexName == 'PRIMARY') { @@ -517,54 +596,26 @@ public function prepareFlatTable($store) } } + // modify colunm + foreach ($modifyColumns as $columnName => $columnProp) { + $sql .= sprintf(' MODIFY COLUMN %s,', + $this->_sqlColunmDefinition($columnName, $columnProp)); + } + // add columns foreach ($addColumns as $columnName => $columnProp) { - //var_dump($columnProp); - $columnNameQuote = $this->_getWriteAdapter()->quoteIdentifier($columnName); - $sql .= " ADD COLUMN {$columnNameQuote} {$columnProp['type']}"; - $sql .= ($columnProp['unsigned'] ? ' UNSIGNED' : ''); - $sql .= ($columnProp['extra'] ? ' ' . $columnProp['extra'] : ''); - $sql .= ($columnProp['is_null'] === false ? ' NOT NULL' : ''); - $sql .= ($columnProp['default'] === null - ? ' DEFAULT NULL' - : $this->_getReadAdapter()->quoteInto(' DEFAULT ?', $columnProp['default'])); - if ($afterField = $this->_arrayPrevKey($columns, $columnName)) { - $sql .= ' AFTER ' . $this->_getWriteAdapter()->quoteIdentifier($afterField); + $sql .= sprintf(' ADD COLUMN %s', + $this->_sqlColunmDefinition($columnName, $columnProp)); + $afterColumn = $this->_arrayPrevKey($columns, $columnName); + if ($afterColumn) { + $sql .= ' AFTER ' . $this->_getWriteAdapter()->quoteIdentifier($afterColumn); } - $sql .= ","; + $sql .= ','; } // add indexes foreach ($addIndexes as $indexName => $indexProp) { - $indexNameQuote = $this->_getWriteAdapter()->quoteIdentifier($indexName); - - switch (strtolower($indexProp['type'])) { - case 'primary': - $condition = 'PRIMARY KEY'; - break; - case 'unique': - $condition = 'UNIQUE ' . $indexNameQuote; - break; - case 'fulltext': - $condition = 'FULLTEXT ' . $indexNameQuote; - break; - default: - $condition = 'INDEX ' . $indexNameQuote; - break; - } - - $fields = $indexProp['fields']; - if (is_array($fields)) { - $fieldSql = array(); - foreach ($fields as $field) { - $fieldSql[] = $this->_getReadAdapter()->quoteIdentifier($field); - } - $fieldSql = join(',', $fieldSql); - } - else { - $fieldSql = $this->_getReadAdapter()->quoteIdentifier($fields); - } - - $sql .= " ADD {$condition} ({$fieldSql}),"; + $sql .= sprintf(' ADD %s,', + $this->_sqlIndexDefinition($indexName, $indexProp)); } $sql = rtrim($sql, ","); $this->_getWriteAdapter()->query($sql); diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable/Attribute.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable/Attribute.php index 160fe465fb..7890ec1264 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable/Attribute.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Type/Configurable/Attribute.php @@ -269,4 +269,26 @@ public function savePrices($attribute) return $this; } + + /** + * Retrieve Used in Configurable Products Attributes + * + * @param int $setId The specific attribute set + * @return array + */ + public function getUsedAttributes($setId) + { + $select = $this->_getReadAdapter()->select() + ->distinct(true) + ->from(array('e' => $this->getTable('catalog/product')), null) + ->join( + array('a' => $this->getMainTable()), + 'e.entity_id=a.product_id', + array('attribute_id') + ) + ->where('e.attribute_set_id=?', $setId) + ->where('e.type_id=?', Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE); + + return $this->_getReadAdapter()->fetchCol($select); + } } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Url.php b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Url.php index 4581879c3f..b1b644e2c0 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Url.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Url.php @@ -20,7 +20,7 @@ * * @category Mage * @package Mage_Catalog - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ @@ -29,7 +29,7 @@ * * @category Mage * @package Mage_Catalog - * @author Magento Core Team + * @author Magento Core Team */ class Mage_Catalog_Model_Resource_Eav_Mysql4_Url extends Mage_Core_Model_Mysql4_Abstract { @@ -151,6 +151,14 @@ public function getRewriteByRequestPath($requestPath, $storeId) return $rewrite; } + /** + * Prepare rewrites for condition + * + * @param int $storeId + * @param int|array $categoryIds + * @param int|array $productIds + * @return array + */ public function prepareRewrites($storeId, $categoryIds = null, $productIds = null) { $rewrites = array(); @@ -185,7 +193,7 @@ public function prepareRewrites($storeId, $categoryIds = null, $productIds = nul } /** - * Save rewrite url + * Save rewrite URL * * @param array $rewriteData * @param Varien_Object $rewriteObject @@ -208,7 +216,7 @@ public function saveRewrite($rewriteData, $rewrite) $this->_getWriteAdapter()->insert($this->getMainTable(), $rewriteData); } catch (Exception $e) { - Mage::throwException(Mage::helper('catalog')->__('Url rewrie save problem.')); + Mage::throwException(Mage::helper('catalog')->__('URL rewrite save problem.')); } } unset($rewriteData); @@ -499,7 +507,7 @@ public function _getProductAttribute($attributeCode, $productIds, $storeId) protected function _prepareCategoryParentId(Varien_Object $category) { if ($category->getPath() != $category->getId()) { - $split = split('/', $category->getPath()); + $split = explode('/', $category->getPath()); $category->setParentId($split[(count($split) - 2)]); } else { @@ -537,6 +545,14 @@ protected function _prepareStoreRootCategories($stores) return $stores; } + /** + * Retrieve categories objects + * + * @param int|array $categoryIds + * @param int $storeId + * @param string $path + * @return array + */ protected function _getCategories($categoryIds, $storeId = null, $path = null) { $isActiveAttribute = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_category', 'is_active'); @@ -592,6 +608,13 @@ protected function _getCategories($categoryIds, $storeId = null, $path = null) return $categories; } + /** + * Retrieve category data object + * + * @param int $categoryId + * @param int $storeId + * @return Varien_Object + */ public function getCategory($categoryId, $storeId) { if (!$categoryId || !$storeId) { @@ -605,6 +628,13 @@ public function getCategory($categoryId, $storeId) return false; } + /** + * Retrieve categories data objects by ids + * + * @param int|array $categoryIds + * @param int $storeId + * @return array + */ public function getCategories($categoryIds, $storeId) { if (!$categoryIds || !$storeId) { @@ -614,6 +644,12 @@ public function getCategories($categoryIds, $storeId) return $this->_getCategories($categoryIds, $storeId); } + /** + * Retrieve category childs data objects + * + * @param Varien_Object $category + * @return Varien_Object + */ public function loadCategoryChilds(Varien_Object $category) { if (is_null($category->getId()) || is_null($category->getStoreId())) { @@ -643,6 +679,12 @@ public function loadCategoryChilds(Varien_Object $category) return $category; } + /** + * Retrieve category parent path + * + * @param Varien_Object $category + * @return string + */ public function getCategoryParentPath(Varien_Object $category) { $store = Mage::app()->getStore($category->getStoreId()); @@ -658,6 +700,12 @@ public function getCategoryParentPath(Varien_Object $category) } } + /** + * Retrieve product ids by category + * + * @param Varien_Object|int $category + * @return array + */ public function getProductIdsByCategory($category) { $productIds = array(); @@ -679,6 +727,15 @@ public function getProductIdsByCategory($category) return $productIds; } + /** + * Retrieve Product data objects + * + * @param int|array $productIds + * @param int $storeId + * @param int $entityId + * @param int $lastEntityId + * @return array + */ protected function _getProducts($productIds = null, $storeId, $entityId = 0, &$lastEntityId) { $products = array(); @@ -690,7 +747,7 @@ protected function _getProducts($productIds = null, $storeId, $entityId = 0, &$l } } $select = $this->_getWriteAdapter()->select() - ->from(array('e' => $this->getTable('catalog/product')), array('entity_id', 'category_ids')) + ->from(array('e' => $this->getTable('catalog/product')), array('entity_id')) ->join( array('w' => $this->getTable('catalog/product_website')), $this->_getWriteAdapter()->quoteInto('e.entity_id=w.product_id AND w.website_id=?', $websiteId), @@ -703,11 +760,11 @@ protected function _getProducts($productIds = null, $storeId, $entityId = 0, &$l $select->where('e.entity_id IN(?)', $productIds); } - $query = $this->_getWriteAdapter()->query((string)$select); + $query = $this->_getWriteAdapter()->query($select); while ($row = $query->fetch()) { $product = new Varien_Object($row); $product->setIdFieldName('entity_id'); - $product->setCategoryIds(split(',', $product->getCategoryIds())); + $product->setCategoryIds(array()); $products[$product->getId()] = $product; $lastEntityId = $product->getId(); } @@ -715,6 +772,18 @@ protected function _getProducts($productIds = null, $storeId, $entityId = 0, &$l unset($query); if ($products) { + $select = $this->_getReadAdapter()->select() + ->from( + $this->getTable('catalog/category_product'), + array('product_id', 'category_id')) + ->where('product_id IN(?)', array_keys($products)); + $categories = $this->_getReadAdapter()->fetchPairs($select); + foreach ($categories as $productId => $categoryId) { + $categoryIds = $products[$productId]->getCategoryIds(); + $categoryIds[] = $categoryId; + $products[$productId]->setCategoryIds($categoryIds); + } + foreach (array('name', 'url_key', 'url_path') as $attributeCode) { $attributes = $this->_getProductAttribute($attributeCode, array_keys($products), $storeId); foreach ($attributes as $productId => $attributeValue) { @@ -726,6 +795,13 @@ protected function _getProducts($productIds = null, $storeId, $entityId = 0, &$l return $products; } + /** + * Retrieve Product data object + * + * @param int $productId + * @param int $storeId + * @return Varien_Object + */ public function getProduct($productId, $storeId) { $lastId = 0; @@ -736,11 +812,25 @@ public function getProduct($productId, $storeId) return false; } + /** + * Retrieve Product data obects for store + * + * @param int $storeId + * @param int $lastEntityId + * @return array + */ public function getProductsByStore($storeId, &$lastEntityId) { return $this->_getProducts(null, $storeId, $lastEntityId, $lastEntityId); } + /** + * Retrieve Product data objects in category + * + * @param Varien_Object $category + * @param int $lastEntityId + * @return array + */ public function getProductsByCategory(Varien_Object $category, &$lastEntityId) { $productIds = $this->getProductIdsByCategory($category); @@ -750,6 +840,12 @@ public function getProductsByCategory(Varien_Object $category, &$lastEntityId) return $this->_getProducts($productIds, $category->getStoreId(), $lastEntityId, $lastEntityId); } + /** + * Remove unused rewrite URLs + * + * @param int $storeId + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Url + */ public function clearCategoryProduct($storeId) { $select = $this->_getWriteAdapter()->select() @@ -771,6 +867,8 @@ public function clearCategoryProduct($storeId) $where = $this->_getWriteAdapter()->quoteInto($this->getIdFieldName() . ' IN(?)', $rewriteIds); $this->_getWriteAdapter()->delete($this->getMainTable(), $where); } + + return $this; } /** @@ -791,100 +889,57 @@ public function deleteCategoryProductRewrites($categoryId, $productIds) return $this; } + /** + * Retrieve rewrites and visibility by store + * + * Input array format: + * product_id as key and store_id as value + * + * Output array format (product_id as key) + * store_id int; store id + * visibility int; visibility for store + * url_rewrite string; rewrite URL for store + * + * @param array $products + * @return array + */ + public function getRewriteByProductStore(array $products) + { + $result = array(); + + if (empty($products)) { + return $result; + } + + $select = $this->_getReadAdapter()->select() + ->from( + array('i' => $this->getTable('catalog/category_product_index')), + array('product_id', 'store_id', 'visibility')) + ->joinLeft( + array('r' => $this->getMainTable()), + 'i.product_id = r.product_id AND i.store_id=r.store_id AND r.category_id IS NULL', + array('request_path') + ); + foreach ($products as $productId => $storeId) { + $catId = Mage::app()->getStore($storeId)->getRootCategoryId(); + $cond = join(' AND ', array( + $this->_getReadAdapter()->quoteInto('i.product_id=?', $productId), + $this->_getReadAdapter()->quoteInto('i.store_id=?', $storeId), + $this->_getReadAdapter()->quoteInto('i.category_id=?', $catId), + )); + + $select->orWhere($cond); + } -// protected $_rewrite = array(); -// protected $_catRoots = array(); -// -// public function addCategoryToRewrite($category) -// { -// $a = array('refresh'=>true); -// if (is_object($category)) { -// $a['path'] = $category->getPath(); -// $this->_rewrite[0]['category'][$category->getId()] = $a; -// } elseif (is_numeric($category)) { -// $this->_rewrite[0]['category'][$category] = $a; -// } -// return $this; -// } -// -// public function addProductToRewrite($product) -// { -// $a = array('refresh'=>true); -// if (is_object($product)) { -// $this->_rewrite[0]['product'][$product->getId()] = $a; -// } else { -// $this->_rewrite[0]['product'][$product] = $a; -// } -// return $this; -// } -// -// protected function _getCategoryRootsByStore() -// { -// if (!$this->_catRoots) { -// $res = Mage::getSingleton('core/resource'); -// /* @var $res Mage_Core_Model_Resource */ -// $read = $res->getConnection('core_read'); -// /* @var $read Zend_Db_Adapter_Abstract */ -// $select = $read->select()->from(array( -// 's' => $res->getTableName('core/store'), -// 'g' => $res->getTableName('core/store_group'), -// 'c' => $res->getTableName('catalog/category'), -// ), array('s.store_id', 'g.category_root_id'=>'category_id', 'c.path')) -// ->where('s.group_id=g.group_id and c.entity_id=g.category_root_id'); -// $categories = $read->fetchAll($select); -// $this->_catRoots = array(); -// foreach ($categories as $c) { -// $this->_catRoots[$c['store_id']] = $c; -// } -// } -// return $this->_catRoots; -// } -// -// protected function _loadCategories() -// { -// if (empty($this->_rewrite[0]['category'])) { -// return; -// } -// $res = Mage::getSingleton('core/resource'); -// /* @var $res Mage_Core_Model_Resource */ -// $read = $res->getConnection('core_read'); -// /* @var $read Zend_Db_Adapter_Abstract */ -// -// // first load categories that don't have path -// $req1 = array(); -// foreach ($this->_rewrite[0]['category'] as $cId=>$rData) { -// if (empty($rData['path'])) { -// $req1[] = $cId; -// } -// } -// $select = $read->select()->from(array( -// 'c' => $res->getTableName('catalog/category'), -// 'url_key' => $res->getTableName('catalog/category').'_varchar', -// 'url_path' => $res->getTableName('catalog/category').'_varchar', -// ), array('c.entity_id'=>'category_id', 'c.path', 'url_path.value'=>'url_path')) -// // now load all required categories (children) -// $req2 = array(); -// foreach ($this->_rewrite[0]['category'] as $cId=>$rData) { -// $req2[] = "path like ''"; -// foreach ($this->_getCategoryRootsByStore() as $storeId=>$c) { -// $this->_rewrite[$storeId]['category'] -// } -// } -// $collection = Mage::getModel('catalog/category')->getCollection() -// ->; -// } -// -// protected function _loadProducts() -// { -// if (empty($this->_rewrite[0]['product'])) { -// return; -// } -// } -// -// public function commitRewrites() -// { -// $this->_loadCategories(); -// $this->_loadProducts(); -// -// } -} \ No newline at end of file + $query = $this->_getReadAdapter()->query($select); + while ($row = $query->fetch()) { + $result[$row['product_id']] = array( + 'store_id' => $row['store_id'], + 'visibility' => $row['visibility'], + 'url_rewrite' => $row['request_path'], + ); + } + + return $result; + } +} diff --git a/app/code/core/Mage/Catalog/etc/config.xml b/app/code/core/Mage/Catalog/etc/config.xml index bfdfaa9a0d..ca298ca945 100644 --- a/app/code/core/Mage/Catalog/etc/config.xml +++ b/app/code/core/Mage/Catalog/etc/config.xml @@ -28,7 +28,7 @@ - 0.7.69 + 0.7.72 @@ -130,7 +130,7 @@ 11 1 - 1 + 11 111 1 @@ -419,15 +419,6 @@ - - - - singleton - catalog/product_flat_observer - catalogCategoryChangeProducts - - - @@ -586,7 +577,7 @@ - Url Rewrite Management + URL Rewrite Management adminhtml/urlrewrite/index diff --git a/app/code/core/Mage/Catalog/etc/system.xml b/app/code/core/Mage/Catalog/etc/system.xml index c3fdff57b7..18b44818ca 100644 --- a/app/code/core/Mage/Catalog/etc/system.xml +++ b/app/code/core/Mage/Catalog/etc/system.xml @@ -352,7 +352,16 @@ 1 1 1 + Example format 200x300 + + + text + 150 + 1 + 1 + 1 + image @@ -363,6 +372,7 @@ 1 1 1 + Allowed file types (jpeg, gif, png). diff --git a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.39-0.7.40.php b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.39-0.7.40.php index 75642ad4cf..9c347e2441 100644 --- a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.39-0.7.40.php +++ b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.39-0.7.40.php @@ -32,7 +32,7 @@ $fieldList = array('price','special_price','special_from_date','special_to_date', 'minimal_price','cost','tier_price'); foreach ($fieldList as $field) { - $applyTo = split(',', $installer->getAttribute('catalog_product', $field, 'apply_to')); + $applyTo = explode(',', $installer->getAttribute('catalog_product', $field, 'apply_to')); if (!in_array('virtual', $applyTo)) { $applyTo[] = 'virtual'; $installer->updateAttribute('catalog_product', $field, 'apply_to', join(',', $applyTo)); diff --git a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.69-0.7.70.php b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.69-0.7.70.php new file mode 100644 index 0000000000..69fe8bff06 --- /dev/null +++ b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.69-0.7.70.php @@ -0,0 +1,39 @@ +startSetup(); +$installer->getConnection()->addColumn($installer->getTable('catalog/compare_item'), 'store_id', + 'smallint unsigned default null'); +$installer->getConnection()->addConstraint('FK_CATALOG_COMPARE_ITEM_STORE', + $installer->getTable('catalog/compare_item'), 'store_id', + $installer->getTable('core/store'), 'store_id', + 'set null', 'cascade' +); +$installer->endSetup(); diff --git a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.70-0.7.71.php b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.70-0.7.71.php new file mode 100644 index 0000000000..a14b87df2f --- /dev/null +++ b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.70-0.7.71.php @@ -0,0 +1,29 @@ +startSetup(); +$this->getConnection()->query("DROP TABLE IF EXISTS `{$this->getTable('catalog/category_flat')}`;"); +$this->endSetup(); diff --git a/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.71-0.7.72.php b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.71-0.7.72.php new file mode 100644 index 0000000000..29a3b3fd4e --- /dev/null +++ b/app/code/core/Mage/Catalog/sql/catalog_setup/mysql4-upgrade-0.7.71-0.7.72.php @@ -0,0 +1,34 @@ +startSetup(); +$installer->removeAttribute('catalog_category', 'category_ids'); +$installer->getConnection()->dropColumn($installer->getTable('catalog/product'), 'category_ids'); +$installer->endSetup(); diff --git a/app/code/core/Mage/CatalogIndex/Model/Aggregation.php b/app/code/core/Mage/CatalogIndex/Model/Aggregation.php index b1a03b112b..4b04cc87d7 100644 --- a/app/code/core/Mage/CatalogIndex/Model/Aggregation.php +++ b/app/code/core/Mage/CatalogIndex/Model/Aggregation.php @@ -151,11 +151,11 @@ protected function _processTags($tags) { $newTags = array(); foreach ($tags as $tag) { - $tagInfo = split(':', $tag); + $tagInfo = explode(':', $tag); if (count($tagInfo)==1) { $newTags[] = $tagInfo[0]; } else { - $tagVariants = split('/', $tagInfo[1]); + $tagVariants = explode('/', $tagInfo[1]); foreach ($tagVariants as $tagVariant) { $newTags[] = $tagInfo[0] . $tagVariant; } diff --git a/app/code/core/Mage/CatalogIndex/etc/config.xml b/app/code/core/Mage/CatalogIndex/etc/config.xml index 82f496d2f9..3a1339b63c 100644 --- a/app/code/core/Mage/CatalogIndex/etc/config.xml +++ b/app/code/core/Mage/CatalogIndex/etc/config.xml @@ -220,26 +220,6 @@ - - - - - - - - - - - - - - - singleton - catalogindex/observer - processProductsWebsitesChange - - - @@ -277,6 +257,26 @@ + + + + + + + + + + + + + + singleton + catalogindex/observer + processProductsWebsitesChange + + + + diff --git a/app/code/core/Mage/CatalogInventory/Model/Stock.php b/app/code/core/Mage/CatalogInventory/Model/Stock.php index 0d3846d9b2..6b94098e69 100644 --- a/app/code/core/Mage/CatalogInventory/Model/Stock.php +++ b/app/code/core/Mage/CatalogInventory/Model/Stock.php @@ -71,13 +71,13 @@ public function addItemsToProducts($productCollection) ->addProductsFilter($productCollection) ->joinStockStatus($productCollection->getStoreId()) ->load(); + $stockItems = array(); foreach ($items as $item) { - foreach($productCollection as $product){ - if($product->getId()==$item->getProductId()){ - if($product instanceof Mage_Catalog_Model_Product) { - $item->assignProduct($product); - } - } + $stockItems[$item->getProductId()] = $item; + } + foreach ($productCollection as $product) { + if (isset($stockItems[$product->getId()])) { + $stockItems[$product->getId()]->assignProduct($product); } } return $this; diff --git a/app/code/core/Mage/CatalogInventory/etc/wsdl.xml b/app/code/core/Mage/CatalogInventory/etc/wsdl.xml index ee4559e55b..28fa870fa2 100644 --- a/app/code/core/Mage/CatalogInventory/etc/wsdl.xml +++ b/app/code/core/Mage/CatalogInventory/etc/wsdl.xml @@ -26,6 +26,22 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/CatalogRule/Model/Rule.php b/app/code/core/Mage/CatalogRule/Model/Rule.php index b2f4a31bd4..3c13a6aa8a 100644 --- a/app/code/core/Mage/CatalogRule/Model/Rule.php +++ b/app/code/core/Mage/CatalogRule/Model/Rule.php @@ -162,14 +162,6 @@ public function getMatchingProductIds() public function callbackValidateProduct($args) { $product = $args['product']->setData($args['row']); - if (!empty($args['attributes']['category_ids'])) { - $categoryCollection = $product->getCategoryCollection()->load(); - $categories = array(); - foreach ($categoryCollection as $category) { - $categories[] = $category->getId(); - } - $product->setCategoryIds($categories); - } if ($this->getConditions()->validate($product)) { $this->_productIds[] = $product->getId(); } diff --git a/app/code/core/Mage/CatalogRule/Model/Rule/Condition/Product.php b/app/code/core/Mage/CatalogRule/Model/Rule/Condition/Product.php index 2bf9bc91b7..ba3cad9bdd 100644 --- a/app/code/core/Mage/CatalogRule/Model/Rule/Condition/Product.php +++ b/app/code/core/Mage/CatalogRule/Model/Rule/Condition/Product.php @@ -193,9 +193,14 @@ public function getAttributeElement() */ public function collectValidatedAttributes($productCollection) { + if ($this->getAttribute() == 'category_ids') { + return $this; + } + $attributes = $this->getRule()->getCollectedAttributes(); $attributes[$this->getAttribute()] = true; $this->getRule()->setCollectedAttributes($attributes); + $productCollection->addAttributeToSelect($this->getAttribute(), 'left'); return $this; } @@ -342,6 +347,10 @@ public function loadArray($arr) */ public function validate(Varien_Object $object) { + if ($this->getAttribute() == 'category_ids') { + return $this->validateAttribute($object->getAvailableInCategories()); + } + $attr = $object->getResource()->getAttribute($this->getAttribute()); if ($attr && $attr->getBackendType()=='datetime' && !is_int($this->getValue())) { $this->setValue(strtotime($this->getValue())); @@ -349,16 +358,12 @@ public function validate(Varien_Object $object) return $this->validateAttribute($value); } - if ($this->getAttribute() == 'category_ids') { - return $this->validateAttribute($object->getAvailableInCategories()); - } - if ($attr && $attr->getFrontendInput() == 'multiselect') { $value = $object->getData($this->getAttribute()); if (!strlen($value)) { $value = array(); } else { - $value = split(',', $value); + $value = explode(',', $value); } return $this->validateAttribute($value); } diff --git a/app/code/core/Mage/CatalogRule/sql/catalogrule_setup/mysql4-upgrade-0.7.1-0.7.2.php b/app/code/core/Mage/CatalogRule/sql/catalogrule_setup/mysql4-upgrade-0.7.1-0.7.2.php index d9502f917a..7023e6f5b4 100644 --- a/app/code/core/Mage/CatalogRule/sql/catalogrule_setup/mysql4-upgrade-0.7.1-0.7.2.php +++ b/app/code/core/Mage/CatalogRule/sql/catalogrule_setup/mysql4-upgrade-0.7.1-0.7.2.php @@ -41,7 +41,7 @@ foreach ($rows as $r) { $websiteIds = array(); - foreach (split(',', $r['store_ids']) as $storeId) { + foreach (explode(',', $r['store_ids']) as $storeId) { if (($storeId!=='') && isset($websites[$storeId])) { $websiteIds[$websites[$storeId]] = true; } diff --git a/app/code/core/Mage/CatalogSearch/Block/Autocomplete.php b/app/code/core/Mage/CatalogSearch/Block/Autocomplete.php index 18ad6cc024..4f44f47fdd 100644 --- a/app/code/core/Mage/CatalogSearch/Block/Autocomplete.php +++ b/app/code/core/Mage/CatalogSearch/Block/Autocomplete.php @@ -68,8 +68,8 @@ protected function _toHtml() public function getSuggestData() { if (!$this->_suggestData) { - $collection = $this->helper('catalogSearch')->getSuggestCollection(); - $query = $this->helper('catalogSearch')->getQueryText(); + $collection = $this->helper('catalogsearch')->getSuggestCollection(); + $query = $this->helper('catalogsearch')->getQueryText(); $counter = 0; $data = array(); foreach ($collection as $item) { diff --git a/app/code/core/Mage/CatalogSearch/Block/Layer.php b/app/code/core/Mage/CatalogSearch/Block/Layer.php index a221f50713..3e550d873f 100644 --- a/app/code/core/Mage/CatalogSearch/Block/Layer.php +++ b/app/code/core/Mage/CatalogSearch/Block/Layer.php @@ -24,8 +24,22 @@ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ +/** + * Layered Navigation block for search + * + */ class Mage_CatalogSearch_Block_Layer extends Mage_Catalog_Block_Layer_View { + /** + * Get attribute filter block name + * + * @return string + */ + protected function _getAttributeFilterBlockName() + { + return 'catalogsearch/layer_filter_attribute'; + } + /** * Get layer object * diff --git a/app/code/core/Mage/CatalogSearch/Block/Layer/Filter/Attribute.php b/app/code/core/Mage/CatalogSearch/Block/Layer/Filter/Attribute.php new file mode 100644 index 0000000000..0be0d28644 --- /dev/null +++ b/app/code/core/Mage/CatalogSearch/Block/Layer/Filter/Attribute.php @@ -0,0 +1,42 @@ +_filterModelName = 'catalogsearch/layer_filter_attribute'; + } +} diff --git a/app/code/core/Mage/CatalogSearch/Block/Result.php b/app/code/core/Mage/CatalogSearch/Block/Result.php index 9ca3315673..0ea80649a7 100644 --- a/app/code/core/Mage/CatalogSearch/Block/Result.php +++ b/app/code/core/Mage/CatalogSearch/Block/Result.php @@ -48,7 +48,7 @@ class Mage_CatalogSearch_Block_Result extends Mage_Core_Block_Template */ protected function _getQuery() { - return $this->helper('catalogSearch')->getQuery(); + return $this->helper('catalogsearch')->getQuery(); } /** @@ -58,7 +58,7 @@ protected function _getQuery() */ protected function _prepareLayout() { - $title = $this->__("Search results for: '%s'", $this->helper('catalogSearch')->getEscapedQueryText()); + $title = $this->__("Search results for: '%s'", $this->helper('catalogsearch')->getEscapedQueryText()); // add Home breadcrumb if ($breadcrumbs = $this->getLayout()->getBlock('breadcrumbs')) { @@ -180,8 +180,8 @@ public function getResultCount() */ public function getNoResultText() { - if (Mage::helper('catalogSearch')->isMinQueryLength()) { - return Mage::helper('catalogSearch')->__('Minimum Search query length is %s', $this->_getQuery()->getMinQueryLenght()); + if (Mage::helper('catalogsearch')->isMinQueryLength()) { + return Mage::helper('catalogsearch')->__('Minimum Search query length is %s', $this->_getQuery()->getMinQueryLenght()); } return $this->_getData('no_result_text'); } @@ -193,6 +193,6 @@ public function getNoResultText() */ public function getNoteMessages() { - return Mage::helper('catalogSearch')->getNoteMessages(); + return Mage::helper('catalogsearch')->getNoteMessages(); } } diff --git a/app/code/core/Mage/CatalogSearch/Model/Fulltext.php b/app/code/core/Mage/CatalogSearch/Model/Fulltext.php index 4afd2669f7..20fb78a91b 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Fulltext.php +++ b/app/code/core/Mage/CatalogSearch/Model/Fulltext.php @@ -101,9 +101,9 @@ public function resetSearchResults() public function prepareResult($query = null) { if (!$query instanceof Mage_CatalogSearch_Model_Query) { - $query = Mage::helper('catalogSearch')->getQuery(); + $query = Mage::helper('catalogsearch')->getQuery(); } - $queryText = Mage::helper('catalogSearch')->getQueryText(); + $queryText = Mage::helper('catalogsearch')->getQueryText(); if ($query->getSynonimFor()) { $queryText = $query->getSynonimFor(); } diff --git a/app/code/core/Mage/CatalogSearch/Model/Layer.php b/app/code/core/Mage/CatalogSearch/Model/Layer.php index 710290b4d4..70aa2fbc1a 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Layer.php +++ b/app/code/core/Mage/CatalogSearch/Model/Layer.php @@ -56,7 +56,7 @@ public function getProductCollection() public function prepareProductCollection($collection) { $collection->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes()) - ->addSearchFilter(Mage::helper('catalogSearch')->getEscapedQueryText()) + ->addSearchFilter(Mage::helper('catalogsearch')->getEscapedQueryText()) ->setStore(Mage::app()->getStore()) ->addMinimalPrice() ->addFinalPrice() @@ -77,7 +77,7 @@ public function prepareProductCollection($collection) public function getStateKey() { if ($this->_stateKey === null) { - $this->_stateKey = 'Q_'.Mage::helper('catalogSearch')->getQuery()->getId() + $this->_stateKey = 'Q_'.Mage::helper('catalogsearch')->getQuery()->getId() .'_'.parent::getStateKey(); } return $this->_stateKey; diff --git a/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php new file mode 100644 index 0000000000..050b1d9a39 --- /dev/null +++ b/app/code/core/Mage/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -0,0 +1,44 @@ +getIsFilterableInSearch(); + } + +} diff --git a/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php b/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php index a5f73b1891..cf2f9804ea 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php +++ b/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext.php @@ -47,6 +47,13 @@ class Mage_CatalogSearch_Model_Mysql4_Fulltext extends Mage_Core_Model_Mysql4_Ab */ protected $_separator = ' '; + /** + * Array of Zend_Date objects per store + * + * @var array + */ + protected $_dates = array(); + /** * Product Type Instances cache * @@ -101,6 +108,8 @@ protected function _rebuildStoreIndex($storeId, $productIds = null) 'int' => array_keys($this->_getSearchableAttributes('int')), 'varchar' => array_keys($this->_getSearchableAttributes('varchar')), 'text' => array_keys($this->_getSearchableAttributes('text')), + 'decimal' => array_keys($this->_getSearchableAttributes('decimal')), + 'datetime' => array_keys($this->_getSearchableAttributes('datetime')), ); // status and visibility filter @@ -182,13 +191,13 @@ protected function _getSearchableProducts($storeId, array $staticFields, $produc $entityType = $this->getEavConfig()->getEntityType('catalog_product'); $store = Mage::app()->getStore($storeId); - $select = $this->_getReadAdapter()->select() + $select = $this->_getWriteAdapter()->select() ->from( array('e' => $this->getTable('catalog/product')), array_merge(array('entity_id', 'type_id'), $staticFields)) ->joinInner( array('website' => $this->getTable('catalog/product_website')), - $this->_getReadAdapter()->quoteInto('website.product_id=e.entity_id AND website.website_id=?', $store->getWebsiteId()), + $this->_getWriteAdapter()->quoteInto('website.product_id=e.entity_id AND website.website_id=?', $store->getWebsiteId()), array() ); @@ -199,8 +208,7 @@ protected function _getSearchableProducts($storeId, array $staticFields, $produc $select->where('e.entity_id>?', $lastProductId) ->limit($limit) ->order('e.entity_id'); - - return $this->_getReadAdapter()->fetchAll($select); + return $this->_getWriteAdapter()->fetchAll($select); } /** @@ -337,15 +345,15 @@ protected function _getSearchableAttributes($backendType = null) $entity = $entityType->getEntity(); $whereCond = array( - $this->_getReadAdapter()->quoteInto('is_searchable=?', 1), - $this->_getReadAdapter()->quoteInto('attribute_code IN(?)', array('status', 'visibility')) + $this->_getWriteAdapter()->quoteInto('is_searchable=?', 1), + $this->_getWriteAdapter()->quoteInto('attribute_code IN(?)', array('status', 'visibility')) ); - $select = $this->_getReadAdapter()->select() + $select = $this->_getWriteAdapter()->select() ->from($this->getTable('eav/attribute')) ->where('entity_type_id=?', $entityType->getEntityTypeId()) ->where(join(' OR ', $whereCond)); - $attributesData = $this->_getReadAdapter()->fetchAll($select); + $attributesData = $this->_getWriteAdapter()->fetchAll($select); $this->getEavConfig()->importAttributesData($entityType, $attributesData); foreach ($attributesData as $attributeData) { $attributeCode = $attributeData['attribute_code']; @@ -407,13 +415,13 @@ protected function _getProductAttributes($storeId, array $productIds, array $atr foreach ($atributeTypes as $backendType => $attributeIds) { if ($attributeIds) { $tableName = $this->getTable('catalog/product') . '_' . $backendType; - $selects[] = $this->_getReadAdapter()->select() + $selects[] = $this->_getWriteAdapter()->select() ->from( array('t_default' => $tableName), array('entity_id', 'attribute_id')) ->joinLeft( array('t_store' => $tableName), - $this->_getReadAdapter()->quoteInto("t_default.entity_id=t_store.entity_id AND t_default.attribute_id=t_store.attribute_id AND t_store.store_id=?", $storeId), + $this->_getWriteAdapter()->quoteInto("t_default.entity_id=t_store.entity_id AND t_default.attribute_id=t_store.attribute_id AND t_store.store_id=?", $storeId), array('value'=>'IFNULL(t_store.value, t_default.value)')) ->where('t_default.store_id=?', 0) ->where('t_default.attribute_id IN(?)', $attributeIds) @@ -423,7 +431,7 @@ protected function _getProductAttributes($storeId, array $productIds, array $atr if ($selects) { $select = '('.join(')UNION(', $selects).')'; - $query = $this->_getReadAdapter()->query($select); + $query = $this->_getWriteAdapter()->query($select); while ($row = $query->fetch()) { $result[$row['entity_id']][$row['attribute_id']] = $row['value']; } @@ -545,6 +553,12 @@ protected function _getAttributeValue($attributeId, $value, $storeId) $attribute->setStoreId($storeId); $value = $attribute->getSource()->getOptionText($value); } + if ($attribute->getBackendType() == 'datetime') { + $value = $this->_getStoreDate($storeId, $value); + } + if ($attribute->getFrontend()->getInputType() == 'price') { + $value = Mage::app()->getStore($storeId)->roundPrice($value); + } if (is_array($value)) { $value = implode($this->_separator, $value); @@ -599,4 +613,31 @@ protected function _saveProductIndexes($storeId, $productIndexes) return $this; } + + /** + * Retrieve Date value for store + * + * @param int $storeId + * @param string $date + * @return string + */ + protected function _getStoreDate($storeId, $date = null) + { + if (!isset($this->_dates[$storeId])) { + $timezone = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE, $storeId); + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $storeId); + $locale = new Zend_Locale($locale); + + $dateObj = new Zend_Date(null, null, $locale); + $dateObj->setTimezone($timezone); + $this->_dates[$storeId] = array($dateObj, $locale->getTranslation(null, 'date', $locale)); + } + + if (!is_empty_date($date)) { + list($dateObj, $format) = $this->_dates[$storeId]; + $dateObj->setDate($date, Varien_Date::DATETIME_INTERNAL_FORMAT); + return $dateObj->toString($format); + } + return null; + } } diff --git a/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext/Collection.php b/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext/Collection.php index 9e573ea05b..fee9c62628 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext/Collection.php +++ b/app/code/core/Mage/CatalogSearch/Model/Mysql4/Fulltext/Collection.php @@ -35,7 +35,7 @@ class Mage_CatalogSearch_Model_Mysql4_Fulltext_Collection */ protected function _getQuery() { - return Mage::helper('catalogSearch')->getQuery(); + return Mage::helper('catalogsearch')->getQuery(); } /** diff --git a/app/code/core/Mage/CatalogSearch/controllers/ResultController.php b/app/code/core/Mage/CatalogSearch/controllers/ResultController.php index 647d07851e..0a0f50d0da 100644 --- a/app/code/core/Mage/CatalogSearch/controllers/ResultController.php +++ b/app/code/core/Mage/CatalogSearch/controllers/ResultController.php @@ -43,13 +43,13 @@ protected function _getSession() */ public function indexAction() { - $query = Mage::helper('catalogSearch')->getQuery(); + $query = Mage::helper('catalogsearch')->getQuery(); /* @var $query Mage_CatalogSearch_Model_Query */ $query->setStoreId(Mage::app()->getStore()->getId()); if ($query->getQueryText()) { - if (Mage::helper('catalogSearch')->isMinQueryLength()) { + if (Mage::helper('catalogsearch')->isMinQueryLength()) { $query->setId(0) ->setIsActive(1) ->setIsProcessed(1); @@ -72,14 +72,14 @@ public function indexAction() } } - Mage::helper('catalogSearch')->checkNotes(); + Mage::helper('catalogsearch')->checkNotes(); $this->loadLayout(); $this->_initLayoutMessages('catalog/session'); $this->_initLayoutMessages('checkout/session'); $this->renderLayout(); - if (!Mage::helper('catalogSearch')->isMinQueryLength()) { + if (!Mage::helper('catalogsearch')->isMinQueryLength()) { $query->save(); } } diff --git a/app/code/core/Mage/Checkout/Block/Cart.php b/app/code/core/Mage/Checkout/Block/Cart.php index 8726a8456e..eac84b80ce 100644 --- a/app/code/core/Mage/Checkout/Block/Cart.php +++ b/app/code/core/Mage/Checkout/Block/Cart.php @@ -33,6 +33,49 @@ */ class Mage_Checkout_Block_Cart extends Mage_Checkout_Block_Cart_Abstract { + /** + * Prepare Quote Item Product URLs + * + */ + public function __construct() + { + parent::__construct(); + + // prepare cart items URLs + $products = array(); + /* @var $item Mage_Sales_Model_Quote_Item */ + foreach ($this->getItems() as $item) { + $product = $item->getProduct(); + $option = $item->getOptionByCode('product_type'); + if ($option) { + $product = $option->getProduct(); + } + + if ($item->getStoreId() != Mage::app()->getStore()->getId() + && !$item->getRedirectUrl() + && !$product->isVisibleInSiteVisibility()) + { + $products[$product->getId()] = $item->getStoreId(); + } + } + + if ($products) { + $products = Mage::getResourceSingleton('catalog/url') + ->getRewriteByProductStore($products); + foreach ($this->getItems() as $item) { + $product = $item->getProduct(); + $option = $item->getOptionByCode('product_type'); + if ($option) { + $product = $option->getProduct(); + } + + if (isset($products[$product->getId()])) { + $object = new Varien_Object($products[$product->getId()]); + $item->setUrlDataObject($object); + } + } + } + } public function chooseTemplate() { 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 0743423c7c..74f3b3d025 100644 --- a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer.php +++ b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer.php @@ -20,7 +20,7 @@ * * @category Mage * @package Mage_Checkout - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ @@ -95,7 +95,39 @@ public function overrideProductUrl($productUrl) } /** - * Get url to item product + * Check Product has URL + * + * @return this + */ + public function hasProductUrl() + { + if ($this->_productUrl || $this->getItem()->getRedirectUrl()) { + return true; + } + + $product = $this->getProduct(); + $option = $this->getItem()->getOptionByCode('product_type'); + if ($option) { + $product = $option->getProduct(); + } + + if ($product->isVisibleInSiteVisibility()) { + return true; + } + else { + if ($this->getItem()->hasUrlDataObject()) { + $data = $this->getItem()->getUrlDataObject(); + if (in_array($data->getVisibility(), $product->getVisibleInSiteVisibilities())) { + return true; + } + } + } + + return false; + } + + /** + * Retrieve URL to item Product * * @return string */ @@ -104,7 +136,17 @@ public function getProductUrl() if (!is_null($this->_productUrl)) { return $this->_productUrl; } - return $this->getProduct()->getProductUrl(); + if ($this->getItem()->getRedirectUrl()) { + return $this->getItem()->getRedirectUrl(); + } + + $product = $this->getProduct(); + $option = $this->getItem()->getOptionByCode('product_type'); + if ($option) { + $product = $option->getProduct(); + } + + return $product->getUrlModel()->getUrl($product); } /** diff --git a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Configurable.php b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Configurable.php index ce4d45b6a8..95f49e7008 100644 --- a/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Configurable.php +++ b/app/code/core/Mage/Checkout/Block/Cart/Item/Renderer/Configurable.php @@ -1,124 +1,114 @@ - - */ -class Mage_Checkout_Block_Cart_Item_Renderer_Configurable extends Mage_Checkout_Block_Cart_Item_Renderer -{ - const CONFIGURABLE_PRODUCT_IMAGE= 'checkout/cart/configurable_product_image'; - const USE_PARENT_IMAGE = 'parent'; - - /** - * Get item configurable product - * - * @return Mage_Catalog_Model_Product - */ - public function getConfigurableProduct() - { - if ($option = $this->getItem()->getOptionByCode('product_type')) { - return $option->getProduct(); - } - return $this->getProduct(); - } - - /** - * Get item configurable child product - * - * @return Mage_Catalog_Model_Product - */ - public function getChildProduct() - { - if ($option = $this->getItem()->getOptionByCode('simple_product')) { - return $option->getProduct(); - } - return $this->getProduct(); - } - - /** - * Get product thumbnail image - * - * @return Mage_Catalog_Model_Product_Image - */ - public function getProductThumbnail() - { - $product = $this->getChildProduct(); - if (!$product || !$product->getData('thumbnail') - || ($product->getData('thumbnail') == 'no_selection') - || (Mage::getStoreConfig(self::CONFIGURABLE_PRODUCT_IMAGE) == self::USE_PARENT_IMAGE)) { - $product = $this->getProduct(); - } - return $this->helper('catalog/image')->init($product, 'thumbnail'); - } - - /** - * Get item product name - * - * @return string - */ - public function getProductName() - { - return $this->getProduct()->getName(); - } - - /** - * Get url to item product - * - * @return string - */ - public function getProductUrl() - { - return $this->getProduct()->getProductUrl(); - } - - /** - * Get selected for configurable product attributes - * - * @return array - */ - public function getProductAttributes() - { - $attributes = $this->getProduct()->getTypeInstance(true) - ->getSelectedAttributesInfo($this->getProduct()); - return $attributes; - } - - /** - * Get list of all otions for product - * - * @return array - */ - public function getOptionList() - { - $options = array_merge($this->getProductAttributes(), $this->getProductOptions()); - return $options; - } - + + */ +class Mage_Checkout_Block_Cart_Item_Renderer_Configurable extends Mage_Checkout_Block_Cart_Item_Renderer +{ + const CONFIGURABLE_PRODUCT_IMAGE= 'checkout/cart/configurable_product_image'; + const USE_PARENT_IMAGE = 'parent'; + + /** + * Get item configurable product + * + * @return Mage_Catalog_Model_Product + */ + public function getConfigurableProduct() + { + if ($option = $this->getItem()->getOptionByCode('product_type')) { + return $option->getProduct(); + } + return $this->getProduct(); + } + + /** + * Get item configurable child product + * + * @return Mage_Catalog_Model_Product + */ + public function getChildProduct() + { + if ($option = $this->getItem()->getOptionByCode('simple_product')) { + return $option->getProduct(); + } + return $this->getProduct(); + } + + /** + * Get product thumbnail image + * + * @return Mage_Catalog_Model_Product_Image + */ + public function getProductThumbnail() + { + $product = $this->getChildProduct(); + if (!$product || !$product->getData('thumbnail') + || ($product->getData('thumbnail') == 'no_selection') + || (Mage::getStoreConfig(self::CONFIGURABLE_PRODUCT_IMAGE) == self::USE_PARENT_IMAGE)) { + $product = $this->getProduct(); + } + return $this->helper('catalog/image')->init($product, 'thumbnail'); + } + + /** + * Get item product name + * + * @return string + */ + public function getProductName() + { + return $this->getProduct()->getName(); + } + + /** + * Get selected for configurable product attributes + * + * @return array + */ + public function getProductAttributes() + { + $attributes = $this->getProduct()->getTypeInstance(true) + ->getSelectedAttributesInfo($this->getProduct()); + return $attributes; + } + + /** + * Get list of all otions for product + * + * @return array + */ + public function getOptionList() + { + $options = array_merge($this->getProductAttributes(), $this->getProductOptions()); + return $options; + } + } \ No newline at end of file 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 89da4dc78f..84d866ba91 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 @@ -43,7 +43,8 @@ class Mage_Checkout_Block_Cart_Item_Renderer_Grouped extends Mage_Checkout_Block */ public function getGroupedProduct() { - if ($option = $this->getItem()->getOptionByCode('product_type')) { + $option = $this->getItem()->getOptionByCode('product_type'); + if ($option) { return $option->getProduct(); } return $this->getProduct(); @@ -65,16 +66,6 @@ public function getProductThumbnail() return $this->helper('catalog/image')->init($product, 'thumbnail'); } - /** - * Get url to item product - * - * @return string - */ - public function getProductUrl() - { - return $this->getGroupedProduct()->getProductUrl(); - } - /** * Prepare item html * @@ -86,12 +77,11 @@ protected function _toHtml() { $renderer = $this->getRenderedBlock()->getItemRenderer($this->getItem()->getRealProductType()); $renderer->setItem($this->getItem()); - $renderer->overrideProductUrl($this->getProductUrl()); +// $renderer->overrideProductUrl($this->getProductUrl()); $renderer->overrideProductThumbnail($this->getProductThumbnail()); $rendererHtml = $renderer->toHtml(); - $renderer->overrideProductUrl(null); +// $renderer->overrideProductUrl(null); $renderer->overrideProductThumbnail(null); return $rendererHtml; } - -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Checkout/Block/Cart/Totals.php b/app/code/core/Mage/Checkout/Block/Cart/Totals.php index 8701f26673..17ea3f9341 100644 --- a/app/code/core/Mage/Checkout/Block/Cart/Totals.php +++ b/app/code/core/Mage/Checkout/Block/Cart/Totals.php @@ -28,7 +28,6 @@ class Mage_Checkout_Block_Cart_Totals extends Mage_Checkout_Block_Cart_Abstract { protected $_totalRenderers; protected $_defaultRenderer = 'checkout/total_default'; - protected $_totals = null; public function getTotals() @@ -47,16 +46,22 @@ public function setTotals($value) protected function _getTotalRenderer($code) { - if (!isset($this->_totalRenderers[$code])) { - $this->_totalRenderers[$code] = $this->_defaultRenderer; + $blockName = $code.'_total_renderer'; + $block = $this->getLayout()->getBlock($blockName); + if (!$block) { + $block = $this->_defaultRenderer; $config = Mage::getConfig()->getNode("global/sales/quote/totals/{$code}/renderer"); - if ($config) - $this->_totalRenderers[$code] = (string) $config; + if ($config) { + $block = (string) $config; + } - $this->_totalRenderers[$code] = $this->getLayout()->createBlock($this->_totalRenderers[$code], "{$code}_total_renderer"); + $block = $this->getLayout()->createBlock($block, $blockName); } - - return $this->_totalRenderers[$code]; + /** + * Transfer totals to renderer + */ + $block->setTotals($this->getTotals()); + return $block; } public function renderTotal($total, $area = null, $colspan = 1) @@ -72,18 +77,51 @@ public function renderTotal($total, $area = null, $colspan = 1) ->toHtml(); } + /** + * Render totals html for specific totals area (footer, body) + * + * @param null|string $area + * @param int $colspan + * @return string + */ public function renderTotals($area = null, $colspan = 1) { $html = ''; - foreach($this->getTotals() as $total) { if ($total->getArea() != $area && $area != -1) { continue; } - $html .= $this->renderTotal($total, $area, $colspan); } - return $html; } + + /** + * Check if we have display grand total in base currency + * + * @return bool + */ + public function needDisplayBaseGrandtotal() + { + $quote = $this->getQuote(); + if ($quote->getBaseCurrencyCode() != $quote->getQuoteCurrencyCode()) { + return true; + } + return false; + } + + /** + * Get formated in base currency base grand total value + * + * @return string + */ + public function displayBaseGrandtotal() + { + $firstTotal = reset($this->_totals); + if ($firstTotal) { + $total = $firstTotal->getAddress()->getBaseGrandTotal(); + return Mage::app()->getStore()->getBaseCurrency()->format($total, array(), true); + } + return '-'; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Checkout/Block/Multishipping/Overview.php b/app/code/core/Mage/Checkout/Block/Multishipping/Overview.php index 356a922c8e..a9f7d23e5b 100644 --- a/app/code/core/Mage/Checkout/Block/Multishipping/Overview.php +++ b/app/code/core/Mage/Checkout/Block/Multishipping/Overview.php @@ -215,9 +215,13 @@ public function getBillinAddressTotals() } - public function renderTotals($totals) + public function renderTotals($totals, $colspan=null) { - $colspan = $this->helper('tax')->displayCartBothPrices() ? 5 : 3; - return $this->getChild('totals')->setTotals($totals)->renderTotals(-1, $colspan); + if ($colspan === null) { + $colspan = $this->helper('tax')->displayCartBothPrices() ? 5 : 3; + } + $totals = $this->getChild('totals')->setTotals($totals)->renderTotals('', $colspan) + . $this->getChild('totals')->setTotals($totals)->renderTotals('footer', $colspan); + return $totals; } } diff --git a/app/code/core/Mage/Checkout/Block/Onepage/Login.php b/app/code/core/Mage/Checkout/Block/Onepage/Login.php index aed6b91516..8879de82b7 100644 --- a/app/code/core/Mage/Checkout/Block/Onepage/Login.php +++ b/app/code/core/Mage/Checkout/Block/Onepage/Login.php @@ -37,7 +37,7 @@ class Mage_Checkout_Block_Onepage_Login extends Mage_Checkout_Block_Onepage_Abst protected function _construct() { if (!$this->isCustomerLoggedIn()) { - $this->getCheckout()->setStepData('login', array('label'=>Mage::helper('checkout')->__('Checkout method'), 'allow'=>true)); + $this->getCheckout()->setStepData('login', array('label'=>Mage::helper('checkout')->__('Checkout Method'), 'allow'=>true)); } parent::_construct(); } @@ -64,12 +64,12 @@ public function getMethodData() public function getSuccessUrl() { - return $this->getUrl('*/*'); + return $this->getUrl('*/*'); } public function getErrorUrl() { - return $this->getUrl('*/*'); + return $this->getUrl('*/*'); } /** diff --git a/app/code/core/Mage/Checkout/Block/Total/Default.php b/app/code/core/Mage/Checkout/Block/Total/Default.php index fc72d89e79..14a1fa9b65 100644 --- a/app/code/core/Mage/Checkout/Block/Total/Default.php +++ b/app/code/core/Mage/Checkout/Block/Total/Default.php @@ -30,11 +30,38 @@ * * @author Magento Core Team */ -class Mage_Checkout_Block_Total_Default extends Mage_Core_Block_Template +class Mage_Checkout_Block_Total_Default extends Mage_Checkout_Block_Cart_Totals { protected $_template = 'checkout/total/default.phtml'; + protected $_store; - protected function _construct(){ + protected function _construct() + { $this->setTemplate($this->_template); + $this->_store = Mage::app()->getStore(); + } + + /** + * Get style assigned to total object + * + * @return string + */ + public function getStyle() + { + return $this->getTotal()->getStyle(); + } + + public function setTotal($total) + { + $this->setData('total', $total); + if ($total->getAddress()) { + $this->_store = $total->getAddress()->getQuote()->getStore(); + } + return $this; + } + + public function getStore() + { + return $this->_store; } } \ No newline at end of file diff --git a/app/code/core/Mage/Checkout/Helper/Cart.php b/app/code/core/Mage/Checkout/Helper/Cart.php index 66ef01c79a..da51cbc50f 100644 --- a/app/code/core/Mage/Checkout/Helper/Cart.php +++ b/app/code/core/Mage/Checkout/Helper/Cart.php @@ -49,42 +49,29 @@ public function getCart() */ public function getAddUrl($product, $additional = array()) { - /** - * Identify continue shopping url - */ -// if ($currentProduct = Mage::registry('current_product')) { -// /** -// * go to product view page -// */ -// $continueShoppingUrl = $currentProduct->getProductUrl(); -// } elseif ($currentCategory = Mage::registry('current_category')) { -// /** -// * go to category view page -// */ -// -// $continueShoppingUrl = $currentCategory->getUrl().(count($this->_getRequest()->getQuery())!=0?'?'.http_build_qu//ery($this->_getRequest()->getQuery(), '', '&'):''); -// -// } else { -// $continueShoppingUrl = $this->_getUrl('*/*/*', array('_current'=>true)); -// } + $continueUrl = Mage::helper('core')->urlEncode($this->getCurrentUrl()); + $urlParamName = Mage_Core_Controller_Front_Action::PARAM_NAME_URL_ENCODED; - $continueShoppingUrl = $this->getCurrentUrl(); - - $params = array( - Mage_Core_Controller_Front_Action::PARAM_NAME_URL_ENCODED => Mage::helper('core')->urlEncode($continueShoppingUrl), - 'product' => $product->getId() + $routeParams = array( + $urlParamName => $continueUrl, + 'product' => $product->getEntityId() ); - if ($this->_getRequest()->getRouteName() == 'checkout' - && $this->_getRequest()->getControllerName() == 'cart') { - $params['in_cart'] = 1; + if (!empty($additional)) { + $routeParams = array_merge($routeParams, $additional); } - if (count($additional)){ - $params = array_merge($params, $additional); + if ($product->hasUrlDataObject()) { + $routeParams['_store'] = $product->getUrlDataObject()->getStoreId(); + $routeParams['_store_to_url'] = true; + } + + if ($this->_getRequest()->getRouteName() == 'checkout' + && $this->_getRequest()->getControllerName() == 'cart') { + $routeParams['in_cart'] = 1; } - return $this->_getUrl('checkout/cart/add', $params); + return $this->_getUrl('checkout/cart/add', $routeParams); } /** diff --git a/app/code/core/Mage/Checkout/Helper/Data.php b/app/code/core/Mage/Checkout/Helper/Data.php index 842b446556..e346048825 100644 --- a/app/code/core/Mage/Checkout/Helper/Data.php +++ b/app/code/core/Mage/Checkout/Helper/Data.php @@ -93,43 +93,40 @@ public function canOnepageCheckout() return true; } + /** + * Get sales item (quote item, order item etc) price including tax based on row total and tax amount + * + * @param Varien_Object $item + * @return float + */ public function getPriceInclTax($item) { - //$price = ($item->getCalculationPrice() ? $item->getCalculationPrice() : $item->getPrice()); + if ($item->getPriceInclTax()) { + return $item->getPriceInclTax(); + } $qty = ($item->getQty() ? $item->getQty() : ($item->getQtyOrdered() ? $item->getQtyOrdered() : 1)); - //$tax = ($item->getTaxBeforeDiscount() ? $item->getTaxBeforeDiscount() : $item->getTaxAmount()); - //$price = Mage::app()->getStore()->roundPrice($price+($tax/$qty)); $price = Mage::app()->getStore()->roundPrice(($item->getRowTotal()+$item->getTaxAmount())/$qty); return $price; } + /** + * Get sales item (quote item, order item etc) row total price including tax + * + * @param Varien_Object $item + * @return float + */ public function getSubtotalInclTax($item) { - if ($item instanceof Mage_Sales_Model_Order_Item) { - $store = $item->getOrder()->getStore(); - } elseif($item instanceof Mage_Sales_Model_Order_Invoice_Item) { - $store = $item->getInvoice()->getOrder()->getStore(); - } elseif($item instanceof Mage_Sales_Model_Order_Shipment_Item) { - $store = $item->getShipment()->getOrder()->getStore(); - } elseif($item instanceof Mage_Sales_Model_Order_Creditmemo_Item) { - $store = $item->getCreditmemo()->getOrder()->getStore(); - } else { - $store = $item->getQuote()->getStore(); - } - if (!Mage::helper('tax')->applyTaxAfterDiscount($store) and $item->getTaxBeforeDiscount()) { - $tax = $item->getTaxBeforeDiscount(); - } else { - $tax = $item->getTaxAmount(); + if ($item->getRowTotalInclTax()) { + return $item->getRowTotalInclTax(); } + $tax = $item->getTaxAmount(); return $item->getRowTotal() + $tax; } public function getBasePriceInclTax($item) { - //$price = ($item->getCalculationPrice() ? $item->getCalculationPrice() : $item->getPrice()); $qty = ($item->getQty() ? $item->getQty() : ($item->getQtyOrdered() ? $item->getQtyOrdered() : 1)); - //$tax = ($item->getTaxBeforeDiscount() ? $item->getTaxBeforeDiscount() : $item->getTaxAmount()); - //$price = Mage::app()->getStore()->roundPrice($price+($tax/$qty)); $price = Mage::app()->getStore()->roundPrice(($item->getBaseRowTotal()+$item->getBaseTaxAmount())/$qty); return $price; } diff --git a/app/code/core/Mage/Checkout/Model/Type/Multishipping.php b/app/code/core/Mage/Checkout/Model/Type/Multishipping.php index 3702040a1b..5f51e06c1f 100644 --- a/app/code/core/Mage/Checkout/Model/Type/Multishipping.php +++ b/app/code/core/Mage/Checkout/Model/Type/Multishipping.php @@ -307,8 +307,8 @@ public function setPaymentMethod($payment) Mage::throwException(Mage::helper('checkout')->__('Payment method is not defined')); } $this->getQuote()->getPayment() - ->importData($payment) - ->save(); + ->importData($payment); + $this->getQuote()->save(); return $this; } diff --git a/app/code/core/Mage/Checkout/Model/Type/Onepage.php b/app/code/core/Mage/Checkout/Model/Type/Onepage.php index c6a215b5d7..f0f356ee3d 100644 --- a/app/code/core/Mage/Checkout/Model/Type/Onepage.php +++ b/app/code/core/Mage/Checkout/Model/Type/Onepage.php @@ -353,7 +353,7 @@ public function savePayment($data) $payment->importData($data); $this->getQuote()->getShippingAddress()->setPaymentMethod($payment->getMethod()); - $this->getQuote()->collectTotals()->save(); + $this->getQuote()->save(); $this->getCheckout() ->setStepData('payment', 'complete', true) diff --git a/app/code/core/Mage/Checkout/controllers/OnepageController.php b/app/code/core/Mage/Checkout/controllers/OnepageController.php index b5f9a73faa..8d79e23af8 100644 --- a/app/code/core/Mage/Checkout/controllers/OnepageController.php +++ b/app/code/core/Mage/Checkout/controllers/OnepageController.php @@ -56,21 +56,27 @@ protected function _ajaxRedirectResponse() return $this; } + /** + * Validate ajax request and redirect on failure + * + * @return bool + */ protected function _expireAjax() { if (!$this->getOnepage()->getQuote()->hasItems() || $this->getOnepage()->getQuote()->getHasError() || $this->getOnepage()->getQuote()->getIsMultiShipping()) { $this->_ajaxRedirectResponse(); - exit; + return true; } $action = $this->getRequest()->getActionName(); if (Mage::getSingleton('checkout/session')->getCartWasUpdated(true) && !in_array($action, array('index', 'progress'))) { $this->_ajaxRedirectResponse(); - exit; + return true; } - Mage::getSingleton('core/translate_inline')->setIsAjaxRequest(true); + + return false; } protected function _getShippingMethodsHtml() @@ -156,21 +162,27 @@ public function indexAction() */ public function progressAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } $this->loadLayout(false); $this->renderLayout(); } public function shippingMethodAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } $this->loadLayout(false); $this->renderLayout(); } public function reviewAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } $this->loadLayout(false); $this->renderLayout(); } @@ -222,7 +234,9 @@ public function getAdditionalAction() */ public function getAddressAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } $addressId = $this->getRequest()->getParam('address', false); if ($addressId) { $address = $this->getOnepage()->getAddress($addressId); @@ -233,11 +247,13 @@ public function getAddressAction() public function saveMethodAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } if ($this->getRequest()->isPost()) { $method = $this->getRequest()->getPost('method'); $result = $this->getOnepage()->saveCheckoutMethod($method); - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } @@ -246,7 +262,9 @@ public function saveMethodAction() */ public function saveBillingAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } if ($this->getRequest()->isPost()) { $data = $this->getRequest()->getPost('billing', array()); $customerAddressId = $this->getRequest()->getPost('billing_address_id', false); @@ -278,13 +296,15 @@ public function saveBillingAction() } } - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } public function saveShippingAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } if ($this->getRequest()->isPost()) { $data = $this->getRequest()->getPost('shipping', array()); $customerAddressId = $this->getRequest()->getPost('shipping_address_id', false); @@ -302,13 +322,15 @@ public function saveShippingAction() // $result['shipping_methods_html'] = $this->getLayout()->getBlock('root')->toHtml(); // $result['shipping_methods_html'] = $this->_getShippingMethodsHtml(); - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } public function saveShippingMethodAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } if ($this->getRequest()->isPost()) { $data = $this->getRequest()->getPost('shipping_method', ''); $result = $this->getOnepage()->saveShippingMethod($data); @@ -317,7 +339,7 @@ public function saveShippingMethodAction() */ if(!$result) { Mage::dispatchEvent('checkout_controller_onepage_save_shipping_method', array('request'=>$this->getRequest(), 'quote'=>$this->getOnepage()->getQuote())); - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); $result['goto_section'] = 'payment'; $result['update_section'] = array( @@ -327,56 +349,64 @@ public function saveShippingMethodAction() // $result['payment_methods_html'] = $this->_getPaymentMethodsHtml(); } - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } + /** + * Save payment ajax action + * + * Sets either redirect or a JSON response + */ public function savePaymentAction() { - $this->_expireAjax(); - if ($this->getRequest()->isPost()) { + if ($this->_expireAjax()) { + return; + } + try { + if (!$this->getRequest()->isPost()) { + $this->_ajaxRedirectResponse(); + return; + } + + // set payment to quote + $result = array(); $data = $this->getRequest()->getPost('payment', array()); - /* - * first to check payment information entered is correct or not - */ + $result = $this->getOnepage()->savePayment($data); - try { - $result = $this->getOnepage()->savePayment($data); - } - catch (Mage_Payment_Exception $e) { - if ($e->getFields()) { - $result['fields'] = $e->getFields(); - } - $result['error'] = $e->getMessage(); - } - catch (Exception $e) { - $result['error'] = $e->getMessage(); - } + // get section and redirect data $redirectUrl = $this->getOnePage()->getQuote()->getPayment()->getCheckoutRedirectUrl(); if (empty($result['error']) && !$redirectUrl) { $this->loadLayout('checkout_onepage_review'); - $result['goto_section'] = 'review'; $result['update_section'] = array( 'name' => 'review', 'html' => $this->_getReviewHtml() ); - -// $result['review_html'] = $this->getLayout()->getBlock('root')->toHtml(); } - if ($redirectUrl) { $result['redirect'] = $redirectUrl; } - - $this->getResponse()->setBody(Zend_Json::encode($result)); + } catch (Mage_Payment_Exception $e) { + if ($e->getFields()) { + $result['fields'] = $e->getFields(); + } + $result['error'] = $e->getMessage(); + } catch (Mage_Core_Exception $e) { + $result['error'] = $e->getMessage(); + } catch (Exception $e) { + Mage::logException($e); + $result['error'] = $this->__('Unable to set Payment Method.'); } + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } public function saveOrderAction() { - $this->_expireAjax(); + if ($this->_expireAjax()) { + return; + } $result = array(); try { @@ -386,7 +416,7 @@ public function saveOrderAction() $result['success'] = false; $result['error'] = true; $result['error_messages'] = $this->__('Please agree to all Terms and Conditions before placing the order.'); - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); return; } } @@ -440,7 +470,7 @@ public function saveOrderAction() $result['redirect'] = $redirectUrl; } - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } } diff --git a/app/code/core/Mage/Chronopay/Block/Form.php b/app/code/core/Mage/Chronopay/Block/Form.php new file mode 100644 index 0000000000..6617a0f747 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Block/Form.php @@ -0,0 +1,42 @@ + + */ +class Mage_Chronopay_Block_Form extends Mage_Payment_Block_Form_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('chronopay/form.phtml'); + } + +} diff --git a/app/code/core/Mage/Chronopay/Block/Info.php b/app/code/core/Mage/Chronopay/Block/Info.php new file mode 100644 index 0000000000..62275166c4 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Block/Info.php @@ -0,0 +1,48 @@ + + */ +class Mage_Chronopay_Block_Info extends Mage_Payment_Block_Info_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('chronopay/info.phtml'); + } + + public function toPdf() + { + $this->setTemplate('chronopay/pdf/info.phtml'); + return $this->toHtml(); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Block/Standard/Failure.php b/app/code/core/Mage/Chronopay/Block/Standard/Failure.php new file mode 100644 index 0000000000..ef1155fb01 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Block/Standard/Failure.php @@ -0,0 +1,37 @@ + +*/ + +class Mage_Chronopay_Block_Standard_Failure extends Mage_Core_Block_Template +{ +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Block/Standard/Form.php b/app/code/core/Mage/Chronopay/Block/Standard/Form.php new file mode 100644 index 0000000000..0556672e13 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Block/Standard/Form.php @@ -0,0 +1,43 @@ + + */ + +class Mage_Chronopay_Block_Standard_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('chronopay/standard/form.phtml'); + parent::_construct(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Block/Standard/Redirect.php b/app/code/core/Mage/Chronopay/Block/Standard/Redirect.php new file mode 100644 index 0000000000..398a81a7b3 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Block/Standard/Redirect.php @@ -0,0 +1,59 @@ + + */ + +class Mage_Chronopay_Block_Standard_Redirect extends Mage_Core_Block_Abstract +{ + + protected function _toHtml() + { + $standard = Mage::getModel('chronopay/standard'); + $form = new Varien_Data_Form(); + $form->setAction($standard->getChronopayUrl()) + ->setId('chronopay_standard_checkout') + ->setName('chronopay_standard_checkout') + ->setMethod('POST') + ->setUseContainer(true); + foreach ($standard->setOrder($this->getOrder())->getStandardCheckoutFormFields() as $field => $value) { + $form->addField($field, 'hidden', array('name' => $field, 'value' => $value)); + } + $html = ''; + $html.= $this->__('You will be redirected to ChronoPay in a few seconds.'); + $html.= $form->toHtml(); + $html.= ''; + $html.= ''; + + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Helper/Data.php b/app/code/core/Mage/Chronopay/Helper/Data.php new file mode 100644 index 0000000000..67e1b0eba3 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Helper/Data.php @@ -0,0 +1,38 @@ + + */ +class Mage_Chronopay_Helper_Data extends Mage_Core_Helper_Abstract +{ + +} diff --git a/app/code/core/Mage/Chronopay/Model/Api/Debug.php b/app/code/core/Mage/Chronopay/Model/Api/Debug.php new file mode 100644 index 0000000000..966449b0e9 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Api/Debug.php @@ -0,0 +1,41 @@ + + */ +class Mage_Chronopay_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('chronopay/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Config.php b/app/code/core/Mage/Chronopay/Model/Config.php new file mode 100644 index 0000000000..a860f43410 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Config.php @@ -0,0 +1,126 @@ + + */ + +class Mage_Chronopay_Model_Config extends Varien_Object +{ + /** + * Return config var + * + * @param string Var key + * @param string Default value for non-existing key + * @return mixed + */ + public function getConfigData($key, $default=false) + { + if (!$this->hasData($key)) { + $value = Mage::getStoreConfig('payment/chronopay_standard/'.$key); + if (is_null($value) || false===$value) { + $value = $default; + } + $this->setData($key, $value); + } + return $this->getData($key); + } + + /** + * Return Site ID registered in ChronoPay Admnin Panel + * + * @return string Site ID + */ + public function getSiteId () + { + return $this->getConfigData('site_id'); + } + + /** + * Return Product ID (general type payments) registered in ChronoPay Admnin Panel + * + * @return string Product ID + */ + public function getProductId () + { + return $this->getConfigData('product_id'); + } + + /** + * Return Store description sent to Chronopay + * + * @return string Description + */ + public function getDescription () + { + $description = $this->getConfigData('description'); + return $description; + } + + /** + * Return new order status + * + * @return string New order status + */ + public function getNewOrderStatus () + { + return $this->getConfigData('order_status'); + } + + /** + * Return debug flag + * + * @return boolean Debug flag (0/1) + */ + public function getDebug () + { + return $this->getConfigData('debug_flag'); + } + + /** + * Return accepted currency + * + * @return string Currenc + */ + public function getCurrency () + { + return $this->getConfigData('currency'); + } + + /** + * Return client interface language + * + * @return string(2) Accepted language + */ + public function getLanguage () + { + return $this->getConfigData('language'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Gateway.php b/app/code/core/Mage/Chronopay/Model/Gateway.php new file mode 100644 index 0000000000..5ae99a2457 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Gateway.php @@ -0,0 +1,336 @@ + + */ + +class Mage_Chronopay_Model_Gateway extends Mage_Payment_Model_Method_Cc +{ + const CGI_URL = 'https://secure.chronopay.com/gateway.cgi'; + + const OPCODE_CHARGING = 1; + const OPCODE_REFUND = 2; + const OPCODE_AUTHORIZE = 4; + const OPCODE_VOID_AUTHORIZE = 5; + const OPCODE_CONFIRM_AUTHORIZE = 6; + const OPCODE_CUSTOMER_FUND_TRANSFER = 8; + + protected $_code = 'chronopay_gateway'; + + protected $_formBlockType = 'chronopay/form'; + protected $_infoBlockType = 'chronopay/info'; + + /** + * Availability options + */ + protected $_isGateway = true; + protected $_canAuthorize = true; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = true; + protected $_canVoid = false; + protected $_canUseInternal = true; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = true; + protected $_canSaveCc = false; + + /** + * Return ip address of customer + * + * @return string + */ + protected function _getIp () + { + return Mage::helper('core/http')->getRemoteAddr(); + } + + /** + * Return shared secret key from config + * + * @return string + */ + protected function _getSharedSecret () + { + return $this->getConfigData('shared_secret'); + } + + public function authorize(Varien_Object $payment, $amount) + { + $payment->setAmount($amount); + $payment->setOpcode(self::OPCODE_AUTHORIZE); + + $request = $this->_buildRequest($payment); + $result = $this->_postRequest($request); + + if (!$result->getError()) { + $payment->setStatus(self::STATUS_APPROVED); + $payment->setCcTransId($result->getTransaction()); + } else { + Mage::throwException($result->getError()); + } + + return $this; + } + + public function capture(Varien_Object $payment, $amount) + { + $payment->setAmount($amount); + if ($payment->getCcTransId()) { + $this->setTransactionId($payment->getCcTransId()); + $payment->setOpcode(self::OPCODE_CONFIRM_AUTHORIZE); + } else { + $payment->setOpcode(self::OPCODE_CHARGING); + } + + $request = $this->_buildRequest($payment); + $result = $this->_postRequest($request); + + if (!$result->getError()) { + $payment->setStatus(self::STATUS_APPROVED); + $payment->setLastTransId($result->getTransaction()); + } else { + Mage::throwException($result->getError()); + } + + return $this; + } + + public function refund(Varien_Object $payment, $amount) + { + $payment->setAmount($amount); + $payment->setOpcode(self::OPCODE_REFUND); + + $this->setTransactionId($payment->getRefundTransactionId()); + + $request = $this->_buildRequest($payment); + $result = $this->_postRequest($request); + + $payment->setStatus(self::STATUS_APPROVED); + $payment->setLastTransId($result->getTransaction()); + + return $this; + } + + /** + * Building request array + * + * @param Varien_Object + * @return array + */ + protected function _buildRequest(Varien_Object $payment) + { + $order = $payment->getOrder(); + $billing = $order->getBillingAddress(); + $streets = $billing->getStreet(); + $street = isset($streets[0]) && $streets[0] != '' + ? $streets[0] + : (isset($streets[1]) && $streets[1] != '' ? $streets[1] : ''); + + $request = Mage::getModel('chronopay/gateway_request') + ->setOpcode($payment->getOpcode()) + ->setProductId($this->getConfigData('product_id')); + + switch ($request->getOpcode()) { + case self::OPCODE_CUSTOMER_FUND_TRANSFER : + $request->setCustomer($order->getCustomerId()) + ->setAmount(sprintf('%.2f', $payment->getAmount())) + ->setCurrency($order->getBaseCurrencyCode()); + break; + case self::OPCODE_CHARGING : + case self::OPCODE_REFUND : + case self::OPCODE_AUTHORIZE : + case self::OPCODE_VOID_AUTHORIZE : + $request->setFname($billing->getFirstname()) + ->setLname($billing->getLastname()) + ->setCardholder($payment->getCcOwner()) + ->setZip($billing->getPostcode()) + ->setStreet($street) + ->setCity($billing->getCity()) + ->setState($billing->getRegionModel()->getCode()) + ->setCountry($billing->getCountryModel()->getIso3Code()) + ->setEmail($order->getCustomerEmail()) + ->setPhone($billing->getTelephone()) + ->setIp($this->_getIp()) + ->setCardNo($payment->getCcNumber()) + ->setCvv($payment->getCcCid()) + ->setExpirey($payment->getCcExpYear()) + ->setExpirem(sprintf('%02d', $payment->getCcExpMonth())) + ->setAmount(sprintf('%.2f', $payment->getAmount())) + ->setCurrency($order->getBaseCurrencyCode()); + break; + default : + Mage::throwException( + Mage::helper('chronopay')->__('Invalid operation code') + ); + break; + } + + $request->setShowTransactionId(1); + + if ($this->getTransactionId()) { + $request->setTransaction($this->getTransactionId()); + } + + + $hash = $this->_getHash($request); + $request->setHash($hash); + return $request; + } + + /** + * Send request to gateway + * + * @param Mage_Chronopay_Model_Gateway_Request + * @return mixed + */ + protected function _postRequest(Mage_Chronopay_Model_Gateway_Request $request) + { + $result = Mage::getModel('chronopay/gateway_result'); + + $client = new Varien_Http_Client(); + + $url = $this->getConfigData('cgi_url'); + $client->setUri($url ? $url : self::CGI_URL); + $client->setConfig(array( + 'maxredirects' => 0, + 'timeout' => 30, + )); + $client->setParameterPost($request->getData()); + $client->setMethod(Zend_Http_Client::POST); + + if ($this->getConfigData('debug_flag')) { + $debug = Mage::getModel('chronopay/api_debug') + ->setRequestBody($client->getUri() . "\n" . print_r($request->getData(), 1)) + ->save(); + } + + try { + $response = $client->request(); + $body = $response->getRawBody(); + + if (preg_match('/(T\|(.+)\|[\r\n]{0,}){0,1}(Y\|(.+)?|\|)|(N\|(.+[\r\n]{0,}.+){0,})/', $body, $matches)) { + + $transactionId = $matches[2]; + $message = isset($matches[4])?trim($matches[4], '|'):''; + + if (isset($matches[5], $matches[6])) { + $result->setError($matches[6]); + Mage::throwException($matches[6]); + } + + if ($message == 'Completed') { + $result->setTransaction($request->getTransaction()); + } + + if (strlen($transactionId)) { + $result->setTransaction($transactionId); + } + + if (!$result->getTransaction()) { + Mage::throwException(Mage::helper('chronopay')->__('Transaction ID is invalid')); + } + } else { + Mage::throwException(Mage::helper('chronopay')->__('Invalid response format')); + } + + if ($this->getConfigData('debug_flag')) { + $debug->setResponseBody($body)->save(); + } + + } catch (Exception $e) { + $result->setResponseCode(-1) + ->setResponseReasonCode($e->getCode()) + ->setResponseReasonText($e->getMessage()); + + + $exceptionMsg = Mage::helper('chronopay')->__('Gateway request error: %s', $e->getMessage()); + + if ($this->getConfigData('debug_flag')) { + $debug->setResponseBody($body)->save(); + } + + Mage::throwException($exceptionMsg); + } + return $result; + } + + /** + * Generate MD5 hash for transaction checksum + * + * @param Mage_Chronopay_Model_Gateway_Request + * @return string MD5 + */ + protected function _getHash(Mage_Chronopay_Model_Gateway_Request $request) + { + $hashArray = array( + $this->_getSharedSecret(), + $request->getOpcode(), + $request->getProductId() + ); + + switch ($request->getOpcode()) { + case self::OPCODE_CHARGING : + case self::OPCODE_AUTHORIZE : + $hashArray[] = $request->getFname(); + $hashArray[] = $request->getLname(); + $hashArray[] = $request->getStreet(); + $hashArray[] = $this->_getIp(); + $hashArray[] = $request->getCardNo(); + $hashArray[] = $request->getAmount(); + break; + + case self::OPCODE_VOID_AUTHORIZE : + case self::OPCODE_CONFIRM_AUTHORIZE : + $hashArray[] = $request->getTransaction(); + break; + + case self::OPCODE_REFUND : + $hashArray[] = $request->getTransaction(); + $hashArray[] = $request->getAmount(); + break; + + case self::OPCODE_CUSTOMER_FUND_TRANSFER : + $hashArray[] = $request->getCustomer(); + $hashArray[] = $request->getTransaction(); + $hashArray[] = $request->getAmount(); + break; + + default : + Mage::throwException( + Mage::helper('chronopay')->__('Invalid operation code') + ); + break; + } + + return md5(implode('', $hashArray)); + } + +} diff --git a/app/code/core/Mage/Chronopay/Model/Gateway/Request.php b/app/code/core/Mage/Chronopay/Model/Gateway/Request.php new file mode 100644 index 0000000000..5eaa8645c5 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Gateway/Request.php @@ -0,0 +1,38 @@ + + */ + +class Mage_Chronopay_Model_Gateway_Request extends Varien_Object +{ +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Gateway/Result.php b/app/code/core/Mage/Chronopay/Model/Gateway/Result.php new file mode 100644 index 0000000000..fa1afa1d83 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Gateway/Result.php @@ -0,0 +1,38 @@ + + */ + +class Mage_Chronopay_Model_Gateway_Result extends Varien_Object +{ +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Chronopay/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..f69398cc18 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Mysql4/Api/Debug.php @@ -0,0 +1,42 @@ + + */ + +class Mage_Chronopay_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('chronopay/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Chronopay/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..82278cc9a8 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,41 @@ + + */ +class Mage_Chronopay_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('chronopay/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Mysql4/Setup.php b/app/code/core/Mage/Chronopay/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..7bc8a0897a --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Mysql4/Setup.php @@ -0,0 +1,39 @@ + + */ + +class Mage_Chronopay_Model_Mysql4_Setup extends Mage_Sales_Model_Mysql4_Setup +{ + +} diff --git a/app/code/core/Mage/Chronopay/Model/Source/Cctype.php b/app/code/core/Mage/Chronopay/Model/Source/Cctype.php new file mode 100644 index 0000000000..6383becb18 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Source/Cctype.php @@ -0,0 +1,40 @@ + + */ +class Mage_Chronopay_Model_Source_Cctype extends Mage_Payment_Model_Source_Cctype +{ + public function getAllowedTypes() + { + return array('VI', 'MC', 'AE', 'DICL', 'JCB', 'ENR', 'SS', 'DI'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Source/Language.php b/app/code/core/Mage/Chronopay/Model/Source/Language.php new file mode 100644 index 0000000000..4e97de1175 --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Source/Language.php @@ -0,0 +1,50 @@ + + */ + +class Mage_Chronopay_Model_Source_Language +{ + public function toOptionArray() + { + return array( + array('value' => 'EN', 'label' => Mage::helper('chronopay')->__('English')), + array('value' => 'RU', 'label' => Mage::helper('chronopay')->__('Russian')), + array('value' => 'NL', 'label' => Mage::helper('chronopay')->__('Dutch')), + array('value' => 'DE', 'label' => Mage::helper('chronopay')->__('German')), + ); + } +} + + + diff --git a/app/code/core/Mage/Chronopay/Model/Source/PaymentAction.php b/app/code/core/Mage/Chronopay/Model/Source/PaymentAction.php new file mode 100644 index 0000000000..2f7b23158a --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Source/PaymentAction.php @@ -0,0 +1,49 @@ + + */ +class Mage_Chronopay_Model_Source_PaymentAction +{ + public function toOptionArray() + { + return array( + array( + 'value' => Mage_Chronopay_Model_Gateway::ACTION_AUTHORIZE, + 'label' => Mage::helper('chronopay')->__('Authorize Only') + ), + array( + 'value' => Mage_Chronopay_Model_Gateway::ACTION_AUTHORIZE_CAPTURE, + 'label' => Mage::helper('chronopay')->__('Authorize and Capture') + ), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/Model/Standard.php b/app/code/core/Mage/Chronopay/Model/Standard.php new file mode 100644 index 0000000000..48f45e975d --- /dev/null +++ b/app/code/core/Mage/Chronopay/Model/Standard.php @@ -0,0 +1,269 @@ + + */ +class Mage_Chronopay_Model_Standard extends Mage_Payment_Model_Method_Abstract +{ + protected $_code = 'chronopay_standard'; + protected $_formBlockType = 'chronopay/standard_form'; + + protected $_isGateway = false; + protected $_canAuthorize = true; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + protected $_order = null; + + + /** + * Get Config model + * + * @return object Mage_Chronopay_Model_Config + */ + public function getConfig() + { + return Mage::getSingleton('chronopay/config'); + } + + /** + * Payment validation + * + * @param none + * @return Mage_Chronopay_Model_Standard + */ + public function validate() + { + parent::validate(); + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $currency_code = $paymentInfo->getOrder()->getBaseCurrencyCode(); + } else { + $currency_code = $paymentInfo->getQuote()->getBaseCurrencyCode(); + } + if ($currency_code != $this->getConfig()->getCurrency()) { + Mage::throwException(Mage::helper('chronopay')->__('Selected currency code ('.$currency_code.') is not compatible with ChronoPay')); + } + return $this; + } + + /** + * Capture payment + * + * @param Varien_Object $orderPayment + * @return Mage_Payment_Model_Abstract + */ + public function capture (Varien_Object $payment, $amount) + { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + + return $this; + } + + /** + * Returns Target URL + * + * @return string Target URL + */ + public function getChronopayUrl () + { + return 'https://secure.chronopay.com/index_shop.cgi'; + } + + /** + * Return URL for Chronopay success response + * + * @return string URL + */ + protected function getSuccessURL () + { + return Mage::getUrl('chronopay/standard/success', array('_secure' => true)); + } + + /** + * Return URL for Chronopay notification + * + * @return string Notification URL + */ + protected function getNotificationURL () + { + return Mage::getUrl('chronopay/standard/notify', array('_secure' => true)); + } + + /** + * Return URL for Chronopay failure response + * + * @return string URL + */ + protected function getFailureURL () + { + return Mage::getUrl('chronopay/standard/failure', array('_secure' => true)); + } + + /** + * Form block description + * + * @return object + */ + public function createFormBlock($name) + { + $block = $this->getLayout()->createBlock('chronopay/form_standard', $name); + $block->setMethod($this->_code); + $block->setPayment($this->getPayment()); + + return $block; + } + + /** + * Return Order Place Redirect URL + * + * @return string Order Redirect URL + */ + public function getOrderPlaceRedirectUrl() + { + return Mage::getUrl('chronopay/standard/redirect'); + } + + /** + * Return Standard Checkout Form Fields for request to Chronopay + * + * @return array Array of hidden form fields + */ + public function getStandardCheckoutFormFields () + { + $order = $this->getOrder(); + if (!($order instanceof Mage_Sales_Model_Order)) { + Mage::throwException($this->_getHelper()->__('Cannot retrieve order object')); + } + + $billingAddress = $order->getBillingAddress(); + + $streets = $billingAddress->getStreet(); + $street = isset($streets[0]) && $streets[0] != '' + ? $streets[0] + : (isset($streets[1]) && $streets[1] != '' ? $streets[1] : ''); + + if ($this->getConfig()->getDescription()) { + $transDescription = $this->getConfig()->getDescription(); + } else { + $transDescription = Mage::helper('chronopay')->__('Order #%s', $order->getRealOrderId()); + } + + if ($order->getCustomerEmail()) { + $email = $order->getCustomerEmail(); + } elseif ($billingAddress->getEmail()) { + $email = $billingAddress->getEmail(); + } else { + $email = ''; + } + + $fields = array( + 'product_id' => $this->getConfig()->getProductId(), + 'product_name' => $transDescription, + 'product_price' => $order->getBaseGrandTotal(), + 'language' => $this->getConfig()->getLanguage(), + 'f_name' => $billingAddress->getFirstname(), + 's_name' => $billingAddress->getLastname(), + 'street' => $street, + 'city' => $billingAddress->getCity(), + 'state' => $billingAddress->getRegionModel()->getCode(), + 'zip' => $billingAddress->getPostcode(), + 'country' => $billingAddress->getCountryModel()->getIso3Code(), + 'phone' => $billingAddress->getTelephone(), + 'email' => $email, + 'cb_url' => $this->getNotificationURL(), + 'cb_type' => 'P', // POST method used (G - GET method) + 'decline_url' => $this->getFailureURL(), + 'cs1' => Mage::helper('core')->encrypt($order->getRealOrderId()) + ); + + if ($this->getConfig()->getDebug()) { + $debug = Mage::getModel('chronopay/api_debug') + ->setRequestBody($this->getChronopayUrl()."\n".print_r($fields,1)) + ->save(); + $fields['cs2'] = $debug->getId(); + } + + return $fields; + } + + /** + * Validate Response from ChronoPay + * + * @param array Post data returned from ChronoPay + * @return mixed + */ + public function validateResponse ($data) + { + $order = $this->getOrder(); + + if (!($order instanceof Mage_Sales_Model_Order)) { + Mage::throwException($this->_getHelper()->__('Cannot retrieve order object')); + } + + try { + $ok = is_array($data) + && isset($data['transaction_type']) && $data['transaction_type'] != '' + && isset($data['customer_id']) && $data['customer_id'] != '' + && isset($data['site_id']) && $data['site_id'] != '' + && isset($data['product_id']) && $data['product_id'] != ''; + + if (!$ok) { + throw new Exception('Cannot restore order or invalid order ID'); + } + + // validate site ID + if ($this->getConfig()->getSiteId() != $data['site_id']) { + throw new Exception('Invalid site ID'); + } + + // validate product ID + if ($this->getConfig()->getProductId() != $data['product_id']) { + throw new Exception('Invalid product ID'); + } + + // Successful transaction type + if (!in_array($data['transaction_type'], array('initial', 'onetime'))) { + throw new Exception('Transaction is not successful'); + } + + } catch (Exception $e) { + return $e; + } + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/controllers/StandardController.php b/app/code/core/Mage/Chronopay/controllers/StandardController.php new file mode 100644 index 0000000000..0b594c7c4e --- /dev/null +++ b/app/code/core/Mage/Chronopay/controllers/StandardController.php @@ -0,0 +1,219 @@ + +*/ +class Mage_Chronopay_StandardController extends Mage_Core_Controller_Front_Action +{ + /** + * Order instance + */ + protected $_order; + + /** + * Return debug flag + * + * @return boolean + */ + public function getDebug () + { + return Mage::getSingleton('chronopay/config')->getDebug(); + } + + /** + * Get order + * + * @return Mage_Sales_Model_Order + */ + public function getOrder () + { + if ($this->_order == null) { + $session = Mage::getSingleton('checkout/session'); + $this->_order = Mage::getModel('sales/order'); + $this->_order->loadByIncrementId($session->getLastRealOrderId()); + } + return $this->_order; + } + + /** + * When a customer chooses Chronopay on Checkout/Payment page + * + */ + public function redirectAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setChronopayStandardQuoteId($session->getQuoteId()); + + $order = $this->getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('chronopay')->__('Customer was redirected to Chronopay') + ); + $order->save(); + + $this->getResponse() + ->setBody($this->getLayout() + ->createBlock('chronopay/standard_redirect') + ->setOrder($order) + ->toHtml()); + + $session->unsQuoteId(); + } + + /** + * Success response from Chronopay + */ + public function successAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setQuoteId($session->getChronopayStandardQuoteId()); + $session->unsChronopayStandardQuoteId(); + + $order = $this->getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('chronopay')->__('Customer successfully returned from Chronopay') + ); + + $order->save(); + $this->_redirect('checkout/onepage/success'); + } + + + /** + * Notification Action from ChronoPay + */ + public function notifyAction () + { + $postData = $this->getRequest()->getPost(); + + if (!count($postData)) { + $this->norouteAction(); + return; + } + + if ($this->getDebug()) { + $debug = Mage::getModel('chronopay/api_debug'); + if (isset($postData['cs2']) && $postData['cs2'] > 0) { + $debug->setId($postData['cs2']); + } + $debug->setResponseBody(print_r($postData,1))->save(); + } + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId(Mage::helper('core')->decrypt($postData['cs1'])); + if ($order->getId()) { + $result = $order->getPayment()->getMethodInstance()->setOrder($order)->validateResponse($postData); + + if ($result instanceof Exception) { + if ($order->getId()) { + $order->addStatusToHistory( + $order->getStatus(), + $result->getMessage() + ); + $order->cancel(); + } + Mage::throwException($result->getMessage()); + return; + } + + $order->sendNewOrderEmail(); + + $order->getPayment()->getMethodInstance()->setTransactionId($postData['transaction_id']); + + if ($this->saveInvoice($order)) { + $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true); + } + $order->save(); + } + } + + /** + * Save invoice for order + * + * @param Mage_Sales_Model_Order $order + * @return boolean Can save invoice or not + */ + protected function saveInvoice (Mage_Sales_Model_Order $order) + { + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + return true; + } + + return false; + } + + /** + * Failure response from Chronopay + */ + public function failureAction () + { + $errorMsg = Mage::helper('chronopay')->__('There was an error occurred during paying process.'); + + $order = $this->getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + + if ($order instanceof Mage_Sales_Model_Order && $order->getId()) { + $order->addStatusToHistory($order->getStatus(), $errorMsg); + $order->cancel(); + $order->save(); + } + + $this->loadLayout(); + $this->renderLayout(); + + Mage::getSingleton('checkout/session')->unsLastRealOrderId(); + } + +} diff --git a/app/code/core/Mage/Chronopay/etc/config.xml b/app/code/core/Mage/Chronopay/etc/config.xml new file mode 100644 index 0000000000..667d5aae3e --- /dev/null +++ b/app/code/core/Mage/Chronopay/etc/config.xml @@ -0,0 +1,160 @@ + + + + + + 0.1.0 + + + + + + Mage_Chronopay_Model + chronopay_mysql4 + + + Mage_Chronopay_Model_Mysql4 + + chronopay_api_debug
+
+
+
+ + + + Mage_Chronopay + Mage_Chronopay_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Chronopay_Block + + + + + + DI + Discover + 60 + + + JCB + JCB + 61 + + + DICL + Diners Club + 62 + + + ENR + enRoute + 63 + + + SS + Maestro/Solo + 64 + + + + +
+ + + /chronopay/standard + + + + standard + + Mage_Chronopay + chronopay + + + + + + + + Mage_Chronopay.csv + + + + + + + + chronopay.xml + + + + + + + + + + Mage_Chronopay.csv + + + + + + + + + chronopay/standard + ChronoPay Standard + 0 + O + + + chronopay/gateway + ChronoPay Gateway + 0 + O + + + +
\ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/etc/system.xml b/app/code/core/Mage/Chronopay/etc/system.xml new file mode 100644 index 0000000000..df4b90a0bb --- /dev/null +++ b/app/code/core/Mage/Chronopay/etc/system.xml @@ -0,0 +1,272 @@ + + + + + + + + + Please specify Access URL: {STORE_URL}/chronopay/standard/success in the ChronoPay Client Admin. + text + 251 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>2</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + text + 3 + 1 + 1 + 0 + + + + text + 4 + 1 + 1 + 0 + + + + Order number will be used if left empty + text + 5 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_currency + 6 + 1 + 1 + 0 + + + + select + chronopay/source_language + 7 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 8 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 9 + 1 + 1 + 0 + + + + allowspecific + 10 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 11 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 12 + 1 + 1 + 0 + + + + + + text + 251 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 0 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>10</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + select + chronopay/source_paymentAction + 20 + 1 + 1 + 0 + + + + text + 30 + 1 + 1 + 0 + + + + text + 40 + 1 + 1 + 0 + + + + text + 50 + 1 + 1 + 0 + + + + multiselect + chronopay/source_cctype + 60 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_currency + 70 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 80 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 90 + 1 + 1 + 0 + + + + allowspecific + 100 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 110 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 120 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Chronopay/sql/chronopay_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Chronopay/sql/chronopay_setup/mysql4-install-0.1.0.php new file mode 100644 index 0000000000..1b673aad9a --- /dev/null +++ b/app/code/core/Mage/Chronopay/sql/chronopay_setup/mysql4-install-0.1.0.php @@ -0,0 +1,45 @@ +startSetup(); + +$installer->run(" +CREATE TABLE `{$this->getTable('chronopay_api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); \ 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 595c7065ef..04a9b53fd5 100644 --- a/app/code/core/Mage/Cms/Controller/Router.php +++ b/app/code/core/Mage/Cms/Controller/Router.php @@ -18,22 +18,40 @@ * 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_Cms - * @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_Cms + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + +/** + * Cms Controller Router + * + * @category Mage + * @package Mage_Cms + * @author Magento Core Team + */ class Mage_Cms_Controller_Router extends Mage_Core_Controller_Varien_Router_Abstract { + /** + * Initialize Controller Router + * + * @param Varien_Event_Observer $observer + */ public function initControllerRouters($observer) { $front = $observer->getEvent()->getFront(); - $cms = new Mage_Cms_Controller_Router(); - $front->addRouter('cms', $cms); + $front->addRouter('cms', $this); } + /** + * Validate and Match Cms Page and modify request + * + * @param Zend_Controller_Request_Http $request + * @return bool + */ public function match(Zend_Controller_Request_Http $request) { if (!Mage::isInstalled()) { @@ -45,20 +63,21 @@ public function match(Zend_Controller_Request_Http $request) $identifier = trim($request->getPathInfo(), '/'); - $page = Mage::getModel('cms/page'); + $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') + $request->setModuleName('cms') + ->setControllerName('page') + ->setActionName('view') ->setParam('page_id', $pageId); - $request->setAlias( - Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, - $identifier - ); + $request->setAlias( + Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, + $identifier + ); + return true; } -} \ 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 a0c1765246..3d5def7b47 100644 --- a/app/code/core/Mage/Cms/Model/Page.php +++ b/app/code/core/Mage/Cms/Model/Page.php @@ -18,32 +18,47 @@ * 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_Cms - * @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_Cms + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + /** - * CMS page model + * Cms Page Model * - * @category Mage - * @package Mage_Cms + * @category Mage + * @package Mage_Cms * @author Magento Core Team */ - class Mage_Cms_Model_Page extends Mage_Core_Model_Abstract { - const NOROUTE_PAGE_ID = 'no-route'; + /** + * Prefix of model events names + * + * @var string + */ protected $_eventPrefix = 'cms_page'; + /** + * Initialize resource model + * + */ protected function _construct() { $this->_init('cms/page'); } + /** + * Load object data + * + * @param mixed $id + * @param string $field + * @return Mage_Cms_Model_Page + */ public function load($id, $field=null) { if (is_null($id)) { @@ -52,19 +67,23 @@ public function load($id, $field=null) return parent::load($id, $field); } + /** + * Load No-Route Page + * + * @return Mage_Cms_Model_Page + */ public function noRoutePage() { - $this->setData($this->load(self::NOROUTE_PAGE_ID, $this->getIdFieldName())); - return $this; + return $this->load(self::NOROUTE_PAGE_ID, $this->getIdFieldName()); } /** * Check if page identifier exist for specific store * return page id if page exists * - * @param string $identifier - * @param int $storeId - * @return int + * @param string $identifier + * @param int $storeId + * @return int */ public function checkIdentifier($identifier, $storeId) { diff --git a/app/code/core/Mage/Compiler/controllers/ProcessController.php b/app/code/core/Mage/Compiler/controllers/ProcessController.php index 480737aef9..073d2245b8 100644 --- a/app/code/core/Mage/Compiler/controllers/ProcessController.php +++ b/app/code/core/Mage/Compiler/controllers/ProcessController.php @@ -81,8 +81,11 @@ public function runAction() public function recompileAction() { - $this->_getCompiler()->clear(); + /** + * Add redirect heades before clear compiled sources + */ $this->_redirect('*/*/run'); + $this->_getCompiler()->clear(); } public function disableAction() diff --git a/app/code/core/Mage/Core/Block/Html/Calendar.php b/app/code/core/Mage/Core/Block/Html/Calendar.php index 265867fe85..5dfc225fec 100644 --- a/app/code/core/Mage/Core/Block/Html/Calendar.php +++ b/app/code/core/Mage/Core/Block/Html/Calendar.php @@ -41,32 +41,32 @@ protected function _toHtml() // get days names $days = Zend_Locale_Data::getList($localeCode, 'days'); $this->assign('days', array( - 'wide' => Zend_Json::encode(array_values($days['format']['wide'])), - 'abbreviated' => Zend_Json::encode(array_values($days['format']['abbreviated'])) + 'wide' => Mage::helper('core')->jsonEncode(array_values($days['format']['wide'])), + 'abbreviated' => Mage::helper('core')->jsonEncode(array_values($days['format']['abbreviated'])) )); // get months names $months = Zend_Locale_Data::getList($localeCode, 'months'); $this->assign('months', array( - 'wide' => Zend_Json::encode(array_values($months['format']['wide'])), - 'abbreviated' => Zend_Json::encode(array_values($months['format']['abbreviated'])) + 'wide' => Mage::helper('core')->jsonEncode(array_values($months['format']['wide'])), + 'abbreviated' => Mage::helper('core')->jsonEncode(array_values($months['format']['abbreviated'])) )); // get "today" and "week" words - $this->assign('today', Zend_Json::encode(Zend_Locale_Data::getContent($localeCode, 'relative', 0))); - $this->assign('week', Zend_Json::encode(Zend_Locale_Data::getContent($localeCode, 'field', 'week'))); + $this->assign('today', Mage::helper('core')->jsonEncode(Zend_Locale_Data::getContent($localeCode, 'relative', 0))); + $this->assign('week', Mage::helper('core')->jsonEncode(Zend_Locale_Data::getContent($localeCode, 'field', 'week'))); // get "am" & "pm" words - $this->assign('am', Zend_Json::encode(Zend_Locale_Data::getContent($localeCode, 'am'))); - $this->assign('pm', Zend_Json::encode(Zend_Locale_Data::getContent($localeCode, 'pm'))); + $this->assign('am', Mage::helper('core')->jsonEncode(Zend_Locale_Data::getContent($localeCode, 'am'))); + $this->assign('pm', Mage::helper('core')->jsonEncode(Zend_Locale_Data::getContent($localeCode, 'pm'))); // get first day of week and weekend days $this->assign('firstDay', (int)Mage::getStoreConfig('general/locale/firstday')); - $this->assign('weekendDays', Zend_Json::encode((string)Mage::getStoreConfig('general/locale/weekend'))); + $this->assign('weekendDays', Mage::helper('core')->jsonEncode((string)Mage::getStoreConfig('general/locale/weekend'))); // define default format and tooltip format - $this->assign('defaultFormat', Zend_Json::encode(Mage::app()->getLocale()->getDateStrFormat(Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM))); - $this->assign('toolTipFormat', Zend_Json::encode(Mage::app()->getLocale()->getDateStrFormat(Mage_Core_Model_Locale::FORMAT_TYPE_LONG))); + $this->assign('defaultFormat', Mage::helper('core')->jsonEncode(Mage::app()->getLocale()->getDateStrFormat(Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM))); + $this->assign('toolTipFormat', Mage::helper('core')->jsonEncode(Mage::app()->getLocale()->getDateStrFormat(Mage_Core_Model_Locale::FORMAT_TYPE_LONG))); // get days and months for en_US locale - calendar will parse exactly in this locale $days = Zend_Locale_Data::getList('en_US', 'days'); @@ -75,7 +75,7 @@ protected function _toHtml() $enUS->m = new stdClass(); $enUS->m->wide = array_values($months['format']['wide']); $enUS->m->abbr = array_values($months['format']['abbreviated']); - $this->assign('enUS', Zend_Json::encode($enUS)); + $this->assign('enUS', Mage::helper('core')->jsonEncode($enUS)); return parent::_toHtml(); } diff --git a/app/code/core/Mage/Core/Block/Messages.php b/app/code/core/Mage/Core/Block/Messages.php index 51657b7be0..732fd6020f 100644 --- a/app/code/core/Mage/Core/Block/Messages.php +++ b/app/code/core/Mage/Core/Block/Messages.php @@ -40,28 +40,11 @@ class Mage_Core_Block_Messages extends Mage_Core_Block_Template */ protected $_messages; - /** - * Flag which require message text escape - * - * @var bool - */ - protected $_escapeMessageFlag = false; - public function _prepareLayout() { $this->addMessages(Mage::getSingleton('core/session')->getMessages(true)); - parent::_prepareLayout(); - } - /** - * Set message escape flag - * @param bool $flag - * @return Mage_Core_Block_Messages - */ - public function setEscapeMessageFlag($flag) - { - $this->_escapeMessageFlag = $flag; - return $this; + parent::_prepareLayout(); } /** @@ -76,12 +59,6 @@ public function setMessages(Mage_Core_Model_Message_Collection $messages) return $this; } - /** - * Add messages to display - * - * @param Mage_Core_Model_Message_Collection $messages - * @return Mage_Core_Block_Messages - */ public function addMessages(Mage_Core_Model_Message_Collection $messages) { foreach ($messages->getItems() as $message) { @@ -184,9 +161,7 @@ public function getHtml($type=null) { $html = '
    '; foreach ($this->getMessages($type) as $message) { - $html.= '
  • ' - . ($this->_escapeMessageFlag) ? $this->htmlEscape($message->getText()) : $message->getText() - . '
  • '; + $html.= '
  • '.$message->getText().'
  • '; } $html .= '
'; return $html; @@ -217,7 +192,7 @@ public function getGroupedHtml() foreach ( $messages as $message ) { $html.= '
  • '; - $html.= ($this->_escapeMessageFlag) ? $this->htmlEscape($message->getText()) : $message->getText(); + $html.= $message->getText(); $html.= '
  • '; } $html .= ''; diff --git a/app/code/core/Mage/Core/Controller/Request/Http.php b/app/code/core/Mage/Core/Controller/Request/Http.php index 72f3e3e7ad..269151b7cf 100644 --- a/app/code/core/Mage/Core/Controller/Request/Http.php +++ b/app/code/core/Mage/Core/Controller/Request/Http.php @@ -271,7 +271,7 @@ public function getHttpHost($trimPort = true) return false; } if ($trimPort) { - $host = split(':', $_SERVER['HTTP_HOST']); + $host = explode(':', $_SERVER['HTTP_HOST']); return $host[0]; } return $_SERVER['HTTP_HOST']; diff --git a/app/code/core/Mage/Core/Controller/Varien/Action.php b/app/code/core/Mage/Core/Controller/Varien/Action.php index b8becf7573..e3ec66df0d 100644 --- a/app/code/core/Mage/Core/Controller/Varien/Action.php +++ b/app/code/core/Mage/Core/Controller/Varien/Action.php @@ -434,7 +434,9 @@ public function preDispatch() if (!$this->getFlag('', self::FLAG_NO_START_SESSION)) { $namespace = $this->getLayout()->getArea(); $checkCookie = in_array($this->getRequest()->getActionName(), $this->_cookieCheckActions); - if ($checkCookie && !Mage::getSingleton('core/cookie')->get($namespace)) { + $checkCookie = $checkCookie && !$this->getRequest()->getParam('nocookie', false); + $cookies = Mage::getSingleton('core/cookie')->get(); + if ($checkCookie && empty($cookies)) { $this->setFlag('', self::FLAG_NO_COOKIES_REDIRECT, true); } Mage::getSingleton('core/session', array('name' => $namespace))->start(); @@ -553,9 +555,6 @@ protected function _initLayoutMessages($messagesStorage) { if ($storage = Mage::getSingleton($messagesStorage)) { $this->getLayout()->getMessagesBlock()->addMessages($storage->getMessages(true)); - $this->getLayout()->getMessagesBlock()->setEscapeMessageFlag( - $storage->getEscapeMessages(true) - ); } else { Mage::throwException( diff --git a/app/code/core/Mage/Core/Controller/Varien/Front.php b/app/code/core/Mage/Core/Controller/Varien/Front.php index 54ecdf72af..6731ddc95d 100644 --- a/app/code/core/Mage/Core/Controller/Varien/Front.php +++ b/app/code/core/Mage/Core/Controller/Varien/Front.php @@ -108,6 +108,21 @@ public function getRouter($name) return false; } + /** + * Retrieve routers collection + * + * @return array + */ + public function getRouters() + { + return $this->_routers; + } + + /** + * Init Fron Controller + * + * @return Mage_Core_Controller_Varien_Front + */ public function init() { Mage::dispatchEvent('controller_front_init_before', array('front'=>$this)); @@ -117,14 +132,14 @@ public function init() Varien_Profiler::start('mage::app::init_front_controller::collect_routers'); foreach ($routersInfo as $routerCode => $routerInfo) { if (isset($routerInfo['disabled']) && $routerInfo['disabled']) { - continue; + continue; } if (isset($routerInfo['class'])) { - $router = new $routerInfo['class']; - if (isset($routerInfo['area'])) { - $router->collectRoutes($routerInfo['area'], $routerCode); - } - $this->addRouter($routerCode, $router); + $router = new $routerInfo['class']; + if (isset($routerInfo['area'])) { + $router->collectRoutes($routerInfo['area'], $routerCode); + } + $this->addRouter($routerCode, $router); } } Varien_Profiler::stop('mage::app::init_front_controller::collect_routers'); @@ -236,7 +251,7 @@ public function rewrite() $to = $this->_processRewriteUrl($to); $pathInfo = preg_replace($from, $to, $request->getPathInfo()); - + if (isset($rewrite->complete)) { $request->setPathInfo($pathInfo); } else { diff --git a/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php b/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php index 8ac8de684c..917dd09225 100644 --- a/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php +++ b/app/code/core/Mage/Core/Controller/Varien/Router/Standard.php @@ -222,7 +222,7 @@ public function match(Zend_Controller_Request_Http $request) } // instantiate controller class - $controllerInstance = new $controllerClassName($request, $front->getResponse()); + $controllerInstance = Mage::getControllerInstance($request, $front->getResponse()); if (!$controllerInstance->hasAction($action)) { return false; diff --git a/app/code/core/Mage/Core/Helper/Abstract.php b/app/code/core/Mage/Core/Helper/Abstract.php index 18f5a704af..a64b2b76b9 100644 --- a/app/code/core/Mage/Core/Helper/Abstract.php +++ b/app/code/core/Mage/Core/Helper/Abstract.php @@ -128,6 +128,24 @@ protected function _getModuleName() return $this->_moduleName; } + /** + * Check whether or not the module output is enabled in Configuration + * TODO: add checking if module exists + * + * @param string $moduleName Full module name + * @return boolean + */ + public function isModuleOutputEnabled($moduleName = null) + { + if ($moduleName === null) { + $moduleName = $this->_getModuleName(); + } + if (Mage::getStoreConfigFlag('advanced/modules_disable_output/' . $moduleName)) { + return false; + } + return true; + } + /** * Translate * diff --git a/app/code/core/Mage/Core/Helper/Data.php b/app/code/core/Mage/Core/Helper/Data.php index 60945b8c35..8f2b56869f 100644 --- a/app/code/core/Mage/Core/Helper/Data.php +++ b/app/code/core/Mage/Core/Helper/Data.php @@ -554,4 +554,38 @@ public function xmlToAssoc(SimpleXMLElement $xml) } return $array; } -} \ No newline at end of file + + /** + * Encode the mixed $valueToEncode into the JSON format + * + * @param mixed $valueToEncode + * @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default + * @param array $options Additional options used during encoding + * @return string + */ + public function jsonEncode($valueToEncode, $cycleCheck = false, $options = array()) + { + $json = Zend_Json::encode($valueToEncode, $cycleCheck, $options); + /* @var $inline Mage_Core_Model_Translate_Inline */ + $inline = Mage::getSingleton('core/translate_inline'); + if ($inline->isAllowed()) { + $inline->setIsJson(true); + $inline->processResponseBody($json); + $inline->setIsJson(false); + } + + return $json; + } + + /** + * Decodes the given $encodedValue string which is + * encoded in the JSON format + * + * @param string $encodedValue + * @return mixed + */ + public function jsonDecode($encodedValue, $objectDecodeType = Zend_Json::TYPE_ARRAY) + { + return Zend_Json::decode($encodedValue, $objectDecodeType); + } +} diff --git a/app/code/core/Mage/Core/Helper/Js.php b/app/code/core/Mage/Core/Helper/Js.php index 9650c41d68..01253e18ad 100644 --- a/app/code/core/Mage/Core/Helper/Js.php +++ b/app/code/core/Mage/Core/Helper/Js.php @@ -45,7 +45,7 @@ class Mage_Core_Helper_Js extends Mage_Core_Helper_Abstract */ public function getTranslateJson() { - return Zend_Json::encode($this->_getTranslateData()); + return Mage::helper('core')->jsonEncode($this->_getTranslateData()); } /** @@ -174,16 +174,24 @@ protected function _getTranslateData() 'Your order can not be completed at this time as there is no payment methods available for it.' => $this->__('Your order can not be completed at this time as there is no payment methods available for it.'), 'Please specify payment method.' => $this->__('Please specify payment method.'), - + 'Credit card number doesn\'t match credit card type' => $this->__('Credit card number doesn\'t match credit card type'), + 'Card type doesn\'t match credit card number' => $this->__('Card type doesn\'t match credit card number'), + 'Please enter a valid credit card verification number.' => $this->__('Please enter a valid credit card verification number.'), + 'Please use only letters (a-z or A-Z), numbers (0-9) or underscore(_) in this field, first character should be a letter.' => + $this->__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore(_) in this field, first character should be a letter.'), + 'Please input a valid CSS-length. For example 100px or 77pt or 20em or .5ex or 50%' => $this->__('Please input a valid CSS-length. For example 100px or 77pt or 20em or .5ex or 50%'), + 'Maximum length exceeded.' => $this->__('Maximum length exceeded.'), + + //Mage_Rule - + 'Your session has been expired, you will be relogged in now.' => $this->__('Your session has been expired, you will be relogged in now.'), 'Incorrect credit card expiration date' => $this->__('Incorrect credit card expiration date'), ); foreach ($this->_translateData as $key=>$value) { if ($key == $value) { unset($this->_translateData[$key]); - } + } } } return $this->_translateData; diff --git a/app/code/core/Mage/Core/Model/App.php b/app/code/core/Mage/Core/Model/App.php index eb9b35c0de..a4f9c4004e 100644 --- a/app/code/core/Mage/Core/Model/App.php +++ b/app/code/core/Mage/Core/Model/App.php @@ -135,13 +135,6 @@ class Mage_Core_Model_App */ protected $_cache; - /** - * Helpers array - * - * @var array - */ - protected $_helpers = array(); - /** * Use Cache * @@ -843,19 +836,12 @@ public function getTranslator() /** * Retrieve helper object * - * @param helper name $name - * @return Mage_Core_Helper_Abstract + * @param string $name + * @return Mage_Core_Helper_Abstract */ public function getHelper($name) { - if (strpos($name, '/') === false) { - $name .= '/data'; - } - if (!isset($this->_helpers[$name])) { - $class = Mage::getConfig()->getHelperClassName($name); - $this->_helpers[$name] = new $class(); - } - return $this->_helpers[$name]; + return Mage::helper($name); } /** diff --git a/app/code/core/Mage/Core/Model/Email/Template/Filter.php b/app/code/core/Mage/Core/Model/Email/Template/Filter.php index 3618caa056..952a929856 100644 --- a/app/code/core/Mage/Core/Model/Email/Template/Filter.php +++ b/app/code/core/Mage/Core/Model/Email/Template/Filter.php @@ -169,10 +169,18 @@ public function layoutDirective($construction) } $block->setDataUsingMethod($k, $v); - $layout->addOutputBlock($blockName); } } + /** + * Add output method for first block + */ + $allBlocks = $layout->getAllBlocks(); + $firstBlock = reset($allBlocks); + if ($firstBlock) { + $layout->addOutputBlock($firstBlock->getNameInLayout()); + } + $layout->setDirectOutput(false); return $layout->getOutput(); } @@ -373,4 +381,38 @@ public function modifierEscape($value, $type = 'html') } return $value; } + + /** + * HTTP Protocol directive + * + * Using: + * {{protocol}} - current protocol http or https + * {{protocol url="www.domain.com/"}} domain URL with current protocol + * {{protocol http="http://url" https="https://url"} + * also allow additional parameter "store" + * + * @param array $construction + * @return string + */ + public function protocolDirective($construction) + { + $params = $this->_getIncludeParameters($construction[2]); + $store = null; + if (isset($params['store'])) { + $store = Mage::app()->getSafeStore($params['store']); + } + $isSecure = Mage::app()->getStore($store)->isCurrentlySecure(); + $protocol = $isSecure ? 'https' : 'http'; + if (isset($params['url'])) { + return $protocol . '://' . $params['url']; + } + elseif (isset($params['http']) && isset($params['https'])) { + if ($isSecure) { + return $params['https']; + } + return $params['http']; + } + + return $protocol; + } } diff --git a/app/code/core/Mage/Core/Model/Layout.php b/app/code/core/Mage/Core/Model/Layout.php index fb2156ee5a..2f38aa4af6 100644 --- a/app/code/core/Mage/Core/Model/Layout.php +++ b/app/code/core/Mage/Core/Model/Layout.php @@ -154,19 +154,29 @@ public function generateXml() { $xml = $this->getUpdate()->asSimplexml(); $removeInstructions = $xml->xpath("//remove"); - foreach ($removeInstructions as $infoNode) { - $attributes = $infoNode->attributes(); - if ($acl = (string)$attributes->acl && !Mage::getSingleton('admin/session')->isAllowed($acl)) { - $block->addAttribute('ignore', true); - } - if ($blockName = (string)$attributes->name) { - $ignoreNodes = $xml->xpath("//block[@name='".$blockName."']"); - foreach ($ignoreNodes as $block) { - $block->addAttribute('ignore', true); - } - $ignoreNodes = $xml->xpath("//reference[@name='".$blockName."']"); - foreach ($ignoreNodes as $block) { - $block->addAttribute('ignore', true); + + if (is_array($removeInstructions)) { + foreach ($removeInstructions as $infoNode) { + $attributes = $infoNode->attributes(); + if ($blockName = (string)$attributes->name) { + $ignoreNodes = $xml->xpath("//block[@name='".$blockName."']"); + if (!is_array($ignoreNodes)) { + continue; + } + $ignoreReferences = $xml->xpath("//reference[@name='".$blockName."']"); + if (is_array($ignoreReferences)) { + $ignoreNodes = array_merge($ignoreNodes, $ignoreReferences); + } + + foreach ($ignoreNodes as $block) { + if ($block->getAttribute('ignore') !== null) { + continue; + } + if (($acl = (string)$attributes->acl) && Mage::getSingleton('admin/session')->isAllowed($acl)) { + continue; + } + $block->addAttribute('ignore', true); + } } } } @@ -330,7 +340,7 @@ protected function _generateAction($node, $parent) if (isset($node['json'])) { $json = explode(' ', (string)$node['json']); foreach ($json as $arg) { - $args[$arg] = Zend_Json::decode($args[$arg]); + $args[$arg] = Mage::helper('core')->jsonDecode($args[$arg]); } } @@ -456,9 +466,7 @@ protected function _getBlockInstance($block, array $attributes=array()) Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block)); } } - $fileName = mageFindClassFile($block); - if ($fileName!==false) { - //include_once ($fileName); + if (class_exists($block, false) || mageFindClassFile($block)) { $block = new $block($attributes); } } @@ -575,7 +583,7 @@ public function getBlockSingleton($type) */ public function helper($name) { - $helper = Mage::app()->getHelper($name); + $helper = Mage::helper($name); if (!$helper) { return false; } diff --git a/app/code/core/Mage/Core/Model/Mysql4/Abstract.php b/app/code/core/Mage/Core/Model/Mysql4/Abstract.php index 3c21ad0c55..c2ed5e936c 100644 --- a/app/code/core/Mage/Core/Model/Mysql4/Abstract.php +++ b/app/code/core/Mage/Core/Model/Mysql4/Abstract.php @@ -93,6 +93,13 @@ abstract class Mage_Core_Model_Mysql4_Abstract extends Mage_Core_Model_Resource_ */ protected $_isPkAutoIncrement = true; + /** + * Fields List for update in forsedSave + * + * @var array + */ + protected $_fieldsForUpdate = array(); + protected $_mainTableFields; /** @@ -375,6 +382,32 @@ public function save(Mage_Core_Model_Abstract $object) return $this; } + /** + * Forsed save object data + * forsed update If duplicate unique key data + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Core_Model_Mysql4_Abstract + */ + public function forsedSave(Mage_Core_Model_Abstract $object) + { + $this->_beforeSave($object); + + // update + if (!is_null($object->getId()) && $this->_isPkAutoIncrement) { + $condition = $this->_getWriteAdapter()->quoteInto($this->getIdFieldName().'=?', $object->getId()); + $this->_getWriteAdapter()->update($this->getMainTable(), $this->_prepareDataForSave($object), $condition); + } + else { + $this->_getWriteAdapter()->insertOnDuplicate($this->getMainTable(), $this->_prepareDataForSave($object), $this->_fieldsForUpdate); + $object->setId($this->_getWriteAdapter()->lastInsertId($this->getMainTable())); + } + + $this->_afterSave($object); + + return $this; + } + /** * Delete the object * diff --git a/app/code/core/Mage/Core/Model/Store.php b/app/code/core/Mage/Core/Model/Store.php index 667640c26c..709bab5655 100644 --- a/app/code/core/Mage/Core/Model/Store.php +++ b/app/code/core/Mage/Core/Model/Store.php @@ -385,10 +385,15 @@ public function getDefaultBasePath() * @param array $params * @return string */ - public function getUrl($route='', $params=array()) + public function getUrl($route = '', $params = array()) { + /* @var $url Mage_Core_Model_Url */ $url = Mage::getModel('core/url') ->setStore($this); + if (Mage::app()->getStore()->getId() != $this->getId()) { + $params['_store_to_url'] = true; + } + return $url->getUrl($route, $params); } diff --git a/app/code/core/Mage/Core/Model/Translate/Inline.php b/app/code/core/Mage/Core/Model/Translate/Inline.php index ccaeef7033..52cd96b891 100644 --- a/app/code/core/Mage/Core/Model/Translate/Inline.php +++ b/app/code/core/Mage/Core/Model/Translate/Inline.php @@ -34,22 +34,64 @@ */ class Mage_Core_Model_Translate_Inline { + /** + * Regular Expression for detected and replace translate + * + * @var string + */ protected $_tokenRegex = '\{\{\{(.*?)\}\}\{\{(.*?)\}\}\{\{(.*?)\}\}\{\{(.*?)\}\}\}'; + + /** + * Response body or JSON content string + * + * @var string + */ protected $_content; + + /** + * Is enabled and allowed inline translates flags + * + * @var bool + */ protected $_isAllowed; - protected $_isScriptInserted = false; - protected $_isAjaxRequest = null; - public function isAllowed($storeId=null) + /** + * Flag about inserted styles and scripts for inline translates + * + * @var bool + */ + protected $_isScriptInserted = false; + + /** + * Current content is JSON or Response body + * + * @var bool + */ + protected $_isJson = false; + + /** + * Is enabled and allowed Inline Translates + * + * @param mixed $store + * @return bool + */ + public function isAllowed($store = null) { + if (is_null($store)) { + $store = Mage::app()->getStore(); + } + if (!$store instanceof Mage_Core_Model_Store) { + $store = Mage::app()->getStore($store); + } + if (is_null($this->_isAllowed)) { - if (Mage::getDesign()->getArea()==='adminhtml') { - $active = Mage::getStoreConfigFlag('dev/translate_inline/active_admin', $storeId); + if (Mage::getDesign()->getArea() == 'adminhtml') { + $active = Mage::getStoreConfigFlag('dev/translate_inline/active_admin', $store); } else { - $active = Mage::getStoreConfigFlag('dev/translate_inline/active', $storeId); + $active = Mage::getStoreConfigFlag('dev/translate_inline/active', $store); } - $this->_isAllowed = $active && Mage::helper('core')->isDevAllowed($storeId); + $this->_isAllowed = $active && Mage::helper('core')->isDevAllowed($store); } $translate = Mage::getSingleton('core/translate'); @@ -58,64 +100,86 @@ public function isAllowed($storeId=null) return $translate->getTranslateInline() && $this->_isAllowed; } + /** + * Parse and save edited translate + * + * @param array $translate + * @return Mage_Core_Model_Translate_Inline + */ public function processAjaxPost($translate) { if (!$this->isAllowed()) { - return; + return $this; } - $resource = Mage::getResourceModel('core/translate_string'); /* @var $resource Mage_Core_Model_Mysql4_Translate_String */ + $resource = Mage::getResourceModel('core/translate_string'); foreach ($translate as $t) { if (Mage::getDesign()->getArea() == 'adminhtml') { $storeId = 0; - } - elseif (empty($t['perstore'])) { + } else if (empty($t['perstore'])) { $resource->deleteTranslate($t['original'], null, false); $storeId = 0; - } - else { + } else { $storeId = Mage::app()->getStore()->getId(); } $resource->saveTranslate($t['original'], $t['custom'], null, $storeId); } + + return $this; } + /** + * Strip inline translations from text + * + * @param array|string $body + * @return Mage_Core_Model_Translate_Inline + */ public function stripInlineTranslations(&$body) { if (is_array($body)) { - foreach ($body as $i=>&$part) { - if (strpos($part,'{{{')!==false) { - $part = preg_replace('#'.$this->_tokenRegex.'#', '$1', $part); - } + foreach ($body as &$part) { + $this->stripInlineTranslations($part); } - } elseif (is_string($body)) { + } else if (is_string($body)) { $body = preg_replace('#'.$this->_tokenRegex.'#', '$1', $body); } return $this; } - public function processResponseBody(&$bodyArray) + /** + * Replace translate templates to HTML fragments + * + * @param array|string $body + * @return Mage_Core_Model_Translate_Inline + */ + public function processResponseBody(&$body) { if (!$this->isAllowed()) { - // TODO: move translations from exceptions and errors to output - if (Mage::getDesign()->getArea()==='adminhtml') { - $this->stripInlineTranslations($bodyArray); + if (Mage::getDesign()->getArea() == 'adminhtml') { + $this->stripInlineTranslations($body); } - return; + return $this; } - foreach ($bodyArray as $i=>$content) { - $this->_content = $content; + if (is_array($body)) { + foreach ($body as &$part) { + $this->processResponseBody($part); + } + } else if (is_string($body)) { + Mage::log($body); + $this->_content = $body; $this->_tagAttributes(); $this->_specialTags(); $this->_otherText(); $this->_insertInlineScriptsHtml(); - $bodyArray[$i] = $this->_content; + $body = $this->_content; } + + return $this; } protected function _insertInlineScriptsHtml() @@ -150,104 +214,106 @@ protected function _insertInlineScriptsHtml() $this->_isScriptInserted = true; } + /** + * Escape Translate data + * + * @param string $string + * @return string + */ protected function _escape($string) { return str_replace("'", "\\'", htmlspecialchars($string)); } + /** + * Prepare tags inline translates + * + */ protected function _tagAttributes() { -#echo __METHOD__; - - if ($this->getIsAjaxRequest()) { - $quoteHtml = '\"'; + if ($this->getIsJson()) { $quotePatern = '\\\\"'; + $quoteHtml = '\"'; } else { - $quoteHtml = '"'; $quotePatern = '"'; + $quoteHtml = '"'; } - $nextTag = 0; $i=0; - while (preg_match('#<([a-z]+)\s*?[^>]+?(('.$this->_tokenRegex.')[^/>]*?)+(/?(>))#i', - $this->_content, $tagMatch, PREG_OFFSET_CAPTURE, $nextTag)) { -#echo ''.print_r($tagMatch[0][0],1).'
    '; - - $next = 0; - $tagHtml = $tagMatch[0][0]; - $trArr = array(); - - while (preg_match('#'.$this->_tokenRegex.'#i', - $tagHtml, $m, PREG_OFFSET_CAPTURE, $next)) { - + $tagMatch = array(); + $nextTag = 0; + $tagRegExp = '#<([a-z]+)\s*?[^>]+?(('.$this->_tokenRegex.')[^/>]*?)+(/?(>))#i'; + while (preg_match($tagRegExp, $this->_content, $tagMatch, PREG_OFFSET_CAPTURE, $nextTag)) { + $next = 0; + $tagHtml = $tagMatch[0][0]; + $trArr = array(); + $m = array(); + $attrRegExp = '#'.$this->_tokenRegex.'#'; + + while (preg_match($attrRegExp, $tagHtml, $m, PREG_OFFSET_CAPTURE, $next)) { $trArr[] = '{shown:\''.$this->_escape($m[1][0]).'\',' - .'translated:\''.$this->_escape($m[2][0]).'\',' - .'original:\''.$this->_escape($m[3][0]).'\',' - .'location:\'Tag attribute (ALT, TITLE, etc.)\',' - .'scope:\''.$this->_escape($m[4][0]).'\'}'; + . 'translated:\''.$this->_escape($m[2][0]).'\',' + . 'original:\''.$this->_escape($m[3][0]).'\',' + . 'location:\'Tag attribute (ALT, TITLE, etc.)\',' + . 'scope:\''.$this->_escape($m[4][0]).'\'}'; $tagHtml = substr_replace($tagHtml, $m[1][0], $m[0][1], strlen($m[0][0])); $next = $m[0][1]; } - if (preg_match('# translate='.$quotePatern.'\[(.+?)\]'.$quotePatern.'#i', $tagMatch[0][0], $m, PREG_OFFSET_CAPTURE)) { - foreach ($trArr as $i=>$tr) { - if (strpos($m[1][0], $tr)!==false) { + $transRegExp = '# translate='.$quotePatern.'\[(.+?)\]'.$quotePatern.'#i'; + if (preg_match($transRegExp, $tagHtml, $m, PREG_OFFSET_CAPTURE)) { + foreach ($trArr as $i => $tr) { + if (strpos($m[1][0], $tr) !== false) { unset($trArr[$i]); } } array_unshift($trArr, $m[1][0]); - $start = $tagMatch[0][1]+$m[0][1]; - $len = strlen($m[0][0]); - } else { - $start = $tagMatch[8][1]; - $len = 0; + $tagHtml = substr_replace($tagHtml, '', $m[0][1], strlen($m[0][0])); } - $trAttr = ' translate="['.join(',', $trArr).']"'; - $tagHtml = preg_replace('#/?>$#', $trAttr.'$0', $tagHtml); + $trAttr = ' translate='.$quoteHtml.'['.join(',', $trArr).']'.$quoteHtml; + $tagHtml = preg_replace('#/?>$#', $trAttr . '$0', $tagHtml); - - $this->_content = substr_replace($this->_content, $tagHtml, - $tagMatch[0][1], $tagMatch[9][1]+1-$tagMatch[0][1]); + $this->_content = substr_replace($this->_content, $tagHtml, $tagMatch[0][1], + $tagMatch[9][1]+1-$tagMatch[0][1]); $nextTag = $tagMatch[0][1]; } } + /** + * Prepare special tags + * + */ protected function _specialTags() { -#echo __METHOD__; - - if ($this->getIsAjaxRequest()) { - $quoteHtml = '\"'; + if ($this->getIsJson()) { $quotePatern = '\\\\"'; + $quoteHtml = '\"'; } else { - $quoteHtml = '"'; $quotePatern = '"'; + $quoteHtml = '"'; } $nextTag = 0; $location = array( 'script' => 'String in Javascript', - 'title' => 'Page title', + 'title' => 'Page title', 'select' => 'Dropdown option', 'button' => 'Button label', - 'a' => 'Link label', + 'a' => 'Link label', ); - while (preg_match('#<(script|title|select|button|a)(\s+[^>]*|)(>)#i', - $this->_content, $tagMatch, PREG_OFFSET_CAPTURE, $nextTag)) { -#echo ''.print_r($tagMatch[0][0],1).'
    '; - + $tagRegExp = '#<(script|title|select|button|a)(\s+[^>]*|)(>)#i'; + $tagMatch = array(); + while (preg_match($tagRegExp, $this->_content, $tagMatch, PREG_OFFSET_CAPTURE, $nextTag)) { $tagClosure = ''; $tagLength = stripos($this->_content, $tagClosure, $tagMatch[0][1])-$tagMatch[0][1]+strlen($tagClosure); - $next = 0; - $tagHtml = substr($this->_content, $tagMatch[0][1], $tagLength); - $trArr = array(); - - while (preg_match('#'.$this->_tokenRegex.'#i', - $tagHtml, $m, PREG_OFFSET_CAPTURE, $next)) { - + $next = 0; + $tagHtml = substr($this->_content, $tagMatch[0][1], $tagLength); + $trArr = array(); + $m = array(); + while (preg_match('#'.$this->_tokenRegex.'#i', $tagHtml, $m, PREG_OFFSET_CAPTURE, $next)) { $trArr[] = '{shown:\''.$this->_escape($m[1][0]).'\',' .'translated:\''.$this->_escape($m[2][0]).'\',' .'original:\''.$this->_escape($m[3][0]).'\',' @@ -255,18 +321,16 @@ protected function _specialTags() .'scope:\''.$this->_escape($m[4][0]).'\'}'; $tagHtml = substr_replace($tagHtml, $m[1][0], $m[0][1], strlen($m[0][0])); - - $next = $m[0][1]; + $next = $m[0][1]; } if (!empty($trArr)) { $trArr = array_unique($trArr); - - $tag = strtolower($tagMatch[1][0]); + $tag = strtolower($tagMatch[1][0]); switch ($tag) { case 'script': case 'title': $tagHtml .= ''.strtoupper($tag).''; + .'" translate='.$quoteHtml.'['.join(',', $trArr).']'.$quoteHtml.'>'.strtoupper($tag).''; break; } $this->_content = substr_replace($this->_content, $tagHtml, $tagMatch[0][1], $tagLength); @@ -288,7 +352,7 @@ protected function _specialTags() } $this->_content = substr_replace($this->_content, - ' translate='.$quoteHtml.'['.join(',',$trArr).']'.$quoteHtml, $start, $len); + ' translate='.$quoteHtml.'['.join(',', $trArr).']'.$quoteHtml, $start, $len); break; } } @@ -298,24 +362,21 @@ protected function _specialTags() } + /** + * Prepare other text inline translates + * + */ protected function _otherText() { -#return; -#echo __METHOD__; -#echo "".$this->_content."
    "; -#exit; - - if ($this->getIsAjaxRequest()) { + if ($this->getIsJson()) { $quoteHtml = '\"'; } else { $quoteHtml = '"'; } $next = 0; - while (preg_match('#('.$this->_tokenRegex.')#', - $this->_content, $m, PREG_OFFSET_CAPTURE, $next)) { -#echo ''.print_r($m[0][0],1).'
    '; - + $m = array(); + while (preg_match('#('.$this->_tokenRegex.')#', $this->_content, $m, PREG_OFFSET_CAPTURE, $next)) { $tr = '{shown:\''.$this->_escape($m[2][0]).'\',' .'translated:\''.$this->_escape($m[3][0]).'\',' .'original:\''.$this->_escape($m[4][0]).'\',' @@ -330,17 +391,50 @@ protected function _otherText() } + /** + * Check is a Request contain Json flag + * + * @deprecated 1.3.2.2 + * @return bool + */ public function getIsAjaxRequest() { - if (!is_null($this->_isAjaxRequest)) { - return $this->_isAjaxRequest; - } else { - return Mage::app()->getRequest()->getQuery('isAjax'); - } + return (bool)Mage::app()->getRequest()->getQuery('isAjax'); } - public function setIsAjaxRequest($status) + /** + * Set is a Request contain Json flag + * + * @param bool $flag + * @deprecated 1.3.2.2 + * @return Mage_Core_Model_Translate_Inline + */ + public function setIsAjaxRequest($flag) { - $this->_isAjaxRequest = $status; + Mage::app()->getRequest()->setQuery('isAjax', intval((bool)$flag)); + return $this; + } + + /** + * Retrieve flag about parsed content is Json + * + * @return bool + */ + public function getIsJson() + { + return $this->_isJson; + } + + /** + * Set flag about parsed content is Json + * + * @param bool $flag + * @return Mage_Core_Model_Translate_Inline + */ + public function setIsJson($flag) + { + $this->_isJson = (bool)$flag; + return $this; } } + diff --git a/app/code/core/Mage/Core/Model/Url.php b/app/code/core/Mage/Core/Model/Url.php index 97c3065685..a60389d962 100644 --- a/app/code/core/Mage/Core/Model/Url.php +++ b/app/code/core/Mage/Core/Model/Url.php @@ -606,6 +606,9 @@ public function getRouteUrl($routePath=null, $routeParams=null) $this->unsetData('route_params'); if (isset($routeParams['_direct'])) { + if (is_array($routeParams)) { + $this->setRouteParams($routeParams, false); + } return $this->getBaseUrl().$routeParams['_direct']; } @@ -692,24 +695,48 @@ public function getQuery($escape = false) return $this->_getData('query'); } - public function setQueryParams(array $data, $useCurrent = false) + /** + * Set query Params as array + * + * @param array $data + * @return Mage_Core_Model_Url + */ + public function setQueryParams(array $data) { $this->unsetData('query'); - if ($useCurrent) { - $params = $this->_getData('query_params'); - foreach ($data as $param => $value) { - $params[$param] = $value; - } - $this->setData('query_params', $params); + + if ($this->_getData('query_params') == $data) { return $this; } - if ($this->_getData('query_params')==$data) { - return $this; + $params = $this->_getData('query_params'); + if (!is_array($params)) { + $params = array(); + } + foreach ($data as $param => $value) { + $params[$param] = $value; } - return $this->setData('query_params', $data); + $this->setData('query_params', $params); + + return $this; + } + + /** + * Purge Query params array + * + * @return Mage_Core_Model_Url + */ + public function purgeQueryParams() + { + $this->setData('query_params', array()); + return $this; } + /** + * Retrurn Query Params + * + * @return array + */ public function getQueryParams() { if (!$this->hasData('query_params')) { @@ -788,6 +815,7 @@ public function getUrl($routePath=null, $routeParams=null) $query = null; if (isset($routeParams['_query'])) { + $this->purgeQueryParams(); $query = $routeParams['_query']; unset($routeParams['_query']); } diff --git a/app/code/core/Mage/Core/Model/Url/Rewrite.php b/app/code/core/Mage/Core/Model/Url/Rewrite.php index 1e0d437b4a..ea48def69e 100644 --- a/app/code/core/Mage/Core/Model/Url/Rewrite.php +++ b/app/code/core/Mage/Core/Model/Url/Rewrite.php @@ -196,7 +196,7 @@ public function rewrite(Zend_Controller_Request_Http $request=null, Zend_Control */ if (!$this->getId() && isset($_GET['___from_store'])) { try { - $fromStoreId = Mage::app()->getStore($_GET['___from_store']); + $fromStoreId = Mage::app()->getStore($_GET['___from_store'])->getId(); } catch (Exception $e) { return false; diff --git a/app/code/core/Mage/Core/etc/system.xml b/app/code/core/Mage/Core/etc/system.xml index 1eb83fca63..0843279ab2 100644 --- a/app/code/core/Mage/Core/etc/system.xml +++ b/app/code/core/Mage/Core/etc/system.xml @@ -586,15 +586,15 @@ 1 1 - - - select + + + multiselect + 3 adminhtml/system_config_source_country - 1 1 1 - 1 - + 0 + diff --git a/app/code/core/Mage/Core/functions.php b/app/code/core/Mage/Core/functions.php index b945db58ce..9541627c7a 100644 --- a/app/code/core/Mage/Core/functions.php +++ b/app/code/core/Mage/Core/functions.php @@ -174,6 +174,9 @@ function mageCoreErrorHandler($errno, $errstr, $errfile, $errline){ if (!defined('E_RECOVERABLE_ERROR')) { define('E_RECOVERABLE_ERROR', 4096); } + if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 8192); + } // PEAR specific message handling if (stripos($errfile.$errstr, 'pear') !== false) { @@ -229,6 +232,9 @@ function mageCoreErrorHandler($errno, $errstr, $errfile, $errline){ case E_RECOVERABLE_ERROR: $errorMessage .= "Recoverable Error"; break; + case E_DEPRECATED: + $errorMessage .= "Deprecated functionality"; + break; default: $errorMessage .= "Unknown error ($errno)"; break; diff --git a/app/code/core/Mage/Customer/Model/Address/Abstract.php b/app/code/core/Mage/Customer/Model/Address/Abstract.php index 8c1996d868..1ac0d4c662 100644 --- a/app/code/core/Mage/Customer/Model/Address/Abstract.php +++ b/app/code/core/Mage/Customer/Model/Address/Abstract.php @@ -347,7 +347,8 @@ public function validate() $errors[] = $helper->__('Please enter telephone.'); } - if (!Zend_Validate::is($this->getPostcode(), 'NotEmpty')) { + $_havingOptionalZip = Mage::helper('directory')->getCountriesWithOptionalZip(); + if (!in_array($this->getCountryId(), $_havingOptionalZip) && !Zend_Validate::is($this->getPostcode(), 'NotEmpty')) { $errors[] = $helper->__('Please enter zip/postal code.'); } diff --git a/app/code/core/Mage/Customer/Model/Address/Api.php b/app/code/core/Mage/Customer/Model/Address/Api.php index 2e3c8044dc..7136142749 100644 --- a/app/code/core/Mage/Customer/Model/Address/Api.php +++ b/app/code/core/Mage/Customer/Model/Address/Api.php @@ -34,7 +34,7 @@ class Mage_Customer_Model_Address_Api extends Mage_Customer_Model_Api_Resource { protected $_mapAttributes = array( - 'customer_id' => 'entity_id' + 'customer_address_id' => 'entity_id' ); public function __construct() @@ -73,8 +73,8 @@ public function items($customerId) } } - $row['is_default_billing'] = $customer->getDefaultBillingAddress() == $address->getId(); - $row['is_default_shipping'] = $customer->getDefaultShippingAddress() == $address->getId(); + $row['is_default_billing'] = $customer->getDefaultBilling() == $address->getId(); + $row['is_default_shipping'] = $customer->getDefaultShipping() == $address->getId(); $result[] = $row; @@ -160,8 +160,8 @@ public function info($addressId) if ($customer = $address->getCustomer()) { - $result['is_default_billing'] = $customer->getDefaultBillingAddress() == $address->getId(); - $result['is_default_shipping'] = $customer->getDefaultShippingAddress() == $address->getId(); + $result['is_default_billing'] = $customer->getDefaultBilling() == $address->getId(); + $result['is_default_shipping'] = $customer->getDefaultShipping() == $address->getId(); } return $result; diff --git a/app/code/core/Mage/Customer/Model/Address/Api/V2.php b/app/code/core/Mage/Customer/Model/Address/Api/V2.php index c3416caa40..6b35df5187 100644 --- a/app/code/core/Mage/Customer/Model/Address/Api/V2.php +++ b/app/code/core/Mage/Customer/Model/Address/Api/V2.php @@ -110,8 +110,8 @@ public function info($addressId) if ($customer = $address->getCustomer()) { - $result['is_default_billing'] = $customer->getDefaultBillingAddress() == $address->getId(); - $result['is_default_shipping'] = $customer->getDefaultShippingAddress() == $address->getId(); + $result['is_default_billing'] = $customer->getDefaultBilling() == $address->getId(); + $result['is_default_shipping'] = $customer->getDefaultShipping() == $address->getId(); } return $result; diff --git a/app/code/core/Mage/Customer/Model/Convert/Adapter/Customer.php b/app/code/core/Mage/Customer/Model/Convert/Adapter/Customer.php index c9f5561842..e5280238fa 100644 --- a/app/code/core/Mage/Customer/Model/Convert/Adapter/Customer.php +++ b/app/code/core/Mage/Customer/Model/Convert/Adapter/Customer.php @@ -497,7 +497,7 @@ public function saveRow($importData) $setValue = $value; if ($attribute->getFrontendInput() == 'multiselect') { - $value = split(self::MULTI_DELIMITER, $value); + $value = explode(self::MULTI_DELIMITER, $value); $isArray = true; $setValue = array(); } diff --git a/app/code/core/Mage/Customer/Model/Customer.php b/app/code/core/Mage/Customer/Model/Customer.php index a35571d60a..f02b3f45e5 100644 --- a/app/code/core/Mage/Customer/Model/Customer.php +++ b/app/code/core/Mage/Customer/Model/Customer.php @@ -450,7 +450,7 @@ public function isAddressPrimary(Mage_Customer_Model_Address $address) * * @return Mage_Customer_Model_Customer */ - public function sendNewAccountEmail($type = 'registered', $backUrl = '') + public function sendNewAccountEmail($type = 'registered', $backUrl = '', $store_id = '0') { $types = array( 'registered' => self::XML_PATH_REGISTER_EMAIL_TEMPLATE, // welcome email, when confirmation is disabled @@ -465,7 +465,7 @@ public function sendNewAccountEmail($type = 'registered', $backUrl = '') /* @var $translate Mage_Core_Model_Translate */ $translate->setTranslateInline(false); - $storeId = $this->getStoreId(); + $storeId = ($store_id == '0')?$this->getStoreId():$store_id; if ($this->getWebsiteId() != '0' && $storeId == '0') { $storeIds = Mage::app()->getWebsite($this->getWebsiteId())->getStoreIds(); reset($storeIds); @@ -475,8 +475,8 @@ public function sendNewAccountEmail($type = 'registered', $backUrl = '') Mage::getModel('core/email_template') ->setDesignConfig(array('area'=>'frontend', 'store'=>$storeId)) ->sendTransactional( - Mage::getStoreConfig($types[$type]), - Mage::getStoreConfig(self::XML_PATH_REGISTER_EMAIL_IDENTITY), + Mage::getStoreConfig($types[$type], $storeId), + Mage::getStoreConfig(self::XML_PATH_REGISTER_EMAIL_IDENTITY, $storeId), $this->getEmail(), $this->getName(), array('customer' => $this, 'back_url' => $backUrl)); @@ -1042,7 +1042,7 @@ public function canSkipConfirmation() return $this->getId() && $this->hasSkipConfirmationIfEmail() && strtolower($this->getSkipConfirmationIfEmail()) === strtolower($this->getEmail()); } - + public function __clone() { $newAddressCollection = $this->getPrimaryAddresses(); diff --git a/app/code/core/Mage/Customer/controllers/AccountController.php b/app/code/core/Mage/Customer/controllers/AccountController.php index ae24d2a5d2..dc98ca5f8a 100644 --- a/app/code/core/Mage/Customer/controllers/AccountController.php +++ b/app/code/core/Mage/Customer/controllers/AccountController.php @@ -318,10 +318,7 @@ public function createPostAction() ->addException($e, $this->__('Can\'t save customer')); } } - /** - * Protect XSS injection in user input - */ - $this->_getSession()->setEscapeMessages(true); + $this->_redirectError(Mage::getUrl('*/*/create', array('_secure'=>true))); } diff --git a/app/code/core/Mage/Customer/etc/wsdl.xml b/app/code/core/Mage/Customer/etc/wsdl.xml index 623ff749ec..58c4564da9 100644 --- a/app/code/core/Mage/Customer/etc/wsdl.xml +++ b/app/code/core/Mage/Customer/etc/wsdl.xml @@ -72,9 +72,9 @@ - + - + @@ -99,7 +99,7 @@ - + @@ -168,7 +168,7 @@ - + diff --git a/app/code/core/Mage/Cybermut/Block/Error.php b/app/code/core/Mage/Cybermut/Block/Error.php new file mode 100644 index 0000000000..84cf3fa4fd --- /dev/null +++ b/app/code/core/Mage/Cybermut/Block/Error.php @@ -0,0 +1,37 @@ + +*/ +class Mage_Cybermut_Block_Error extends Mage_Core_Block_Template +{ +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Block/Form.php b/app/code/core/Mage/Cybermut/Block/Form.php new file mode 100644 index 0000000000..1a7d7f7dd9 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Block/Form.php @@ -0,0 +1,43 @@ + + */ +class Mage_Cybermut_Block_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('cybermut/form.phtml'); + parent::_construct(); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Block/Redirect.php b/app/code/core/Mage/Cybermut/Block/Redirect.php new file mode 100644 index 0000000000..c190c31bc9 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Block/Redirect.php @@ -0,0 +1,67 @@ + + */ +class Mage_Cybermut_Block_Redirect extends Mage_Core_Block_Abstract +{ + + protected function _toHtml() + { + $standard = Mage::getModel('cybermut/payment'); + $form = new Varien_Data_Form(); + $form->setAction($standard->getCybermutUrl()) + ->setId('cybermut_payment_checkout') + ->setName('cybermut_payment_checkout') + ->setMethod('POST') + ->setUseContainer(true); + foreach ($standard->setOrder($this->getOrder())->getStandardCheckoutFormFields() as $field => $value) { + $form->addField($field, 'hidden', array('name' => $field, 'value' => $value)); + } + + $formHTML = $form->toHtml(); + + $html = ''; + $html.= $this->__('You will be redirected to Cybermut in a few seconds.'); + $html.= $formHTML; + $html.= ''; + $html.= ''; + + if ($standard->getConfigData('debug_flag')) { + Mage::getModel('cybermut/api_debug') + ->setRequestBody($formHTML) + ->save(); + } + + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Helper/Data.php b/app/code/core/Mage/Cybermut/Helper/Data.php new file mode 100644 index 0000000000..d74e0f7451 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Helper/Data.php @@ -0,0 +1,38 @@ + + */ +class Mage_Cybermut_Helper_Data extends Mage_Core_Helper_Abstract +{ + +} diff --git a/app/code/core/Mage/Cybermut/Model/Api/Debug.php b/app/code/core/Mage/Cybermut/Model/Api/Debug.php new file mode 100644 index 0000000000..3945524e6e --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Api/Debug.php @@ -0,0 +1,41 @@ + + */ +class Mage_Cybermut_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('cybermut/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Cybermut/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..77c8a9c873 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Mysql4/Api/Debug.php @@ -0,0 +1,42 @@ + + */ + +class Mage_Cybermut_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('cybermut/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Cybermut/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..af8c6fdd21 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,41 @@ + + */ +class Mage_Cybermut_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('cybermut/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Model/Mysql4/Setup.php b/app/code/core/Mage/Cybermut/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..6685e3e520 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Mysql4/Setup.php @@ -0,0 +1,39 @@ + + */ + +class Mage_Cybermut_Model_Mysql4_Setup extends Mage_Sales_Model_Mysql4_Setup +{ + +} diff --git a/app/code/core/Mage/Cybermut/Model/Payment.php b/app/code/core/Mage/Cybermut/Model/Payment.php new file mode 100644 index 0000000000..2503598703 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Payment.php @@ -0,0 +1,369 @@ + + */ + +class Mage_Cybermut_Model_Payment extends Mage_Payment_Model_Method_Abstract +{ + protected $_code = 'cybermut_payment'; + protected $_formBlockType = 'cybermut/form'; + + // Cybermut return codes of payment + const RETURN_CODE_ACCEPTED = 'paiement'; + const RETURN_CODE_TEST_ACCEPTED = 'payetest'; + const RETURN_CODE_ERROR = 'Annulation'; + + // Payment configuration + protected $_isGateway = false; + protected $_canAuthorize = true; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + // Order instance + protected $_order = null; + + /** + * Return CyberMut protocol version + * + * @return string Protocol version + */ + protected function getVersion() + { + return '1.2open'; + } + + /** + * Returns Target URL + * + * @return string Target URL + */ + public function getCybermutUrl() + { + $url = ''; + switch ($this->getConfigData('bank')) + { + default: + case 'mutuel': + $url = $this->getConfigData('test_mode') + ? 'https://paiement.creditmutuel.fr/test/paiement.cgi' + : 'https://paiement.creditmutuel.fr/paiement.cgi'; + break; + case 'cic': + $url = $this->getConfigData('test_mode') + ? 'https://ssl.paiement.cic-banques.fr/test/paiement.cgi' + : 'https://ssl.paiement.cic-banques.fr/paiement.cgi'; + break; + case 'obc': + $url = $this->getConfigData('test_mode') + ? 'https://ssl.paiement.banque-obc.fr/test/paiement.cgi' + : 'https://ssl.paiement.banque-obc.fr/paiement.cgi'; + break; + } + return $url; + } + + /** + * Return back URL + * + * @return string URL + */ + protected function getReturnURL() + { + return $this->getSuccessURL(); + } + + /** + * Return URL for Cybermut success response + * + * @return string URL + */ + protected function getSuccessURL() + { + return Mage::getUrl('cybermut/payment/success', array('_secure' => true)); + } + + /** + * Return URL for Cybermut failure response + * + * @return string URL + */ + protected function getErrorURL() + { + return Mage::getUrl('cybermut/payment/error', array('_secure' => true)); + } + + /** + * Capture payment + * + * @param Varien_Object $orderPayment + * @return Mage_Payment_Model_Abstract + */ + public function capture(Varien_Object $payment, $amount) + { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + + return $this; + } + + /** + * Form block description + * + * @return object + */ + public function createFormBlock($name) + { + $block = $this->getLayout()->createBlock('cybermut/form_payment', $name); + $block->setMethod($this->_code); + $block->setPayment($this->getPayment()); + + return $block; + } + + /** + * Return Order Place Redirect URL + * + * @return string Order Redirect URL + */ + public function getOrderPlaceRedirectUrl() + { + return Mage::getUrl('cybermut/payment/redirect'); + } + + /** + * Return Standard Checkout Form Fields for request to Cybermut + * + * @return array Array of hidden form fields + */ + public function getStandardCheckoutFormFields() + { + $order = $this->getOrder(); + if (!($order instanceof Mage_Sales_Model_Order)) { + Mage::throwException($this->_getHelper()->__('Cannot retrieve order object')); + } + + $description = $this->getConfigData('description') + ? $this->getConfigData('description') + : Mage::helper('cybermut')->__('Order #%s', $order->getRealOrderId()); + + $fields = array( + 'version' => $this->getVersion(), + 'TPE' => $this->getConfigData('tpe_no'), + 'date' => date('d/m/Y:H:i:s'), + 'montant' => sprintf('%.2f', $order->getBaseGrandTotal()) . $order->getBaseCurrencyCode(), + 'reference' => $order->getRealOrderId(), + 'texte-libre' => $description, + 'lgue' => $this->getConfigData('language'), + 'societe' => $this->getConfigData('site_code'), + 'url_retour' => $this->getReturnURL(), + 'url_retour_ok' => $this->getSuccessURL(), + 'url_retour_err' => $this->getErrorURL(), + 'bouton' => 'ButtonLabel' + ); + + $fields['MAC'] = $this->_getMAC($fields); + + return $fields; + } + + /** + * Prepare string for MAC generation + * + * @param array $data + * @return string MAC string + */ + protected function _getMAC($data) + { + $string = sprintf('%s*%s*%s*%s*%s*%s*%s*%s*', + $data['TPE'], + $data['date'], + $data['montant'], + $data['reference'], + $data['texte-libre'], + $data['version'], + $data['lgue'], + $data['societe'] + ); + + return $this->_CMCIC_hmac($string); + } + + /** + * Return SHA key + * + * @return string SHA key + */ + protected function _getSHAKey() + { + return $this->getConfigData('sha_key'); + } + + /** + * Return merchant key + * + * @return string Merchant key + */ + protected function _getKey() + { + return $this->getConfigData('key'); + } + + /** + * Return MAC string for payment authentification + * + * @param string $string + * @return string MAC + */ + protected function _CMCIC_hmac($string) + { + $pass = $this->_getSHAKey(); + $k1 = pack("H*",sha1($this->_getSHAKey())); + $l1 = strlen($k1); + $k2 = pack("H*", $this->_getKey()); + $l2 = strlen($k2); + if ($l1 > $l2) { + $k2 = str_pad($k2, $l1, chr(0x00)); + } elseif ($l2 > $l1) { + $k1 = str_pad($k1, $l2, chr(0x00)); + } + return strtolower($this->_hmacSHA1($k1 ^ $k2, $string)); + } + + /** + * MAC generation algorithm + * + * @param string $key + * @param string $string + * @return string MAC + */ + protected function _hmacSHA1($key, $string) + { + $length = 64; // block length for SHA1 + if (strlen($key) > $length) { + $key = pack("H*",sha1($key)); + } + $key = str_pad($key, $length, chr(0x00)); + $ipad = str_pad('', $length, chr(0x36)); + $opad = str_pad('', $length, chr(0x5c)); + $k_ipad = $key ^ $ipad ; + $k_opad = $key ^ $opad; + + return sha1($k_opad . pack("H*",sha1($k_ipad . $string))); + } + + /** + * Return MAC string on basis of Cybermut response data + * + * @param array $data + * @return string MAC + */ + public function getResponseMAC($data) + { + $string = sprintf('%s%s+%s+%s+%s+%s+%s+%s+', + $data['retourPLUS'], + $data['TPE'], + $data['date'], + $data['montant'], + $data['reference'], + $data['texte-libre'], + $this->getVersion(), + $data['code-retour'] + ); + return strtoupper($this->_CMCIC_hmac($string)); + } + + /** + * Transaction successful or not + * + * @param string $returnCode + * @return boolean + */ + public function isSuccessfulPayment($returnCode) + { + return in_array($returnCode, array(self::RETURN_CODE_ACCEPTED, self::RETURN_CODE_TEST_ACCEPTED)); + } + + /** + * Output success response and stop the script + */ + public function generateSuccessResponse() + { + die($this->getSuccessResponse()); + } + + /** + * Output failure response and stop the script + */ + public function generateErrorResponse() + { + die($this->getErrorResponse()); + } + + /** + * Return response for Cybermut success payment + * + * @return string Success response string + */ + public function getSuccessResponse() + { + $response = array( + 'Pragma: no-cache', + 'Content-type : text/plain', + 'Version: 1', + 'OK' + ); + return implode("\n", $response) . "\n"; + } + + /** + * Return response for Cybermut failure payment + * + * @return string Failure response string + */ + public function getErrorResponse() + { + $response = array( + 'Pragma: no-cache', + 'Content-type : text/plain', + 'Version: 1', + 'Document falsifie' + ); + return implode("\n", $response) . "\n"; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/Model/Source/Bank.php b/app/code/core/Mage/Cybermut/Model/Source/Bank.php new file mode 100644 index 0000000000..c2a88b1f18 --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Source/Bank.php @@ -0,0 +1,49 @@ + + */ + +class Mage_Cybermut_Model_Source_Bank +{ + public function toOptionArray() + { + return array( + array('value' => 'mutuel', 'label' => Mage::helper('cybermut')->__('Credit Mutuel')), + array('value' => 'cic', 'label' => Mage::helper('cybermut')->__('Groupe CIC')), + array('value' => 'obc', 'label' => Mage::helper('cybermut')->__('OBC')), + ); + } +} + + + diff --git a/app/code/core/Mage/Cybermut/Model/Source/Language.php b/app/code/core/Mage/Cybermut/Model/Source/Language.php new file mode 100644 index 0000000000..40d2767b6d --- /dev/null +++ b/app/code/core/Mage/Cybermut/Model/Source/Language.php @@ -0,0 +1,52 @@ + + */ + +class Mage_Cybermut_Model_Source_Language +{ + public function toOptionArray() + { + return array( + array('value' => 'EN', 'label' => Mage::helper('cybermut')->__('English')), + array('value' => 'FR', 'label' => Mage::helper('cybermut')->__('French')), + array('value' => 'DE', 'label' => Mage::helper('cybermut')->__('German')), + array('value' => 'IT', 'label' => Mage::helper('cybermut')->__('Italian')), + array('value' => 'ES', 'label' => Mage::helper('cybermut')->__('Spain')), + array('value' => 'NL', 'label' => Mage::helper('cybermut')->__('Dutch')), + ); + } +} + + + diff --git a/app/code/core/Mage/Cybermut/controllers/PaymentController.php b/app/code/core/Mage/Cybermut/controllers/PaymentController.php new file mode 100644 index 0000000000..c1954f7803 --- /dev/null +++ b/app/code/core/Mage/Cybermut/controllers/PaymentController.php @@ -0,0 +1,207 @@ + + */ +class Mage_Cybermut_PaymentController extends Mage_Core_Controller_Front_Action +{ + /** + * Order instance + */ + protected $_order; + + /** + * Get order + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + if ($this->_order == null) { + $session = Mage::getSingleton('checkout/session'); + $this->_order = Mage::getModel('sales/order'); + $this->_order->loadByIncrementId($session->getLastRealOrderId()); + } + return $this->_order; + } + + /** + * When a customer chooses Cybermut on Checkout/Payment page + * + */ + public function redirectAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setCybermutPaymentQuoteId($session->getQuoteId()); + + $order = $this->getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('cybermut')->__('Customer was redirected to Cybermut') + ); + $order->save(); + + $this->getResponse() + ->setBody($this->getLayout() + ->createBlock('cybermut/redirect') + ->setOrder($order) + ->toHtml()); + + $session->unsQuoteId(); + } + + /** + * Cybermut response router + * + */ + public function notifyAction() + { + $model = Mage::getModel('cybermut/payment'); + + if (!$this->getRequest()->isPost()) { + $model->generateErrorResponse(); + } + + $postData = $this->getRequest()->getPost(); + $returnedMAC = $this->getRequest()->getPost('MAC'); + $correctMAC = $model->getResponseMAC($postData); + + if ($model->getConfigData('debug_flag')) { + Mage::getModel('cybermut/api_debug') + ->setResponseBody(print_r($postData ,1)) + ->save(); + } + + $order = Mage::getModel('sales/order') + ->loadByIncrementId($this->getRequest()->getPost('reference')); + + if (!$order->getId()) { + $model->generateErrorResponse(); + } + + if ($returnedMAC == $correctMAC && $model->isSuccessfulPayment($this->getRequest()->getPost('code-retour'))) { + $order->addStatusToHistory($model->getConfigData('order_status')); + $order->sendNewOrderEmail(); + if ($this->saveInvoice($order)) { +// $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true); + } + $order->save(); + $model->generateSuccessResponse(); + } else { + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('cybermut')->__('Returned MAC is invalid. Order cancelled.') + ); + $order->cancel(); + $order->save(); + $model->generateErrorResponse(); + } + } + + /** + * Save invoice for order + * + * @param Mage_Sales_Model_Order $order + * @return boolean Can save invoice or not + */ + protected function saveInvoice(Mage_Sales_Model_Order $order) + { + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + return true; + } + + return false; + } + + /** + * Success payment page + */ + public function successAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setQuoteId($session->getCybermutPaymentQuoteId()); + $session->unsCybermutPaymentQuoteId(); + + $order = $this->getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('cybermut')->__('Customer successfully returned from Cybermut') + ); + + $order->save(); + $this->_redirect('checkout/onepage/success'); + } + + /** + * Failure payment page + */ + public function errorAction() + { + $errorMsg = Mage::helper('cybermut')->__(' There was an error occurred during paying process.'); + + $order = $this->getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + if ($order instanceof Mage_Sales_Model_Order && $order->getId()) { + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('cybermut')->__('Customer returned from Cybermut.') . $errorMsg + ); + $order->cancel(); + $order->save(); + } + + $this->loadLayout(); + $this->renderLayout(); + Mage::getSingleton('checkout/session')->unsLastRealOrderId(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/etc/config.xml b/app/code/core/Mage/Cybermut/etc/config.xml new file mode 100644 index 0000000000..1e22f107a3 --- /dev/null +++ b/app/code/core/Mage/Cybermut/etc/config.xml @@ -0,0 +1,123 @@ + + + + + + 0.1.0 + + + + + + Mage_Cybermut_Model + cybermut_mysql4 + + + Mage_Cybermut_Model_Mysql4 + + cybermut_api_debug
    +
    +
    +
    + + + + Mage_Cybermut + Mage_Cybermut_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Cybermut_Block + +
    + + + /cybermut/payment + + + + standard + + Mage_Cybermut + cybermut + + + + + + + + Mage_Cybermut.csv + + + + + + + + cybermut.xml + + + + + + + + + + Mage_Cybermut.csv + + + + + + + + + cybermut/payment + CyberMUT-P@iement + 0 + O + + + +
    \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/etc/system.xml b/app/code/core/Mage/Cybermut/etc/system.xml new file mode 100644 index 0000000000..d6c6715109 --- /dev/null +++ b/app/code/core/Mage/Cybermut/etc/system.xml @@ -0,0 +1,174 @@ + + + + + + + + + text + 300 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>2</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + text + 3 + 1 + 1 + 0 + + + + text + 4 + 1 + 1 + 0 + + + + text + 5 + 1 + 1 + 0 + + + + text + 6 + 1 + 1 + 0 + + + + select + cybermut/source_bank + 7 + 1 + 1 + 0 + + + + Order number will be used if left empty + text + 8 + 1 + 1 + 0 + + + + select + cybermut/source_language + 9 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 10 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 11 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status + 12 + 1 + 1 + 0 + + + + allowspecific + 13 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 14 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 15 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Cybermut/sql/cybermut_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Cybermut/sql/cybermut_setup/mysql4-install-0.1.0.php new file mode 100644 index 0000000000..4654ce38db --- /dev/null +++ b/app/code/core/Mage/Cybermut/sql/cybermut_setup/mysql4-install-0.1.0.php @@ -0,0 +1,45 @@ +startSetup(); + +$installer->run(" +CREATE TABLE `{$this->getTable('cybermut_api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Block/Form.php b/app/code/core/Mage/Cybersource/Block/Form.php new file mode 100644 index 0000000000..4ef8fe0a67 --- /dev/null +++ b/app/code/core/Mage/Cybersource/Block/Form.php @@ -0,0 +1,96 @@ +setTemplate('cybersource/form.phtml'); + } + + protected function _getConfig() + { + return Mage::getSingleton('cybersource/config'); + } + + /** + * Retrieve availables credit card types + * + * @return array + */ + public function getCcAvailableTypes() + { + $types = $this->_getConfig()->getCcTypes(); + if ($method = $this->getMethod()) { + $availableTypes = $method->getConfigData('cctypes'); + if ($availableTypes) { + $availableTypes = explode(',', $availableTypes); + foreach ($types as $code=>$name) { + if (!in_array($code, $availableTypes)) { + unset($types[$code]); + } + } + } + } + return $types; + } + + + /* + * solo/switch card start year + * @return array + */ + public function getSsStartYears() + { + $years = array(); + $first = date("Y"); + + for ($index=5; $index>=0; $index--) { + $year = $first - $index; + $years[$year] = $year; + } + $years = array(0=>$this->__('Year'))+$years; + return $years; + } + + /* + * switch/solo card type available + */ + public function hasSsCardType() + { + $availableTypes =$this->getMethod()->getConfigData('cctypes'); + if ($availableTypes) { + $availableTypes = explode(',', $availableTypes); + if (in_array('SS', $availableTypes)) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Block/Info.php b/app/code/core/Mage/Cybersource/Block/Info.php new file mode 100644 index 0000000000..b0b4ba4734 --- /dev/null +++ b/app/code/core/Mage/Cybersource/Block/Info.php @@ -0,0 +1,75 @@ +setTemplate('cybersource/info.phtml'); + } + + protected function _getConfig() + { + return Mage::getSingleton('cybersource/config'); + } + + + /** + * Retrieve credit card type name + * + * @return string + */ + public function getCcTypeName() + { + $types = $this->_getConfig()->getCcTypes(); + if (isset($types[$this->getInfo()->getCcType()])) { + return $types[$this->getInfo()->getCcType()]; + } + return $this->getInfo()->getCcType(); + } + + /** + * Retrieve CC start month for switch/solo card + * + * @return string + */ + public function getCcStartMonth() + { + $month = $this->getInfo()->getCcSsStartMonth(); + if ($month<10) { + $month = '0'.$month; + } + return $month; + } + + public function toPdf() + { + $this->setTemplate('cybersource/pdf/info.phtml'); + return $this->toHtml(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Helper/Data.php b/app/code/core/Mage/Cybersource/Helper/Data.php new file mode 100644 index 0000000000..45ecd1025e --- /dev/null +++ b/app/code/core/Mage/Cybersource/Helper/Data.php @@ -0,0 +1,33 @@ + + */ +class Mage_Cybersource_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('cybersource/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Model/Api/ExtendedSoapClient.php b/app/code/core/Mage/Cybersource/Model/Api/ExtendedSoapClient.php new file mode 100644 index 0000000000..1f4379f067 --- /dev/null +++ b/app/code/core/Mage/Cybersource/Model/Api/ExtendedSoapClient.php @@ -0,0 +1,121 @@ +_storeId = $storeId; + return $this; + } + + /** + * Store Id getter + * + * @return int + */ + public function getStoreId() + { + return $this->_storeId; + } + + /** + * XPaths that should be replaced in debug with '***' + * + * @var array + */ + protected $_debugReplacePrivateDataXPaths = array( + '//*[contains(name(),\'merchantID\')]/text()', + '//*[contains(name(),\'card\')]/*/text()', + '//*[contains(name(),\'UsernameToken\')]/*/text()' + ); + + public function __construct($wsdl, $options = array()) + { + parent::__construct($wsdl, $options); + } + + protected function getBaseApi() + { + return Mage::getSingleton('cybersource/soap'); + } + + public function __doRequest($request, $location, $action, $version) + { + $api = $this->getBaseApi(); + $user = $api->getConfigData('merchant_id', $this->getStoreId()); + $password = $api->getConfigData('security_key', $this->getStoreId()); + $soapHeader = "$user$password"; + + $requestDOM = new DOMDocument('1.0'); + $soapHeaderDOM = new DOMDocument('1.0'); + $requestDOM->loadXML($request); + $soapHeaderDOM->loadXML($soapHeader); + + $node = $requestDOM->importNode($soapHeaderDOM->firstChild, true); + $requestDOM->firstChild->insertBefore( + $node, $requestDOM->firstChild->firstChild); + + $request = $requestDOM->saveXML(); + if ($api->getConfigData('debug', $this->getStoreId())) { + + $requestDOMXPath = new DOMXPath($requestDOM); + + foreach ($this->_debugReplacePrivateDataXPaths as $xPath) { + foreach ($requestDOMXPath->query($xPath) as $element) { + $element->data = '***'; + } + } + + $debug = Mage::getModel('cybersource/api_debug') + ->setAction($action) + ->setRequestBody($requestDOM->saveXML()) + ->save(); + } + + $response = parent::__doRequest($request, $location, $action, $version); + + if (!empty($debug)) { + $debug + ->setResponseBody($response) + ->save(); + } + + return $response; + } +} diff --git a/app/code/core/Mage/Cybersource/Model/Config.php b/app/code/core/Mage/Cybersource/Model/Config.php new file mode 100644 index 0000000000..62c9c2ed2b --- /dev/null +++ b/app/code/core/Mage/Cybersource/Model/Config.php @@ -0,0 +1,62 @@ +_ccTypes = array(); + $added = false; + foreach ($pTypes as $code => $name) { + if ($code=='OT') { + $added = true; + $this->addExtraCcTypes(); + } + $this->_ccTypes[$code] = $name; + } + if (!$added) { + $this->addExtraCcTypes(); + } + return $this->_ccTypes; + } + + public function addExtraCcTypes() + { + $this->_ccTypes['JCB'] = Mage::helper('cybersource')->__('JCB'); + $this->_ccTypes['LASER'] = Mage::helper('cybersource')->__('Laser'); + $this->_ccTypes['UATP'] = Mage::helper('cybersource')->__('UATP'); + $this->_ccTypes['MCI'] = Mage::helper('cybersource')->__('Maestro (International)'); + $this->_ccTypes[Mage_Cybersource_Model_Soap::CC_CARDTYPE_SS] = Mage::helper('cybersource')->__('Maestro/Solo(UK Domestic)'); + + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Cybersource/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..ad56f58673 --- /dev/null +++ b/app/code/core/Mage/Cybersource/Model/Mysql4/Api/Debug.php @@ -0,0 +1,39 @@ + + */ +class Mage_Cybersource_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('cybersource/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Cybersource/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..3c3231f6ea --- /dev/null +++ b/app/code/core/Mage/Cybersource/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,39 @@ + + */ +class Mage_Cybersource_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('cybersource/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Model/Mysql4/Setup.php b/app/code/core/Mage/Cybersource/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..ce4af91562 --- /dev/null +++ b/app/code/core/Mage/Cybersource/Model/Mysql4/Setup.php @@ -0,0 +1,38 @@ +getInfoInstance(); + + if ($data->getCcType()==self::CC_CARDTYPE_SS) { + $info->setCcSsIssue($data->getCcSsIssue()) + ->setCcSsStartMonth($data->getCcSsStartMonth()) + ->setCcSsStartYear($data->getCcSsStartYear()) + ; + } + return $this; + } + + /** + * Validate payment method information object + * + * @param Mage_Payment_Model_Info $info + * @return Mage_Payment_Model_Abstract + */ + public function validate() + { + if (!extension_loaded('soap')) { + Mage::throwException(Mage::helper('cybersource')->__('SOAP extension is not enabled. Please contact us.')); + } + /** + * to validate paymene method is allowed for billing country or not + */ + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $billingCountry = $paymentInfo->getOrder()->getBillingAddress()->getCountryId(); + } else { + $billingCountry = $paymentInfo->getQuote()->getBillingAddress()->getCountryId(); + } + if (!$this->canUseForCountry($billingCountry)) { + Mage::throwException($this->_getHelper()->__('Selected payment type is not allowed for billing country.')); + } + + $info = $this->getInfoInstance(); + $errorMsg = false; + $availableTypes = explode(',',$this->getConfigData('cctypes')); + + $ccNumber = $info->getCcNumber(); + + // remove credit card number delimiters such as "-" and space + $ccNumber = preg_replace('/[\-\s]+/', '', $ccNumber); + $info->setCcNumber($ccNumber); + + $ccType = ''; + + if (!$this->_validateExpDate($info->getCcExpYear(), $info->getCcExpMonth())) { + $errorCode = 'ccsave_expiration,ccsave_expiration_yr'; + $errorMsg = $this->_getHelper()->__('Incorrect credit card expiration date'); + } + + if (in_array($info->getCcType(), $availableTypes)){ + if ($this->validateCcNum($ccNumber) + // Other credit card type number validation + || ($this->OtherCcType($info->getCcType()) && $this->validateCcNumOther($ccNumber))) { + + $ccType = 'OT'; + $ccTypeRegExpList = array( + 'VI' => '/^4[0-9]{12}([0-9]{3})?$/', // Visa + 'MC' => '/^5[1-5][0-9]{14}$/', // Master Card + 'AE' => '/^3[47][0-9]{13}$/', // American Express + 'DI' => '/^6011[0-9]{12}$/', // Discovery + 'JCB' => '/^(3[0-9]{15}|(2131|1800)[0-9]{12})$/', // JCB + 'LASER' => '/^(6304|6706|6771|6709)[0-9]{12}([0-9]{3})?$/' // LASER + ); + + foreach ($ccTypeRegExpList as $ccTypeMatch=>$ccTypeRegExp) { + if (preg_match($ccTypeRegExp, $ccNumber)) { + $ccType = $ccTypeMatch; + break; + } + } + + if (!$this->OtherCcType($info->getCcType()) && $ccType!=$info->getCcType()) { + $errorCode = 'ccsave_cc_type,ccsave_cc_number'; + $errorMsg = $this->_getHelper()->__('Credit card number mismatch with credit card type'); + } + } + else { + $errorCode = 'ccsave_cc_number'; + $errorMsg = $this->_getHelper()->__('Invalid Credit Card Number'); + } + + } + else { + $errorCode = 'ccsave_cc_type'; + $errorMsg = $this->_getHelper()->__('Credit card type is not allowed for this payment method'); + } + + //validate credit card verification number + if ($errorMsg === false && $this->hasVerification()) { + $verifcationRegEx = $this->getVerificationRegEx(); + $regExp = isset($verifcationRegEx[$info->getCcType()]) ? $verifcationRegEx[$info->getCcType()] : ''; + if (!$info->getCcCid() || !$regExp || !preg_match($regExp ,$info->getCcCid())){ + $errorMsg = $this->_getHelper()->__('Please enter a valid credit card verification number.'); + } + } + + if($errorMsg){ + Mage::throwException($errorMsg); + } + return $this; + } + + /** + * Getting Soap Api object + * + * @param array $options + * @return Mage_Cybersource_Model_Api_ExtendedSoapClient + */ + protected function getSoapApi($options = array()) + { + $wsdl = $this->getConfigData('test') ? self::WSDL_URL_TEST : self::WSDL_URL_LIVE; + $_api = new Mage_Cybersource_Model_Api_ExtendedSoapClient($wsdl, $options); + $_api->setStoreId($this->getStore()); + return $_api; + } + + /** + * Initializing soap header + */ + protected function iniRequest() + { + $this->_request = new stdClass(); + $this->_request->merchantID = $this->getConfigData('merchant_id'); + $this->_request->merchantReferenceCode = $this->_generateReferenceCode(); + + $this->_request->clientLibrary = "PHP"; + $this->_request->clientLibraryVersion = phpversion(); + $this->_request->clientEnvironment = php_uname(); + } + + /** + * Random generator for merchant referenc code + * + * @return random number + */ + protected function _generateReferenceCode() + { + return md5(microtime() . rand(0, time())); + } + + /** + * Getting customer IP address + * + * @return IP address string + */ + protected function getIpAddress() + { + return Mage::helper('core/http')->getRemoteAddr(); + } + + /** + * Assigning billing address to soap + * + * @param Varien_Object $billing + * @param String $email + */ + protected function addBillingAddress($billing, $email) + { + if (!$email) { + $email = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress()->getEmail(); + } + $billTo = new stdClass(); + $billTo->firstName = $billing->getFirstname(); + $billTo->lastName = $billing->getLastname(); + $billTo->company = $billing->getCompany(); + $billTo->street1 = $billing->getStreet(1); + $billTo->street2 = $billing->getStreet(2); + $billTo->city = $billing->getCity(); + $billTo->state = $billing->getRegion(); + $billTo->postalCode = $billing->getPostcode(); + $billTo->country = $billing->getCountry(); + $billTo->phoneNumber = $billing->getTelephone(); + $billTo->email = ($email ? $email : Mage::getStoreConfig('trans_email/ident_general/email')); + $billTo->ipAddress = $this->getIpAddress(); + $this->_request->billTo = $billTo; + } + + /** + * Assigning shipping address to soap object + * + * @param Varien_Object $shipping + */ + protected function addShippingAddress($shipping) + { + //checking if we have shipping address, in case of virtual order we will not have it + if ($shipping) { + $shipTo = new stdClass(); + $shipTo->firstName = $shipping->getFirstname(); + $shipTo->lastName = $shipping->getLastname(); + $shipTo->company = $shipping->getCompany(); + $shipTo->street1 = $shipping->getStreet(1); + $shipTo->street2 = $shipping->getStreet(2); + $shipTo->city = $shipping->getCity(); + $shipTo->state = $shipping->getRegion(); + $shipTo->postalCode = $shipping->getPostcode(); + $shipTo->country = $shipping->getCountry(); + $shipTo->phoneNumber = $shipping->getTelephone(); + $this->_request->shipTo = $shipTo; + } + } + + /** + * Assigning credit card information + * + * @param Mage_Model_Order_Payment $payment + */ + protected function addCcInfo($payment) + { + $card = new stdClass(); + $card->fullName = $payment->getCcOwner(); + $card->accountNumber = $payment->getCcNumber(); + $card->expirationMonth = $payment->getCcExpMonth(); + $card->expirationYear = $payment->getCcExpYear(); + if ($payment->hasCcCid()) { + $card->cvNumber = $payment->getCcCid(); + } + if ($payment->getCcType()==self::CC_CARDTYPE_SS && $payment->hasCcSsIssue()) { + $card->issueNumber = $payment->getCcSsIssue(); + } + if ($payment->getCcType()==self::CC_CARDTYPE_SS && $payment->hasCcSsStartYear()) { + $card->startMonth = $payment->getCcSsStartMonth(); + $card->startYear = $payment->getCcSsStartYear(); + } + $this->_request->card = $card; + } + + /** + * Authorizing payment + * + * @param Varien_Object $payment + * @param float $amount + * @return Mage_Cybersource_Model_Soap + */ + public function authorize(Varien_Object $payment, $amount) + { + $error = false; + + $soapClient = $this->getSoapApi(); + + $this->iniRequest(); + + $ccAuthService = new stdClass(); + $ccAuthService->run = "true"; + $this->_request->ccAuthService = $ccAuthService; + $this->addBillingAddress($payment->getOrder()->getBillingAddress(), $payment->getOrder()->getCustomerEmail()); + $this->addShippingAddress($payment->getOrder()->getShippingAddress()); + $this->addCcInfo($payment); + + $purchaseTotals = new stdClass(); + $purchaseTotals->currency = $payment->getOrder()->getBaseCurrencyCode(); + $purchaseTotals->grandTotalAmount = $amount; + $this->_request->purchaseTotals = $purchaseTotals; + + try { + $result = $soapClient->runTransaction($this->_request); + if ($result->reasonCode==self::RESPONSE_CODE_SUCCESS) { + $payment->setLastTransId($result->requestID) + ->setCcTransId($result->requestID) + ->setCybersourceToken($result->requestToken) + ->setCcAvsStatus($result->ccAuthReply->avsCode); + /* + * checking if we have cvCode in response bc + * if we don't send cvn we don't get cvCode in response + */ + if (isset($result->ccAuthReply->cvCode)) { + $payment->setCcCidStatus($result->ccAuthReply->cvCode); + } + } else { + $error = Mage::helper('cybersource')->__('There is an error in processing payment. Please try again or contact us.'); + } + } catch (Exception $e) { + Mage::throwException( + Mage::helper('cybersource')->__('Gateway request error: %s', $e->getMessage()) + ); + } + + if ($error !== false) { + Mage::throwException($error); + } + return $this; + } + + /** + * Capturing payment + * + * @param Varien_Object $payment + * @param float $amount + * @return Mage_Cybersource_Model_Soap + */ + public function capture(Varien_Object $payment, $amount) + { + $error = false; + $soapClient = $this->getSoapApi(); + $this->iniRequest(); + + if ($payment->getCcTransId() && $payment->getCybersourceToken()) { + $ccCaptureService = new stdClass(); + $ccCaptureService->run = "true"; + $ccCaptureService->authRequestToken = $payment->getCybersourceToken(); + $ccCaptureService->authRequestID = $payment->getCcTransId(); + $this->_request->ccCaptureService = $ccCaptureService; + + $item0 = new stdClass(); + $item0->unitPrice = $amount; + $item0->id = 0; + $this->_request->item = array($item0); + } else { + $ccAuthService = new stdClass(); + $ccAuthService->run = "true"; + $this->_request->ccAuthService = $ccAuthService; + + $ccCaptureService = new stdClass(); + $ccCaptureService->run = "true"; + $this->_request->ccCaptureService = $ccCaptureService; + + $this->addBillingAddress($payment->getOrder()->getBillingAddress(), $payment->getOrder()->getCustomerEmail()); + $this->addShippingAddress($payment->getOrder()->getShippingAddress()); + $this->addCcInfo($payment); + + $purchaseTotals = new stdClass(); + $purchaseTotals->currency = $payment->getOrder()->getBaseCurrencyCode(); + $purchaseTotals->grandTotalAmount = $amount; + $this->_request->purchaseTotals = $purchaseTotals; + } + try { + $result = $soapClient->runTransaction($this->_request); + if ($result->reasonCode==self::RESPONSE_CODE_SUCCESS) { + /* + for multiple capture we need to use the latest capture transaction id + */ + $payment->setLastTransId($result->requestID) + ->setLastCybersourceToken($result->requestToken) + ->setCcTransId($result->requestID) + ->setCybersourceToken($result->requestToken) + ; + } else { + $error = Mage::helper('cybersource')->__('There is an error in processing payment. Please try again or contact us.'); + } + } catch (Exception $e) { + Mage::throwException( + Mage::helper('cybersource')->__('Gateway request error: %s', $e->getMessage()) + ); + } + if ($error !== false) { + Mage::throwException($error); + } + return $this; + } + + /** + * To assign transaction id and token after capturing payment + * + * @param Mage_Sale_Model_Order_Invoice $invoice + * @param Mage_Sale_Model_Order_Payment $payment + * @return Mage_Cybersource_Model_Soap + */ + public function processInvoice($invoice, $payment) + { + parent::processInvoice($invoice, $payment); + $invoice->setTransactionId($payment->getLastTransId()); + $invoice->setCybersourceToken($payment->getLastCybersourceToken()); + return $this; + } + + /** + * To assign transaction id and token before voiding the transaction + * + * @param Mage_Sale_Model_Order_Invoice $invoice + * @param Mage_Sale_Order_Payment $payment + * @return Mage_Cybersource_Model_Soap + */ + public function processBeforeVoid($invoice, $payment) + { + parent::processBeforeVoid($invoice, $payment); + $payment->setVoidTransactionId($document->getTransactionId()); + $payment->setVoidCybersourceToken($invoice->getCybersourceToken()); + return $this; + } + + /** + * Void the payment transaction + * + * @param Mage_Sale_Model_Order_Payment $payment + * @return Mage_Cybersource_Model_Soap + */ + public function void(Varien_Object $payment) + { + $error = false; + if ($payment->getVoidTransactionId() && $payment->getVoidCybersourceToken()) { + $soapClient = $this->getSoapApi(); + $this->iniRequest(); + $voidService = new stdClass(); + $voidService->run = "true"; + $voidService->voidRequestToken = $payment->getVoidCybersourceToken(); + $voidService->voidRequestID = $payment->getVoidTransactionId(); + $this->_request->voidService = $voidService; + try { + $result = $soapClient->runTransaction($this->_request); + if ($result->reasonCode==self::RESPONSE_CODE_SUCCESS) { + $payment->setLastTransId($result->requestID) + ->setCcTransId($result->requestID) + ->setCybersourceToken($result->requestToken) + ; + } else { + $error = Mage::helper('cybersource')->__('There is an error in processing payment. Please try again or contact us.'); + } + } catch (Exception $e) { + Mage::throwException( + Mage::helper('cybersource')->__('Gateway request error: %s', $e->getMessage()) + ); + } + }else{ + $error = Mage::helper('cybersource')->__('Invalid transaction id or token'); + } + if ($error !== false) { + Mage::throwException($error); + } + return $this; + } + + /** + * To assign correct transaction id and token before refund + * + * @param Mage_Sale_Model_Order_Invoice $invoice + * @param Mage_Sale_Model_Order_Payment $payment + * @return Mage_Cybersource_Model_Soap + */ + public function processBeforeRefund($invoice, $payment) + { + parent::processBeforeRefund($invoice, $payment); + $payment->setRefundTransactionId($invoice->getTransactionId()); + $payment->setRefundCybersourceToken($invoice->getCybersourceToken()); + return $this; + } + + /** + * Refund the payment transaction + * + * @param Mage_Sale_Model_Order_Payment $payment + * @param flaot $amount + * @return Mage_Cybersource_Model_Soap + */ + public function refund(Varien_Object $payment, $amount) + { + $error = false; + if ($payment->getRefundTransactionId() && $payment->getRefundCybersourceToken() && $amount>0) { + $soapClient = $this->getSoapApi(); + $this->iniRequest(); + $ccCreditService = new stdClass(); + $ccCreditService->run = "true"; + $ccCreditService->captureRequestToken = $payment->getCybersourceToken(); + $ccCreditService->captureRequestID = $payment->getCcTransId(); + $this->_request->ccCreditService = $ccCreditService; + + $purchaseTotals = new stdClass(); + $purchaseTotals->grandTotalAmount = $amount; + $this->_request->purchaseTotals = $purchaseTotals; + + try { + $result = $soapClient->runTransaction($this->_request); + if ($result->reasonCode==self::RESPONSE_CODE_SUCCESS) { + $payment->setLastTransId($result->requestID) + ->setLastCybersourceToken($result->requestToken) + ; + } else { + $error = Mage::helper('cybersource')->__('There is an error in processing payment. Please try again or contact us.'); + } + } catch (Exception $e) { + Mage::throwException( + Mage::helper('cybersource')->__('Gateway request error: %s', $e->getMessage()) + ); + } + } else { + $error = Mage::helper('cybersource')->__('Error in refunding the payment'); + } + if ($error !== false) { + Mage::throwException($error); + } + return $this; + } + + + /** + * To assign correct transaction id and token after refund + * + * @param Mage_Sale_Model_Order_Creditmemo $creditmemo + * @param Mage_Sale_Model_Order_Payment $payment + * @return Mage_Cybersource_Model_Soap + */ + public function processCreditmemo($creditmemo, $payment) + { + parent::processCreditmemo($creditmemo, $payment); + $creditmemo->setTransactionId($payment->getLastTransId()); + $creditmemo->setCybersourceToken($payment->getLastCybersourceToken()); + return $this; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/Model/Source/Cctype.php b/app/code/core/Mage/Cybersource/Model/Source/Cctype.php new file mode 100644 index 0000000000..3dac091eba --- /dev/null +++ b/app/code/core/Mage/Cybersource/Model/Source/Cctype.php @@ -0,0 +1,33 @@ + + */ +class Mage_Cybersource_Model_Source_PaymentAction +{ + public function toOptionArray() + { + return array( + array('value' => Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE, 'label' => Mage::helper('cybersource')->__('Authorization')), + array('value' => Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE_CAPTURE, 'label' => Mage::helper('cybersource')->__('Sale')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/etc/config.xml b/app/code/core/Mage/Cybersource/etc/config.xml new file mode 100644 index 0000000000..44032c0041 --- /dev/null +++ b/app/code/core/Mage/Cybersource/etc/config.xml @@ -0,0 +1,151 @@ + + + + + + 0.7.0 + + + + + + Mage_Cybersource_Model + cybersource_mysql4 + + + Mage_Cybersource_Model_Mysql4 + + cybersource_api_debug
    +
    +
    +
    + + + + Mage_Cybersource + Mage_Cybersource_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Cybersource_Block + + + + + + JCB + JCB + 50 + + + LASER + Laser + 51 + + + UATP + UATP + 52 + + + MCI + Maestro (International) + 53 + + + SS + Maestro/Solo(UK Domestic) + 53 + + + + +
    + + + /cybersource/soap + + + + standard + + Mage_Cybersource + cybersource + + + + + + + + Mage_Cybersource.csv + + + + + + + + + + + Mage_Cybersource.csv + + + + + + + + + + authorize + Credit Card (Cybersource) + processing + AE,VI,MC,DI + cybersource/soap + + + + + +
    diff --git a/app/code/core/Mage/Cybersource/etc/system.xml b/app/code/core/Mage/Cybersource/etc/system.xml new file mode 100644 index 0000000000..82726a99e0 --- /dev/null +++ b/app/code/core/Mage/Cybersource/etc/system.xml @@ -0,0 +1,160 @@ + + + + + + + + + text + 500 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>2</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + adminhtml/system_config_backend_encrypted + obscure + 3 + 1 + 1 + 0 + + + + adminhtml/system_config_backend_encrypted + obscure + 4 + 1 + 1 + 0 + + + + select + cybersource/source_paymentAction + 5 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 6 + 1 + 1 + 0 + + + + multiselect + cybersource/source_cctype + 15 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 16 + 1 + 1 + 0 + + + + allowspecific + 50 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 51 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 55 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 56 + 1 + 1 + 0 + + + + text + 100 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Cybersource/sql/cybersource_setup/mysql4-install-0.7.0.php b/app/code/core/Mage/Cybersource/sql/cybersource_setup/mysql4-install-0.7.0.php new file mode 100644 index 0000000000..49ef373974 --- /dev/null +++ b/app/code/core/Mage/Cybersource/sql/cybersource_setup/mysql4-install-0.7.0.php @@ -0,0 +1,54 @@ +startSetup(); + +$installer->run(" + +-- DROP TABLE IF EXISTS `{$this->getTable('cybersource_api_debug')}`; +CREATE TABLE `{$this->getTable('cybersource_api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `action` varchar(255), + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); + +$installer->addAttribute('quote_payment', 'cybersource_token', array()); +$installer->addAttribute('order_payment', 'cybersource_token', array()); +$installer->addAttribute('invoice', 'cybersource_token', array()); +$installer->addAttribute('creditmemo', 'cybersource_token', array()); + diff --git a/app/code/core/Mage/Directory/Block/Data.php b/app/code/core/Mage/Directory/Block/Data.php index 25daddb237..cc70d18c70 100644 --- a/app/code/core/Mage/Directory/Block/Data.php +++ b/app/code/core/Mage/Directory/Block/Data.php @@ -147,7 +147,7 @@ public function getRegionsJs() 'name'=>$region->getName() ); } - $regionsJs = Zend_Json::encode($regions); + $regionsJs = Mage::helper('core')->jsonEncode($regions); } Varien_Profiler::stop('TEST: '.__METHOD__); return $regionsJs; diff --git a/app/code/core/Mage/Directory/Helper/Data.php b/app/code/core/Mage/Directory/Helper/Data.php index 93948dfc28..8c5601f75c 100644 --- a/app/code/core/Mage/Directory/Helper/Data.php +++ b/app/code/core/Mage/Directory/Helper/Data.php @@ -87,7 +87,7 @@ public function getRegionJson() 'name'=>$region->getName() ); } - $json = Zend_Json::encode($regions); + $json = Mage::helper('core')->jsonEncode($regions); if (Mage::app()->useCache('config')) { Mage::app()->saveCache($json, $cacheKey, array('config')); @@ -111,4 +111,28 @@ public function currencyConvert($amount, $from, $to=null) $converted = $this->_currencyCache[$from]->convert($amount, $to); return $converted; } + + /** + * Return ISO2 country codes, which have optional Zip/Postal pre-configured + * + * @param mixed $storeId Store + * @return array + */ + public function getCountriesWithOptionalZip($storeId = null) + { + $value = Mage::getStoreConfig('general/country/optional_zip_countries', $storeId); + return preg_split('/\,/', $value, 0, PREG_SPLIT_NO_EMPTY); + } + + /** + * Same as getCountriesWithOptionalZip() but result is json-encoded + * + * @see getCountriesWithOptionalZip + * @param mixed $storeId Store + * @return string + */ + public function getCountriesWithOptionalZipJson($storeId = null) + { + return Mage::helper('core')->jsonEncode($this->getCountriesWithOptionalZip($storeId)); + } } diff --git a/app/code/core/Mage/Directory/Model/Currency.php b/app/code/core/Mage/Directory/Model/Currency.php index 5e8f3e0f0d..cb8714f201 100644 --- a/app/code/core/Mage/Directory/Model/Currency.php +++ b/app/code/core/Mage/Directory/Model/Currency.php @@ -194,6 +194,24 @@ public function getFilter() */ public function format($price, $options=array(), $includeContainer = true, $addBrackets = false) { + return $this->formatPrecision($price, 2, $options, $includeContainer, $addBrackets); + } + + /** + * Apply currency format to number with specific rounding precision + * + * @param float $price + * @param int $precision + * @param array $options + * @param bool $includeContainer + * @param bool $addBrackets + * @return string + */ + public function formatPrecision($price, $precision, $options=array(), $includeContainer = true, $addBrackets = false) + { + if (!isset($options['precision'])) { + $options['precision'] = $precision; + } if ($includeContainer) { return '' . ($addBrackets ? '[' : '') . $this->formatTxt($price, $options) . ($addBrackets ? ']' : '') . ''; } 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 e703cc6821..a015e936d7 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 @@ -276,7 +276,7 @@ public function getConfigJson($type='links') $this->getConfig()->setReplaceBrowseWithRemove(true); $this->getConfig()->setWidth('32'); $this->getConfig()->setHideUploadButton(true); - return Zend_Json::encode($this->getConfig()->getData()); + return Mage::helper('core')->jsonEncode($this->getConfig()->getData()); } /** diff --git a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php index a8c3da3497..d990998445 100644 --- a/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php +++ b/app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php @@ -185,7 +185,7 @@ public function getConfigJson() $this->getConfig()->setReplaceBrowseWithRemove(true); $this->getConfig()->setWidth('32'); $this->getConfig()->setHideUploadButton(true); - return Zend_Json::encode($this->getConfig()->getData()); + return Mage::helper('core')->jsonEncode($this->getConfig()->getData()); } /** diff --git a/app/code/core/Mage/Downloadable/Block/Catalog/Product/Links.php b/app/code/core/Mage/Downloadable/Block/Catalog/Product/Links.php index 4b81214cf4..6a095f8763 100644 --- a/app/code/core/Mage/Downloadable/Block/Catalog/Product/Links.php +++ b/app/code/core/Mage/Downloadable/Block/Catalog/Product/Links.php @@ -123,7 +123,7 @@ public function getJsonConfig() $config[$link->getId()] = Mage::helper('core')->currency($link->getPrice(), false, false); } - return Zend_Json::encode($config); + return Mage::helper('core')->jsonEncode($config); } public function getLinkSamlpeUrl($link) diff --git a/app/code/core/Mage/Downloadable/Helper/Download.php b/app/code/core/Mage/Downloadable/Helper/Download.php index eedc1c0f05..e1082334e2 100644 --- a/app/code/core/Mage/Downloadable/Helper/Download.php +++ b/app/code/core/Mage/Downloadable/Helper/Download.php @@ -207,7 +207,7 @@ public function getContentType() } elseif ($this->_linkType == self::LINK_TYPE_URL) { if (isset($this->_urlHeaders['content-type'])) { - $contentType = split('; ', $this->_urlHeaders['content-type']); + $contentType = explode('; ', $this->_urlHeaders['content-type']); return $contentType[0]; } } @@ -222,7 +222,7 @@ public function getFilename() } elseif ($this->_linkType == self::LINK_TYPE_URL) { if (isset($this->_urlHeaders['content-disposition'])) { - $contentDisposition = split('; ', $this->_urlHeaders['content-disposition']); + $contentDisposition = explode('; ', $this->_urlHeaders['content-disposition']); if (!empty($contentDisposition[1]) && strpos($contentDisposition[1], 'filename=') !== false) { return substr($contentDisposition[1], 9); } diff --git a/app/code/core/Mage/Downloadable/Model/Mysql4/Link.php b/app/code/core/Mage/Downloadable/Model/Mysql4/Link.php index e3bb63b603..551ab26cb7 100644 --- a/app/code/core/Mage/Downloadable/Model/Mysql4/Link.php +++ b/app/code/core/Mage/Downloadable/Model/Mysql4/Link.php @@ -94,13 +94,35 @@ public function saveItemTitleAndPrice($linkObject) } } else { if (!$linkObject->getUseDefaultPrice()) { - $this->_getWriteAdapter()->insert( - $this->getTable('downloadable/link_price'), - array( - 'link_id' => $linkObject->getId(), - 'website_id' => $linkObject->getWebsiteId(), - 'price' => $linkObject->getPrice() - )); + $dataToInsert[] = array( + 'link_id' => $linkObject->getId(), + 'website_id' => $linkObject->getWebsiteId(), + 'price' => $linkObject->getPrice() + ); + $_isNew = $linkObject->getOrigData('link_id') != $linkObject->getLinkId(); + if ($linkObject->getWebsiteId() == 0 && $_isNew && !Mage::helper('catalog')->isPriceGlobal()) { + $websiteIds = $linkObject->getProductWebsiteIds(); + foreach ($websiteIds as $websiteId) { + $baseCurrency = Mage::app()->getBaseCurrencyCode(); + $websiteCurrency = Mage::app()->getWebsite($websiteId)->getBaseCurrencyCode(); + if ($websiteCurrency == $baseCurrency) { + continue; + } + $rate = Mage::getModel('directory/currency')->load($baseCurrency)->getRate($websiteCurrency); + if (!$rate) { + $rate = 1; + } + $newPrice = $linkObject->getPrice() * $rate; + $dataToInsert[] = array( + 'link_id' => $linkObject->getId(), + 'website_id' => $websiteId, + 'price' => $newPrice + ); + } + } + foreach ($dataToInsert as $_data) { + $this->_getWriteAdapter()->insert($this->getTable('downloadable/link_price'), $_data); + } } } return $this; diff --git a/app/code/core/Mage/Downloadable/Model/Observer.php b/app/code/core/Mage/Downloadable/Model/Observer.php index d62f69ec29..9d11ce4e02 100644 --- a/app/code/core/Mage/Downloadable/Model/Observer.php +++ b/app/code/core/Mage/Downloadable/Model/Observer.php @@ -164,11 +164,12 @@ public function setLinkStatus($observer) } elseif ($order->getState() == Mage_Sales_Model_Order::STATE_PENDING_PAYMENT) { $status = Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_PENDING_PAYMENT; } else { + $availableStatuses = array($orderItemStatusToEnable, Mage_Sales_Model_Order_Item::STATUS_INVOICED); foreach ($order->getAllItems() as $item) { if ($item->getProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE || $item->getRealProductType() == Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE) { - if ($item->getStatusId() == $orderItemStatusToEnable) { + if (in_array($item->getStatusId(), $availableStatuses)) { $orderItemsIds[] = $item->getId(); } } diff --git a/app/code/core/Mage/Downloadable/Model/Product/Type.php b/app/code/core/Mage/Downloadable/Model/Product/Type.php index c0a5fcf12f..3dac7c132c 100644 --- a/app/code/core/Mage/Downloadable/Model/Product/Type.php +++ b/app/code/core/Mage/Downloadable/Model/Product/Type.php @@ -171,7 +171,7 @@ public function save($product = null) $sampleModel = Mage::getModel('downloadable/sample'); $files = array(); if (isset($sampleItem['file'])) { - $files = Zend_Json::decode($sampleItem['file']); + $files = Mage::helper('core')->jsonDecode($sampleItem['file']); unset($sampleItem['file']); } @@ -209,7 +209,7 @@ public function save($product = null) } $files = array(); if (isset($linkItem['file'])) { - $files = Zend_Json::decode($linkItem['file']); + $files = Mage::helper('core')->jsonDecode($linkItem['file']); unset($linkItem['file']); } $sample = array(); @@ -222,7 +222,8 @@ public function save($product = null) ->setLinkType($linkItem['type']) ->setProductId($product->getId()) ->setStoreId($product->getStoreId()) - ->setWebsiteId($product->getStore()->getWebsiteId()); + ->setWebsiteId($product->getStore()->getWebsiteId()) + ->setProductWebsiteIds($product->getWebsiteIds()); if (null === $linkModel->getPrice()) { $linkModel->setPrice(0); } @@ -235,7 +236,7 @@ public function save($product = null) $linkModel->setSampleUrl($sample['url']); } $linkModel->setSampleType($sample['type']); - $sampleFile = Zend_Json::decode($sample['file']); + $sampleFile = Mage::helper('core')->jsonDecode($sample['file']); } if ($linkModel->getLinkType() == Mage_Downloadable_Helper_Download::LINK_TYPE_FILE) { $linkFileName = Mage::helper('downloadable/file')->moveFileFromTmp( diff --git a/app/code/core/Mage/Downloadable/controllers/FileController.php b/app/code/core/Mage/Downloadable/controllers/FileController.php index 30464a1f81..faf0a6085d 100644 --- a/app/code/core/Mage/Downloadable/controllers/FileController.php +++ b/app/code/core/Mage/Downloadable/controllers/FileController.php @@ -65,7 +65,7 @@ public function uploadAction() $result = array('error'=>$e->getMessage(), 'errorcode'=>$e->getCode()); } - $this->getResponse()->setBody(Zend_Json::encode($result)); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); } /** diff --git a/app/code/core/Mage/Downloadable/etc/config.xml b/app/code/core/Mage/Downloadable/etc/config.xml index 3ed7b20b9a..62bf6b870d 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.14 + 0.1.15 diff --git a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-install-0.1.0.php index f925f7ee52..586a691c90 100644 --- a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-install-0.1.0.php +++ b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-install-0.1.0.php @@ -43,7 +43,7 @@ // make these attributes applicable to downloadable products foreach ($fieldList as $field) { - $applyTo = split(',', $installer->getAttribute('catalog_product', $field, 'apply_to')); + $applyTo = explode(',', $installer->getAttribute('catalog_product', $field, 'apply_to')); if (!in_array('downloadable', $applyTo)) { $applyTo[] = 'downloadable'; $installer->updateAttribute('catalog_product', $field, 'apply_to', join(',', $applyTo)); diff --git a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.0-0.1.1.php b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.0-0.1.1.php index d89ea5fd76..4105ff3b9f 100644 --- a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.0-0.1.1.php +++ b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.0-0.1.1.php @@ -29,7 +29,7 @@ $installer->startSetup(); // make attribute 'weight' not applicable to downloadable products -$applyTo = split(',', $installer->getAttribute('catalog_product', 'weight', 'apply_to')); +$applyTo = explode(',', $installer->getAttribute('catalog_product', 'weight', 'apply_to')); if (in_array('downloadable', $applyTo)) { $newApplyTo = array(); foreach ($applyTo as $key=>$value) { diff --git a/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.14-0.1.15.php b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.14-0.1.15.php new file mode 100644 index 0000000000..456ce7706d --- /dev/null +++ b/app/code/core/Mage/Downloadable/sql/downloadable_setup/mysql4-upgrade-0.1.14-0.1.15.php @@ -0,0 +1,35 @@ +startSetup(); +$installer->getConnection()->addKey($installer->getTable('downloadable/link_title'), 'UNQ_LINK_TITLE_STORE', + array('link_id', 'store_id'), 'unique'); +$installer->getConnection()->addKey($installer->getTable('downloadable/sample_title'), 'UNQ_SAMPLE_TITLE_STORE', + array('sample_id', 'store_id'), 'unique'); +$installer->endSetup(); diff --git a/app/code/core/Mage/Eav/Model/Convert/Adapter/Entity.php b/app/code/core/Mage/Eav/Model/Convert/Adapter/Entity.php index 7fc9312017..82c0614f5b 100644 --- a/app/code/core/Mage/Eav/Model/Convert/Adapter/Entity.php +++ b/app/code/core/Mage/Eav/Model/Convert/Adapter/Entity.php @@ -104,7 +104,7 @@ public function setFilter($attrFilterArray, $attrToDb = null, $bind = null, $joi if ($type == 'dateFromTo' || $type == 'datetimeFromTo') { foreach ($filters as $k => $v) { if (strpos($k, $key . '/') === 0) { - $split = split('/', $k); + $split = explode('/', $k); $filters[$key][$split[1]] = $v; } } diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute.php b/app/code/core/Mage/Eav/Model/Entity/Attribute.php index 276c44c963..16768b743f 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute.php @@ -124,7 +124,12 @@ protected function _beforeSave() // save default date value as timestamp if ($defaultValue = $this->getDefaultValue()) { $format = Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT); - $this->setDefaultValue(Mage::app()->getLocale()->date($defaultValue, $format, null, false)->toValue()); + try { + $defaultValue = Mage::app()->getLocale()->date($defaultValue, $format, null, false)->toValue(); + $this->setDefaultValue($defaultValue); + } catch (Exception $e) { + throw new Exception("Invalid default date."); + } } } diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Abstract.php index c9e04e535d..366e9540ef 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Abstract.php @@ -82,7 +82,7 @@ public function getOptionText($value) // Fixed for tax_class_id and custom_design if (sizeof($options) > 0) foreach($options as $option) { if (isset($option['value']) && $option['value'] == $value) { - return $option; + return isset($option['label']) ? $option['label'] : $option['value']; } } // End if (isset($options[$value])) { diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Table.php index 1468ce692a..a7f7f19c1d 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Table.php @@ -146,14 +146,16 @@ public function addValueSortToCollection($collection, $dir = 'asc') public function getFlatColums() { $columns = array(); + $isMulti = $this->getAttribute()->getFrontend()->getInputType() == 'multiselect'; $columns[$this->getAttribute()->getAttributeCode()] = array( - 'type' => 'int', + 'type' => $isMulti ? 'varchar(255)' : 'int', 'unsigned' => false, 'is_null' => true, 'default' => null, 'extra' => null ); - if ($this->getAttribute()->getFrontend()->getInputType() != 'multiselect') { + + if (!$isMulti) { $columns[$this->getAttribute()->getAttributeCode() . '_value'] = array( 'type' => 'varchar(255)', 'unsigned' => false, diff --git a/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php b/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php index 3409eb60b8..d07c367406 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php @@ -673,7 +673,7 @@ public function joinField($alias, $table, $field, $bind, $cond=null, $joinType=' /** * Join a table * - * @param string $table + * @param string|array $table * @param string $bind * @param string|array $fields * @param null|array $cond @@ -682,11 +682,21 @@ public function joinField($alias, $table, $field, $bind, $cond=null, $joinType=' */ public function joinTable($table, $bind, $fields=null, $cond=null, $joinType='inner') { + $tableAlias = null; + if (is_array($table)) { + list($tableAlias, $tableName) = each($table); + } + else { + $tableName = $table; + } + // validate table - if (strpos($table, '/')!==false) { - $table = Mage::getSingleton('core/resource')->getTableName($table); + if (strpos($tableName, '/') !== false) { + $tableName = Mage::getSingleton('core/resource')->getTableName($tableName); + } + if (empty($tableAlias)) { + $tableAlias = $tableName; } - $tableAlias = $table; // validate fields and aliases if (!$fields) { @@ -697,14 +707,14 @@ public function joinTable($table, $bind, $fields=null, $cond=null, $joinType='in throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Joined field with this alias (%s) is already declared', $alias)); } $this->_joinFields[$alias] = array( - 'table'=>$tableAlias, - 'field'=>$field, + 'table' => $tableAlias, + 'field' => $field, ); } // validate bind list($pk, $fk) = explode('=', $bind); - $bindCond = $tableAlias.'.'.$pk.'='.$this->_getAttributeFieldName($fk); + $bindCond = $tableAlias . '.' . $pk . '=' . $this->_getAttributeFieldName($fk); // process join type switch ($joinType) { @@ -730,7 +740,7 @@ public function joinTable($table, $bind, $fields=null, $cond=null, $joinType='in $cond = '('.join(') AND (', $condArr).')'; // join table - $this->getSelect()->$joinMethod(array($tableAlias=>$table), $cond, $fields); + $this->getSelect()->$joinMethod(array($tableAlias => $tableName), $cond, $fields); return $this; } 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 29411a72e5..2c54c658a8 100644 --- a/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute.php +++ b/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute.php @@ -428,5 +428,4 @@ public function getFlatUpdateSelect(Mage_Eav_Model_Entity_Attribute_Abstract $at public function describeTable($table) { return $this->_getReadAdapter()->describeTable($table); } - } diff --git a/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute/Collection.php b/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute/Collection.php index 7c5e887991..064c1ab469 100644 --- a/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute/Collection.php +++ b/app/code/core/Mage/Eav/Model/Mysql4/Entity/Attribute/Collection.php @@ -370,32 +370,18 @@ protected function _addSetInfo() protected function _afterLoadData() { $this->_addSetInfo(); + return parent::_afterLoadData(); } /** - * TODO: issue #5126 + * Load is used in configurable products flag * - * @return unknown + * @deprecated + * @return Mage_Eav_Model_Mysql4_Entity_Attribute_Collection */ public function checkConfigurableProducts() { -// was: -/* -SELECT `main_table`.*, `entity_attribute`.* -FROM `eav_attribute` AS `main_table` - INNER JOIN `eav_entity_attribute` AS `entity_attribute` ON entity_attribute.attribute_id=main_table.attribute_id -WHERE (entity_attribute.attribute_group_id='46') AND (main_table.is_visible=1) -*/ -// to be done: left join catalog_product_super_attribute and count appropriate lines -/* -SELECT `main_table`.*, `entity_attribute`.*, COUNT(`super`.attribute_id) AS `is_used_in_configurable` -FROM `eav_attribute` AS `main_table` - INNER JOIN `eav_entity_attribute` AS `entity_attribute` ON entity_attribute.attribute_id=main_table.attribute_id - LEFT JOIN `catalog_product_super_attribute` AS `super` ON `main_table`.attribute_id=`super`.attribute_id -WHERE (entity_attribute.attribute_group_id='46') AND (main_table.is_visible=1) -GROUP BY `main_table`.attribute_id -*/ return $this; } diff --git a/app/code/core/Mage/Eway/Block/Form.php b/app/code/core/Mage/Eway/Block/Form.php new file mode 100644 index 0000000000..0795cdef34 --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Form.php @@ -0,0 +1,42 @@ + + */ +class Mage_Eway_Block_Form extends Mage_Payment_Block_Form_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('eway/form.phtml'); + } + +} diff --git a/app/code/core/Mage/Eway/Block/Info.php b/app/code/core/Mage/Eway/Block/Info.php new file mode 100644 index 0000000000..9ae51957d7 --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Info.php @@ -0,0 +1,48 @@ + + */ +class Mage_Eway_Block_Info extends Mage_Payment_Block_Info_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('eway/info.phtml'); + } + + public function toPdf() + { + $this->setTemplate('eway/pdf/info.phtml'); + return $this->toHtml(); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Block/Secure/Failure.php b/app/code/core/Mage/Eway/Block/Secure/Failure.php new file mode 100644 index 0000000000..71632de15b --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Secure/Failure.php @@ -0,0 +1,53 @@ + + */ + +class Mage_Eway_Block_Secure_Failure extends Mage_Core_Block_Template +{ + /** + * Return Error message + * + * @return string + */ + public function getErrorMessage () + { + return Mage::getSingleton('checkout/session')->getEwayErrorMessage(); + } + + /** + * Get continue shopping url + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Block/Secure/Form.php b/app/code/core/Mage/Eway/Block/Secure/Form.php new file mode 100644 index 0000000000..24f6335700 --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Secure/Form.php @@ -0,0 +1,41 @@ + + */ +class Mage_Eway_Block_Secure_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('eway/secure/form.phtml'); + parent::_construct(); + } +} diff --git a/app/code/core/Mage/Eway/Block/Secure/Redirect.php b/app/code/core/Mage/Eway/Block/Secure/Redirect.php new file mode 100644 index 0000000000..365b5a0f44 --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Secure/Redirect.php @@ -0,0 +1,57 @@ + + */ +class Mage_Eway_Block_Secure_Redirect extends Mage_Core_Block_Abstract +{ + protected function _toHtml() + { + $secure = $this->getOrder()->getPayment()->getMethodInstance(); + + $form = new Varien_Data_Form(); + $form->setAction($secure->getEwaySecureUrl()) + ->setId('eway_secure_checkout') + ->setName('eway_secure_checkout') + ->setMethod('POST') + ->setUseContainer(true); + foreach ($secure->getFormFields() as $field=>$value) { + $form->addField($field, 'hidden', array('name'=>$field, 'value'=>$value)); + } + $html = ''; + $html.= $this->__('You will be redirected to eWAY 3D-Secure in a few seconds.'); + $html.= $form->toHtml(); + $html.= ''; + $html.= ''; + + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Block/Shared/Failure.php b/app/code/core/Mage/Eway/Block/Shared/Failure.php new file mode 100644 index 0000000000..63d1881ebe --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Shared/Failure.php @@ -0,0 +1,55 @@ + + */ + +class Mage_Eway_Block_Shared_Failure extends Mage_Core_Block_Template +{ + /** + * Return Error message + * + * @return string + */ + public function getErrorMessage () + { + $msg = Mage::getSingleton('checkout/session')->getEwayErrorMessage(); + Mage::getSingleton('checkout/session')->unsEwayErrorMessage(); + return $msg; + } + + /** + * Get continue shopping url + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Block/Shared/Form.php b/app/code/core/Mage/Eway/Block/Shared/Form.php new file mode 100644 index 0000000000..edf18d0e78 --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Shared/Form.php @@ -0,0 +1,42 @@ + + */ +class Mage_Eway_Block_Shared_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('eway/shared/form.phtml'); + parent::_construct(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Block/Shared/Redirect.php b/app/code/core/Mage/Eway/Block/Shared/Redirect.php new file mode 100644 index 0000000000..dce8a7115f --- /dev/null +++ b/app/code/core/Mage/Eway/Block/Shared/Redirect.php @@ -0,0 +1,58 @@ + + */ +class Mage_Eway_Block_Shared_Redirect extends Mage_Core_Block_Abstract +{ + protected function _toHtml() + { + $shared = $this->getOrder()->getPayment()->getMethodInstance(); + + $form = new Varien_Data_Form(); + $form->setAction($shared->getEwaySharedUrl()) + ->setId('eway_shared_checkout') + ->setName('eway_shared_checkout') + ->setMethod('POST') + ->setUseContainer(true); + foreach ($shared->getFormFields() as $field=>$value) { + $form->addField($field, 'hidden', array('name'=>$field, 'value'=>$value)); + } + + $html = ''; + $html.= $this->__('You will be redirected to eWAY in a few seconds.'); + $html.= $form->toHtml(); + $html.= ''; + $html.= ''; + + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Controller/Abstract.php b/app/code/core/Mage/Eway/Controller/Abstract.php new file mode 100644 index 0000000000..0f53dde9f1 --- /dev/null +++ b/app/code/core/Mage/Eway/Controller/Abstract.php @@ -0,0 +1,178 @@ + + */ +abstract class Mage_Eway_Controller_Abstract extends Mage_Core_Controller_Front_Action +{ + protected function _expireAjax() + { + if (!$this->getCheckout()->getQuote()->hasItems()) { + $this->getResponse()->setHeader('HTTP/1.1','403 Session Expired'); + exit; + } + } + + /** + * Redirect Block + * need to be redeclared + */ + protected $_redirectBlockType; + + /** + * Get singleton of Checkout Session Model + * + * @return Mage_Checkout_Model_Session + */ + public function getCheckout() + { + return Mage::getSingleton('checkout/session'); + } + + /** + * when customer select eWay payment method + */ + public function redirectAction() + { + $session = $this->getCheckout(); + $session->setEwayQuoteId($session->getQuoteId()); + $session->setEwayRealOrderId($session->getLastRealOrderId()); + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($session->getLastRealOrderId()); + $order->addStatusToHistory($order->getStatus(), Mage::helper('eway')->__('Customer was redirected to eWAY.')); + $order->save(); + + $this->getResponse()->setBody( + $this->getLayout() + ->createBlock($this->_redirectBlockType) + ->setOrder($order) + ->toHtml() + ); + + $session->unsQuoteId(); + } + + /** + * eWay returns POST variables to this action + */ + public function successAction() + { + $status = $this->_checkReturnedPost(); + + $session = $this->getCheckout(); + + $session->unsEwayRealOrderId(); + $session->setQuoteId($session->getEwayQuoteId(true)); + $session->getQuote()->setIsActive(false)->save(); + + $order = Mage::getModel('sales/order'); + $order->load($this->getCheckout()->getLastOrderId()); + if($order->getId()) { + $order->sendNewOrderEmail(); + } + + if ($status) { + $this->_redirect('checkout/onepage/success'); + } else { + $this->_redirect('*/*/failure'); + } + } + + /** + * Display failure page if error + * + */ + public function failureAction() + { + if (!$this->getCheckout()->getEwayErrorMessage()) { + $this->norouteAction(); + return; + } + + $this->getCheckout()->clear(); + + $this->loadLayout(); + $this->renderLayout(); + } + + /** + * Checking POST variables. + * Creating invoice if payment was successfull or cancel order if payment was declined + */ + protected function _checkReturnedPost() + { + if (!$this->getRequest()->isPost()) { + $this->norouteAction(); + return; + } + $status = true; + $response = $this->getRequest()->getPost(); + + if ($this->getCheckout()->getEwayRealOrderId() != $response['ewayTrxnNumber'] || + $this->getCheckout()->getEwayRealOrderId() != Mage::helper('core')->decrypt($response['eWAYoption2'])) { + $this->norouteAction(); + return; + } + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($response['ewayTrxnNumber']); + + $paymentInst = $order->getPayment()->getMethodInstance(); + $paymentInst->setResponse($response); + + if ($paymentInst->parseResponse()) { + + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + + $paymentInst->setTransactionId($response['ewayTrxnReference']); + $order->addStatusToHistory($order->getStatus(), Mage::helper('eway')->__('Customer successfully returned from eWAY')); + } + } else { + $paymentInst->setTransactionId($response['ewayTrxnReference']); + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), Mage::helper('eway')->__('Customer was rejected by eWAY')); + $status = false; + $this->getCheckout()->setEwayErrorMessage($response['eWAYresponseText']); + } + + $order->save(); + + return $status; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Helper/Data.php b/app/code/core/Mage/Eway/Helper/Data.php new file mode 100644 index 0000000000..326211e692 --- /dev/null +++ b/app/code/core/Mage/Eway/Helper/Data.php @@ -0,0 +1,37 @@ + + */ +class Mage_Eway_Helper_Data extends Mage_Core_Helper_Abstract +{ + +} diff --git a/app/code/core/Mage/Eway/Model/Api/Debug.php b/app/code/core/Mage/Eway/Model/Api/Debug.php new file mode 100644 index 0000000000..68fb947ab1 --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Eway_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('eway/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Model/Direct.php b/app/code/core/Mage/Eway/Model/Direct.php new file mode 100644 index 0000000000..7db6c2a91d --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Direct.php @@ -0,0 +1,296 @@ + + */ +class Mage_Eway_Model_Direct extends Mage_Payment_Model_Method_Cc +{ + protected $_code = 'eway_direct'; + + protected $_isGateway = true; + protected $_canAuthorize = false; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = true; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = true; + protected $_canSaveCc = true; + + protected $_formBlockType = 'eway/form'; + protected $_infoBlockType = 'eway/info'; + + /** + * Get debug flag + * + * @return string + */ + public function getDebug() + { + return Mage::getStoreConfig('payment/eway_direct/debug_flag'); + } + + /** + * Get flag to use CCV or not + * + * @return string + */ + public function getUseccv() + { + return Mage::getStoreConfig('payment/eway_direct/useccv'); + } + + /** + * Get api url of eWAY Direct payment + * + * @return string + */ + public function getApiGatewayUrl() + { + $value = Mage::getStoreConfig('payment/eway_direct/api_url'); + if (!$value || $value === false) { + return 'https://www.eway.com.au/gateway/xmlpayment.asp'; + } + return $value; + } + + /** + * Get Customer Id + * + * @return string + */ + public function getCustomerId() + { + return Mage::getStoreConfig('payment/eway_direct/customer_id'); + } + + /** + * Get currency that accepted by eWAY account + * + * @return string + */ + public function getAccepteCurrency() + { + return Mage::getStoreConfig('payment/' . $this->getCode() . '/currency'); + } + + public function validate() + { + parent::validate(); + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $currency_code = $paymentInfo->getOrder()->getBaseCurrencyCode(); + } else { + $currency_code = $paymentInfo->getQuote()->getBaseCurrencyCode(); + } + if ($currency_code != $this->getAccepteCurrency()) { + Mage::throwException(Mage::helper('eway')->__('Selected currency code ('.$currency_code.') is not compatible with eWAY')); + } + return $this; + } + + public function capture(Varien_Object $payment, $amount) + { + $this->setAmount($amount) + ->setPayment($payment); + + $result = $this->callDoDirectPayment($payment)!==false; + + if ($result) { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + } else { + $e = $this->getError(); + if (isset($e['message'])) { + $message = Mage::helper('eway')->__('There has been an error processing your payment.') . $e['message']; + } else { + $message = Mage::helper('eway')->__('There has been an error processing your payment. Please try later or contact us for help.'); + } + Mage::throwException($message); + } + return $this; + } + + public function cancel(Varien_Object $payment) + { + $payment->setStatus(self::STATUS_DECLINED); + return $this; + } + + /** + * prepare params to send to gateway + * + * @return bool | array + */ + public function callDoDirectPayment() + { + $payment = $this->getPayment(); + $billing = $payment->getOrder()->getBillingAddress(); + + $invoiceDesc = ''; + $lengs = 0; + foreach ($payment->getOrder()->getAllItems() as $item) { + if ($item->getParentItem()) { + continue; + } + if (Mage::helper('core/string')->strlen($invoiceDesc.$item->getName()) > 10000) { + break; + } + $invoiceDesc .= $item->getName() . ', '; + } + $invoiceDesc = Mage::helper('core/string')->substr($invoiceDesc, 0, -2); + + $address = clone $billing; + $address->unsFirstname(); + $address->unsLastname(); + $address->unsPostcode(); + $formatedAddress = ''; + $tmpAddress = explode(' ', str_replace("\n", ' ', trim($address->format('text')))); + foreach ($tmpAddress as $part) { + if (strlen($part) > 0) $formatedAddress .= $part . ' '; + } +// $this->getQuote()->reserveOrderId(); + $xml = ""; + $xml .= "" . $this->getCustomerId() . ""; + $xml .= "" . ($this->getAmount()*100) . ""; + $xml .= "" . htmlentities(trim($payment->getCcOwner()), ENT_QUOTES, 'UTF-8') . ""; + $xml .= "" . $payment->getCcNumber() . ""; + $xml .= "" . $payment->getCcExpMonth() . ""; + $xml .= "" . $payment->getCcExpYear() . ""; + $xml .= "" . '' . ""; + $xml .= "" . htmlentities(trim($invoiceDesc), ENT_QUOTES, 'UTF-8') . ""; + $xml .= "" . htmlentities(trim($billing->getFirstname()), ENT_QUOTES, 'UTF-8') . ""; + $xml .= "" . htmlentities(trim($billing->getLastname()), ENT_QUOTES, 'UTF-8') . ""; + $xml .= "" . htmlentities(trim($payment->getOrder()->getCustomerEmail()), ENT_QUOTES, 'UTF-8') . ""; + $xml .= "" . htmlentities(trim($formatedAddress), ENT_QUOTES, 'UTF-8') . ""; + $xml .= "" . htmlentities(trim($billing->getPostcode()), ENT_QUOTES, 'UTF-8') . ""; +// $xml .= "" . $this->getQuote()->getReservedOrderId() . ""; + $xml .= "" . '' . ""; + + if ($this->getUseccv()) { + $xml .= "" . $payment->getCcCid() . ""; + } + + $xml .= "" . '' . ""; + $xml .= "" . '' . ""; + $xml .= "" . '' . ""; + + + if (Mage::getStoreConfig('payment/eway_direct/use_anti_fraud')) { + $xml .= "". Mage::helper('core/http')->getRemoteAddr() .""; + $xml .= "". $billing->getCountryId() .""; + } + + $xml .= ""; + + $resultArr = $this->call($xml); + + if ($resultArr === false) { + return false; + } + + $this->setTransactionId($resultArr['ewayTrxnNumber']); + + return $resultArr; + } + + /** + * Send params to gateway + * + * @param string $xml + * @return bool | array + */ + public function call($xml) + { + if ($this->getDebug()) { + $debug = Mage::getModel('eway/api_debug') + ->setRequestBody($xml) + ->save(); + } + + $http = new Varien_Http_Adapter_Curl(); + $config = array('timeout' => 30); + + $http->setConfig($config); + $http->write(Zend_Http_Client::POST, $this->getApiGatewayUrl(), '1.1', array(), $xml); + $response = $http->read(); + + $response = preg_split('/^\r?$/m', $response, 2); + $response = trim($response[1]); + + if ($this->getDebug()) { + $debug->setResponseBody($response)->save(); + } + + if ($http->getErrno()) { + $http->close(); + $this->setError(array( + 'message' => $http->getError() + )); + return false; + } + $http->close(); + + $parsedResArr = $this->parseXmlResponse($response); + + if ($parsedResArr['ewayTrxnStatus'] == 'True') { + $this->unsError(); + return $parsedResArr; + } + + if (isset($parsedResArr['ewayTrxnError'])) { + $this->setError(array( + 'message' => $parsedResArr['ewayTrxnError'] + )); + } + + return false; + } + + /** + * parse response of gateway + * + * @param string $xmlResponse + * @return array + */ + public function parseXmlResponse($xmlResponse) + { + $xmlObj = simplexml_load_string($xmlResponse); + $newResArr = array(); + foreach ($xmlObj as $key => $val) { + $newResArr[$key] = (string)$val; + } + + return $newResArr; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Eway/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..87195fdf19 --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Mysql4/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Eway_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('eway/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Eway/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..f6d3877714 --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,40 @@ + + */ +class Mage_Eway_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('eway/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/Model/Mysql4/Setup.php b/app/code/core/Mage/Eway/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..cc8f4abcf7 --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Mysql4/Setup.php @@ -0,0 +1,38 @@ + + */ +class Mage_Eway_Model_Mysql4_Setup extends Mage_Sales_Model_Mysql4_Setup +{ + +} diff --git a/app/code/core/Mage/Eway/Model/Secure.php b/app/code/core/Mage/Eway/Model/Secure.php new file mode 100644 index 0000000000..2f0d5fd60b --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Secure.php @@ -0,0 +1,54 @@ + + */ +class Mage_Eway_Model_Secure extends Mage_Eway_Model_Shared +{ + protected $_code = 'eway_secure'; + + protected $_formBlockType = 'eway/secure_form'; + protected $_paymentMethod = 'secure'; + + /** + * Get url of eWAY 3D-Secure Payment + * + * @return string + */ + public function getEwaySecureUrl() + { + if (!$url = Mage::getStoreConfig('payment/' . $this->getCode() . '/api_url')) { + $url = 'https://www.eway.com.au/gateway_3d/payment.asp'; + } + return $url; + } + +} diff --git a/app/code/core/Mage/Eway/Model/Shared.php b/app/code/core/Mage/Eway/Model/Shared.php new file mode 100644 index 0000000000..40d59c898c --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Shared.php @@ -0,0 +1,252 @@ + + */ +class Mage_Eway_Model_Shared extends Mage_Payment_Model_Method_Abstract +{ + protected $_code = 'eway_shared'; + + protected $_isGateway = false; + protected $_canAuthorize = false; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + protected $_formBlockType = 'eway/shared_form'; + protected $_paymentMethod = 'shared'; + + protected $_order; + + /** + * Get order model + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + if (!$this->_order) { + $paymentInfo = $this->getInfoInstance(); + $this->_order = Mage::getModel('sales/order') + ->loadByIncrementId($paymentInfo->getOrder()->getRealOrderId()); + } + return $this->_order; + } + + /** + * Get Customer Id + * + * @return string + */ + public function getCustomerId() + { + return Mage::getStoreConfig('payment/' . $this->getCode() . '/customer_id'); + } + + /** + * Get currency that accepted by eWAY account + * + * @return string + */ + public function getAccepteCurrency() + { + return Mage::getStoreConfig('payment/' . $this->getCode() . '/currency'); + } + + public function validate() + { + parent::validate(); + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $currency_code = $paymentInfo->getOrder()->getBaseCurrencyCode(); + } else { + $currency_code = $paymentInfo->getQuote()->getBaseCurrencyCode(); + } + if ($currency_code != $this->getAccepteCurrency()) { + Mage::throwException(Mage::helper('eway')->__('Selected currency code ('.$currency_code.') is not compatible with eWAY')); + } + return $this; + } + + public function getOrderPlaceRedirectUrl() + { + return Mage::getUrl('eway/' . $this->_paymentMethod . '/redirect'); + } + + /** + * prepare params array to send it to gateway page via POST + * + * @return array + */ + public function getFormFields() + { + $billing = $this->getOrder()->getBillingAddress(); + $fieldsArr = array(); + $invoiceDesc = ''; + $lengs = 0; + foreach ($this->getOrder()->getAllItems() as $item) { + if ($item->getParentItem()) { + continue; + } + if (Mage::helper('core/string')->strlen($invoiceDesc.$item->getName()) > 10000) { + break; + } + $invoiceDesc .= $item->getName() . ', '; + } + $invoiceDesc = Mage::helper('core/string')->substr($invoiceDesc, 0, -2); + + $address = clone $billing; + $address->unsFirstname(); + $address->unsLastname(); + $address->unsPostcode(); + $formatedAddress = ''; + $tmpAddress = explode(' ', str_replace("\n", ' ', trim($address->format('text')))); + foreach ($tmpAddress as $part) { + if (strlen($part) > 0) $formatedAddress .= $part . ' '; + } + $paymentInfo = $this->getInfoInstance(); + $fieldsArr['ewayCustomerID'] = $this->getCustomerId(); + $fieldsArr['ewayTotalAmount'] = ($this->getOrder()->getBaseGrandTotal()*100); + $fieldsArr['ewayCustomerFirstName'] = $billing->getFirstname(); + $fieldsArr['ewayCustomerLastName'] = $billing->getLastname(); + $fieldsArr['ewayCustomerEmail'] = $this->getOrder()->getCustomerEmail(); + $fieldsArr['ewayCustomerAddress'] = trim($formatedAddress); + $fieldsArr['ewayCustomerPostcode'] = $billing->getPostcode(); +// $fieldsArr['ewayCustomerInvoiceRef'] = ''; + $fieldsArr['ewayCustomerInvoiceDescription'] = $invoiceDesc; + $fieldsArr['eWAYSiteTitle '] = Mage::app()->getStore()->getName(); + $fieldsArr['eWAYAutoRedirect'] = 1; + $fieldsArr['ewayURL'] = Mage::getUrl('eway/' . $this->_paymentMethod . '/success', array('_secure' => true)); + $fieldsArr['eWAYTrxnNumber'] = $paymentInfo->getOrder()->getRealOrderId(); + $fieldsArr['ewayOption1'] = ''; + $fieldsArr['ewayOption2'] = Mage::helper('core')->encrypt($fieldsArr['eWAYTrxnNumber']); + $fieldsArr['ewayOption3'] = ''; + + $request = ''; + foreach ($fieldsArr as $k=>$v) { + $request .= '<' . $k . '>' . $v . ''; + } + + if ($this->getDebug()) { + $debug = Mage::getModel('eway/api_debug') + ->setRequestBody($request) + ->save(); + $fieldsArr['ewayOption1'] = $debug->getId(); + } + + return $fieldsArr; + } + + /** + * Get url of eWAY Shared Payment + * + * @return string + */ + public function getEwaySharedUrl() + { + if (!$url = Mage::getStoreConfig('payment/eway_shared/api_url')) { + $url = 'https://www.eway.com.au/gateway/payment.asp'; + } + return $url; + } + + /** + * Get debug flag + * + * @return string + */ + public function getDebug() + { + return Mage::getStoreConfig('payment/' . $this->getCode() . '/debug_flag'); + } + + public function capture(Varien_Object $payment, $amount) + { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + + return $this; + } + + public function cancel(Varien_Object $payment) + { + $payment->setStatus(self::STATUS_DECLINED) + ->setLastTransId($this->getTransactionId()); + + return $this; + } + + /** + * parse response POST array from gateway page and return payment status + * + * @return bool + */ + public function parseResponse() + { + $response = $this->getResponse(); + + if ($this->getDebug()) { + $debug = Mage::getModel('eway/api_debug') + ->load($response['eWAYoption1']) + ->setResponseBody(print_r($response, 1)) + ->save(); + } + + if ($response['ewayTrxnStatus'] == 'True') { + return true; + } + return false; + } + + /** + * Return redirect block type + * + * @return string + */ + public function getRedirectBlockType() + { + return $this->_redirectBlockType; + } + + /** + * Return payment method type string + * + * @return string + */ + public function getPaymentMethodType() + { + return $this->_paymentMethod; + } +} diff --git a/app/code/core/Mage/Eway/Model/Source/Cctype.php b/app/code/core/Mage/Eway/Model/Source/Cctype.php new file mode 100644 index 0000000000..158714dca9 --- /dev/null +++ b/app/code/core/Mage/Eway/Model/Source/Cctype.php @@ -0,0 +1,40 @@ + + */ +class Mage_Eway_Model_Source_Cctype extends Mage_Payment_Model_Source_Cctype +{ + public function getAllowedTypes() + { + return array('VI', 'MC', 'AE', 'DICL', 'JCB'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/controllers/SecureController.php b/app/code/core/Mage/Eway/controllers/SecureController.php new file mode 100644 index 0000000000..8161feb5b8 --- /dev/null +++ b/app/code/core/Mage/Eway/controllers/SecureController.php @@ -0,0 +1,42 @@ + + */ +class Mage_Eway_SecureController extends Mage_Eway_Controller_Abstract +{ + /** + * Redirect Block Type + * + * @var string + */ + protected $_redirectBlockType = 'eway/secure_redirect'; +} \ No newline at end of file diff --git a/app/code/core/Mage/Eway/controllers/SharedController.php b/app/code/core/Mage/Eway/controllers/SharedController.php new file mode 100644 index 0000000000..12378ba8c6 --- /dev/null +++ b/app/code/core/Mage/Eway/controllers/SharedController.php @@ -0,0 +1,42 @@ + + */ +class Mage_Eway_SharedController extends Mage_Eway_Controller_Abstract +{ + /** + * Redirect Block Type + * + * @var string + */ + protected $_redirectBlockType = 'eway/shared_redirect'; +} diff --git a/app/code/core/Mage/Eway/etc/config.xml b/app/code/core/Mage/Eway/etc/config.xml new file mode 100644 index 0000000000..4544ded182 --- /dev/null +++ b/app/code/core/Mage/Eway/etc/config.xml @@ -0,0 +1,160 @@ + + + + + + 0.1.0 + + + + + + Mage_Eway_Model + eway_mysql4 + + + Mage_Eway_Model_Mysql4 + + eway_api_debug
    +
    +
    +
    + + + + Mage_Eway + Mage_Eway_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Eway_Block + + + + + + JCB + JCB + 50 + + + DICL + Diners Club + 60 + + + + +
    + + + /eway/shared + + + + standard + + Mage_Eway + eway + + + + + + + + Mage_Eway.csv + + + + + + + + eway.xml + + + + + + + + + + Mage_Eway.csv + + + + + + + + + + AUD + authorize_capture + eway/direct + eWAY Direct + processing + AE,VI,MC,DICL,JCB + 0 + 0 + + + + AUD + eway/shared + eWAY Shared + processing + 0 + + + + AUD + eway/secure + eWAY 3D-Secure + processing + 0 + + + +
    diff --git a/app/code/core/Mage/Eway/etc/system.xml b/app/code/core/Mage/Eway/etc/system.xml new file mode 100644 index 0000000000..70600f34cb --- /dev/null +++ b/app/code/core/Mage/Eway/etc/system.xml @@ -0,0 +1,354 @@ + + + + + + + + + text + 201 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>2</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + obscure + adminhtml/system_config_backend_encrypted + 3 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_currency + 4 + 1 + 1 + 0 + + + + Please set Beagle Anti-Fraud Gateway URL + select + adminhtml/system_config_source_yesno + 4 + 1 + 1 + 0 + + + + text + 5 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 6 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 7 + 1 + 1 + 0 + + + + multiselect + eway/source_cctype + 8 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 9 + 1 + 1 + 0 + + + + allowspecific + 10 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 11 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 12 + 1 + 1 + 0 + + + + + + text + 202 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>2</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + obscure + adminhtml/system_config_backend_encrypted + 3 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_currency + 4 + 1 + 1 + 0 + + + + text + 5 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 6 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 7 + 1 + 1 + 0 + + + + allowspecific + 8 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 9 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 10 + 1 + 1 + 0 + + + + + + text + 203 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>2</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + obscure + adminhtml/system_config_backend_encrypted + 3 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_currency + 4 + 1 + 1 + 0 + + + + text + 5 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 6 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 7 + 1 + 1 + 0 + + + + allowspecific + 8 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 9 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 10 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Eway/sql/eway_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Eway/sql/eway_setup/mysql4-install-0.1.0.php new file mode 100644 index 0000000000..6f9d3f2240 --- /dev/null +++ b/app/code/core/Mage/Eway/sql/eway_setup/mysql4-install-0.1.0.php @@ -0,0 +1,46 @@ +startSetup(); + +$installer->run(" + +-- DROP TABLE IF EXISTS `{$this->getTable('eway/api_debug')}`; +CREATE TABLE `{$this->getTable('eway/api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Flo2Cash/Block/Form.php b/app/code/core/Mage/Flo2Cash/Block/Form.php new file mode 100644 index 0000000000..2e5fe06ba1 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Block/Form.php @@ -0,0 +1,41 @@ + + */ +class Mage_Flo2Cash_Block_Form extends Mage_Payment_Block_Form_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('flo2cash/form.phtml'); + } +} diff --git a/app/code/core/Mage/Flo2Cash/Block/Info.php b/app/code/core/Mage/Flo2Cash/Block/Info.php new file mode 100644 index 0000000000..bc5690b730 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Block/Info.php @@ -0,0 +1,48 @@ + + */ +class Mage_Flo2Cash_Block_Info extends Mage_Payment_Block_Info_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('flo2cash/info.phtml'); + } + + public function toPdf() + { + $this->setTemplate('flo2cash/pdf/info.phtml'); + return $this->toHtml(); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/Helper/Data.php b/app/code/core/Mage/Flo2Cash/Helper/Data.php new file mode 100644 index 0000000000..106473c46e --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Helper/Data.php @@ -0,0 +1,37 @@ + + */ +class Mage_Flo2Cash_Helper_Data extends Mage_Core_Helper_Abstract +{ + +} diff --git a/app/code/core/Mage/Flo2Cash/Model/Api/Debug.php b/app/code/core/Mage/Flo2Cash/Model/Api/Debug.php new file mode 100644 index 0000000000..0f9c6ee75c --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Flo2Cash_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('flo2cash/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Flo2Cash/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..43103febcb --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Mysql4/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Flo2Cash_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('flo2cash/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Flo2Cash/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..be441e9b33 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,40 @@ + + */ +class Mage_Flo2Cash_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('flo2cash/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/Model/Mysql4/Setup.php b/app/code/core/Mage/Flo2Cash/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..92d9d9d10e --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Mysql4/Setup.php @@ -0,0 +1,37 @@ + + */ +class Mage_Flo2Cash_Model_Mysql4_Setup extends Mage_Sales_Model_Mysql4_Setup +{ + +} diff --git a/app/code/core/Mage/Flo2Cash/Model/Source/Cctype.php b/app/code/core/Mage/Flo2Cash/Model/Source/Cctype.php new file mode 100644 index 0000000000..ea8dcb8ecc --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Source/Cctype.php @@ -0,0 +1,40 @@ + + */ +class Mage_Flo2Cash_Model_Source_Cctype extends Mage_Payment_Model_Source_Cctype +{ + public function getAllowedTypes() + { + return array('VI', 'MC', 'AE', 'DICL'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/Model/Source/PaymentAction.php b/app/code/core/Mage/Flo2Cash/Model/Source/PaymentAction.php new file mode 100644 index 0000000000..22b06c58cf --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Source/PaymentAction.php @@ -0,0 +1,49 @@ + + */ +class Mage_Flo2Cash_Model_Source_PaymentAction +{ + public function toOptionArray() + { + return array( + array( + 'value' => Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE, + 'label' => Mage::helper('flo2cash')->__('Authorize') + ), + array( + 'value' => Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE_CAPTURE, + 'label' => Mage::helper('flo2cash')->__('Purchase') + ), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/Model/Web.php b/app/code/core/Mage/Flo2Cash/Model/Web.php new file mode 100644 index 0000000000..489c71e02f --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/Model/Web.php @@ -0,0 +1,298 @@ + + */ +class Mage_Flo2Cash_Model_Web extends Mage_Payment_Model_Method_Cc +{ + const WSDL_URL_DEMO = 'http://demo.flo2cash.co.nz/ws/paynzws.asmx?wsdl'; + const WSDL_URL_LIVE = 'https://secure.flo2cash.co.nz/ws/paynzws.asmx?wsdl'; + + protected $_code = 'flo2cash_web'; + + protected $_allowCurrencyCode = array('NZD'); + + /** + * Availability options + */ + protected $_isGateway = true; + protected $_canAuthorize = true; + protected $_canCapture = true; + protected $_canCapturePartial = true; + protected $_canRefund = true; + protected $_canVoid = false; + protected $_canUseInternal = true; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = true; + protected $_canSaveCc = false; + + protected $_ccTypesConvert = array( + 'VI' => 'VISA', + 'MC' => 'MC', + 'DICL' => 'DINERS', + 'AE' => 'AMEX' + ); + + const TRANSACTION_TYPE_PURCHASE = 1; + const TRANSACTION_TYPE_REFUND = 2; + const TRANSACTION_TYPE_AUTHORISE = 3; + const TRANSACTION_TYPE_CAPTURE = 4; + + const TRANSACTION_STATUS_ACCEPTED = 1; + const TRANSACTION_STATUS_DECLINED = 2; + + protected $_formBlockType = 'flo2cash/form'; + protected $_infoBlockType = 'flo2cash/info'; + + /** + * Get Account Id for selected payment action + * + * @return string + */ + public function getAccountId() + { + if ($this->getConfigData('payment_action') == self::ACTION_AUTHORIZE_CAPTURE) { + $acountId = $this->getConfigData('payzn_purchase_account_id'); + } else { + $acountId = $this->getConfigData('payzn_account_id'); + } + return $acountId; + } + + /** + * validate the currency code is avaialable to use for Flo2Cash Basic or not + * + * @return bool + */ + public function validate() + { + parent::validate(); + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $currency_code = $paymentInfo->getOrder()->getBaseCurrencyCode(); + } else { + $currency_code = $paymentInfo->getQuote()->getBaseCurrencyCode(); + } + if (!in_array($currency_code, $this->_allowCurrencyCode)) { + Mage::throwException(Mage::helper('flo2cash')->__('Selected currency code (%s) is not compatible with Flo2Cash', $currency_code)); + } + return $this; + } + + public function authorize(Varien_Object $payment, $amount) + { + $txnDetails = $this->_prepareTxnDetails($payment, $amount); + + $response = $this->_sendRequest($txnDetails); + + if ($response['txn_status'] == self::TRANSACTION_STATUS_DECLINED) { + Mage::throwException(Mage::helper('flo2cash')->__('Payment transaction has been declined.')); + } + + $payment->setStatus(self::STATUS_APPROVED); + $payment->setCcTransId($response['transaction_id']); + $payment->setFlo2cashAccountId($response['paynz_account_id']); + + return $this; + } + + public function capture(Varien_Object $payment, $amount) + { + $txnDetails = $this->_prepareTxnDetails($payment, $amount); + + $response = $this->_sendRequest($txnDetails); + + if ($response['txn_status'] == self::TRANSACTION_STATUS_DECLINED) { + Mage::throwException(Mage::helper('flo2cash')->__('Payment transaction has been declined.')); + } + + $payment->setStatus(self::STATUS_APPROVED); + $payment->setLastTransId($response['transaction_id']); + $payment->setFlo2cashAccountId($response['paynz_account_id']); + + return $this; + } + + public function void(Varien_Object $payment) + { + $payment->setStatus(self::STATUS_SUCCESS ); + return $this; + } + + public function refund(Varien_Object $payment, $amount) + { + if ($payment->getRefundTransactionId() && $amount>0) { + + $transId = $payment->getCcTransId(); + //if transaction type was purchase (authorize & capture) + if (is_null($transId)) { + $transId = $payment->getLastTransId(); + } + + $txnDetails = array( + 'txn_type' => self::TRANSACTION_TYPE_REFUND, + 'refund_transaction_id' => $transId, + 'paynz_account_id' => $payment->getFlo2cashAccountId(), + 'amount' => sprintf('%.2f', $amount) + ); + } else { + Mage::throwException(Mage::helper('flo2cash')->__('Error in refunding the payment.')); + } + + $response = $this->_sendRequest($txnDetails); + + if ($response['txn_status'] == self::TRANSACTION_STATUS_DECLINED) { + Mage::throwException(Mage::helper('flo2cash')->__('Payment transaction has been declined.')); + } + + $payment->setLastTransId($response['transaction_id']); + + return $this; + } + + /** + * Sending SOAP request to gateway + * + * @param array $txnDetails + * @return void + */ + protected function _sendRequest($txnDetails) + { + if ($this->getConfigData('demo_mode')) { + $url = self::WSDL_URL_DEMO; + } else { + $url = self::WSDL_URL_LIVE; + } + + $client = new SoapClient($url); + + $parameters = array( + 'username' => $this->getConfigData('username'), + 'password' => $this->getConfigData('password'), + 'txn_details' => $txnDetails + ); + + try { + $response = $client->ProcessPayment($parameters); + + if ($this->getConfigData('debug_flag')) { + $debug = Mage::getModel('flo2cash/api_debug') + ->setRequestBody(print_r($parameters, true)) + ->setResponseBody(print_r($response, true)) + ->save(); + } + return (array)$response->ProcessPaymentResult; + } catch (SoapFault $e) { + if ($this->getConfigData('debug_flag')) { + $debug = Mage::getModel('flo2cash/api_debug') + ->setRequestBody(print_r($parameters, true)) + ->setException($e->getMessage()) + ->save(); + } + + if (strpos($e->getMessage(), ' ---> ') !== FALSE) { + list($title, $error) = explode(' ---> ', $e->getMessage()); + } else { + $error = $e->getMessage(); + } + + Mage::throwException(Mage::helper('flo2cash')->__('Gateway returned an error message: %s', $error)); + } + } + + /** + * Preapare basic paramters for transaction + * + * @param Varien_Object $payment + * @param decimal $amount + * @return array + */ + protected function _prepareTxnDetails(Varien_Object $payment, $amount) + { + if ($payment->getCcTransId()) { + $txnDetails = array( + 'txn_type' => self::TRANSACTION_TYPE_CAPTURE, + 'capture_transaction_id' => $payment->getCcTransId() + ); + } else { + $billingAddress = $payment->getOrder()->getBillingAddress(); + + if ($payment->getOrder()->getCustomerEmail()) { + $customerEmail = $payment->getOrder()->getCustomerEmail(); + } elseif ($billingAddress->getEmail()) { + $customerEmail = $billingAddress->getEmail(); + } else { + $customerEmail = ''; + } + + $txnDetails = array( + 'card_holder_name' => $payment->getCcOwner(), + 'card_number' => $payment->getCcNumber(), + 'card_type' => $this->_convertCcType($payment->getCcType()), + 'card_expiry' => sprintf('%02d', $payment->getCcExpMonth()).substr($payment->getCcExpYear(), 2, 2), + 'card_csc' => $payment->getCcCid(), + 'customer_email' => $customerEmail + ); + + if ($this->getConfigData('payment_action') == self::ACTION_AUTHORIZE) { + $txnDetails['txn_type'] = self::TRANSACTION_TYPE_AUTHORISE; + } else { + $txnDetails['txn_type'] = self::TRANSACTION_TYPE_PURCHASE; + } + } + + $accountId = $payment->getFlo2cashAccountId(); + //if transaction type is authorize & capture or only authorize + if (is_null($accountId)) { + $accountId = $this->getAccountId(); + } + + $txnDetails = array_merge($txnDetails, array( + //'txn_reference' => $payment->getOrder()->getIncrementId(), + 'merchant_reference' => $payment->getOrder()->getIncrementId(), + 'paynz_account_id' => $accountId, + 'amount' => sprintf('%.2f', $amount), + )); + + return $txnDetails; + } + + /** + * Converst CC Types Code from Magento to Flo2Cash + * + * @param string $magentoCcType + * @return string + */ + protected function _convertCcType($magentoCcType = 'VI') + { + return $this->_ccTypesConvert[$magentoCcType]; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/etc/config.xml b/app/code/core/Mage/Flo2Cash/etc/config.xml new file mode 100644 index 0000000000..ca43c21b55 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/etc/config.xml @@ -0,0 +1,134 @@ + + + + + + 0.1.1 + + + + + + Mage_Flo2Cash_Model + flo2cash_mysql4 + + + Mage_Flo2Cash_Model_Mysql4 + + flo2cash_api_debug
    +
    +
    +
    + + + + Mage_Flo2Cash + Mage_Flo2Cash_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Flo2Cash_Block + + + Mage_Flo2Cash_Helper + + + + + + DICL + Diners Club + 60 + + + + +
    + + + + standard + + Mage_Flo2Cash + flo2cash + + + + + + + + Mage_Flo2Cash.csv + + + + + + + + flo2cash.xml + + + + + + + + + + Mage_Flo2Cash.csv + + + + + + + + + Flo2Cash Web Service + AE,VI,MC,DICL + 0 + flo2cash/web + + + +
    diff --git a/app/code/core/Mage/Flo2Cash/etc/system.xml b/app/code/core/Mage/Flo2Cash/etc/system.xml new file mode 100644 index 0000000000..741e2c0190 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/etc/system.xml @@ -0,0 +1,176 @@ + + + + + + + + + text + 501 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 0 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>10</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + text + 20 + 1 + 1 + 0 + + + + text + 30 + 1 + 1 + 0 + + + + For Authorize or Capture Transactions only + text + 31 + 1 + 1 + 0 + + + + For Purchase Transactions only + text + 32 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 33 + 1 + 1 + 0 + + + + select + flo2cash/source_paymentAction + 40 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 50 + 1 + 1 + 0 + + + + multiselect + flo2cash/source_cctype + 60 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 70 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 75 + 1 + 1 + 0 + + + + allowspecific + 80 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 90 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 100 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Flo2Cash/sql/flo2cash_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Flo2Cash/sql/flo2cash_setup/mysql4-install-0.1.0.php new file mode 100644 index 0000000000..1a5d25c5b8 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/sql/flo2cash_setup/mysql4-install-0.1.0.php @@ -0,0 +1,47 @@ +startSetup(); + +$installer->run(" + +DROP TABLE IF EXISTS `{$this->getTable('flo2cash/api_debug')}`; +CREATE TABLE `{$this->getTable('flo2cash/api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `request_body` text, + `response_body` text, + `exception` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Flo2Cash/sql/flo2cash_setup/mysql4-upgrade-0.1.0-0.1.1.php b/app/code/core/Mage/Flo2Cash/sql/flo2cash_setup/mysql4-upgrade-0.1.0-0.1.1.php new file mode 100644 index 0000000000..ef49333af3 --- /dev/null +++ b/app/code/core/Mage/Flo2Cash/sql/flo2cash_setup/mysql4-upgrade-0.1.0-0.1.1.php @@ -0,0 +1,32 @@ +startSetup(); + +$installer->addAttribute('order_payment', 'flo2cash_account_id', array()); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Giftcert/Helper/Data.php b/app/code/core/Mage/Giftcert/Helper/Data.php deleted file mode 100644 index 3dd65199c4..0000000000 --- a/app/code/core/Mage/Giftcert/Helper/Data.php +++ /dev/null @@ -1,33 +0,0 @@ -getGiftcertByCode($address->getGiftcertCode()); - if ($gift) { - $address->setGiftcertAmount(min($address->getGrandTotal(), $gift['balance_amount'])); - } else { - $address->setGiftcertAmount(0); - } - - $address->setGrandTotal($address->getGrandTotal() - $address->getGiftcertAmount()); - - return $this; - } - -} diff --git a/app/code/core/Mage/Giftcert/Model/Entity/Quote/Address/Attribute/Frontend/Giftcert.php b/app/code/core/Mage/Giftcert/Model/Entity/Quote/Address/Attribute/Frontend/Giftcert.php deleted file mode 100644 index 2202a70f72..0000000000 --- a/app/code/core/Mage/Giftcert/Model/Entity/Quote/Address/Attribute/Frontend/Giftcert.php +++ /dev/null @@ -1,39 +0,0 @@ -getGiftcertAmount(); - if ($amount) { - $address->addTotal(array('code'=>'giftcert', 'title'=>Mage::helper('giftcert')->__('Gift Certificate').' ('.$address->getGiftcertCode().')', 'value'=>-$amount, 'output'=>true)); - } - return $this; - } -} diff --git a/app/code/core/Mage/Giftcert/Model/Mysql4/Giftcert.php b/app/code/core/Mage/Giftcert/Model/Mysql4/Giftcert.php deleted file mode 100644 index 57431357f8..0000000000 --- a/app/code/core/Mage/Giftcert/Model/Mysql4/Giftcert.php +++ /dev/null @@ -1,45 +0,0 @@ -_read = Mage::getSingleton('core/resource')->getConnection('sales_read'); - $this->_write = Mage::getSingleton('core/resource')->getConnection('sales_write'); - } - - public function getGiftcertByCode($giftCode) - { - $giftTable = Mage::getSingleton('core/resource')->getTableName('sales/giftcert'); - $result = $this->_read->fetchRow("select * from ".$giftTable." where giftcert_code=?", $giftCode); - return $result; - } -} \ No newline at end of file diff --git a/app/code/core/Mage/Giftcert/etc/config.xml b/app/code/core/Mage/Giftcert/etc/config.xml deleted file mode 100644 index 2af7bc11d3..0000000000 --- a/app/code/core/Mage/Giftcert/etc/config.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - 0.7.0 - - - - - - Mage_Giftcert_Model - giftcert_mysql4 - - - Mage_Giftcert_Model_Mysql4 - - - giftcert_code
    -
    -
    -
    -
    - - - - Mage_Giftcert - - - core_setup - - - - - core_write - - - - - core_read - - - -
    - - - - - - - Mage_Giftcert.csv - - - - - - - - - - - - Mage_Giftcert.csv - - - - - -
    \ No newline at end of file diff --git a/app/code/core/Mage/Giftcert/sql/giftcert_setup/mysql4-install-0.7.0.php b/app/code/core/Mage/Giftcert/sql/giftcert_setup/mysql4-install-0.7.0.php deleted file mode 100644 index 008888af89..0000000000 --- a/app/code/core/Mage/Giftcert/sql/giftcert_setup/mysql4-install-0.7.0.php +++ /dev/null @@ -1,51 +0,0 @@ -startSetup(); - -$installer->run(" - --- drop table if exists {$this->getTable('sales_counter')}; --- drop table if exists {$this->getTable('sales_discount_coupon')}; --- DROP TABLE IF EXISTS {$this->getTable('sales_giftcert')}; - --- DROP TABLE IF EXISTS {$this->getTable('giftcert_code')}; -CREATE TABLE {$this->getTable('giftcert_code')} ( - `giftcert_id` int(10) unsigned NOT NULL auto_increment, - `giftcert_code` varchar(50) NOT NULL default '', - `balance_amount` decimal(12,4) NOT NULL default '0.0000', - PRIMARY KEY (`giftcert_id`), - UNIQUE KEY `gift_code` (`giftcert_code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -insert into {$this->getTable('giftcert_code')} (`giftcert_id`,`giftcert_code`,`balance_amount`) values (1,'test',20.0000); - - "); - -$installer->endSetup(); 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 be3c861ff9..501edcfea4 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 @@ -135,6 +135,6 @@ public function _getAttributes($setId, $escapeJsQuotes = false) protected function _toJson($data) { - return Zend_Json::encode($data); + return Mage::helper('core')->jsonEncode($data); } } \ No newline at end of file diff --git a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Abstract.php b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Abstract.php index 04760fb402..f48e322303 100644 --- a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Abstract.php +++ b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Abstract.php @@ -200,4 +200,19 @@ protected function _getCallbackUrl() { return Mage::getUrl('googlecheckout/api', array('_forced_secure'=>Mage::getStoreConfig('google/checkout/use_secure_callback_url', $this->getStoreId()))); } + + /** + * Recalculate amount to store currency + * + * @param float $amount + * @param Mage_Sales_Model_Quote $quote + * @return float + */ + protected function _reCalculateToStoreCurrency($amount, $quote) + { + if ($quote->getQuoteCurrencyCode() != $quote->getBaseCurrencyCode()) { + $amount = $amount * $quote->getStoreToQuoteRate(); + } + return $amount; + } } 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 ab1432e718..042f85d94b 100644 --- a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php +++ b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php @@ -234,8 +234,8 @@ protected function _responseMerchantCalculationCallback() $address->setCollectShippingRates(true)->collectTotals(); $billingAddress->setCollectShippingRates(true)->collectTotals(); - $taxAmount = $address->getTaxAmount(); - $taxAmount += $billingAddress->getTaxAmount(); + $taxAmount = $address->getBaseTaxAmount(); + $taxAmount += $billingAddress->getBaseTaxAmount(); $result->setTaxDetails($taxAmount); } @@ -250,8 +250,8 @@ protected function _responseMerchantCalculationCallback() $address->setCollectShippingRates(true)->collectTotals(); $billingAddress->setCollectShippingRates(true)->collectTotals(); - $taxAmount = $address->getTaxAmount(); - $taxAmount += $billingAddress->getTaxAmount(); + $taxAmount = $address->getBaseTaxAmount(); + $taxAmount += $billingAddress->getBaseTaxAmount(); $result = new GoogleResult($addressId); $result->setTaxDetails($taxAmount); @@ -278,7 +278,7 @@ protected function _responseNewOrderNotification() // IMPORT GOOGLE ORDER DATA INTO QUOTE $quoteId = $this->getData('root/shopping-cart/merchant-private-data/quote-id/VALUE'); - $quote = Mage::getModel('sales/quote')->load($quoteId); + $quote = Mage::getModel('sales/quote')->load($quoteId)->setIsActive(true); // // $quoteItems = $quote->getItemsCollection(); // foreach ($this->getData('root/shopping-cart/items') as $item) { @@ -373,6 +373,8 @@ protected function _responseNewOrderNotification() ->setLastSuccessQuoteId($quote->getId()) ->setLastRealOrderId($order->getIncrementId()); + $quote->setIsActive(false)->save(); + if ($emailAllowed) { Mage::getModel('newsletter/subscriber')->subscribe($order->getCustomerEmail()); } @@ -427,7 +429,11 @@ protected function _importGoogleAddress($gAddress, Varien_Object $qAddress=null) protected function _importGoogleTotals($qAddress) { - $qAddress->setTaxAmount($this->getData('root/order-adjustment/total-tax/VALUE')); + $qAddress->setTaxAmount( + $this->_reCalculateToStoreCurrency( + $this->getData('root/order-adjustment/total-tax/VALUE'), $qAddress->getQuote() + ) + ); $qAddress->setBaseTaxAmount($this->getData('root/order-adjustment/total-tax/VALUE')); $prefix = 'root/order-adjustment/shipping/'; @@ -444,13 +450,18 @@ protected function _importGoogleTotals($qAddress) $excludingTax = $shipping['shipping-cost']['VALUE']; $qAddress->setShippingMethod($method) ->setShippingDescription($shipping['shipping-name']['VALUE']) - ->setShippingAmount($excludingTax, true) + ->setShippingAmount( + $this->_reCalculateToStoreCurrency($excludingTax, $qAddress->getQuote()), + true + ) ->setBaseShippingAmount($excludingTax, true); if (!Mage::helper('tax')->shippingPriceIncludesTax()) { $includingTax = Mage::helper('tax')->getShippingPrice($excludingTax, true, $qAddress, $qAddress->getQuote()->getCustomerTaxClassId()); $shippingTax = $includingTax - $excludingTax; - $qAddress->setShippingTaxAmount($shippingTax) + $qAddress->setShippingTaxAmount( + $this->_reCalculateToStoreCurrency($shippingTax, $qAddress->getQuote()) + ) ->setBaseShippingTaxAmount($shippingTax); } else { if ($method == 'googlecheckout_carrier') { @@ -463,7 +474,11 @@ protected function _importGoogleTotals($qAddress) } - $qAddress->setGrandTotal($this->getData('root/order-total/VALUE')); + $qAddress->setGrandTotal( + $this->_reCalculateToStoreCurrency( + $this->getData('root/order-total/VALUE'), $qAddress->getQuote() + ) + ); $qAddress->setBaseGrandTotal($this->getData('root/order-total/VALUE')); } diff --git a/app/code/core/Mage/GoogleCheckout/controllers/RedirectController.php b/app/code/core/Mage/GoogleCheckout/controllers/RedirectController.php index 2bef1fdc70..8c38ef5a6d 100644 --- a/app/code/core/Mage/GoogleCheckout/controllers/RedirectController.php +++ b/app/code/core/Mage/GoogleCheckout/controllers/RedirectController.php @@ -31,7 +31,6 @@ */ class Mage_GoogleCheckout_RedirectController extends Mage_Core_Controller_Front_Action { - /** * Send request to Google Checkout and return Responce Api * @@ -59,7 +58,6 @@ protected function _getApi () $baseCurrency = $session->getQuote()->getBaseCurrencyCode(); $currency = Mage::app()->getStore($session->getQuote()->getStoreId())->getBaseCurrency(); $session->getQuote() - ->setForcedCurrency($currency) ->collectTotals() ->save(); @@ -71,6 +69,8 @@ protected function _getApi () if ($api->getError()) { Mage::getSingleton('checkout/session')->addError($api->getError()); } else { + $oldQuote = $session->getQuote(); + $oldQuote->setIsActive(false)->save(); $session->replaceQuote($storeQuote); Mage::getModel('checkout/cart')->init()->save(); if (Mage::getStoreConfigFlag('google/checkout/hide_cart_contents')) { @@ -134,12 +134,12 @@ public function continueAction() if ($quoteId = $session->getGoogleCheckoutQuoteId()) { $quote = Mage::getModel('sales/quote')->load($quoteId) ->setIsActive(false)->save(); - $session->unsQuoteId(); + $session->clear(); } -// if (Mage::getStoreConfigFlag('google/checkout/hide_cart_contents')) { -// $session->unsGoogleCheckoutQuoteId(); -// } + if (Mage::getStoreConfigFlag('google/checkout/hide_cart_contents')) { + $session->setGoogleCheckoutQuoteId(null); + } $url = Mage::getStoreConfig('google/checkout/continue_shopping_url'); if (empty($url)) { diff --git a/app/code/core/Mage/GoogleOptimizer/controllers/IndexController.php b/app/code/core/Mage/GoogleOptimizer/controllers/IndexController.php index 33ad40df3a..9db3cc4fa0 100644 --- a/app/code/core/Mage/GoogleOptimizer/controllers/IndexController.php +++ b/app/code/core/Mage/GoogleOptimizer/controllers/IndexController.php @@ -51,7 +51,7 @@ public function codesAction() $result[$id] = $code; } } - $this->getResponse()->setBody( Zend_Json::encode($result) ); + $this->getResponse()->setBody( Mage::helper('core')->jsonEncode($result) ); } } } \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Advanced/Failure.php b/app/code/core/Mage/Ideal/Block/Advanced/Failure.php new file mode 100644 index 0000000000..747e30d91e --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Advanced/Failure.php @@ -0,0 +1,55 @@ + + */ + +class Mage_Ideal_Block_Advanced_Failure extends Mage_Core_Block_Template +{ + /** + * Returns error from session and clears it + * + * @return string + */ + public function getErrorMessage () + { + $error = Mage::getSingleton('checkout/session')->getIdealErrorMessage(); + Mage::getSingleton('checkout/session')->unsIdealErrorMessage(); + return $error; + } + + /** + * Get continue shopping url + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Advanced/Form.php b/app/code/core/Mage/Ideal/Block/Advanced/Form.php new file mode 100755 index 0000000000..7c06a376c2 --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Advanced/Form.php @@ -0,0 +1,52 @@ + + */ + +class Mage_Ideal_Block_Advanced_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('ideal/advanced/form.phtml'); + parent::_construct(); + } + + /** + * Return array that contains issuer list + * + * @return array + */ + public function getIssuerList() + { + return $this->getMethod()->getIssuerList(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Advanced/Info.php b/app/code/core/Mage/Ideal/Block/Advanced/Info.php new file mode 100644 index 0000000000..7124e37bdf --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Advanced/Info.php @@ -0,0 +1,63 @@ + + */ +class Mage_Ideal_Block_Advanced_Info extends Mage_Payment_Block_Info +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('ideal/advanced/info.phtml'); + } + + public function toPdf() + { + $this->setTemplate('ideal/advanced/pdf/info.phtml'); + return $this->toHtml(); + } + + /** + * Gets Issuer Title from Payment Attribute + * + * @return string + */ + public function getIssuerTitle() + { + if ($this->getInfo() instanceof Mage_Sales_Model_Quote_Payment) { + $issuerList = unserialize($this->getInfo()->getIdealIssuerList()); + return $issuerList[$this->getInfo()->getIdealIssuerId()]; + } else { + return $this->getInfo()->getIdealIssuerTitle(); + } + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Advanced/Redirect.php b/app/code/core/Mage/Ideal/Block/Advanced/Redirect.php new file mode 100755 index 0000000000..7be67e4f6c --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Advanced/Redirect.php @@ -0,0 +1,45 @@ + + */ + +class Mage_Ideal_Block_Advanced_Redirect extends Mage_Core_Block_Abstract +{ + protected function _toHtml() + { + $html = ''; + $html.= $this->getMessage(); + $html.= ''; + $html.= ''; + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Basic/Failure.php b/app/code/core/Mage/Ideal/Block/Basic/Failure.php new file mode 100644 index 0000000000..4ead0a8d6e --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Basic/Failure.php @@ -0,0 +1,55 @@ + + */ + +class Mage_Ideal_Block_Basic_Failure extends Mage_Core_Block_Template +{ + /** + * Returns error from session and clears it + * + * @return string + */ + public function getErrorMessage () + { + $error = Mage::getSingleton('checkout/session')->getIdealErrorMessage(); + Mage::getSingleton('checkout/session')->unsIdealErrorMessage(); + return $error; + } + + /** + * Get continue shopping url + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Basic/Form.php b/app/code/core/Mage/Ideal/Block/Basic/Form.php new file mode 100755 index 0000000000..6feea1c2c4 --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Basic/Form.php @@ -0,0 +1,42 @@ + + */ + +class Mage_Ideal_Block_Basic_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('ideal/basic/form.phtml'); + parent::_construct(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Block/Basic/Redirect.php b/app/code/core/Mage/Ideal/Block/Basic/Redirect.php new file mode 100755 index 0000000000..157ef3bc8d --- /dev/null +++ b/app/code/core/Mage/Ideal/Block/Basic/Redirect.php @@ -0,0 +1,59 @@ + + */ + +class Mage_Ideal_Block_Basic_Redirect extends Mage_Core_Block_Abstract +{ + protected function _toHtml() + { + $basic = $this->getOrder()->getPayment()->getMethodInstance(); + + $form = new Varien_Data_Form(); + $form->setAction($basic->getApiUrl()) + ->setId('ideal_basic_checkout') + ->setName('ideal_basic_checkout') + ->setMethod('POST') + ->setUseContainer(true); + + foreach ($basic->getBasicCheckoutFormFields() as $field=>$value) { + $form->addField($field, 'hidden', array('name'=>$field, 'value'=>$value)); + } + + $html = ''; + $html.= $this->__('You will be redirected to iDEAL in a few seconds.'); + $html.= $form->toHtml(); + $html.= ''; + $html.= ''; + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Helper/Data.php b/app/code/core/Mage/Ideal/Helper/Data.php new file mode 100755 index 0000000000..9a01743310 --- /dev/null +++ b/app/code/core/Mage/Ideal/Helper/Data.php @@ -0,0 +1,46 @@ + + */ + +class Mage_Ideal_Helper_Data extends Mage_Core_Helper_Abstract +{ + public function encrypt($token) + { + return bin2hex(base64_decode(Mage::helper('core')->encrypt($token))); + } + + public function decrypt($token) + { + return Mage::helper('core')->decrypt(base64_encode(pack('H*', $token))); + } +} diff --git a/app/code/core/Mage/Ideal/Model/Advanced.php b/app/code/core/Mage/Ideal/Model/Advanced.php new file mode 100755 index 0000000000..79b92c4e69 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Advanced.php @@ -0,0 +1,227 @@ + + */ + +class Mage_Ideal_Model_Advanced extends Mage_Payment_Model_Method_Abstract +{ + protected $_code = 'ideal_advanced'; + protected $_formBlockType = 'ideal/advanced_form'; + protected $_infoBlockType = 'ideal/advanced_info'; + protected $_allowCurrencyCode = array('EUR', 'GBP', 'USD', 'CAD', 'SHR', 'NOK', 'SEK', 'DKK'); + + protected $_isGateway = false; + protected $_canAuthorize = false; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + protected $_issuersList = null; + + public function canUseCheckout() + { + if ($this->getIssuerList() && parent::canUseCheckout()) { + return true; + } else { + return false; + } + } + + public function getOrderPlaceRedirectUrl() + { + return Mage::getUrl('ideal/advanced/redirect', array('_secure' => true)); + } + + /** + * Get iDEAL API Model + * + * @return Mage_Ideal_Api_Advanced + */ + public function getApi() + { + return Mage::getSingleton('ideal/api_advanced'); + } + + public function getIssuerList($saveAttrbute = false) + { + if ($this->_issuersList == null) { + $request = new Mage_Ideal_Model_Api_Advanced_DirectoryRequest(); + $response = $this->getApi()->processRequest($request, $this->getDebug()); + if ($response) { + $this->_issuersList = $response->getIssuerList(); + return $this->_issuersList; + } else { + $this->_issuersList = null; + $this->setError($this->getApi()->getError()); + return false; + } + } else { + $this->getInfoInstance() + ->setIdealIssuerList(serialize($this->_issuersList)) + ->save(); + return $this->_issuersList; + } + } + + /** + * validate the currency code is avaialable to use for iDEAL Advanced or not + * + * @return bool + */ + public function validate() + { + parent::validate(); + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $currency_code = $paymentInfo->getOrder()->getBaseCurrencyCode(); + } else { + $currency_code = $paymentInfo->getQuote()->getBaseCurrencyCode(); + } + + if (!in_array($currency_code,$this->_allowCurrencyCode)) { + Mage::throwException(Mage::helper('ideal')->__('Selected currency code (%s) is not compatible with iDEAL', $currency_code)); + } + + return $this; + } + + /** + * Preapre and send transaction request + * + * @param Mage_Sales_Model_Order $order + * @param string $issuerId + * @return Mage_Ideal_Model_Api_Advanced_AcquirerTrxResponse + */ + public function sendTransactionRequest(Mage_Sales_Model_Order $order, $issuerId) + { + $request = new Mage_Ideal_Model_Api_Advanced_AcquirerTrxRequest(); + $request->setIssuerId($issuerId); + $request->setPurchaseId($order->getIncrementId()); + $request->setEntranceCode(Mage::helper('ideal')->encrypt($order->getIncrementId())); + //we need to be sure that we sending number without decimal part + $request->setAmount(floor($order->getBaseGrandTotal()*100)); + $response = $this->getApi()->processRequest($request, $this->getDebug()); + return $response; + } + + /** + * Prepare and send transaction status request + * + * @param string $transactionId + * @return Mage_Ideal_Model_Api_Advanced_AcquirerStatusResponse + */ + public function getTransactionStatus($transactionId) + { + $request = new Mage_Ideal_Model_Api_Advanced_AcquirerStatusRequest(); + $request->setTransactionId($transactionId); + $response = $this->getApi()->processRequest($request, $this->getDebug()); + return $response; + } + + public function capture(Varien_Object $payment, $amount) + { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + + return $this; + } + + public function cancel(Varien_Object $payment) + { + $payment->setStatus(self::STATUS_DECLINED); + + return $this; + } + + /** + * Executes by cron and check transactions status + * for every iDEAL order that was created in last hour + */ + public function transactionStatusCheck($shedule = null) + { + $gmtStamp = Mage::getModel('core/date')->gmtTimestamp(); + $to = $this->getConfigData('cron_start') > 0?$this->getConfigData('cron_start'):1; + $to = date('Y-m-d H:i:s', $gmtStamp - $to * 3600); + + $from = $this->getConfigData('cron_end') > 0?$this->getConfigData('cron_end'):1; + $from = date('Y-m-d H:i:s', $gmtStamp - $from * 86400); + + $paymentCollection = Mage::getModel('sales/order_payment')->getCollection() + ->addAttributeToFilter('last_trans_id', array('neq' => '')) + ->addAttributeToFilter('method', $this->_code) + ->addAttributeToFilter('created_at', array('from' => $from, 'to' => $to, 'datetime' => true)) + ->addAttributeToFilter('ideal_transaction_checked', array('neq' => '1')); + + $order = Mage::getModel('sales/order'); + foreach($paymentCollection->getItems() as $item) { + $order->reset(); + $order->load($item->getParentId()); + $response = $this->getTransactionStatus($item->getLastTransId()); + + if ($response->getTransactionStatus() == Mage_Ideal_Model_Api_Advanced::STATUS_SUCCESS) { + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + + $order->addStatusToHistory($order->getStatus(), Mage::helper('ideal')->__('Transaction Status Update: finished successfully')); + } + } else if ($response->getTransactionStatus() == Mage_Ideal_Model_Api_Advanced::STATUS_CANCELLED) { + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), Mage::helper('ideal')->__('Transaction Status Update: cancelled by customer')); + } else { + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), Mage::helper('ideal')->__('Transaction Status Update: rejected by iDEAL')); + } + + $order->getPayment()->setIdealTransactionChecked(1); + $order->save(); + } + } + + /** + * Get debug flag + * + * @return boolean + */ + public function getDebug() + { + return $this->getConfigData('debug_flag'); + } +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced.php b/app/code/core/Mage/Ideal/Model/Api/Advanced.php new file mode 100755 index 0000000000..4f6721085f --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced.php @@ -0,0 +1,507 @@ + + */ + +class Mage_Ideal_Model_Api_Advanced extends Varien_Object +{ + /** + * Transaction status returned in Status Request + */ + const STATUS_OPEN = 'Open'; + const STATUS_EXPIRED = 'Expired'; + const STATUS_SUCCESS = 'Success'; + const STATUS_CANCELLED = 'Cancelled'; + const STATUS_FAILED = 'Failed'; + + /** + * @var Mage_Ideal_Model_Api_Advanced_Security + */ + protected $_security; + + /** + * array with configuration values + * + * @var array() + */ + protected $_conf; + + /** + * @var Varien_Http_Adapter_Curl + */ + protected $_http; + + public function __construct() + { + $this->_http = new Varien_Http_Adapter_Curl(); + $this->_security = new Mage_Ideal_Model_Api_Advanced_Security(); + + if ($this->getConfigData('test_flag') == 1) { + $acquirerUrl = 'https://idealtest.secure-ing.com/ideal/iDeal'; + } else { + $acquirerUrl = 'https://ideal.secure-ing.com/ideal/iDeal'; + } + + if (!($description = $this->getConfigData('description'))) { + $description = Mage::app()->getStore()->getName() . ' payment'; + } + + $this->_conf = array( + 'PRIVATEKEY' => $this->getConfigData('private_key'), + 'PRIVATEKEYPASS' => $this->getConfigData('private_keypass'), + 'PRIVATECERT' => $this->getConfigData('private_cert'), + 'AUTHENTICATIONTYPE' => 'SHA1_RSA', + 'CERTIFICATE0' => $this->getConfigData('certificate'), + 'ACQUIRERURL' => $acquirerUrl, + 'ACQUIRERTIMEOUT' => '10', + 'MERCHANTID' => $this->getConfigData('merchant_id'), + 'SUBID' => '0', + 'MERCHANTRETURNURL' => Mage::getUrl('ideal/advanced/result', array('_secure' => true)), + 'CURRENCY' => 'EUR', + 'EXPIRATIONPERIOD' => 'PT10M', + 'LANGUAGE' => 'nl', + 'DESCRIPTION' => $description, + 'ENTRANCECODE' => '' + ); + + if ((int)$this->getConfigData('expire_period') >= 1 && $this->getConfigData('expire_period') < 60) { + $this->_conf['EXPIRATIONPERIOD'] = 'PT' . $this->getConfigData('expire_period') . 'M'; + } else if ($this->getConfigData('description') == 60) { + $this->_conf['EXPIRATIONPERIOD'] = 'PT1H'; + } + } + + /** + * Getting config parametrs + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getConfigData($key, $default=false) + { + if (!$this->hasData($key)) { + $value = Mage::getStoreConfig('payment/ideal_advanced/'.$key); + if (is_null($value) || false===$value) { + $value = $default; + } + $this->setData($key, $value); + } + return $this->getData($key); + } + + /** + * this method processes a request regardless of the type. + */ + + public function processRequest($requestType, $debug = false) + { + if($requestType instanceof Mage_Ideal_Model_Api_Advanced_DirectoryRequest) { + $response = $this->processDirRequest($requestType); + } else if($requestType instanceof Mage_Ideal_Model_Api_Advanced_AcquirerStatusRequest) { + $response = $this->processStatusRequest($requestType); + } else if($requestType instanceof Mage_Ideal_Model_Api_Advanced_AcquirerTrxRequest) { + $response = $this->processTrxRequest($requestType); + } + + if ($debug) { + + if ($response === false) { + $responseData = $this->getError(); + } else { + $responseData = $response->getData(); + } + + Mage::getModel('ideal/api_debug') + ->setResponseBody(get_class($requestType) . "\n" . print_r($responseData, true)) + ->setRequestBody(get_class($requestType) . "\n" . print_r($requestType->getData(), true)) + ->save(); + } + + return $response; + } + + /** + * This method sends HTTP XML DirectoryRequest to the Acquirer system. + * Befor calling, all mandatory properties have to be set in the Request object + * by calling the associated setter methods. + * If the request was successful, the response Object is returned. + * @param Request Object filled with necessary data for the XML Request + * @return Response Object with the data of the XML response. + */ + public function processDirRequest($request) + { + if ($request->getMerchantId() == "") { + $request->setMerchantId($this->_conf["MERCHANTID"]); + } + + if ($request->getSubId() == "") { + $request->setSubId($this->_conf["SUBID"]); + } + + if ($request->getAuthentication() == "") { + $request->setAuthentication($this->_conf["AUTHENTICATIONTYPE"]); + } + + $res = new Mage_Ideal_Model_Api_Advanced_DirectoryResponse(); + + if (!$request->checkMandatory()) { + $res->setError(Mage::helper('ideal')->__('Required fields missing')); + return $res; + } + + // build concatenated string + $timestamp = $this->getGMTTimeMark(); + $token = ""; + $tokenCode = ""; + + if ("SHA1_RSA" == $request->getAuthentication()) { + $message = $timestamp . $request->getMerchantId() . $request->getSubId(); + $message = $this->_strip($message); + + //build fingerprint of your own certificate + $token = $this->_security->createCertFingerprint($this->_conf["PRIVATECERT"]); + + //sign the part of the message that need to be signed + $tokenCode = $this->_security->signMessage($this->_conf["PRIVATEKEY"], $this->_conf["PRIVATEKEYPASS"], $message); + + //encode with base64 + $tokenCode = base64_encode($tokenCode); + } + + $reqMsg = "\n" + . "\n" + . "" . utf8_encode( $timestamp ) . "\n" + . "\n" + . "" . utf8_encode( htmlspecialchars( $request->getMerchantId() ) ) . "\n" + . "" . utf8_encode( $request->getSubId() ) . "\n" + . "" . utf8_encode( $request->getAuthentication() ) . "\n" + . "" . utf8_encode( $token ) . "\n" + . "" . utf8_encode( $tokenCode ) . "\n" + . "\n" + . ""; + + $response = $this->_post($this->_conf["ACQUIRERURL"], $this->_conf["ACQUIRERTIMEOUT"], $reqMsg); + + if ($response === false) { + return false; + } + + $xml = new SimpleXMLElement($response); + + if(!$xml->Error) { + $res->setOk(true); + $res->setAcquirer($xml->Acquirer->acquirerID); + $issuerArray = array(); + foreach ($xml->Directory->Issuer as $issuer) { + $issuerArray[(string)$issuer->issuerID] = (string)$issuer->issuerName; + } + $res->setIssuerList($issuerArray); + return $res; + } else { + $this->setError($xml->Error->consumerMessage); + return false; + } + } + + /** + * This method sends HTTP XML AcquirerTrxRequest to the Acquirer system. + * Befor calling, all mandatory properties have to be set in the Request object + * by calling the associated setter methods. + * If the request was successful, the response Object is returned. + * @param Request Object filled with necessary data for the XML Request + * @return Response Object with the data of the XML response. + */ + public function processTrxRequest($request) { + + if ($request->getMerchantId() == "") + $request->setMerchantId($this->_conf["MERCHANTID"]); + if ($request->getSubId() == "") + $request->setSubId($this->_conf["SUBID"]); + if ($request->getAuthentication() == "") + $request->setAuthentication($this->_conf["AUTHENTICATIONTYPE"]); + if ($request->getMerchantReturnUrl() == "") + $request->setMerchantReturnUrl($this->_conf["MERCHANTRETURNURL"]); + if ($request->getCurrency() == "") + $request->setCurrency($this->_conf["CURRENCY"]); + if ($request->getExpirationPeriod() == "") + $request->setExpirationPeriod($this->_conf["EXPIRATIONPERIOD"]); + if ($request->getLanguage() == "") + $request->setLanguage($this->_conf["LANGUAGE"]); + if ($request->getEntranceCode() == "") + $request->setEntranceCode($this->_conf["ENTRANCECODE"]); + if ($request->getDescription() == "") + $request->setDescription($this->_conf["DESCRIPTION"]); + + $res = new Mage_Ideal_Model_Api_Advanced_AcquirerTrxResponse(); + + if (!$request->checkMandatory()) { + $res->setError(Mage::helper('ideal')->__('Required fields missing')); + return $res; + } + + // build concatenated string + $timestamp = $this->getGMTTimeMark(); + $token = ""; + $tokenCode = ""; + if ( "SHA1_RSA" == $request->getAuthentication() ) { + $message = $timestamp + . $request->getIssuerId() + . $request->getMerchantId() + . $request->getSubId() + . $request->getMerchantReturnUrl() + . $request->getPurchaseId() + . $request->getAmount() + . $request->getCurrency() + . $request->getLanguage() + . $request->getDescription() + . $request->getEntranceCode(); + $message = $this->_strip($message); + + //create fingerprint so the receiver knows what certificate to use + $token = $this->_security->createCertFingerprint($this->_conf["PRIVATECERT"]); + + //sign the message + $tokenCode = $this->_security->signMessage($this->_conf["PRIVATEKEY"], $this->_conf["PRIVATEKEYPASS"], $message); + //encode it with base64 + $tokenCode = base64_encode($tokenCode); + } + + $reqMsg = "\n" + . "\n" + . "" . utf8_encode($timestamp) . "\n" + . "" . "" . utf8_encode(htmlspecialchars($request->getIssuerId())) . "\n" + . "\n" + . "" . "" . utf8_encode(htmlspecialchars($request->getMerchantId())) . "\n" + . "" . utf8_encode($request->getSubId()) . "\n" + . "" . utf8_encode($request->getAuthentication()) . "\n" + . "" . utf8_encode($token) . "\n" + . "" . utf8_encode($tokenCode) . "\n" + . "" . utf8_encode(htmlspecialchars($request->getMerchantReturnUrl())) . "\n" + . "\n" + . "" . "" . utf8_encode(htmlspecialchars($request->getPurchaseId())) . "\n" + . "" . utf8_encode($request->getAmount()) . "\n" + . "" . utf8_encode($request->getCurrency()) . "\n" + . "" . utf8_encode($request->getExpirationPeriod()) . "\n" + . "" . utf8_encode($request->getLanguage()) . "\n" + . "" . utf8_encode(htmlspecialchars($request->getDescription())) . "\n" + . "" . utf8_encode(htmlspecialchars($request->getEntranceCode())) . "\n" + . "" . ""; + + $response = $this->_post($this->_conf["ACQUIRERURL"], $this->_conf["ACQUIRERTIMEOUT"], $reqMsg); + + if ($response === false) { + return false; + } + + $xml = new SimpleXMLElement($response); + + if(!$xml->Error) { + $issuerUrl = (string)$xml->Issuer->issuerAuthenticationURL; + $transactionId = (string)$xml->Transaction->transactionID; + $res->setIssuerAuthenticationUrl($issuerUrl); + $res->setTransactionId($transactionId); + $res->setOk(true); + return $res; + } else { + $this->setError($xml->Error->consumerMessage); + return false; + } + } + + /** + * This method sends HTTP XML AcquirerStatusRequest to the Acquirer system. + * Befor calling, all mandatory properties have to be set in the Request object + * by calling the associated setter methods. + * If the request was successful, the response Object is returned. + * @param Request Object filled with necessary data for the XML Request + * @return Response Object with the data of the XML response. + */ + public function processStatusRequest($request) + { + if ($request->getMerchantId() == "") + $request->setMerchantId($this->_conf["MERCHANTID"]); + if ($request->getSubId() == "") + $request->setSubId($this->_conf["SUBID"]); + if ($request->getAuthentication() == "") + $request->setAuthentication($this->_conf["AUTHENTICATIONTYPE"]); + + $res = new Mage_Ideal_Model_Api_Advanced_AcquirerStatusResponse(); + + if (!$request->checkMandatory()) { + $$request->setErrorMessage(Mage::helper('ideal')->__('Required fields missing')); + return $res; + } + + // build concatenated string + $timestamp = $this->getGMTTimeMark(); + $token = ""; + $tokenCode = ""; + if ("SHA1_RSA" == $request->getAuthentication()) { + $message = $timestamp . $request->getMerchantId() . $request->getSubId() . $request->getTransactionId(); + $message = $this->_strip($message); + + //create fingerprint of your own certificate + $token = $this->_security->createCertFingerprint($this->_conf["PRIVATECERT"]); + //sign the message + $tokenCode = $this->_security->signMessage( $this->_conf["PRIVATEKEY"], $this->_conf["PRIVATEKEYPASS"], $message ); + //encode with base64 + $tokenCode = base64_encode($tokenCode); + } + $reqMsg = "\n" + . "\n" + . "" . utf8_encode($timestamp) . "\n" + . "" . "" . utf8_encode(htmlspecialchars($request->getMerchantId())) . "\n" + . "" . utf8_encode($request->getSubId()) . "\n" + . "" . utf8_encode($request->getAuthentication()) . "\n" + . "" . utf8_encode($token) . "\n" + . "" . utf8_encode($tokenCode) . "\n" + . "\n" + . "" . "" . utf8_encode(htmlspecialchars($request->getTransactionId())) . "\n" + . "" . ""; + + $response = $this->_post($this->_conf["ACQUIRERURL"], $this->_conf["ACQUIRERTIMEOUT"], $reqMsg); + + if ($response === false) { + return false; + } + + //Process response + $xml = new SimpleXMLElement($response); + $status = (string)$xml->Transaction->status; + $creationTime = (string)$xml->createDateTimeStamp; + $transactionId = (string)$xml->Transaction->transactionID; + $consumerAccountNumber = (string)$xml->Transaction->consumerAccountNumber; + $consumerName = (string)$xml->Transaction->consumerName; + $consumerCity = (string)$xml->Transaction->consumerCity; + + //Check status + if ( strtoupper('Success') == strtoupper($status) ) { + $res->setAuthenticated(true); + } else { + $res->setAuthenticated(false); + } + + $res->setTransactionStatus($status); + $res->setTransactionId($transactionId); + $res->setConsumerAccountNumber($consumerAccountNumber); + $res->setConsumerName($consumerName); + $res->setConsumerCity($consumerCity); + $res->setCreationTime($creationTime); + + // now check the signature of the incoming message + //create signed message string + $message = $creationTime . $transactionId . $status . $consumerAccountNumber; + $message = trim( $message ); + + //now we want to check the signature that has been sent + $signature64 = (string)$xml->Signature->signatureValue; + + //decode the base64 encoded signature + $sig = base64_decode($signature64); + + //get the fingerprint out of the response + $fingerprint = (string)$xml->Signature->fingerprint; + + //search for the certificate file with the given fingerprint + $certfile = $this->_security->getCertificateName($fingerprint, $this->_conf); + + if($certfile == false) { + $res->setAuthenticated(false); + $res->setError('Fingerprint unknown.'); + return $res; + } + + //prepend directory + $valid = $this->_security->verifyMessage($certfile, $message, $sig); + + if( $valid != 1 ) { + $res->setAuthenticated(false); + $res->setError('Bad signature.'); + return $res; + } + + $res->setOk(true); + return $res; + } + + /** + * Return GMT Time mark + * + * @return string + */ + public function getGMTTimeMark() + { + return gmdate('Y') . '-' . gmdate('m') . '-' . gmdate('d') . 'T' + . gmdate('H') . ':' . gmdate('i') . ':' . gmdate('s') . '.000Z'; + } + + /** + * Post a request, note that for a HTTPS URL no port is required + * + * @param string $url + * @param string $path + * @param array $params + * @return mixed + */ + protected function _post($url, $timeout, $dataToSend) + { + $config = array('timeout' => 30); + $this->_http->setConfig($config); + $this->_http->write(Zend_Http_Client::POST, $url, '1.1', array(), $dataToSend); + $response = $this->_http->read(); + $response = preg_split('/^\r?$/m', $response, 2); + $response = trim($response[1]); + + if ($this->_http->getErrno()) { + $this->_http->close(); + $this->setError($this->_http->getErrno() . ':' . $this->_http->getError()); + return false; + } + $this->_http->close(); + return $response; + } + + /** + * stripping spaces + * + * @param string $message + * @return string + */ + protected function _strip($message) + { + $message = str_replace(' ', '', $message); + $message = str_replace("\t", '', $message); + $message = str_replace("\n", '', $message ); + return $message; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerStatusRequest.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerStatusRequest.php new file mode 100755 index 0000000000..e3bccc2e2d --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerStatusRequest.php @@ -0,0 +1,61 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_AcquirerStatusRequest extends Mage_Ideal_Model_Api_Advanced_Request +{ + /** + * rests all input data to empty strings + */ + function clear() + { + parent::clear(); + $this->unsTransactionId(); + } + + /** + * this method checks, wheather all mandatory properties were set. + * If done so, true is returned, otherwise false. + * @return If done so, true is returned, otherwise false. + */ + function checkMandatory() + { + if (parent::checkMandatory() && strlen($this->getTransactionId()) > 0) { + return true; + } else { + return false; + } + + } + +} + diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerStatusResponse.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerStatusResponse.php new file mode 100755 index 0000000000..93136bbb8c --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerStatusResponse.php @@ -0,0 +1,44 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_AcquirerStatusResponse extends Mage_Ideal_Model_Api_Advanced_Response +{ + /* available vars + var $authenticated + var $consumerName + var $consumerAccountNumber + var $consumerCity + var $transactionId + var $status + */ +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerTrxRequest.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerTrxRequest.php new file mode 100755 index 0000000000..ea7c0bf707 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerTrxRequest.php @@ -0,0 +1,84 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_AcquirerTrxRequest extends Mage_Ideal_Model_Api_Advanced_Request +{ + /* fields for request xml message + var $issuerId (mandatory) + var $merchantReturnUrl (mandatory) + var $purchaseId (mandatory) + var $amount (mandatory) + var $currency (mandatory) + var $expirationPeriod (mandatory) + var $language (mandatory) + var $description (optional) + var $entranceCode (mandatory) + */ + + function clear() { + parent::clear(); + $this->unsIssuerId(); + $this->unsMerchantReturnUrl(); + $this->unsPurchaseId(); + $this->unsAmount(); + $this->unsCurrency(); + $this->unsExpirationPeriod(); + $this->unsLanguage(); + $this->unsDescription(); + $this->unsEntranceCode(); + } + + /** + * this method checks, whether all mandatory properties were set. + * If done so, true is returned, otherwise false. + * @return If done so, true is returned, otherwise false. + */ + function checkMandatory () { + if ((parent::checkMandatory() == true) + && (strlen($this->getIssuerId()) > 0) + && (strlen($this->getMerchantReturnUrl()) > 0) + && (strlen($this->getPurchaseID()) > 0) + && (strlen($this->getAmount()) > 0) + && (strlen($this->getCurrency()) > 0) + && (strlen($this->getExpirationPeriod()) > 0) + && (strlen($this->getLanguage()) > 0) + && (strlen($this->getEntranceCode()) > 0) + && (strlen($this->getDescription()) > 0) + ) { + return true; + } else { + return false; + } + } + +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerTrxResponse.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerTrxResponse.php new file mode 100755 index 0000000000..18b8c67ff7 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/AcquirerTrxResponse.php @@ -0,0 +1,41 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_AcquirerTrxResponse extends Mage_Ideal_Model_Api_Advanced_Response +{ + /* + var $acquirerId; + var $issuerAuthenticationUrl; + var $transactionId; + */ +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/DirectoryRequest.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/DirectoryRequest.php new file mode 100755 index 0000000000..67a011a8af --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/DirectoryRequest.php @@ -0,0 +1,36 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_DirectoryRequest extends Mage_Ideal_Model_Api_Advanced_Request{ + +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/DirectoryResponse.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/DirectoryResponse.php new file mode 100755 index 0000000000..7b8e025f5e --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/DirectoryResponse.php @@ -0,0 +1,50 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_DirectoryResponse extends Mage_Ideal_Model_Api_Advanced_Response { + /* + var $acquirerId + var $issuerList - array of Issuer Objects + */ + + /** + * adds an Issuer to the IssuerList + */ + function addIssuer($issuer) { + if(is_a($issuer, "Mage_Ideal_Api_Advanced_Issuer")) { + $this->setIssuerList(array_merge((array)$this->getIssuerList(), (array)$issuer)); + } + } +} + +?> diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/Issuer.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/Issuer.php new file mode 100755 index 0000000000..1987085bf4 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/Issuer.php @@ -0,0 +1,41 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_Issuer extends Varien_Object +{ + /* + var $issuerID + var $issuerName + var $issuerList ("Short", "Long") + */ +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/Request.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/Request.php new file mode 100755 index 0000000000..5b5d6eefad --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/Request.php @@ -0,0 +1,80 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_Request extends Varien_Object +{ + /** + * clears all parameters + */ + public function clear() { + $this->unsMerchantId(); + $this->unsSubId(); + $this->unsAuthentication(); + } + + /** + * this method checks, whether all mandatory properties are set. + * @return true if all fields are valid, otherwise returns false + */ + function checkMandatory () { + if (strlen($this->getMerchantId()) > 0 + && strlen($this->getSubID()) > 0 + && strlen($this->getAuthentication()) > 0) { + return true; + } else { + return false; + } + } + + /** + * @param authentication The type of authentication to set. + * Currently only "RSA_SHA1" is implemented. (mandatory) + */ + function setAuthentication($authentication) { + $this->setData('authentication', trim($authentication)); + } + + /** + * @param merchantID The merchantID to set. (mandatory) + */ + function setMerchantId($merchantID) { + $this->setData('merchant_id', trim($merchantID)); + } + + /** + * @param subID The subID to set. (mandatory) + */ + function setSubId($subID) { + $this->setData('sub_id', trim($subID)); + } +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/Response.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/Response.php new file mode 100755 index 0000000000..10e3be507e --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/Response.php @@ -0,0 +1,54 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_Response extends Varien_Object +{ + /* available vars + var $ok + var $errorMessage + var $errorCode + var $errorDetail + var $suggestedAction + var $suggestedExpirationPeriod + var $consumerMessage + */ + + /** + * @return true, if the request was processed successfully, otherwise false. If + * false, additional information can be received calling getErrorMessage() + */ + + function isOk() { + return $this->ok; + } +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Advanced/Security.php b/app/code/core/Mage/Ideal/Model/Api/Advanced/Security.php new file mode 100755 index 0000000000..0793cd2386 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Advanced/Security.php @@ -0,0 +1,155 @@ + + */ +class Mage_Ideal_Model_Api_Advanced_Security +{ + /** + * reads in a certificate file and creates a fingerprint + * @param Filename of the certificate + * @return fingerprint + */ + function createCertFingerprint($filename) + { + if(is_readable($filename)) { + $cert = file_get_contents($filename); + } else { + return false; + } + + $data = openssl_x509_read($cert); + + if(!openssl_x509_export($data, $data)) { + + return false; + } + + $data = str_replace("-----BEGIN CERTIFICATE-----", "", $data); + $data = str_replace("-----END CERTIFICATE-----", "", $data); + + $data = base64_decode($data); + + $fingerprint = sha1($data); + + $fingerprint = strtoupper( $fingerprint ); + + return $fingerprint; + } + + /** + * function to sign a message + * @param filename of the private key + * @param message to sign + * @return signature + */ + function signMessage($priv_keyfile, $key_pass, $data) + { + $data = preg_replace("/\s/","",$data); + if (is_readable($priv_keyfile)) { + $priv_key = file_get_contents($priv_keyfile); + + $params = array($priv_key, $key_pass); + $pkeyid = openssl_pkey_get_private($params); + + // compute signature + openssl_sign($data, $signature, $pkeyid); + + // free the key from memory + openssl_free_key($pkeyid); + + return $signature; + } else { + return false; + } + } + + /** + * function to verify a message + * @param filename of the public key to decrypt the signature + * @param message to verify + * @param sent signature + * @return signature + */ + function verifyMessage($certfile, $data, $signature) + { + // $data and $signature are assumed to contain the data and the signature + $ok = 0; + if (is_readable($certfile)) { + $cert = file_get_contents($certfile); + } else { + return false; + } + + $pubkeyid = openssl_get_publickey($cert); + + // state whether signature is okay or not + $ok = openssl_verify($data, $signature, $pubkeyid); + + // free the key from memory + openssl_free_key($pubkeyid); + + return $ok; + } + + /** + * @param fingerprint that's been sent + * @param the configuration file loaded in as an array + * @return the filename of the certificate with this fingerprint + */ + function getCertificateName($fingerprint, $config) + { + $count = 0; + + if (isset($config["CERTIFICATE" . $count])) { + $certFilename = $config["CERTIFICATE" . $count]; + } else { + return false; + } + + while( isset($certFilename) ) { + $buff = $this->createCertFingerprint($certFilename); + + if( $fingerprint == $buff ) { + return $certFilename; + } + + $count+=1; + if (isset($config["CERTIFICATE" . $count])) { + $certFilename = $config["CERTIFICATE" . $count]; + } else { + return false; + } + } + + return false; + } +} diff --git a/app/code/core/Mage/Ideal/Model/Api/Debug.php b/app/code/core/Mage/Ideal/Model/Api/Debug.php new file mode 100644 index 0000000000..9a48dce295 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Ideal_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('ideal/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Model/Basic.php b/app/code/core/Mage/Ideal/Model/Basic.php new file mode 100755 index 0000000000..7b14ee0436 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Basic.php @@ -0,0 +1,217 @@ + + */ + +class Mage_Ideal_Model_Basic extends Mage_Payment_Model_Method_Abstract +{ + protected $_code = 'ideal_basic'; + protected $_formBlockType = 'ideal/basic_form'; + protected $_allowCurrencyCode = array('EUR', 'GBP', 'USD', 'CAD', 'SHR', 'NOK', 'SEK', 'DKK'); + + protected $_isGateway = false; + protected $_canAuthorize = false; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + /** + * Get debug flag + * + * @return boolean + */ + public function getDebug() + { + return $this->getConfigData('debug_flag'); + } + + /** + * validate the currency code is avaialable to use for iDEAL Basic or not + * + * @return bool + */ + public function validate() + { + parent::validate(); + $paymentInfo = $this->getInfoInstance(); + if ($paymentInfo instanceof Mage_Sales_Model_Order_Payment) { + $currency_code = $paymentInfo->getOrder()->getBaseCurrencyCode(); + } else { + $currency_code = $paymentInfo->getQuote()->getBaseCurrencyCode(); + } + if (!in_array($currency_code, $this->_allowCurrencyCode)) { + Mage::throwException(Mage::helper('ideal')->__('Selected currency code (%s) is not compatible with iDEAL', $currency_code)); + } + return $this; + } + + public function getOrderPlaceRedirectUrl() + { + return Mage::getUrl('ideal/basic/redirect', array('_secure' => true)); + } + + /** + * Return iDEAL Basic Api Url + * + * @return string Payment API URL + */ + public function getApiUrl() + { + if ($this->getConfigData('test_flag') == 1) { + if (($url = trim($this->getConfigData('api_test_url'))) == '') { + $url = "https://idealtest.secure-ing.com/ideal/mpiPayInitIng.do"; + } + } else { + if (($url = trim($this->getConfigData('api_url'))) == '') { + $url = "https://ideal.secure-ing.com/ideal/mpiPayInitIng.do"; + } + } + return $url; + } + + /** + * Generates array of fields for redirect form + * + * @return array + */ + public function getBasicCheckoutFormFields() + { + $order = $this->getInfoInstance()->getOrder(); + + $shippingAddress = $order->getShippingAddress(); + $currency_code = $order->getBaseCurrencyCode(); + + $fields = array( + 'merchantID' => $this->getConfigData('merchant_id'), + 'subID' => '0', + 'amount' => round($order->getBaseGrandTotal()*100), + 'purchaseID' => $order->getIncrementId(), + 'paymentType' => 'ideal', + 'validUntil' => date('Y-m-d\TH:i:s.000\Z', strtotime ('+1 week')) // plus 1 week + ); + + $i = 1; + foreach ($order->getItemsCollection() as $item) { + $fields = array_merge($fields, array( + "itemNumber".$i => $item->getSku(), + "itemDescription".$i => $item->getName(), + "itemQuantity".$i => $item->getQtyOrdered()*1, + "itemPrice".$i => round($item->getBasePrice()*100) + )); + $i++; + } + + if ($order->getBaseShippingAmount() > 0) { + $fields = array_merge($fields, array( + "itemNumber".$i => $order->getShippingMethod(), + "itemDescription".$i => $order->getShippingDescription(), + "itemQuantity".$i => 1, + "itemPrice".$i => round($order->getBaseShippingAmount()*100) + )); + $i++; + } + + if ($order->getBaseTaxAmount() > 0) { + $fields = array_merge($fields, array( + "itemNumber".$i => 'Tax', + "itemDescription".$i => '', + "itemQuantity".$i => 1, + "itemPrice".$i => round($order->getBaseTaxAmount()*100) + )); + $i++; + } + + if ($order->getBaseDiscountAmount() > 0) { + $fields = array_merge($fields, array( + "itemNumber".$i => 'Discount', + "itemDescription".$i => '', + "itemQuantity".$i => 1, + "itemPrice".$i => -round($order->getBaseDiscountAmount()*100) + )); + $i++; + } + + $fields = $this->appendHash($fields); + + $description = $this->getConfigData('description'); + if ($description == '') { + $description = Mage::app()->getStore()->getName() . ' ' . 'payment'; + } + + $fields = array_merge($fields, array( + 'language' => $this->getConfigData('language'), + 'currency' => $currency_code, + 'description' => $description, + 'urlCancel' => Mage::getUrl('ideal/basic/cancel', array('_secure' => true)), + 'urlSuccess' => Mage::getUrl('ideal/basic/success', array('_secure' => true)), + 'urlError' => Mage::getUrl('ideal/basic/failure', array('_secure' => true)) + )); + + $requestString = ''; + $returnArray = array(); + + foreach ($fields as $k=>$v) { + $returnArray[$k] = $v; + $requestString .= '&'.$k.'='.$v; + } + + if ($this->getDebug()) { + Mage::getModel('ideal/api_debug') + ->setRequestBody($this->getApiUrl() . "\n" . $requestString) + ->save(); + } + + return $returnArray; + } + + /** + * Calculates and appends hash to form fields + * + * @param array $returnArray + * @return array + */ + public function appendHash($returnArray) + { + $merchantKey = $this->getConfigData('merchant_key'); + $hashString = $merchantKey.implode('', $returnArray); + $hashString = str_replace( + array(" ", "\t", "\n", "&", "<", ">", ""e;"), + array("", "", "", "&", "<", ">", "\""), + $hashString); + $hash = sha1($hashString); + return array_merge($returnArray, array('hash' => $hash)); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Ideal/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..dfa237ed60 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Mysql4/Api/Debug.php @@ -0,0 +1,42 @@ + + */ + +class Mage_Ideal_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('ideal/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Ideal/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..6ac323dd95 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,41 @@ + + */ +class Mage_Ideal_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('ideal/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/Model/Mysql4/Setup.php b/app/code/core/Mage/Ideal/Model/Mysql4/Setup.php new file mode 100755 index 0000000000..b94323051e --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Mysql4/Setup.php @@ -0,0 +1,37 @@ + + */ +class Mage_Ideal_Model_Observer +{ + /** + * Convert specific attributes from Quote Payment to Order Payment + * + * @param Varien_Object $observer + * @return Mage_Ideal_Model_Observer + */ + public function convertPayment($observer) + { + $orderPayment = $observer->getEvent()->getOrderPayment(); + $quotePayment = $observer->getEvent()->getQuotePayment(); + $orderPayment->setIdealIssuerId($quotePayment->getIdealIssuerId()); + + if ($quotePayment->getIdealIssuerId()) { + $issuerList = unserialize($quotePayment->getIdealIssuerList()); + if (isset($issuerList[$quotePayment->getIdealIssuerId()])) { + $orderPayment->setIdealIssuerTitle( + $issuerList[$quotePayment->getIdealIssuerId()] + ); + } + } + return $this; + } +} diff --git a/app/code/core/Mage/Ideal/Model/Source/Language.php b/app/code/core/Mage/Ideal/Model/Source/Language.php new file mode 100644 index 0000000000..9edab2a070 --- /dev/null +++ b/app/code/core/Mage/Ideal/Model/Source/Language.php @@ -0,0 +1,47 @@ + + */ + +class Mage_Ideal_Model_Source_Language +{ + public function toOptionArray() + { + return array( + array('value' => 'en', 'label' => Mage::helper('ideal')->__('English')), + array('value' => 'nl', 'label' => Mage::helper('ideal')->__('Dutch')) + ); + } +} + + + diff --git a/app/code/core/Mage/Ideal/controllers/AdvancedController.php b/app/code/core/Mage/Ideal/controllers/AdvancedController.php new file mode 100755 index 0000000000..1f52d9b539 --- /dev/null +++ b/app/code/core/Mage/Ideal/controllers/AdvancedController.php @@ -0,0 +1,195 @@ + + */ + +class Mage_Ideal_AdvancedController extends Mage_Core_Controller_Front_Action +{ + public function getCheckout() + { + return Mage::getSingleton('checkout/session'); + } + + /** + * When a customer chooses iDEAL Advanced on Checkout/Payment page + * + */ + public function redirectAction() + { + $order = Mage::getModel('sales/order'); + $order->load($this->getCheckout()->getLastOrderId()); + if($order->getId()){ + $advanced = $order->getPayment()->getMethodInstance(); + $issuerId = $order->getPayment()->getIdealIssuerId(); + + $response = $advanced->sendTransactionRequest($order, $issuerId); + + if ($response) { + $order->getPayment()->setTransactionId($response->getTransactionId()); + $order->getPayment()->setLastTransId($response->getTransactionId()); + $order->getPayment()->setIdealTransactionChecked(0); + + if ($response->getError()) { + $this->getCheckout()->setIdealErrorMessage($response->getError()); + $this->_redirect('*/*/failure'); + return; + } + + $this->getResponse()->setBody( + $this->getLayout()->createBlock('ideal/advanced_redirect') + ->setMessage($this->__('You will be redirected to bank in a few seconds.')) + ->setRedirectUrl($response->getIssuerAuthenticationUrl()) + ->toHtml() + ); + + $order->addStatusToHistory( + $order->getStatus(), + $this->__('Customer was redirected to iDEAL') + ); + $order->save(); + + $this->getCheckout()->setIdealAdvancedQuoteId($this->getCheckout()->getQuoteId(true)); + $this->getCheckout()->setIdealAdvancedOrderId($this->getCheckout()->getLastOrderId(true)); + + + return; + } + } + + $this->getResponse()->setBody( + $this->getLayout()->createBlock('ideal/advanced_redirect') + ->setMessage($this->__('Error occured. You will be redirected back to store.')) + ->setRedirectUrl(Mage::getUrl('checkout/cart')) + ->toHtml() + ); + } + + /** + * When a customer cancels payment from iDEAL + */ + public function cancelAction() + { + $order = Mage::getModel('sales/order'); + $this->getCheckout()->setLastOrderId($this->getCheckout()->getIdealAdvancedOrderId(true)); + $order->load($this->getCheckout()->getLastOrderId()); + + if (!$order->getId()) { + $this->norouteAction(); + return; + } + + $order->addStatusToHistory( + $order->getStatus(), + $this->__('Customer canceled payment.') + ); + $order->cancel(); + $order->save(); + + $$this->getCheckout()->setQuoteId($$this->getCheckout()->setIdealAdvancedQuoteId(true)); + $this->_redirect('checkout/cart'); + } + + /** + * When customer return from iDEAL + */ + public function resultAction() + { + /** + * Decrypt Real Order Id that was sent encrypted + */ + $orderId = Mage::helper('ideal')->decrypt($this->getRequest()->getParam('ec')); + $transactionId = $this->getRequest()->getParam('trxid'); + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($orderId); + + if ($order->getId() > 0) { + $advanced = $order->getPayment()->getMethodInstance(); + $advanced->setTransactionId($transactionId); + $response = $advanced->getTransactionStatus($transactionId); + + $this->getCheckout()->setQuoteId($this->getCheckout()->getIdealAdvancedQuoteId(true)); + $this->getCheckout()->setLastOrderId($this->getCheckout()->getIdealAdvancedOrderId(true)); + + if ($response->getTransactionStatus() == Mage_Ideal_Model_Api_Advanced::STATUS_SUCCESS) { + $this->getCheckout()->getQuote()->setIsActive(false)->save(); + + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + + $order->addStatusToHistory($order->getStatus(), Mage::helper('ideal')->__('Customer successfully returned from iDEAL')); + } + + $order->sendNewOrderEmail(); + + $this->_redirect('checkout/onepage/success'); + } else if ($response->getTransactionStatus() == Mage_Ideal_Model_Api_Advanced::STATUS_CANCELLED) { + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), Mage::helper('ideal')->__('Customer cancelled payment')); + + $this->_redirect('checkout/cart'); + } else { + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), Mage::helper('ideal')->__('Customer was rejected by iDEAL')); + $this->getCheckout()->setIdealErrorMessage( + Mage::helper('ideal')->__('An error occurred while processing your iDEAL transaction. Please contact the web shop or try +again later. Transaction number is %s.', $order->getIncrementId()) + ); + + $this->_redirect('*/*/failure'); + } + $order->getPayment()->setIdealTransactionChecked(1); + $order->save(); + } else { + $this->_redirect('checkout/cart'); + } + } + + /** + * Redirected here when customer returns with error + */ + public function failureAction() + { + if (!$this->getCheckout()->getIdealErrorMessage()) { + $this->norouteAction(); + return; + } + + $this->loadLayout(); + $this->renderLayout(); + } +} diff --git a/app/code/core/Mage/Ideal/controllers/BasicController.php b/app/code/core/Mage/Ideal/controllers/BasicController.php new file mode 100755 index 0000000000..b994fb43e7 --- /dev/null +++ b/app/code/core/Mage/Ideal/controllers/BasicController.php @@ -0,0 +1,247 @@ + + */ +class Mage_Ideal_BasicController extends Mage_Core_Controller_Front_Action +{ + + /** + * Return order instance for last real order ID (stored in session) + * + * @return Mage_Sales_Model_Entity_Order object + */ + protected function _getOrder () + { + $order = Mage::getModel('sales/order'); + $order->load(Mage::getSingleton('checkout/session')->getLastOrderId()); + + if (!$order->getId()) { + return false; + } + + return $order; + } + + /** + * When a customer chooses iDEAL Basic on Checkout/Payment page + */ + public function redirectAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setIdealBasicQuoteId($session->getQuoteId()); + $session->setIdealBasicOrderId($session->getLastOrderId()); + + if (!($order = $this->_getOrder())) { + $this->norouteAction(); + return; + } + $order->addStatusToHistory( + $order->getStatus(), + $this->__('Customer was redirected to iDEAL. Please, check the status of a transaction via the ING iDEAL Dashboard before delivery of the goods purchased.') + ); + $order->save(); + + $this->getResponse()->setBody( + $this->getLayout()->createBlock('ideal/basic_redirect') + ->setOrder($order) + ->toHtml() + ); + $session->unsQuoteId(); + $session->unsLastOrderId(); + } + + /** + * Success response from iDEAL + */ + public function successAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setLastOrderId($session->getIdealBasicOrderId(true)); + + $order = $this->_getOrder(); + if (!$order->getId()) { + $this->norouteAction(); + return false; + } + + $session->setQuoteId($session->getIdealBasicQuoteId(true)); + + $order->addStatusToHistory( + $order->getStatus(), + $this->__('Customer successfully returned from iDEAL') + ); + + $order->sendNewOrderEmail(); + + $this->_saveInvoice($order); + + $order->save(); + + $this->_redirect('checkout/onepage/success'); + } + + /** + * Cancel response from iDEAL + */ + public function cancelAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setLastOrderId($session->getIdealBasicOrderId(true)); + + $order = $this->_getOrder(); + if (!$order->getId()) { + $this->norouteAction(); + return false; + } + + $order->cancel(); + + $history = $this->__('Payment was canceled by customer'); + + $order->addStatusToHistory( + $order->getStatus(), + $history + ); + + $order->save(); + + $session->setQuoteId($session->getIdealBasicQuoteId(true)); + + $this->_redirect('checkout/cart'); + } + + + /** + * Error response from iDEAL + */ + public function failureAction () + { + $session = Mage::getSingleton('checkout/session'); + $session->setLastOrderId($session->getIdealBasicOrderId(true)); + + $order = $this->_getOrder(); + + if (!$order->getId()) { + $this->norouteAction(); + return false; + } + + $order->cancel(); + + $history = $this->__('Error occured with transaction %s.', $order->getIncrementId()) . ' ' + . $this->__('Customer was returned from iDEAL.'); + + $order->addStatusToHistory( + $order->getStatus(), + $history + ); + + $order->save(); + + $session->setQuoteId($session->getIdealBasicQuoteId(true)); + $session->setIdealErrorMessage($this->__('An error occurred while processing your iDEAL transaction. Please contact the web shop or try again later. Transaction number is %s.', $order->getIncrementId())); + + $this->loadLayout(); + $this->renderLayout(); + + } + + /** + * Notification action that calling by iDEAL + * + */ + public function notifyAction() + { + if (isset($HTTP_RAW_POST_DATA)) { + $xmlResponse = $HTTP_RAW_POST_DATA; + } else { + $xmlResponse = file_get_contents("php://input"); + } + + if (!strlen($xmlResponse)) { + $this->norouteAction(); + return; + } + + $xmlObj = simplexml_load_string($xmlResponse); + $status = (string)$xmlObj->status; + + $order = Mage::getModel('sales/order') + ->loadByIncrementId((int)$xmlObj->purchaseID); + + if (!$order->getId()) { + return; + } + + if ($status == 'Success') { + if (!$order->hasInvoices()) { + $this->_saveInvoice($order); + $order->addStatusToHistory($order->getStatus(), + $this->__('Notification from iDEAL was recived with status %s. Invoice was created. Please, check the status of a transaction via the ING iDEAL Dashboard before delivery of the goods purchased.', $status) + ); + } else { + $order->addStatusToHistory($order->getStatus(), + $this->__('Notification from iDEAL was recived with status %s.', $status) + ); + } + } else { + $order->addStatusToHistory($order->getStatus(), + $this->__('Notification from iDEAL was recived with status %s.', $status) + ); + $order->cancel(); + } + + $order->save(); + } + + /** + * Save invoice for order + * + * @param Mage_Sales_Model_Order $order + * @return boolean Can save invoice or not + */ + protected function _saveInvoice(Mage_Sales_Model_Order $order) + { + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + return true; + } + return false; + } + +} diff --git a/app/code/core/Mage/Ideal/etc/config.xml b/app/code/core/Mage/Ideal/etc/config.xml new file mode 100755 index 0000000000..fd664508cc --- /dev/null +++ b/app/code/core/Mage/Ideal/etc/config.xml @@ -0,0 +1,150 @@ + + + + + + 0.1.0 + + + + + + Mage_Ideal_Model + ideal_mysql4 + + + Mage_Ideal_Model_Mysql4 + + ideal_api_debug
    +
    +
    +
    + + + + Mage_Ideal + Mage_Ideal_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Ideal_Block + +
    + + + /ideal/advanced + /ideal/basic + + + + standard + + Mage_Ideal + ideal + + + + + + + + Mage_Ideal.csv + + + + + + + + ideal.xml + + + + + + + + singleton + ideal/observer + convertPayment + + + + + + + + + + + Mage_Ideal.csv + + + + + + + + + ideal/basic + iDEAL Basic + 0 + + + ideal/advanced + iDEAL Advanced + 0 + 10 + 1 + 1 + + + + + + + 0 */1 * * * + ideal/advanced::transactionStatusCheck + + + +
    diff --git a/app/code/core/Mage/Ideal/etc/system.xml b/app/code/core/Mage/Ideal/etc/system.xml new file mode 100755 index 0000000000..593efad21a --- /dev/null +++ b/app/code/core/Mage/Ideal/etc/system.xml @@ -0,0 +1,326 @@ + + + + + + + + + text + 301 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 0 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>10</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + text + 20 + 1 + 1 + 0 + + + + text + 30 + 1 + 1 + 0 + + + + Leave empty for default value + text + 30 + 1 + 1 + 0 + + + + Leave empty for default value + text + 30 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 40 + 1 + 1 + 0 + + + + Store name will be used if left empty + text + 50 + 1 + 1 + 0 + + + + select + ideal/source_language + 60 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 70 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 80 + 1 + 1 + 0 + + + + allowspecific + 90 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 100 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 110 + 1 + 1 + 0 + + + + + + text + 302 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 0 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>10</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + text + 20 + 1 + 1 + 0 + + + + Absolute path to private key file + text + 30 + 1 + 1 + 0 + + + + text + 40 + 1 + 1 + 0 + + + + Absolute path to private certificate file + text + 30 + 1 + 1 + 0 + + + + Absolute path to public payment provider certificate file + text + 50 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 60 + 1 + 1 + 0 + + + + Store name will be used if left empty + text + 70 + 1 + 1 + 0 + + + + In minutes. Minimum is 1 minute and maximum is 60. + text + 80 + 1 + 1 + 0 + + + + Begin to check transaction status if order was made {N} hours ago + text + 90 + 1 + 1 + 0 + + + + Don't check transaction status if order older than {N} days + text + 100 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 110 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 120 + 1 + 1 + 0 + + + + allowspecific + 130 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 140 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 150 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Ideal/sql/ideal_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Ideal/sql/ideal_setup/mysql4-install-0.1.0.php new file mode 100755 index 0000000000..d4ce372000 --- /dev/null +++ b/app/code/core/Mage/Ideal/sql/ideal_setup/mysql4-install-0.1.0.php @@ -0,0 +1,51 @@ +startSetup(); + +$installer->run(" +DROP TABLE IF EXISTS `{$this->getTable('ideal/api_debug')}`; +CREATE TABLE `{$this->getTable('ideal/api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +"); + +$installer->endSetup(); + +$installer->addAttribute('quote_payment', 'ideal_issuer_id', array()); +$installer->addAttribute('quote_payment', 'ideal_issuer_list', array('type' => 'text')); +$installer->addAttribute('order_payment', 'ideal_issuer_id', array()); +$installer->addAttribute('order_payment', 'ideal_issuer_title', array()); +$installer->addAttribute('order_payment', 'ideal_transaction_checked', array('type'=>'int')); \ No newline at end of file diff --git a/app/code/core/Mage/Install/Model/Installer.php b/app/code/core/Mage/Install/Model/Installer.php index 306a2d2563..15aa7b8485 100644 --- a/app/code/core/Mage/Install/Model/Installer.php +++ b/app/code/core/Mage/Install/Model/Installer.php @@ -205,10 +205,35 @@ public function installDb() } /** - * Create admin user + * Prepare admin user data in model and validate it. + * Returns TRUE or array of error messages. * * @param array $data - * @return Mage_Install_Model_Installer + * @return mixed + */ + public function validateAndPrepareAdministrator($data) + { + $user = Mage::getModel('admin/user') + ->load($data['username'], 'username'); + $user->addData($data); + + $result = $user->validate(); + if (is_array($result)) { + foreach ($result as $error) { + $this->getDataModel()->addError($error); + } + return $result; + } + return $user; + } + + /** + * Create admin user. + * Paramater can be prepared user model or array of data. + * Returns TRUE or throws exception. + * + * @param mixed $data + * @return bool */ public function createAdministrator($data) { @@ -218,17 +243,53 @@ public function createAdministrator($data) $user->delete(); } - $user = Mage::getModel('admin/user') - ->load($data['username'], 'username'); - $user->addData($data)->save(); - $user->setRoleIds(array(1))->saveRelations(); + //to support old logic checking if real data was passed + if (is_array($data)) { + $data = $this->validateAndPrepareAdministrator($data); + if (is_array(data)) { + throw new Exception(Mage::helper('install')->__('Please correct user data and try again.')); + } + } + + //run time flag to force saving entered password + $data->setForceNewPassword(true); + + $data->save(); + $data->setRoleIds(array(1))->saveRelations(); /*Mage::getModel("permissions/user")->setRoleId(1) ->setUserId($user->getId()) ->setFirstname($user->getFirstname()) ->add();*/ - return $this; + return true; + } + + /** + * Validating encryption key. + * Returns TRUE or array of error messages. + * + * @param $key + * @return unknown_type + */ + public function validateEncryptionKey($key) + { + $errors = array(); + + try { + if ($key) { + Mage::helper('core')->validateKey($key); + } + } catch (Exception $e) { + $errors[] = $e->getMessage(); + $this->getDataModel()->addError($e->getMessage()); + } + + if (!empty($errors)) { + return $errors; + } + + return true; } /** diff --git a/app/code/core/Mage/Install/Model/Installer/Console.php b/app/code/core/Mage/Install/Model/Installer/Console.php index 05c313d788..c5218d8ac7 100644 --- a/app/code/core/Mage/Install/Model/Installer/Console.php +++ b/app/code/core/Mage/Install/Model/Installer/Console.php @@ -308,7 +308,7 @@ protected function _prepareData() 'lastname' => $this->_args['admin_lastname'], 'email' => $this->_args['admin_email'], 'username' => $this->_args['admin_username'], - 'password' => $this->_args['admin_password'], + 'new_password' => $this->_args['admin_password'], )); return $this; @@ -374,19 +374,37 @@ public function install() } /** - * Create primary administrator user + * Validate entered data for administrator user */ - $installer->createAdministrator($this->_getDataModel()->getAdminData()); + $user = $installer->validateAndPrepareAdministrator($this->_getDataModel()->getAdminData()); if ($this->hasErrors()) { return false; } /** - * Save encryption key or create if empty + * Prepare encryption key and validate it */ $encryptionKey = empty($this->_args['encryption_key']) ? md5(time()) : $this->_args['encryption_key']; $this->_getDataModel()->setEncryptionKey($encryptionKey); + $installer->validateEncryptionKey($encryptionKey); + + if ($this->hasErrors()) { + return false; + } + + /** + * Create primary administrator user + */ + $installer->createAdministrator($user); + + if ($this->hasErrors()) { + return false; + } + + /** + * Save encryption key or create if empty + */ $installer->installEnryptionKey($encryptionKey); if ($this->hasErrors()) { diff --git a/app/code/core/Mage/Install/controllers/WizardController.php b/app/code/core/Mage/Install/controllers/WizardController.php index 1472dbc661..6ee51b91e1 100644 --- a/app/code/core/Mage/Install/controllers/WizardController.php +++ b/app/code/core/Mage/Install/controllers/WizardController.php @@ -384,11 +384,30 @@ public function administratorPostAction() $adminData = $this->getRequest()->getPost('admin'); $encryptionKey = $this->getRequest()->getPost('encryption_key'); - try { - $this->_getInstaller()->createAdministrator($adminData) - ->installEnryptionKey($encryptionKey); + $errors = array(); + + //preparing admin user model with data and validate it + $user = $this->_getInstaller()->validateAndPrepareAdministrator($adminData); + if (is_array($user)) { + $errors = $user; } - catch (Exception $e){ + + //checking if valid encryption key was entered + $result = $this->_getInstaller()->validateEncryptionKey($encryptionKey); + if (is_array($result)) { + $errors = array_merge($errors, $result); + } + + if (!empty($errors)) { + Mage::getSingleton('install/session')->setAdminData($adminData); + $this->getResponse()->setRedirect($step->getUrl()); + return false; + } + + try { + $this->_getInstaller()->createAdministrator($user); + $this->_getInstaller()->installEnryptionKey($encryptionKey); + } catch (Exception $e){ Mage::getSingleton('install/session') ->setAdminData($adminData) ->addError($e->getMessage()); diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import.php new file mode 100644 index 0000000000..88514723d5 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import.php @@ -0,0 +1,43 @@ + + */ + +class Mage_Oscommerce_Block_Adminhtml_Import extends Mage_Adminhtml_Block_Widget_Grid_Container +{ + protected $_blockGroup = 'oscommerce'; + public function __construct() + { + $this->_controller = 'adminhtml_import'; + $this->_headerText = Mage::helper('adminhtml')->__('Manage osCommerce Profiles'); + $this->_addButtonLabel = Mage::helper('adminhtml')->__('Add New osCommerce Profile'); + parent::__construct(); + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit.php new file mode 100644 index 0000000000..f180de5b82 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit.php @@ -0,0 +1,68 @@ + + */ + +class Mage_Oscommerce_Block_Adminhtml_Import_Edit extends Mage_Adminhtml_Block_Widget_Form_Container +{ + protected $_blockGroup = 'oscommerce'; + + public function __construct() + { + $this->_objectId = 'id'; + $this->_controller = 'adminhtml_import'; + $this->_updateButton('save', 'label', Mage::helper('oscommerce')->__('Save Profile')); + $this->_updateButton('delete', 'label', Mage::helper('oscommerce')->__('Delete Profile')); + $this->_addButton('save_and_edit_button', + array( + 'label' => Mage::helper('oscommerce')->__('Save And Continue Edit'), + 'onclick' => 'saveAndContinueEdit()', + 'class' => 'save' + ), 100 + + ); + $this->_formScripts[] = " + function saveAndContinueEdit(){ + editForm.submit($('edit_form').action+'continue/back/'); + } + "; + parent::__construct(); + } + + public function getHeaderText() + { + if (Mage::registry('oscommerce_adminhtml_import')->getId()) { // TOCHECK + return Mage::helper('oscommerce')->__('Edit osCommerce Profile :: %s', Mage::registry('oscommerce_adminhtml_import')->getName()); + } + else { + return Mage::helper('oscommerce')->__('New osCommerce Profile'); + } + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Form.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Form.php new file mode 100644 index 0000000000..15b2f4151a --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Form.php @@ -0,0 +1,52 @@ + + */ + +class Mage_Oscommerce_Block_Adminhtml_Import_Edit_Form extends Mage_Adminhtml_Block_Widget_Form +{ + protected function _prepareForm() + { + $form = new Varien_Data_Form(array('id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'POST')); + + $model = Mage::registry('oscommerce_adminhtml_import'); + + if ($model->getId()) { + $form->addField('import_id', 'hidden', array( + 'name' => 'import_id', + )); + $form->setValues($model->getData()); + } + + $form->setUseContainer(true); + $this->setForm($form); + return parent::_prepareForm(); + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tab/General.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tab/General.php new file mode 100644 index 0000000000..c35a4941e7 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tab/General.php @@ -0,0 +1,110 @@ + + */ +class Mage_Oscommerce_Block_Adminhtml_Import_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form +{ + + function initForm() + { + $model = Mage::registry('oscommerce_adminhtml_import'); + + + $form = new Varien_Data_Form(array('id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post')); + + if ($model->getId()) { + $form->addField('import_id', 'hidden', array( + 'name' => 'import_id', + )); + } + $fieldset = $form->addFieldset('base_fieldset', array('legend'=>Mage::helper('oscommerce')->__('General Information'))); + $fieldset->addField('name', 'text', array( + 'label' => $this->__('Name'), + 'title' => $this->__('Name'), + 'name' => 'name', + 'required' => true, + )); + + $fieldset->addField('host', 'text', array( + 'label' => $this->__('IP or Hostname'), + 'title' => $this->__('IP or Hostname'), + 'name' => 'host', + 'required' => true, + )); + +// $fieldset->addField('port', 'text', array( +// 'label' => $this->__('Port (Default as 3306)'), +// 'title' => $this->__('Port (Default as 3306)'), +// 'name' => 'port', +// 'required' => true, +// 'value' => $model->getData('port') ? $model->getData('port'): Mage_Oscommerce_Model_Oscommerce::DEFAULT_PORT +// )); + + $fieldset->addField('db_name', 'text', array( + 'label' => $this->__('DB Name'), + 'title' => $this->__('DB Name'), + 'name' => 'db_name', + 'required' => true, + )); + + $fieldset->addField('db_user', 'text', array( + 'label' => $this->__('DB Username'), + 'title' => $this->__('DB Username'), + 'name' => 'db_user', + 'required' => true, + )); + + $fieldset->addField('db_password', 'password', array( + 'label' => $this->__('DB Password'), + 'title' => $this->__('DB Password'), + 'name' => 'db_password', + )); + + $fieldset->addField('table_prefix', 'text', array( + 'label' => $this->__('Prefix'), + 'title' => $this->__('Prefix'), + 'name' => 'table_prefix', + )); + + $fieldset->addField('send_subscription', 'checkbox', array( + 'label' => $this->__('Send subscription notify to customers'), + 'title' => $this->__('Send subscription notify to customers'), + 'name' => 'send_subscription', + 'values' => $this->getData('send_subscription'), + 'checked' => $this->getData('send_subscription'), + )); + + $form->setValues($model->getData()); + + $this->setForm($form); + + return $this; + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tab/Run.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tab/Run.php new file mode 100644 index 0000000000..6402eef607 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tab/Run.php @@ -0,0 +1,239 @@ + + */ +class Mage_Oscommerce_Block_Adminhtml_Import_Edit_Tab_Run extends Mage_Adminhtml_Block_Template +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('oscommerce/convert/run.phtml'); + } + + /** + * Prepares layout of block + * + */ + protected function _prepareLayout() + { + $this->setChild('save_button', + $this->getLayout()->createBlock('adminhtml/widget_button') + ->setData(array( + 'label' => Mage::helper('oscommerce')->__('Start Runing!'), + 'class' => 'run', + 'id' => 'run_import' + )) + + ); + + $this->setChild('check_button', + $this->getLayout()->createBlock('adminhtml/widget_button') + ->setData(array( + 'label' => Mage::helper('oscommerce')->__('Check requirements!'), + 'class' => 'run', + 'id' => 'check_import' + )) + + ); + + } + + public function getImportId() + { + return Mage::registry('oscommerce_adminhtml_import')->getId(); + } + + /** + * Retrieve run url + * + * @return string + */ + public function getSubmitUrl() + { + return $this->getUrl('*/*/run/', array('id'=>$this->getOscId())); + } + + /** + * Retrive run button html + * + * @return string + */ + public function getSaveButtonHtml() + { + return $this->getChildHtml('save_button'); + } + + public function getCheckButtonHtml() + { + return $this->getChildHtml('check_button'); + } + + public function getWebsiteOptionHtml() + { + + $html = ''; + return $html; + } + + public function getTimezoneOptionHtml() + { + $html = ''; + return $html; + + } + + /** + * Get list available for mysql connection charsets + * + * @return array + */ + public function getConnectionCharsets() + { + $charsetList = array(); + $fileName = Mage::getModuleDir('etc','Mage_Oscommerce').DS.'charsets.xml'; + if (is_readable($fileName)) { + $xml = new Varien_Simplexml_Config(); + $xml->loadFile($fileName); + $charsets = $xml->getNode('charset'); + foreach($charsets as $charset) { + $attributes = $charset->attributes(); + $code = (string) $attributes['name']; + $charsetList[$code] = (string)$charset->family; + } + } + return $charsetList; + } + + /** + * Get list available for iconv function charsets + * + * @return array + */ + public function getDataCharsets() + { + $charsetList = array( + 'BIG-5' => Mage::helper('oscommerce')->__('Traditional Chinese'), + 'ISO-8859-2' => Mage::helper('oscommerce')->__('Central European'), + 'CP850' => Mage::helper('oscommerce')->__('Western'), + 'ISO-8859-1' => Mage::helper('oscommerce')->__('Western'), + 'HP-ROMAN8' => Mage::helper('oscommerce')->__('Western'), + 'KOI8-R' => Mage::helper('oscommerce')->__('Cyrillic'), + 'ASCII' => Mage::helper('oscommerce')->__('Western'), + 'EUC-JP' => Mage::helper('oscommerce')->__('Japanese'), + 'SHIFT-JIS' => Mage::helper('oscommerce')->__('Japanese'), + 'windows-1251' => Mage::helper('oscommerce')->__('Cyrillic'), + 'ISO-8859-8' => Mage::helper('oscommerce')->__('Hebrew'), + 'TIS-620' => Mage::helper('oscommerce')->__('Thai'), + 'EUC-KR' => Mage::helper('oscommerce')->__('Korean'), + 'ISO-8859-13' => Mage::helper('oscommerce')->__('Baltic'), + 'KOI8-U' => Mage::helper('oscommerce')->__('Cyrillic'), + 'CHINESE' => Mage::helper('oscommerce')->__('Simplified Chinese'), + 'ISO-8859-7' => Mage::helper('oscommerce')->__('Greek'), + 'WINDOWS-1250' => Mage::helper('oscommerce')->__('Central European'), + 'CP936' => Mage::helper('oscommerce')->__('East Asian'), + 'WINDOWS-1257' => Mage::helper('oscommerce')->__('Baltic'), + 'ISO-8859-9' => Mage::helper('oscommerce')->__('South Asian'), + 'ARMSCII-8' => Mage::helper('oscommerce')->__('South Asian'), + 'UTF-8' => Mage::helper('oscommerce')->__('Unicode'), + 'UCS-2' => Mage::helper('oscommerce')->__('Unicode'), + 'CP866' => Mage::helper('oscommerce')->__('Cyrillic'), + 'MACCENTRALEUROPE' => Mage::helper('oscommerce')->__('Central European'), + 'MAC' => Mage::helper('oscommerce')->__('Western'), + 'CP852' => Mage::helper('oscommerce')->__('Central European'), + 'CP1256' => Mage::helper('oscommerce')->__('Arabic'), + 'CP932' => Mage::helper('oscommerce')->__('Japanese'), + ); + return $charsetList; + } + + public function drowOptions($options = array()) + { + asort($options); + $html = ''; + foreach($options as $code => $name) { + $html.= ''; + } + + return $html; + } + + public function getDataCharsetOptionHtml() + { + + $html = ''; + return $html; + } + + public function getConnectionCharsetOptionHtml() + { + $html = ''; + return $html; + } + + /** + * Deprecated + * + * @return string + */ + public function getCharsetOption() + { + $options = ''; + $fileName = Mage::getModuleDir('etc','Mage_Oscommerce').DS.'charsets.xml'; + if (is_readable($fileName)) { + $xml = new Varien_Simplexml_Config(); + $xml->loadFile($fileName); + $charsets = $xml->getNode('charset'); + foreach($charsets as $charset) { + $attributes = $charset->attributes(); + $options .= ''; + } + } + return $options; + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tabs.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tabs.php new file mode 100644 index 0000000000..e10f221c56 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Edit/Tabs.php @@ -0,0 +1,66 @@ + + */ +class Mage_Oscommerce_Block_Adminhtml_Import_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs +{ + public function __construct() + { + parent::__construct(); + $this->setId('oscommerce_import_tabs'); + $this->setDestElementId('edit_form'); + $this->setTitle(Mage::helper('oscommerce')->__('osCommerce Profile')); + } + + protected function _beforeToHtml() + { + $model = Mage::registry('oscommerce_adminhtml_import'); + + $generalBlock = $this->getLayout()->createBlock('oscommerce/adminhtml_import_edit_tab_general'); + $generalBlock->addData($model->getData()); + + $new = !$model->getId(); + + $this->addTab('general', array( + 'label' => Mage::helper('oscommerce')->__('General Information'), + 'content' => $generalBlock->initForm()->toHtml(), + 'active' => true, + )); + + if (!$new) { + $this->addTab('run', array( + 'label' => Mage::helper('oscommerce')->__('Run Profile'), + 'content' => $this->getLayout()->createBlock('oscommerce/adminhtml_import_edit_tab_run')->toHtml(), + )); + } + + return parent::_beforeToHtml(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Grid.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Grid.php new file mode 100644 index 0000000000..7dde0e7054 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Grid.php @@ -0,0 +1,94 @@ + + */ + +class Mage_Oscommerce_Block_Adminhtml_Import_Grid extends Mage_Adminhtml_Block_Widget_Grid +{ + + public function __construct() + { + parent::__construct(); + $this->setId('convertOscGrid'); + $this->setDefaultSort('id'); + } + + protected function _prepareCollection() + { + $collection = Mage::getModel('oscommerce/oscommerce')->getCollection(); + $this->setCollection($collection); + return parent::_prepareCollection(); + } + + protected function _prepareColumns() + { + $this->addColumn('id', array( + 'header' =>Mage::helper('oscommerce')->__('ID'), + 'width' =>'50px', + 'index' =>'import_id', + )); + $this->addColumn('name', array( + 'header' =>Mage::helper('oscommerce')->__('Adapter Name'), + 'index' =>'name', + )); + $this->addColumn('host', array( + 'header' =>Mage::helper('oscommerce')->__('IP or Hostname'), + 'index' =>'host', + 'width' =>'120px', + )); + + $this->addColumn('db_name', array( + 'header' =>Mage::helper('oscommerce')->__('Db Name'), + 'index' =>'db_name', + 'width' =>'120px', + )); + + $this->addColumn('created_at', array( + 'header' =>Mage::helper('oscommerce')->__('Created At'), + 'type' => 'date', + 'align' => 'center', + 'index' =>'created_at', + )); + $this->addColumn('updated_at', array( + 'header' =>Mage::helper('oscommerce')->__('Updated At'), + 'type' => 'date', + 'align' => 'center', + 'index' =>'updated_at', + )); + + return parent::_prepareColumns(); + } + + public function getRowUrl($row) + { + return $this->getUrl('*/*/edit', array('id'=>$row->getId())); + } + +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Run.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Run.php new file mode 100644 index 0000000000..0abccb8cf0 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Import/Run.php @@ -0,0 +1,293 @@ + + */ +class Mage_Oscommerce_Block_Adminhtml_Import_Run extends Mage_Adminhtml_Block_Abstract +{ + public function getImportModel() + { + return Mage::registry('oscommerce_adminhtml_import'); + } + + protected function _toHtml() + { + $importModel = $this->getImportModel(); + + echo ''; + + $headBlock = $this->getLayout()->createBlock('page/html_head'); + $headBlock->addJs('prototype/prototype.js'); + echo $headBlock->getCssJsHtml(); + + echo ' + '.($importModel->getId() ? $this->htmlEscape($importModel->getName()) : $this->__('No osCommerce profile')).' +'; + echo '
      '; + echo '
    • '; + if ($importModel->getId()) { + echo ''; + echo $this->__("Starting profile execution, please wait..."); + echo '
    • '; + echo '
    • '; + echo ''; + echo $this->__("Warning: Please don't close window during importing/exporting data"); + echo '
    • '; + } else { + echo ''; + echo $this->__("No osCommerce profile loaded..."); + } + echo ''; + echo '
    '; + + if ($importModel->getId()) { + echo '
      '; + ob_implicit_flush(); + $showFinished = false; + $countItems = 0; + $batchConfig = array( + 'styles' => array( + 'error' => array( + 'icon' => Mage::getDesign()->getSkinUrl('images/error_msg_icon.gif'), + 'bg' => '#FDD' + ), + 'message' => array( + 'icon' => Mage::getDesign()->getSkinUrl('images/fam_bullet_success.gif'), + 'bg' => '#DDF' + ), + 'loader' => Mage::getDesign()->getSkinUrl('images/ajax-loader.gif') + ), + 'template' => '
    • ' + . '' + . '#{text}' + . '
    • ', + 'text' => $this->__('processed %s%% %s/%s records', '#{percent}', '#{updated}', '#{total}'), + 'successText' => $this->__('Total imported %s records (%s)', '#{updated}', '#{totalImported}') + ); + + + echo ''; + + + echo "
    "; +echo ' + + +'; + +// echo '
      '; + + + if ($totalRecords = $importModel->getTotalRecords()) { + + $maxRows = $importModel->getResource()->getMaxRows(); + echo ''; + foreach($totalRecords as $importType => $totalRecord) { + echo ''; + $page = floor($totalRecord/$maxRows) + 1; + for ($i = 0; $i < $page; $i++) { + $data = array( + 'import_id' => $importModel->getId(), + 'import_type' => $importType, + 'from' => ($i > 0 ? $i * $maxRows:$i), + 'is_done' => ($i == $page - 1)?true:false + ); + echo ''; + } + +// if ($importType=='categories') { +// $data = array( +// 'import_id' => $importModel->getId(), +// 'import_type' => $importType, +// 'page' => 'all', +// 'is_done' => true +// ); +// echo ''; +// +// } else { +// $page = floor($totalRecord/$maxRows) + 1; +// for ($i = 0; $i < $page; $i++) { +// $data = array( +// 'import_id' => $importModel->getId(), +// 'import_type' => $importType, +// 'from' => ($i > 0 ? $i * $maxRows:$i), +// 'is_done' => ($i == $page - 1)?true:false +// ); +// echo ''; +// } +// } + } + echo ''; + + } + + } + echo ''; + exit; + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order.php new file mode 100644 index 0000000000..37c1469f28 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order.php @@ -0,0 +1,43 @@ + + */ + +class Mage_Oscommerce_Block_Adminhtml_Order extends Mage_Adminhtml_Block_Widget_Grid_Container +{ + protected $_blockGroup = 'oscommerce'; + public function __construct() + { + $this->_controller = 'adminhtml_order'; + $this->_headerText = Mage::helper('adminhtml')->__('Manage osCommerce Orders'); + parent::__construct(); + $this->_removeButton('add'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order/Grid.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order/Grid.php new file mode 100644 index 0000000000..ab5e39cb4b --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order/Grid.php @@ -0,0 +1,128 @@ + + */ + +class Mage_Oscommerce_Block_Adminhtml_Order_Grid extends Mage_Adminhtml_Block_Widget_Grid +{ + + public function __construct() + { + parent::__construct(); + $this->setId('oscommerceOrderGrid'); + $this->setDefaultSort('id'); + } + + protected function _prepareCollection() + { + $collection = Mage::getModel('oscommerce/oscommerce_order')->getCollection(); + $this->setCollection($collection); + return parent::_prepareCollection(); + } + + protected function _prepareColumns() + { + $this->addColumn('order_id', array( + 'header' =>Mage::helper('oscommerce')->__('Order #'), + 'width' =>'50px', + 'index' =>'osc_magento_id', + 'type' => 'number', + )); + + $this->addColumn('billing_name', array( + 'header' =>Mage::helper('oscommerce')->__('Billing to Name'), + 'index' =>'billing_name', + )); + + $this->addColumn('delivery_name', array( + 'header' =>Mage::helper('oscommerce')->__('Ship to Name'), + 'index' =>'delivery_name', + )); + + $this->addColumn('currency', array( + 'header' =>Mage::helper('oscommerce')->__('Currency'), + 'width' =>'50px', + 'index' =>'currency', + )); + + $this->addColumn('orders_total', array( + 'header' =>Mage::helper('oscommerce')->__('Order Total'), + 'width' =>'50px', + 'index' =>'orders_total', + 'type' => 'currency', + 'currency'=>'order_currency_code' + )); + + $this->addColumn('orders_status', array( + 'header' =>Mage::helper('oscommerce')->__('Order Status'), + 'width' =>'50px', + 'index' =>'orders_status', + )); + + $this->addColumn('date_purchased', array( + 'header' => Mage::helper('oscommerce')->__('Purchased Year'), + 'width' => '150px', + 'index' => 'date_purchased', + 'type' => 'datetime', + )); + +// $this->addColumn('purchased_year', array( +// 'header' =>Mage::helper('oscommerce')->__('Purchased Year'), +// 'width' =>'50px', +// 'index' =>'purchased_year', +// 'type' => 'currency', +// 'currency' => 'store_currency_code', +// )); +// +// $this->addColumn('purchased_month', array( +// 'header' =>Mage::helper('oscommerce')->__('Purchased Month'), +// 'width' =>'50px', +// 'index' =>'purchased_month', +// 'type' => 'currency', +// 'currency' => 'store_currency_code', +// )); +// +// $this->addColumn('purchased_day', array( +// 'header' =>Mage::helper('oscommerce')->__('Purchased Date'), +// 'width' =>'50px', +// 'index' =>'purchased_day', +// 'type' => 'currency', +// 'currency' => 'store_currency_code', +// )); + + return parent::_prepareColumns(); + } + + public function getRowUrl($row) + { + return $this->getUrl('*/*/view', array('order_id'=>$row->getId())); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order/View.php b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order/View.php new file mode 100644 index 0000000000..046a6608ed --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Adminhtml/Order/View.php @@ -0,0 +1,101 @@ + + */ +class Mage_Oscommerce_Block_Adminhtml_Order_View extends Mage_Adminhtml_Block_Template +{ + + public function __construct() + { + parent::__construct(); + $this->setTemplate('oscommerce/order/view.phtml'); + + + } + + protected function _prepareLayout() + { + $this->setChild('backButton', + $this->getLayout()->createBlock('adminhtml/widget_button') + ->setData(array( + 'label' => Mage::helper('oscommerce')->__('Back'), + 'id' => 'back_button', + 'name' => 'back_button', + 'class' => 'scalable back', + 'onclick'=> "setLocation('".$this->getBackUrl()."')", + )) + ); + } + /** + * Retrieve order model object + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + return Mage::registry('current_oscommerce_order'); + } + + /** + * Retrieve Order Identifier + * + * @return int + */ + public function getOrderId() + { + return $this->getOrder()->getId(); + } + + public function getHeaderText() + { + $text = Mage::helper('oscommerce')->__('osCommerce Order # %s | Order Date %s', + $this->getOrder()->getOrdersId(), + $this->formatDate($this->getOrder()->getDatePurchased(), 'medium', true) + ); + return $text; + } + + public function getUrl($params='', $params2=array()) + { + $params2['order_id'] = $this->getOrderId(); + return parent::getUrl($params, $params2); + } + + + public function getBackButtonHtml() + { + return $this->getChildHtml('backButton'); + } + + public function getBackUrl() + { + return $this->getUrl("*/*/"); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/Block/Order/List.php b/app/code/core/Mage/Oscommerce/Block/Order/List.php new file mode 100644 index 0000000000..5b36840dfd --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Order/List.php @@ -0,0 +1,73 @@ + + */ +class Mage_Oscommerce_Block_Order_List extends Mage_Core_Block_Template +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('oscommerce/order/list.phtml'); + $customerId = Mage::getSingleton('customer/session')->getCustomerId(); + $websiteId = $websiteId = Mage::app()->getStore()->getWebsiteId(); + $osCommerce = Mage::getModel('oscommerce/oscommerce'); + $oscOrders = $osCommerce->loadOrders($customerId, $websiteId); + $this->setOsCommerceOrders($oscOrders); + } + + protected function _prepareLayout() + { + $orderInfo = $this->getOrder(); + $order = $orderInfo['order']; + if ($headBlock = $this->getLayout()->getBlock('head')) { + $headBlock->setTitle($this->__('Order # %s', $order['orders_id'])); + } + } + + public function getViewOscommerceUrl($order) + { + return $this->getUrl('oscommerce/order/view', array('order_id'=>$order['osc_magento_id'])); + } + + /** + * Retrieve current order model instance + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + return Mage::registry('current_oscommerce_order'); + } + + public function getBackUrl() + { + return Mage::getUrl('*/*/history'); + } +} diff --git a/app/code/core/Mage/Oscommerce/Block/Order/View.php b/app/code/core/Mage/Oscommerce/Block/Order/View.php new file mode 100644 index 0000000000..4b796ad6f3 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Block/Order/View.php @@ -0,0 +1,69 @@ + + */ +class Mage_Oscommerce_Block_Order_View extends Mage_Core_Block_Template +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('oscommerce/order/view.phtml'); + } + + protected function _prepareLayout() + { + $orderInfo = $this->getOrder(); + $order = $orderInfo['order']; + if ($headBlock = $this->getLayout()->getBlock('head')) { + $headBlock->setTitle($this->__('Order # %s', $order['orders_id'])); + } + } + + public function getPaymentInfoHtml() + { + return $this->getChildHtml('payment_info'); + } + + /** + * Retrieve current order model instance + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + return Mage::registry('current_oscommerce_order'); + } + + public function getBackUrl() + { + return Mage::getUrl('sales/order/history'); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/Helper/Data.php b/app/code/core/Mage/Oscommerce/Helper/Data.php new file mode 100644 index 0000000000..32dceafbc6 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Helper/Data.php @@ -0,0 +1,35 @@ + + */ +class Mage_Oscommerce_Helper_Data extends Mage_Core_Helper_Data +{ + +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/Model/Config.php b/app/code/core/Mage/Oscommerce/Model/Config.php new file mode 100644 index 0000000000..65a96b3dcd --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Config.php @@ -0,0 +1,49 @@ + + */ +class Mage_Oscommerce_Model_Config extends Mage_Core_Model_Config_Base +{ + public function __construct() + { + parent::__construct(Mage::getConfig()->getNode('global')); + } + + public function initForeignConnection($data) + { + $connectionNode = $this->getNode('resources/oscommerce_foreign/connection'); + if ($connectionNode) { + $connectionNode->addChild('host', isset($data['host']) ? $data['host'] : ''); + $connectionNode->addChild('username', isset($data['db_user']) ? $data['db_user'] : ''); + $connectionNode->addChild('password', isset($data['db_password']) ? $data['db_password'] : ''); + $connectionNode->addChild('dbname', isset($data['db_name']) ? $data['db_name'] : ''); + } + } +} diff --git a/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce.php b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce.php new file mode 100644 index 0000000000..ad8fd454ff --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce.php @@ -0,0 +1,2113 @@ + + */ +class Mage_Oscommerce_Model_Mysql4_Oscommerce extends Mage_Core_Model_Mysql4_Abstract +{ + const DEFAULT_DISPLAY_MODE = 'PRODUCTS'; + const DEFAULT_IS_ANCHOR = '0'; + const DEFAULT_STORE = 'Default'; + const DEFAULT_PRODUCT_TYPE = 'Simple'; + const DEFAULT_ATTRIBUTE_SET = 'Default'; + const DEFAULT_VISIBILITY = 'Catalog, Search'; + const DEFAULT_LOCALE = 'en_US'; + const DEFAULT_MAGENTO_CHARSET = 'UTF-8'; + const DEFAULT_OSC_CHARSET = 'ISO-8859-1'; + const DEFAULT_FIELD_CHARSET = 'utf8'; + + protected $_currentWebsiteId; + protected $_currentWebsite; + + protected $_importType = array(); + protected $_countryIdToCode = array(); + protected $_countryNameToCode = array(); + protected $_regionCode = array(); + protected $_logData = array(); + protected $_languagesToStores = array(); + protected $_prefix = ''; + protected $_storeLocales = array(); + protected $_rootCategory = ''; + + protected $_websiteCode = ''; + protected $_isProductWithCategories = false; + protected $_setupConnection ; + protected $_customerIdPair = array(); + protected $_categoryIdPair = array(); + protected $_prefixPath = ''; + protected $_stores = array(); + protected $_productsToCategories = array(); + protected $_productsToStores = array(); + protected $_connectionCharset; + protected $_dataCharset; + protected $_maxRows; + protected $_oscStores; + protected $_oscDefaultLanguage; + protected $_oscStoreInformation; + protected $_categoryModel; + protected $_customerModel; + protected $_productModel; + protected $_productAdapterModel; + protected $_orderModel; + protected $_addressModel; + protected $_websiteModel; + protected $_storeGroupModel; + protected $_configModel; + protected $_customerGroupModel; + protected $_storeModel; + protected $_importCollection; + protected $_saveRows = 0; + protected $_errors = array(); + protected $_importModel; + protected $_lengthShortDescription; + protected $_currentUserId; + + protected $_oscTables = array( + 'products', 'customers', 'categories', 'orders', 'languages', + 'orders_products', 'orders_status_history', 'orders_total', + 'products_description', 'address_book', 'categories_description' + ); + + protected function _construct() + { + $this->_init('oscommerce/oscommerce', 'import_id'); + $this->_setupConnection = Mage::getSingleton('core/resource')->getConnection('oscommerce_setup'); + $this->_currentWebsite = Mage::app()->getWebsite(); + $this->_currentWebsiteId = $this->_currentWebsite->getId(); + $this->_maxRows = Mage::getStoreConfig('oscommerce/import/max_rows'); + $this->_lengthShortDescription = Mage::getStoreConfig('oscommerce/import/short_description_length'); + } + + /** + * Get paypal session namespace + * + * @return Mage_Paypal_Model_Session + */ + public function getSession() + { + return Mage::getSingleton('oscommerce/session'); + } + + /** + * Get website object + * + * @return Mage_Core_Model_Website + */ + public function getCurrentWebsite() + { + return $this->_currentWebsite; + } + + protected function _beforeSave(Mage_Core_Model_Abstract $object) + { + if (!$object->getCreatedAt()) { + $object->setCreatedAt($this->formatDate(time())); + } + $object->setUpdatedAt($this->formatDate(time())); + parent::_beforeSave($object); + } + + /** + * Getting external connection adapter + * + * @return object + */ + protected function _getForeignAdapter() + { + return $this->_getConnection('foreign'); + } + + /** + * Get store code by id + * + * @param integer $id + * @return string + */ + public function getStoreCodeById($id) + { + if (!$this->_stores) { + $stores = Mage::app()->getStores(); + foreach($stores as $store) { + $this->_stores[$store->getId()] = $store->getCode(); + } + } + if (isset($this->_stores[$id])) { + return $this->_stores[$id]; + } + return false; + } + + public function setWebsiteCode($code) + { + if (isset($code)) $this->_websiteCode = $code; + } + + /** + * Create new website or set current website as default website + * + * @param integer $websiteId + */ + public function createWebsite($websiteId = null) + { + $importModel = $this->getImportModel(); + $websiteModel = $this->getWebsiteModel(); + if (!is_null($websiteId)) { + $websiteModel->load($websiteId); + } + + if (!$websiteModel->getId()) { + $storeInfo = $this->getOscStoreInformation(); + if ($this->_websiteCode && !($websiteModel->load($this->_websiteCode)->getId())) { + $websiteModel->setName($storeInfo['STORE_NAME']); + $websiteModel->setCode($this->_websiteCode ? $this->_websiteCode : $this->_format($storeInfo['STORE_NAME'])); + $websiteModel->save(); + } + } + + + if ($websiteModel->getId()) { + $this->saveLogs(array( 0 => $websiteModel->getId()), 'website'); + } + + /** + * Create Root category + */ + $this->createRootCategory(); + + /** + * Create default store group + */ + $this->createStoreGroup(); + } + + public function createStoreGroup() + { + $importModel = $this->getImportModel(); + $storeInfo = $this->getOscStoreInformation(); + $websiteModel = $this->getWebsiteModel(); + if (!$websiteModel->getId()) { + $websiteModel->load($this->_currentWebsiteId); // NEED TO GET DEFAULT WEBSITE ID FROM CONFIG + } + $storeGroupModel = $this->getStoreGroupModel(); + $storeGroupModel->unsetData(); + $storeGroupModel->setOrigData(); + + $storeGroupName = Mage::helper('oscommerce')->__('%s Store', $websiteModel->getId() == $this->_currentWebsiteId ? $storeInfo['STORE_NAME'] : $websiteModel->getName()); + $storeGroupModel->setWebsiteId($websiteModel->getId()); + $storeGroupModel->setName($storeGroupName); + $storeGroupModel->setRootCategoryId($this->getRootCategory()->getId()); + + try { + $storeGroupModel->save(); + + $websiteModel->setDefaultGroupId($storeGroupModel->getId()); + $websiteModel->save(); + } + catch (Exception $e) { + + } + + $this->saveLogs(array(0 => $storeGroupModel->getId(), 'group')); + return $this; + } + + public function createRootCategory() + { + $importModel = $this->getImportModel(); + $categoryModel = $this->getCategoryModel(); + $categoryModel->unsetData(); + $categoryModel->setOrigData(); + + $websiteModel = $this->getWebsiteModel(); + if (!$websiteModel->getId()) { + $websiteModel->load($this->_currentWebsiteId); // NEED TO GET DEFAULT WEBSITE ID FROM CONFIG + } + + $storeInfo = $this->getOscStoreInformation(); + + $categoryName = Mage::helper('oscommerce')->__('Root category for %s', $websiteModel->getName()); + + $categoryModel->setStoreId(0); + $categoryModel->setIsActive(1); + $categoryModel->setDisplayMode(self::DEFAULT_DISPLAY_MODE); + $categoryModel->setName($categoryName); + $categoryModel->setParentId(1); + $categoryModel->setPath('1'); + + try { + $categoryModel->save(); + $this->saveLogs(array(0 => $categoryModel->getId()), 'root_category'); + } + catch (Exception $e) { + + } + + $this->setRootCategory(clone $categoryModel); + + return $this; + } + + /** + * Importing store data from osCommerce to Magento + * + * @param Mage_Oscommerce_Model_Oscommerce $obj + */ + public function importStores() + { + $importModel = $this->getImportModel(); + $locales = $this->getStoreLocales(); + $defaultStore = ''; + $storeInformation = $this->getOscStoreInformation(); + $defaultStoreCode = $storeInformation['DEFAULT_LANGUAGE']; + $configModel = $this->getConfigModel(); + $storeModel = $this->getStoreModel(); + $storeGroupModel = $this->getStoreGroupModel(); + $storeGroupId = $storeGroupModel->getId(); + $websiteModel = $this->getWebsiteModel(); + $websiteId = $websiteModel->getId(); + $storePairs = array(); + if ($stores = $this->getOscStores()) { + foreach($stores as $store) { + try { + $oscStoreId = $store['id']; + unset($store['id']); + + $store['group_id'] = $storeGroupId; + $store['website_id'] = $websiteId; + $storeModel->unsetData(); + $storeModel->setOrigData(); + $storeModel->load($store['code']); + if ($storeModel->getId() && $storeModel->getCode() == $store['code']) { + $localeCode = $locales[$store['code']]; + unset($locales[$store['code']]); + $store['code'] = $store['code'].'_'.$websiteId.time(); // for unique store code + $locales[$store['code']] = $localeCode; + } + $store['name'] = $this->convert($store['name']); + $storeModel->unsetData(); + $storeModel->setOrigData(); + $storeModel->setData($store); + $storeModel->save(); + + $storePairs[$oscStoreId] = $storeModel->getId(); + + $storeLocale = isset($locales[$storeModel->getCode()])?$locales[$storeModel->getCode()]: $locales['default']; + + $configModel->unsetData(); + $configModel->setOrigData(); + $configModel->setScope('stores') + ->setScopeId($storeModel->getId()) + ->setPath('general/locale/code') + ->setValue($storeLocale) + ->save(); + if ($store['scode'] == $defaultStoreCode) { + $defaultStore = $storeModel->getId(); + } + Mage::dispatchEvent('store_add', array('store'=>$storeModel)); + } catch (Exception $e) { + //echo $e->getMessage(); + } + } + } + if (sizeof($storePairs) > 0) { + $this->saveLogs($storePairs, 'store'); + } + $this->setStoreLocales($locales); + + if ($defaultStore) { + $storeGroupModel->setDefaultStoreId($defaultStore); + $storeGroupModel->save(); + } + Mage::app()->reinitStores(); + unset($stores); + } + + /** + * Importing customer/address from osCommerce to Magento + * + * @param Mage_Oscommerce_Model_Oscommerce $obj + */ + public function importCustomers($startFrom = 0, $useStartFrom = false, $sendSubscription = true) + { + $this->_resetSaveRows(); + $this->_resetErrors(); + $totalCustomers = $this->getTotalCustomers(); + $maxRows = $this->getMaxRows(); + $pages = floor($totalCustomers / $maxRows) + 1; + + if (!$useStartFrom) { + for ($i = 0; $i < $pages; $i++) { + if ($customers = $this->getCustomers(array('from'=>($i * $maxRows),'max'=>$maxRows))) { + foreach ($customers as $customer) { + $customer['sendSubscription'] = $sendSubscription; + $this->_saveCustomer($customer); + } + } + } + } else { + if ($customers = $this->getCustomers(array('from'=> $startFrom ,'max'=>$maxRows))) { + foreach ($customers as $customer) { + $customer['sendSubscription'] = $sendSubscription; + $this->_saveCustomer($customer); + } + } + } + } + + /** + * Save customer data + * + * @param Mage_Oscommerce_Model_Oscommerce $obj + * @param array $data + */ + protected function _saveCustomer($data = null) { + $addressFieldMapping = array( + 'street' => 'entry_street_address', + 'firstname' => 'entry_firstname', + 'lastname' => 'entry_lastname', + 'city' => 'entry_city', + 'region' => 'entry_state' + ); + $importModel = $this->getImportModel(); + $timezone = $importModel->getTimezone(); + if (!is_null($data)) { + $customerAddresses = array(); + // Getting customer group data + $customerGroupId = Mage::getStoreConfig(Mage_Customer_Model_Group::XML_PATH_DEFAULT_ID); + $customerGroupModel = $this->getCustomerGroupModel()->load($customerGroupId); + $websiteId = $this->getWebsiteModel()->getId(); + $customerModel = $this->getCustomerModel(); + $addressModel = $this->getAddressModel(); + $oscCustomerId = $data['id']; + $data['group_id'] = $customerGroupModel->getName(); + + $prepareCreated = explode(' ', $data['created_at']); + $dateFormat = 'YYYY-MM-dd HH:mm:ss'; + $dateCreated = new Zend_Date(); + $dateCreated->setTimezone($timezone); + $dateCreated->setDate($prepareCreated[0], 'YYYY-MM-dd'); + $dateCreated->setTime($prepareCreated[1], 'HH:mm:ss'); + $dateCreated->setTimezone('GMT'); + $data['created_at'] = $dateCreated->toString($dateFormat); + + foreach($data as $field => $value) { + if (in_array($field, array('firstname', 'lastname'))) { + $value = $this->convert($value); + } + $data[$field] = html_entity_decode($value, ENT_QUOTES, self::DEFAULT_MAGENTO_CHARSET); + + } + + + // Getting addresses + $addresses = $this->getAddresses($data['id']); + if ($addresses) { + foreach ($addresses as $address) { + foreach ($address as $field => $value) { + + if ($field == 'street1') { + $field = 'street'; + } + if ($field == 'country_id') { + $value = $this->getCountryCodeById($value); + $field = 'country'; + } + if ($field == 'region_id' + && in_array($address['country_id'], array(38, 223))) { + $field = 'region'; + } + + if (in_array($field, array_keys($addressFieldMapping))) { + $value = $this->convert($value); + } + + if (!in_array($field, array('customers_id'))) { + $address[$field] = $value; + } else { + unset($address[$field]); + } + } + $address['country_id'] = $address['country']; + unset($address['country']); + $customerAddresses[] = $address; + } + } + $defaultBilling = ''; + $defaultBilling = $data['default_billing']; + unset($data['default_billing']); + unset($data['id']); + + try { + $customerModel->setData($data); + $customerModel->setImportMode(true); + $customerModel->setWebsiteId($websiteId > 0 ? $websiteId: $this->getCurrentWebsite()->getId()); + $customerModel->save(); + $customerId = $customerModel->getId(); + + if ($customerAddresses) foreach ($customerAddresses as $customerAddress) { + $customerAddress['telephone'] = $data['telephone']; + $customerAddress['fax'] = $data['fax']; + $addressModel->unsetData(); + $addressModel->setData($customerAddress); + $addressModel->setCustomerId($customerId); + $addressModel->setId(null); + $addressModel->save(); + if ($defaultBilling == $customerAddress['id']) { + $addressId = $addressModel->getId(); + $customerModel->setDefaultBilling($addressId); + $customerModel->setDefaultShipping($addressId); + } + } + $customerModel->save(); + $this->saveLogs(array($oscCustomerId => $customerId), 'customer'); + $this->_saveRows++; + } catch (Exception $e) { + $this->_addErrors(Mage::helper('oscommerce')->__('Email %s cannot be saved because of %s', $data['email'], $e->getMessage())); + } + } + } + + public function getCustomerIdPair() + { + if (!$this->_customerIdPair) { + $this->_customerIdPair = $this->getLogPairsByTypeCode('customer'); + } + return $this->_customerIdPair; + } + + public function setCustomerIdPair($data) + { + if (is_array($data)) { + $this->_customerIdPair = $data; + } + } + + public function importCategories($startFrom = 0, $useStartFrom = false) + { + $importModel = $this->getImportModel(); + $this->_logData['type_id'] = $this->getImportTypeIdByCode('category'); + $this->_logData['import_id'] = $importModel->getId(); + $categoryModel = $this->getCategoryModel(); + + $this->_resetSaveRows(); + $this->_resetErrors(); + $maxRows = $this->getMaxRows(); + $totalCategories = $this->getCategoriesCount(); + + $pages = floor($totalCategories / $maxRows) + 1; + if (!$useStartFrom) { + for ($i = 0; $i < $pages; $i++) { + if ($categories = $this->getCategories(array('from'=> $i * $maxRows,'max'=>$maxRows))) { + foreach ($categories as $category) { + $this->_saveCategory($category); + } + } + } + } else { + if ($categories = $this->getCategories(array('from'=> $startFrom ,'max'=>$maxRows))) { + foreach ($categories as $category) { + $this->_saveCategory($category); + } + } + } + } + + protected function _saveCategory($data) { + $importModel = $this->getImportModel(); + $categoryModel = $this->getCategoryModel(); + $oscCategoryId = $data['id']; + unset($data['id']); + try { + $data['store_id'] = 0; + $data['is_active'] = 1; + $data['display_mode'] = self::DEFAULT_DISPLAY_MODE; + $data['is_anchor'] = self::DEFAULT_IS_ANCHOR; + $data['attribute_set_id'] = $categoryModel->getDefaultAttributeSetId(); + $data['name'] = $this->convert($data['name']); + $data['meta_title'] = html_entity_decode($data['name'], ENT_QUOTES, self::DEFAULT_MAGENTO_CHARSET); + $categoryModel->setData($data); + $categoryModel->save(); + $categoryId = $categoryModel->getId(); + $this->saveLogs(array($oscCategoryId => $categoryId), 'category'); + + // saving data for different (encoding has been done in getCategoryToStores method) + $storeData = $data['stores']; + unset($data['stores']); + if (isset($storeData)) { + foreach($storeData as $storeId=>$catData) { + $categoryModel->setStoreId($storeId)->setName($catData['name'])->setMetaTitle($catData['name']) + ->save(); + } + } + $this->_saveRows++; + } catch (Exception $e) { + $this->_addErrors(Mage::helper('oscommerce')->__('Category %s cannot be saved because of %s', $data['name'], $e->getMessage())); + } + } + + + + public function buildCategoryPath() + { + $categoryIdPair = $this->getCategoryIdPair(); + $importModel = $this->getImportModel(); + if ($categoryIdPair) foreach ($categoryIdPair as $oscommerceId => $magentoId) { + $path = $this->getRootCategory()->getPath().'/'.join('/',$this->getCategoryPath($oscommerceId)); + $this->_getWriteAdapter()->raw_query("UPDATE `{$this->getTable('catalog_category')}` SET `path`='{$path}' WHERE `entity_id`={$magentoId}"); + } + } + + public function getCategoryPath($categoryId) + { + $categoryIdPair = $this->getCategoryIdPair(); + $select = "SELECT `c`.`parent_id` FROM `{$this->getOscTable('categories')}` c "; + $select .= " WHERE `c`.`categories_id`={$categoryId}"; + if ($parentId = $this->_getForeignAdapter()->fetchOne($select)) { + if ($result = $this->getCategoryPath($parentId)) { + if (!isset($results)) { + $results = $result; + } else { + array_merge($results, $result); + } + } else { + $results[] = $categoryIdPair[$parentId]; + } + } + $results[] = $categoryIdPair[$categoryId]; + return $results; + } + + public function getCategoryIdPair() + { + if (!$this->_categoryIdPair) { + $this->_categoryIdPair = $this->getLogPairsByTypeCode('category'); + } + return $this->_categoryIdPair; + } + + public function setCategoryIdPair($data) + { + if (is_array($data)) { + $this->_categoryIdPair = $data; + } + } + + /** + * Import products + * + * @param Mage_Oscommerce_Model_Oscommerce $obj + */ + public function importProducts($startFrom = 0, $useStartFrom = false) + { + $importModel = $this->getImportModel(); + $productAdapterModel = Mage::getModel('catalog/convert_adapter_product'); + $productModel = $this->getProductModel(); + $taxCollections = $this->_getTaxCollections(); + $this->_resetSaveRows(); + $this->_resetErrors(); + $maxRows = $this->getMaxRows(); + $totalProducts = $this->getProductsCount(); + $pages = floor($totalProducts / $maxRows) + 1; + if (!$useStartFrom) { + for ($i = 0; $i < $pages; $i++) { + if ($products = $this->getProducts(array('from'=> $i * $maxRows,'max'=>$maxRows))) { + foreach ($products as $product) { + if (!empty($product['tax_class_id'])) { + $product['tax_class_id'] = $taxCollections[$product['tax_class_id']]; + } + $this->_saveProduct($product); + } + } + } + } else { + if ($products = $this->getProducts(array('from'=> $startFrom ,'max'=>$maxRows))) { + foreach ($products as $product) { + if (!empty($product['tax_class_id'])) { + $product['tax_class_id'] = $taxCollections[$product['tax_class_id']]; + } + $this->_saveProduct($product); + } + } + } + } + + /** + * Save products data + * + * @param Mage_Oscommerce_Model_Oscommerce $obj + * @param array $data + */ + protected function _saveProduct($data) { + $importModel = $this->getImportModel(); + $productAdapterModel = $this->getProductAdapterModel(); + $productModel = $this->getProductModel(); + $mageStores = $this->getLanguagesToStores(); + $storeInfo = $this->getOscStoreInformation(); + $storeName = $storeInfo['STORE_NAME']; + $oscProductId = $data['id']; + unset($data['id']); + if ($this->_isProductWithCategories) { + if ($categories = $this->getProductCategories($oscProductId)) + $data['category_ids'] = $categories; + } + + /** + * Checking product by using sku and website + */ + if (empty($data['sku'])) { + $data['sku'] = $storeName . ' - ' . $oscProductId; + } + $productModel->unsetData(); + $productId = $productModel->getIdBySku($data['sku']); + $productModel->load($productId); + if ($productModel->getId()) { + $websiteIds = $productModel->getWebsiteIds(); + + if ($websiteIds) foreach($websiteIds as $websiteId) { + if ($websiteId == $this->getWebsiteModel()->getId()) { + $this->_addErrors(Mage::helper('oscommerce')->__('SKU %s was not imported since it already exists in %s', + $data['sku'], + $this->getWebsiteModel()->getName())); + return ; + } + } + } + try { + if (isset($data['image'])) { + if (substr($data['image'], 0,1) != DS) { + $data['image'] = DS . $data['image']; + } + + if (!file_exists(Mage::getBaseDir('media'). DS . 'import' . $data['image'])) { + unset($data['image']); + } else { + $data['thumbnail'] = $data['small_image'] = $data['image']; + } + } + if ($stores = $this->getProductStores($oscProductId)) { + + foreach ($stores as $storeId => $store) { + if (!$storeCode = $this->getStoreCodeById($mageStores[$storeId])) { + $storeCode = $this->getCurrentWebsite()->getDefaultStore()->getCode(); + } + $data['store'] = $storeCode; + $data['name'] = html_entity_decode($this->convert($store['name']), ENT_QUOTES, self::DEFAULT_MAGENTO_CHARSET); + $data['description'] = html_entity_decode($this->convert($store['description']), ENT_QUOTES, self::DEFAULT_MAGENTO_CHARSET); + $data['short_description'] = $data['description']; + $productAdapterModel->saveRow($data); + } + } + + $productId = $productAdapterModel->getProductModel()->getId(); + $this->saveLogs(array($oscProductId => $productId), 'product'); + $this->_saveRows++; + } catch (Exception $e) { + $this->_addErrors(Mage::helper('oscommerce')->__('SKU %s cannot be saved because of %s', $data['sku'], $e->getMessage())); + } + } + + public function importOrders($startFrom = 0, $useStartFrom = false) + { + $importModel = $this->getImportModel(); + $this->_resetSaveRows(); + $this->_resetErrors(); + // Get orders + + $totalOrders = $this->getOrdersCount(); + $maxRows = $this->getMaxRows(); + $pages = floor($totalOrders / $maxRows) + 1; + + if (!$useStartFrom) { + for ($i = 0; $i < $pages; $i++) { + $orders = $this->getOrders(array('from' => $i * $maxRows, 'max' => $maxRows)); + if ($orders) foreach($orders as $order) { + $this->_saveOrder($order); + } + } + } else { + $orders = $this->getOrders(array('from' => $startFrom, 'max' => $maxRows)); + if ($orders) foreach($orders as $order) { + $this->_saveOrder($order); + } + } + } + + public function createOrderTables() + { + $importModel = $this->getImportModel(); + $importId = $importModel->getId(); + $websiteId = $this->getWebsiteModel()->getId(); + + $tables = array( + 'orders' => "CREATE TABLE `{$this->getTable('oscommerce_order')}` ( + `osc_magento_id` int(11) NOT NULL auto_increment, + `orders_id` int(11) NOT NULL default '0', + `customers_id` int(11) NOT NULL default '0', + `magento_customers_id` int(11) NOT NULL default '0', + `import_id` int(11) NOT NULL default '0', + `website_id` int(11) NOT NULL default '0', + `customers_name` varchar(64) NOT NULL default '', + `customers_company` varchar(32) default NULL, + `customers_street_address` varchar(64) NOT NULL default '', + `customers_suburb` varchar(32) default NULL, + `customers_city` varchar(32) NOT NULL default '', + `customers_postcode` varchar(10) NOT NULL default '', + `customers_state` varchar(32) default NULL, + `customers_country` varchar(32) NOT NULL default '', + `customers_telephone` varchar(32) NOT NULL default '', + `customers_email_address` varchar(96) NOT NULL default '', + `customers_address_format_id` int(5) NOT NULL default '0', + `delivery_name` varchar(64) NOT NULL default '', + `delivery_company` varchar(32) default NULL, + `delivery_street_address` varchar(64) NOT NULL default '', + `delivery_suburb` varchar(32) default NULL, + `delivery_city` varchar(32) NOT NULL default '', + `delivery_postcode` varchar(10) NOT NULL default '', + `delivery_state` varchar(32) default NULL, + `delivery_country` varchar(32) NOT NULL default '', + `delivery_address_format_id` int(5) NOT NULL default '0', + `billing_name` varchar(64) NOT NULL default '', + `billing_company` varchar(32) default NULL, + `billing_street_address` varchar(64) NOT NULL default '', + `billing_suburb` varchar(32) default NULL, + `billing_city` varchar(32) NOT NULL default '', + `billing_postcode` varchar(10) NOT NULL default '', + `billing_state` varchar(32) default NULL, + `billing_country` varchar(32) NOT NULL default '', + `billing_address_format_id` int(5) NOT NULL default '0', + `payment_method` varchar(255) NOT NULL default '', + `cc_type` varchar(20) default NULL, + `cc_owner` varchar(64) default NULL, + `cc_number` varchar(32) default NULL, + `cc_expires` varchar(4) default NULL, + `last_modified` datetime default NULL, + `date_purchased` datetime default NULL, + `orders_status` varchar(32) default NULL, + `orders_date_finished` datetime default NULL, + `currency` varchar(3) default NULL, + `currency_value` decimal(14,6) default NULL, + `currency_symbol` varchar(3) default NULL, + `orders_total` decimal(14,6) default NULL, + PRIMARY KEY (`osc_magento_id`), + KEY `idx_orders_customers_id` (`customers_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + " + , 'orders_products' => "CREATE TABLE `{$this->getTable('oscommerce_order_products')}` ( + `orders_products_id` int(11) NOT NULL auto_increment, + `osc_magento_id` int(11) NOT NULL default '0', + `products_id` int(11) NOT NULL default '0', + `products_model` varchar(12) default NULL, + `products_name` varchar(64) NOT NULL default '', + `products_price` decimal(15,4) NOT NULL default '0.0000', + `final_price` decimal(15,4) NOT NULL default '0.0000', + `products_tax` decimal(7,4) NOT NULL default '0.0000', + `products_quantity` int(2) NOT NULL default '0', + PRIMARY KEY (`orders_products_id`), + KEY `idx_orders_products_osc_magento_id` (`osc_magento_id`), + KEY `idx_orders_products_products_id` (`products_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + " + + , 'orders_total' => "CREATE TABLE IF NOT EXISTS `{$this->getTable('oscommerce_order_total')}` ( + `orders_total_id` int(10) unsigned NOT NULL auto_increment, + `osc_magento_id` int(11) NOT NULL default '0', + `title` varchar(255) NOT NULL default '', + `text` varchar(255) NOT NULL default '', + `value` decimal(15,4) NOT NULL default '0.0000', + `class` varchar(32) NOT NULL default '', + `sort_order` int(11) NOT NULL default '0', + PRIMARY KEY (`orders_total_id`), + KEY `idx_orders_total_osc_magento_id` (`osc_magento_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;" + + , 'orders_status_history'=>"CREATE TABLE IF NOT EXISTS `{$this->getTable('oscommerce_order_history')}` ( + `orders_status_history_id` int(11) NOT NULL auto_increment, + `osc_magento_id` int(11) NOT NULL default '0', + `orders_status_id` int(5) NOT NULL default '0', + `date_added` datetime NOT NULL default '0000-00-00 00:00:00', + `customer_notified` int(1) default '0', + `comments` text, + `orders_status` varchar(32) default NULL, + PRIMARY KEY (`orders_status_history_id`), + KEY `idx_orders_status_history_osc_magento_id` (`osc_magento_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;" + + ); + + $conn = $this->_setupConnection; + foreach ($tables as $table => $schema) { + $conn->beginTransaction(); + try { + $conn->query($schema); + $conn->commit(); + } catch (Exception $e) { +// $conn->rollBack(); + } + } + + $this->checkOrderField(); + } + + public function setTablePrefix($prefix) + { + if (isset($prefix)) $this->_prefix = $prefix; + } + + public function getTablePrefix() + { + return $this->_prefix; + } + + public function setIsProductWithCategories($yn) + { + if (is_bool($yn)) { + $this->_isProductWithCategories = $yn; + } + } + + /** + * Logging imported data to oscommerce_ref table + * + * @param array data + */ + public function log($data = array()) + { + if (isset($data)) { + $this->_getWriteAdapter()->beginTransaction(); + try { + $this->_getWriteAdapter()->insert($this->getTable('oscommerce_ref'), $data); + $this->_getWriteAdapter()->commit(); + } catch (Exception $e) { + $this->_getWriteAdapter()->rollBack(); + } + } + } + + public function getOscStoreInformation() + { + if (!$this->_oscStoreInformation) { + $select = "SELECT `configuration_key` `key`, `configuration_value` `value` FROM `{$this->getOscTable('configuration')}`"; + $select .= " WHERE `configuration_key` IN ('STORE_NAME', 'STORE_OWNER', 'STORE_OWNER_EMAIL', 'STORE_COUNTRY',' STORE_ZONE','DEFAULT_LANGUAGE')"; + if (!($result = $this->_getForeignAdapter()->fetchPairs($select))) { + $result = array(); + } + $this->_oscStoreInformation = $result; + } + return $this->_oscStoreInformation; + } + + /** + * Getting products data from osCommerce + * + */ + public function getProducts($limit = array()) + { + $defaultLanguage = $this->getOscDefaultLanguage(); + $defaultLanguageId = $defaultLanguage['id']; + $code = $this->getWebsiteModel()->getCode(); + $website = $code? $code: $this->getCurrentWebsite()->getCode(); + $connection = $this->_getForeignAdapter(); + $select = " SELECT `p`.`products_id` `id`, `p`.`products_quantity` `qty` "; + $select .= " , `p`.`products_model` `sku`, `p`.`products_price` `price`"; + $select .= " , `p`.`products_image` `image` "; + $select .= " , `p`.`products_weight` `weight`, IF(`p`.`products_status`,'Enabled','Disabled') `status` "; + $select .= " , IF(`p`.`products_status`,'1','0') `is_in_stock`"; + $select .= " , `pd`.`products_name` `name`, `pd`.`products_description` `description` "; +// $select .= " , `tc`.`tax_class_title` `tax_class_id`, IF(1,'".self::DEFAULT_VISIBILITY."','') `visibility` "; + $select .= " , `p`.`products_tax_class_id` `tax_class_id`, IF(1,'".self::DEFAULT_VISIBILITY."','') `visibility` "; + $select .= " , `sp`.`specials_new_products_price` `special_price` "; + $select .= " , `sp`.`specials_date_added` `special_from_date` "; + $select .= " , `sp`.`expires_date` `special_to_date` "; + $select .= " , IF(1,'".self::DEFAULT_ATTRIBUTE_SET."','') `attribute_set` "; + $select .= " , IF(1,'".self::DEFAULT_PRODUCT_TYPE ."','') `type` "; + //$select .= ", IF(1,'".self::DEFAULT_STORE."','') `store` "; + $select .= " , IF(1,'".$website."','') `website` "; + $select .= " FROM `{$this->getOscTable('products')}` p LEFT JOIN `{$this->getOscTable('products_description')}` pd "; + $select .= " ON `pd`.`products_id`=`p`.`products_id` AND `pd`.`language_id`={$defaultLanguageId} "; +// $select .= " LEFT JOIN `{$this->getOscTable('tax_class')}` tc ON `tc`.`tax_class_id`=`p`.`products_tax_class_id` "; + $select .= " LEFT JOIN `{$this->getOscTable('specials')}` sp ON `sp`.`products_id`=`p`.`products_id` "; + if ($limit && isset($limit['from']) && isset($limit['max'])) { + $select .= " LIMIT {$limit['from']}, {$limit['max']}"; + } + if (!($result = $this->_getForeignAdapter()->fetchAll($select))) { + $result = array(); + } + return $result; + } + + public function getProductsCount() + { + return $this->_getForeignAdapter()->fetchOne("SELECT count(*) FROM `{$this->getOscTable('products')}`"); + } + + public function getCategoriesCount() + { + return $this->_getForeignAdapter()->fetchOne("SELECT count(*) FROM `{$this->getOscTable('categories')}`"); + } + + public function getCustomersCount() + { + return $this->_getForeignAdapter()->fetchOne("SELECT count(*) FROM `{$this->getOscTable('customers')}`"); + } + + public function getOrdersCount() + { + return $this->_getForeignAdapter()->fetchOne("SELECT count(*) FROM `{$this->getOscTable('orders')}`"); + } + + public function getOrders($limit = array()) { + $defaultLanguage = $this->getOscDefaultLanguage(); + $defaultLanguageId = $defaultLanguage['id']; + $select = "SELECT `o`.`orders_id`, `o`.`customers_id`, `o`.`customers_name`"; + $select .= " ,`o`.`customers_company`,`o`.`customers_street_address`"; + $select .= " ,`o`.`customers_suburb`, `o`.`customers_city`"; + $select .= " ,`o`.`customers_postcode`, `o`.`customers_state`"; + $select .= " ,`o`.`customers_country`, `o`.`customers_telephone`"; + $select .= " ,`o`.`customers_email_address`, `o`.`customers_address_format_id`"; + $select .= " ,`o`.`delivery_name`, `o`.`delivery_company`"; + $select .= " ,`o`.`delivery_street_address`, `o`.`delivery_suburb`"; + $select .= " ,`o`.`delivery_city`, `o`.`delivery_postcode`, `o`.`delivery_state`"; + $select .= " ,`o`.`delivery_country`, `o`.`delivery_address_format_id`"; + $select .= " ,`o`.`billing_name`, `o`.`billing_company`"; + $select .= " ,`o`.`billing_street_address`, `o`.`billing_suburb`"; + $select .= " ,`o`.`billing_city`, `o`.`billing_postcode`, `o`.`billing_state`"; + $select .= " ,`o`.`billing_country`, `o`.`billing_address_format_id`"; + $select .= " ,`o`.`payment_method`, `o`.`cc_type`, `o`.`cc_owner`, `o`.`cc_number`"; + $select .= " ,`o`.`cc_expires`, `o`.`last_modified`, `o`.`date_purchased`"; + $select .= " ,`o`.`orders_status`, `o`.`orders_date_finished`, `o`.`currency`, `o`.`currency_value`"; + $select .= " ,`c`.`symbol_left` `currency_symbol`,`ot`.`value` `orders_total`"; + $select .= " ,`os`.`orders_status_name` FROM `{$this->getOscTable('orders')}` `o`"; + $select .= " LEFT JOIN `{$this->getOscTable('currencies')}` `c` ON `c`.`code`=`o`.`currency` "; + $select .= " LEFT JOIN `{$this->getOscTable('orders_total')}` `ot` ON `ot`.`orders_id`=`o`.`orders_id` "; + $select .= " AND `ot`.`class`='ot_total'"; + $select .= " LEFT JOIN `{$this->getOscTable('orders_status')}` os ON `os`.`orders_status_id`=`o`.`orders_status` "; + $select .= " AND `os`.`language_id`={$defaultLanguageId} "; + if (isset($limit) && isset($limit['from']) && isset($limit['max'])) { + $select .= " LIMIT {$limit['from']}, {$limit['max']} "; + } + if (!($result = $this->_getForeignAdapter()->fetchAll($select))) { + $result = array(); + } + return $result; + } + + protected function _saveOrder($data) + { + $fieldNoEnc = array( + 'customers_id', + 'orders_id', + 'date_purchased', + 'last_modified', + 'orders_date_finished', + 'orders_products_id', + 'osc_magento_id', + 'products_id' + ); + + $importModel = $this->getImportModel(); + $timezone = $importModel->getTimezone(); + $customerIdPair = $this->getCustomerIdPair(); + $importId = $importModel->getId(); + $websiteId = $this->getWebsiteModel()->getId(); + if ($data['customers_id'] > 0 && isset($this->_customerIdPair[$data['customers_id']])) { + foreach($data as $field => $value) { + if (!in_array($field, $fieldNoEnc)) { + $data[$field] = $this->convert($value); + } + } + + if ($data['date_purchased']) { + $preparePurchased = explode(' ', $data['date_purchased']); + $dateFormat = 'YYYY-MM-dd HH:mm:ss'; + $datePurchased = new Zend_Date(); + $datePurchased->setTimezone($timezone); + $datePurchased->setDate($preparePurchased[0], 'YYYY-MM-dd'); + $datePurchased->setTime($preparePurchased[1], 'HH:mm:ss'); + $datePurchased->setTimezone('GMT'); + $data['date_purchased'] = $datePurchased->toString($dateFormat); + } + + if ($data['last_modified']) { + $prepareModified = explode(' ', $data['last_modified']); + $dateModified = new Zend_Date(); + $dateModified->setTimezone($timezone); + $dateModified->setDate($prepareModified[0], 'YYYY-MM-dd'); + $dateModified->setTime($prepareModified[1], 'HH:mm:ss'); + $dateModified->setTimezone('GMT'); + $data['last_modified'] = $dateModified->toString($dateFormat); + } + + if ($data['orders_date_finished']) { + $prepareFinished = explode(' ', $data['orders_date_finished']); + $dateFinished = new Zend_Date(); + $dateFinished->setTimezone($timezone); + $dateFinished->setDate($prepareFinished[0], 'YYYY-MM-dd'); + $dateFinished->setTime($prepareFinished[1], 'HH:mm:ss'); + $dateFinished->setTimezone('GMT'); + $data['orders_date_finished'] = $dateFinished->toString($dateFormat); + } + + $data['magento_customers_id'] = $this->_customerIdPair[$data['customers_id']]; // get Magento CustomerId + $data['import_id'] = $importId; + $data['website_id'] = $websiteId; + $data['orders_status'] = $data['orders_status_name']; + unset($data['orders_status_name']); + $this->_getWriteAdapter()->insert($this->getTable('oscommerce_order'), $data); + $oscMagentoId = $this->_getWriteAdapter()->lastInsertId(); + $this->_saveRows++; + + // Get orders products + $select = "SELECT `orders_products_id`, `orders_id`, `products_id` "; + $select .= ", `products_model`, `products_name`, `products_price`, `final_price` "; + $select .= ", `products_tax`, `products_quantity` "; + $select .= " FROM `{$this->getOscTable('orders_products')}` WHERE `orders_id`={$data['orders_id']}"; + if ($orderProducts = $this->_getForeignAdapter()->fetchAll($select)) { + foreach ($orderProducts as $orderProduct) { + unset($orderProduct['orders_id']); + unset($orderProduct['orders_products_id']); + $orderProduct['osc_magento_id'] = $oscMagentoId; + foreach ($orderProduct as $field => $value) { + if (!in_array($field, $fieldNoEnc)) { + $orderProduct[$field] = $this->convert($value); + } + } + $this->_getWriteAdapter()->insert($this->getTable('oscommerce_order_products'), $orderProduct); + } + } + + // Get orders totals + $select = "SELECT `orders_total_id`, `orders_id`, `title`, `text`, `value`, `class`, `sort_order` "; + $select .= " FROM `{$this->getOscTable('orders_total')}` WHERE `orders_id`={$data['orders_id']} ORDER BY `sort_order`"; + + if ($orderTotals = $this->_getForeignAdapter()->fetchAll($select)) { + foreach ($orderTotals as $orderTotal) { + + unset($orderTotal['orders_id']); + unset($orderTotal['orders_total_id']); + $orderTotal['osc_magento_id'] = $oscMagentoId; + $orderTotal['title'] = $this->convert($orderTotal['title']); + $orderTotal['text'] = $this->convert($orderTotal['text']); + $this->_getWriteAdapter()->insert($this->getTable('oscommerce_order_total'), $orderTotal); + } + } + + $defaultLanguage = $this->getOscDefaultLanguage(); + $defaultLanguageId = $defaultLanguage['id']; + + // Get orders status history + $select = "SELECT `osh`.`orders_status_history_id`, `osh`.`orders_id`, `osh`.`orders_status_id` "; + $select .= ", `os`.`orders_status_name` `orders_status`, `osh`.`date_added`, `osh`.`customer_notified`, `osh`.`comments` "; + $select .= " FROM `{$this->getOscTable('orders_status_history')}` osh "; + $select .= " LEFT JOIN `{$this->getOscTable('orders_status')}` os ON `os`.`orders_status_id`=`osh`.`orders_status_id` "; + $select .= " AND `os`.`language_id`={$defaultLanguageId}"; + $select .= " WHERE `osh`.`orders_id`={$data['orders_id']}"; + if ($orderHistories = $this->_getForeignAdapter()->fetchAll($select)) { + foreach ($orderHistories as $orderHistory) { + unset($orderHistory['orders_id']); + unset($orderHistory['orders_status_history_id']); + $orderHistory['osc_magento_id'] = $oscMagentoId; + $prepareAdded = explode(' ', $orderHistory['date_added']); + $dateFormat = 'YYYY-MM-dd HH:mm:ss'; + $dateAdded = new Zend_Date(); + $dateAdded->setTimezone($timezone); + $dateAdded->setDate($prepareAdded[0], 'YYYY-MM-dd'); + $dateAdded->setTime($prepareAdded[1], 'HH:mm:ss'); + $dateAdded->setTimezone('GMT'); + $orderHistory['date_added'] = $dateAdded->toString($dateFormat); + $orderHistory['orders_status'] = $this->convert($orderHistory['orders_status']); + $orderHistory['comments'] = $this->convert($orderHistory['comments']); + $orderHistory['customer_notified'] = $this->convert($orderHistory['customer_notified']); + + $this->_getWriteAdapter()->insert($this->getTable('oscommerce_order_history'), $orderHistory); + } + } + } else { + $this->_addErrors(Mage::helper('oscommerce')->__('Order #%s failed to import because the customer ID #%s associated with this order could not be found.', $data['orders_id'], $data['customers_id'])); + } + } + + /** + * Getting product description for different stores + * + * @param integer $productId + * @return mix array/boolean + */ + public function getProductStores($productId) { + if (!$this->_productsToStores) { + $select = "SELECT `products_id`, `language_id` `store`, `products_name` `name`, `products_description` `description`"; + $select .= " FROM `{$this->getOscTable('products_description')}` "; + if ($results = $this->_getForeignAdapter()->fetchAll($select)) { + foreach ($results as $result) { + $this->_productsToStores[$result['products_id']][$result['store']] = array('name'=>$result['name'], 'description' => $result['description']); + } + } + } + if (isset($this->_productsToStores[$productId])) { + return $this->_productsToStores[$productId]; + } + return false; + } + + /** + * Getting new created categories recursively using products of osCommerce + * + * @param integer $productId + * @return string + */ + public function getProductCategories($productId) + { + $importModel = $this->getImportModel(); + if (!$this->_productsToCategories) { + $select = "SELECT `products_id`, `categories_id` FROM `{$this->getOscTable('products_to_categories')}`"; + + if ($results = $this->_getForeignAdapter()->fetchAll($select)) { + $categories = array(); + foreach ($results as $result) { + $categories[$result['products_id']] = $result['categories_id']; + if (isset($categories[$result['products_id']])) { + $categories[$result['products_id']] .= ','.$result['categories_id']; + } else { + $categories[$result['products_id']] = $result['categories_id']; + } + } + //$categories = join(',', array_values($results)); + + //$this->_getReadAdapter(); + $importId = $importModel->getId(); + $typeId = $this->getImportTypeIdByCode('category'); + + + if ($categories) foreach ($categories as $product => $category) { + $select = $this->_getReadAdapter()->select(); + $select->from(array('osc'=>$this->getTable('oscommerce_ref')), array('id'=>'id','ref_id'=>'ref_id')); + $select->where("`osc`.`import_id`='{$importId}' AND `osc`.`type_id`='{$typeId}' AND `osc`.`value` in (".$category.")"); + $resultCategories = $this->_getReadAdapter()->fetchPairs($select); + if ($resultCategories) { + $this->_productsToCategories[$product] = join(',',array_values($resultCategories)); + } + } + } + } + if (isset($this->_productsToCategories[$productId])) { + return $this->_productsToCategories[$productId]; + } + return false; + } + + public function getCategories($limit = array()) { + $importModel = $this->getImportModel(); + $defaultLanguage = $this->getOscDefaultLanguage(); + $defaultLanguageId = $defaultLanguage['id']; + $select = "SELECT `c`.`categories_id` as `id`, `c`.`parent_id`, `cd`.`categories_name` `name` FROM `{$this->getOscTable('categories')}` c ";// WHERE `c`.`parent_id`={$parentId}"; + $select .= " INNER JOIN `{$this->getOscTable('categories_description')}` cd on `cd`.`categories_id`=`c`.`categories_id`"; + $select .= " AND `cd`.`language_id`={$defaultLanguageId} "; + if ($limit && isset($limit['from']) && isset($limit['max'])) { + $select .= " LIMIT {$limit['from']}, {$limit['max']} "; + } + if (!$results = $this->_getForeignAdapter()->fetchAll($select)) { + $results = array(); + } else { + $stores = $this->getLanguagesToStores(); + foreach($results as $index => $result) { + if ($categoriesToStores = $this->getCategoriesToStores($result['id'])) { + foreach($categoriesToStores as $store => $categoriesName) { + $results[$index]['stores'][$stores[$store]] = array( + 'name'=>html_entity_decode($this->convert($categoriesName), ENT_QUOTES, self::DEFAULT_MAGENTO_CHARSET) + ); + } + } + } + } + return $results; + } + + /** + * Getting language to Magento store data + * + * @return array + */ + public function getLanguagesToStores() + { + $importModel = $this->getImportModel(); + $typeId = $this->getImportTypeIdByCode('store'); + $importId = $importModel->getId(); + if (!$this->_languagesToStores) { + //$this->_languagesToStores[1] = 1; + $select = $this->_getReadAdapter()->select(); + $select->from(array('ref'=>$this->getTable('oscommerce_ref')), array('value'=>'value', 'ref_id'=>'ref_id')); + $select->where("`ref`.`import_id`='{$importId}' AND `ref`.`type_id`='{$typeId}'"); + $this->_languagesToStores = $this->_getReadAdapter()->fetchPairs($select); + } + return $this->_languagesToStores; + } + + /** + * Getting categry description for different languages + * + * @param integer $categoryId + * @return mix array/boolean + */ + public function getCategoriesToStores($categoryId) + { + $select = "SELECT `language_id`, `categories_name` FROM `{$this->getOscTable('categories_description')}`"; + $select .= "WHERE `categories_id`='{$categoryId}'"; + if ($categoryId && $result = $this->_getForeignAdapter()->fetchPairs($select)) { + return $result; + } + return false; + } + + /** + * Getting store data of osCommerce + * + * @return array + */ + public function getOscStores() + { + if (!$this->_oscStores) { + $select = "SELECT `languages_id` `id`, `name`, `code` `scode`, "; + $select .= " `directory` `code`, 1 `is_active` FROM `{$this->getOscTable('languages')}`"; + $this->_oscStores = $this->_getForeignAdapter()->fetchAll($select); + } + return $this->_oscStores; + } + + + public function getOscDefaultLanguage() + { + if (!$this->_oscDefaultLanguage) { + $oscStoreInfo = $this->getOscStoreInformation(); + $languageCode = $oscStoreInfo['DEFAULT_LANGUAGE']; + if ($stores = $this->getOscStores()) foreach($stores as $store) { + if ($store['scode'] == $languageCode) { + $this->_oscDefaultLanguage = $store; + } + } + } + return $this->_oscDefaultLanguage; + } + + /** + * Getting customers from osCommerce + * + * @return array + */ + public function getCustomers($limit = array()) + { + $select = "SELECT `c`.`customers_id` `id`, `c`.`customers_firstname` `firstname` "; + $select .= " ,`c`.`customers_lastname` `lastname`, `c`.`customers_email_address` `email` "; + $select .= " ,`c`.`customers_telephone` `telephone`, `c`.`customers_fax` `fax` "; + $select .= " ,`c`.`customers_password` `password_hash`, `c`.`customers_newsletter` `is_subscribed` "; + $select .= " ,`ci`.`customers_info_date_account_created` `created_at` "; + $select .= " ,`c`.`customers_default_address_id` `default_billing` FROM `{$this->getOscTable('customers')}` c"; + $select .= " LEFT JOIN `customers_info` ci ON `ci`.`customers_info_id`=`c`.`customers_id` "; + if ($limit && isset($limit['from']) && isset($limit['max'])) { + $select .= " LIMIT {$limit['from']}, {$limit['max']}"; + } + + if (!($result = $this->_getForeignAdapter()->fetchAll($select))) { + $result = array(); + } + + return $result; + } + + public function getTotalCustomers() { + return $this->_getForeignAdapter()->fetchOne("SELECT count(*) FROM `{$this->getOscTable('customers')}`"); + } + + public function getCustomerName($name) + { + if (isset($name)) { + $n = explode(" ", $name); + if (sizeof($n) > 1) { + $newName['lastname'] = $n[(sizeof($n) - 1)]; + $newName['fistname'] = Mage::helper('core/string')->substr($name, 0, + Mage::helper('core/string')->strlen($name) - (Mage::helper('core/string')->strlen($newName['lastname'] + 1)) + ); + return $newName; + } else { + return array('firstname' => $n); + } + } + return false; + } + + + /** + * Getting customer address by CustomerId from osCommerce + * + * @param integer $customerId + * @return array + */ + public function getAddresses($customerId) + { + + $select = "SELECT `address_book_id` `id`, `customers_id`, `entry_firstname` `firstname`"; + $select .= ", `entry_lastname` `lastname`, `entry_street_address` `street1`"; + $select .= ", `entry_company` `company` "; + $select .= ", `entry_postcode` `postcode`, `entry_city` `city`"; + $select .= ", `entry_state` `region`, `entry_country_id` `country_id`"; + $select .= ", `entry_zone_id` `region_id` FROM `{$this->getOscTable('address_book')}` WHERE customers_id={$customerId}"; + if (!isset($customerId) || !($result = $this->_getForeignAdapter()->fetchAll($select))) { + $result = array(); + } + return $result; + } + + /** + * Get address from address book + * + * @param integer $address_id + * @return array + */ + public function getAddressById($addressId) + { + + $select = "SELECT `address_book_id` `id`, `customers_id`, `entry_firstname` `firstname`"; + $select .= ", `entry_lastname` `lastname`, `entry_street_address` `street1`"; + $select .= ", `entry_postcode` `postcode`, `entry_city` `city`"; + $select .= ", `entry_state` `region`, `entry_country_id` `country_id`"; + $select .= ", `entry_zone_id` `region_id` FROM `{$this->getOscTable('address_book')}` WHERE address_book_id={$addressId}"; + if (!isset($addressId) || !($result = $this->_getForeignAdapter()->fetchRow($select))) { + $result = array(); + } + return $result; + } + + /** + * Getting importing types for loging into oscommerce_ref + * + * @return array + */ + public function getImportTypes() + { + if (! $this->_importType) { + $connection = $this->_getReadAdapter(); + $select = $connection->select(); + $select->from($this->getTable('oscommerce_type'), array('*')); + $this->_importType = $connection->fetchAll($select); + } + return $this->_importType; + } + + /** + * Getting import_type_id by code + * + * @param integer $code + * @return string/boolean + */ + public function getImportTypeIdByCode($code = '') { + $types = $this->getImportTypes(); + if (isset($code) && $types) { + foreach ($types as $type) { + if ($type['type_code'] == $code) { + return $type['type_id']; + } + } + } + return false; + } + + public function getCountryCodeData() + { + $select = "SELECT * FROM `{$this->getOscTable('countries')}`"; + $countries = $this->_getForeignAdapter()->fetchAll($select); + if ($countries) foreach($countries as $country) { + $this->_countryIdToCode[$country['countries_id']] = $country['countries_iso_code_2']; + $this->_countryNameToCode[$country['countries_name']] = $country['countries_iso_code_2']; + } + } + + /** + * Getting country code by country id + * + * @param integer $id + * @return string/boolean + */ + public function getCountryCodeById($id) + { + if (!$this->_countryIdToCode) { + $this->getCountryCodeData(); + } + $countries = $this->_countryIdToCode; + if (isset($id) && isset($countries[$id])) { + return $countries[$id]; + } + return false; + } + + public function getCountryCodeByName($name) + { + if (!$this->_countryNameToCode) { + $this->getCountryCodeData(); + } + $countries = $this->_countryNameToCode; + if (isset($id) && isset($countries[$name])) { + return $countries[$name]; + } + return false; + } + + public function getCountryIdByCode($countryCode) + { + if (!$this->_countryIdToCode) { + $this->getCountryCodeData(); + } + if (isset($code)) { + foreach($this->_countryToCode as $id => $code) { + if ($code == $countryCode) { + return $id; + } + } + } + return false; + } + + + /** + * Getting regions from osCommerce + * + * @return array + */ + public function getRegions() + { + if (!$this->_regionCode) { + $select = "SELECT `zone_id`, `zone_name` FROM `{$this->getOscTable('zones')}`"; + $this->_regionCode = $this->_getForeignAdapter()->fetchPairs($select); + } + return $this->_regionCode; + } + + /** + * Getting region name by id + * + * @param integer $id + * @return string/boolean + */ + public function getRegionCode($id) + { + $regions = $this->getRegions(); + if (isset($id) && isset($regions[$id])) { + return $regions[$id]; + } + return false; + } + + public function setStoreLocales($locale) + { + if (isset($locale) && is_array($locale)) + $this->_storeLocales = $locale; + } + + public function getStoreLocales() + { + if ($this->_storeLocales) { + return $this->_storeLocales; + } else { + return array('default' => self::DEFAULT_LOCALE ); + } + } + + public function setRootCategory(Mage_Catalog_Model_Category $category) { + $this->_rootCategory = $category; + } + + public function getRootCategory() + { + if (!$this->_rootCategory) { + $this->_rootCategory = $this->getCategoryModel()->load($this->getCurrentWebsite()->getDefaultStoreGroup()->getRootCategoryId()); + } + return $this->_rootCategory; + } + + public function setWebsiteId($id) + { + $this->_websiteId = (int) ($id ? $id : 0); + } + + public function importTaxClasses() + { + $taxModel = Mage::getModel('tax/class'); + $storeInfo = $this->getOscStoreInformation(); + $storeName = $storeInfo['STORE_NAME']; + $taxPairs = array(); + if ($classes = $this->getTaxClasses()) { + $existedClasses = $taxCollections = Mage::getResourceModel('tax/class_collection') + ->addFieldToFilter('class_type', 'PRODUCT') + ->load() + ->toOptionHash(); + + foreach ($classes as $id => $name) { + $taxModel->unsData(); + $className = $name . '_' . $storeName; + if (in_array($className, $existedClasses)) { + $taxId = array_search($className, $existedClasses); + } else { + $taxModel->setId(null); + $taxModel->setClassType('PRODUCT'); + $taxModel->setClassName($name . '_' . $storeName); + $taxModel->save(); + $taxId = $taxModel->getId(); + } + $taxPairs[$id] = $taxId; + } + } + + if (sizeof($taxPairs) > 0) { + $this->saveLogs($taxPairs, 'taxclass'); + } + } + + protected function _getTaxCollections() + { + $taxPairs = $this->getLogPairsByTypeCode('taxclass'); + $flipTaxPairs = array_flip($taxPairs); + $newTaxPairs = array(); + $taxCollections = Mage::getResourceModel('tax/class_collection') + ->addFieldToFilter('class_type', 'PRODUCT') + ->load() + ->toOptionArray(); + if ($taxCollections) { + foreach ($taxCollections as $tax) { + if (isset($flipTaxPairs[$tax['value']])) { + $newTaxPairs[$flipTaxPairs[$tax['value']]] = $tax['label']; + } + } + } + return $newTaxPairs; + } + + public function saveLogs($data, $type = null) + { + $importId = $this->getImportModel()->getId(); + $typeId = $this->getImportTypeIdByCode($type); + $userId = $this->_getCurrentUserId(); + $createdAt = $this->formatDate(time()); + if (is_array($data) && $typeId > 0) { + foreach($data as $value => $refId) { + $log = array( + 'value' => $value, + 'ref_id' => $refId, + 'import_id' => $importId, + 'type_id' => $typeId, + 'user_id' => $userId, + 'created_at'=> $createdAt + ); + $this->_getWriteAdapter()->insert($this->getTable('oscommerce_ref'), $log); + } + } + } + + public function getLogPairsByTypeCode($code) + { + $typeId = $this->getImportTypeIdByCode($code); + $importId = $this->getImportModel()->getId(); + $result = array(); + if (!is_null($typeId)) { + $select = $this->_getReadAdapter()->select(); + $select->from($this->getTable('oscommerce_ref'), array('value','ref_id')) + ->where("import_id={$importId}") + ->where("type_id={$typeId}"); + $result = $this->_getReadAdapter()->fetchPairs($select); + } + return $result; + } + + public function getTaxClasses() + { + $select = "SELECT `tax_class_id` `id`, `tax_class_title` `title` FROM `{$this->getOscTable('tax_class')}`"; + if (!($results = $this->_getForeignAdapter()->fetchPairs($select))) { + $results = array(); + } + return $results; + } + + private function _format($str) + { + $str = preg_replace('#[^0-9a-z\/\.]+#i', '', $str); + $str = strtolower(str_replace('\\s','',$str)); + return $str; + } + + public function setPrefixPath($prefix) { + if ($prefix) { + $this->_prefixPath = $prefix; + } + } + + /** + * Load osCommerce orders + * + * @param integer $customerId + * @param integer $websiteId + * @return array + */ + public function loadOrders($customerId, $websiteId = '') + { + if (!isset($websiteId)) { + $webisteId = $this->_currentWebsiteId; + } + $result = array(); + if (!empty($customerId)) { + $select = $this->_getReadAdapter()->select() + ->from(array('order'=>$this->getTable('oscommerce_order'))) + ->join( + array('order_total'=>$this->getTable('oscommerce_order_total')), + "order_total.osc_magento_id=order.osc_magento_id AND order_total.class='ot_total'", + array('value')) + ->where("order.magento_customers_id={$customerId}") + ->where("order.website_id={$websiteId}"); + $result = $this->_getReadAdapter()->fetchAll($select); + } + return $result; + } + + /** + * Load osCommerce order + * + * @param integer $id + * @return array + */ + public function loadOrderById($id) + { + $result = array(); + if (!empty($id)) { + $select = "SELECT * FROM {$this->getTable('oscommerce_order')} WHERE osc_magento_id={$id}"; + $order = $this->_getReadAdapter()->fetchRow($select); + if ($order) { + $result['order'] = $order; + foreach (array('products','total','history') as $table) { + $select = "SELECT * FROM {$this->getTable('oscommerce_order_'.$table)} WHERE osc_magento_id={$id}"; + $result[$table] = $this->_getReadAdapter()->fetchAll($select); + + } + } + + } + return $result; + } + + // Fix for previous version + protected function checkOrderField() + { + $columnName = 'currency_symbol'; + try { + if (!($result = $this->_getReadAdapter()->fetchRow("SHOW `columns` FROM `{$this->getTable('oscommerce_order')}` WHERE field='{$columnName}'"))) { + $this->_setupConnection()->query("ALTER TABLE `{$this->getTable('oscommerce_order')}` ADD {$columnName} char(3) DEFAULT NULL"); + $this->_setupConnection()->commit(); + } + } catch (Exception $e) { + + } + } + + public function setMaxRows($rows) + { + if (is_integer($rows)) { + $this->_maxRows = $rows; + } + } + + public function getMaxRows() + { + if ($this->_maxRows <= 0) { + $this->_maxRows = Mage::getStoreConfig('oscommerce/import/max_rows'); + } + return $this->_maxRows; + } + + /** + * Retrieve website model cache + * + * @return Mage_Core_Model_Web + */ + public function getWebsiteModel() + { + if (is_null($this->_websiteModel)) { + $object = Mage::getModel('core/website'); + $this->_websiteModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_websiteModel); + } + + /** + * Retrieve store model cache + * + * @return Mage_Core_Model_Store + */ + public function getStoreModel() + { + if (is_null($this->_storeModel)) { + $object = Mage::getModel('core/store'); + $this->_storeModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_storeModel); + } + + /** + * Retrieve customer model cache + * + * @return Mage_Customer_Model_Customer + */ + public function getCustomerModel() + { + if (is_null($this->_customerModel)) { + $object = Mage::getModel('customer/customer'); + $this->_customerModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_customerModel); + } + + /** + * Retrieve customer model cache + * + * @return Mage_Customer_Model_Customer + */ + public function getCustomerGroupModel() + { + if (is_null($this->_customerGroupModel)) { + $object = Mage::getModel('customer/group'); + $this->_customerGroupModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_customerGroupModel); + } + + /** + * Retrieve address model cache + * + * @return Mage_Customer_Model_Address + */ + public function getAddressModel() + { + if (is_null($this->_addressModel)) { + $object = Mage::getModel('customer/address'); + $this->_addressModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_addressModel); + } + + + /** + * Retrieve category model cache + * + * @return Mage_Catalog_Model_Category + */ + public function getCategoryModel() + { + if (is_null($this->_categoryModel)) { + $object = Mage::getModel('catalog/category'); + $this->_categoryModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_categoryModel); + } + + /** + * Retrieve category model cache + * + * @return Mage_Catalog_Model_Category + */ + public function getProductModel() + { + if (is_null($this->_productModel)) { + $object = Mage::getModel('catalog/product'); + $this->_productModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_productModel); + } + + public function getProductAdapterModel() + { + if (is_null($this->_productAdapterModel)) { + $object = Mage::getModel('catalog/convert_adapter_product'); + $this->_productAdapterModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_productAdapterModel); + } + /** + * Retrieve store group model cache + * + * @return Mage_Core_Model_Store_Group + */ + public function getStoreGroupModel() + { + if (is_null($this->_storeGroupModel)) { + $object = Mage::getModel('core/store_group'); + $this->_storeGroupModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_storeGroupModel); + } + + public function getConfigModel() + { + if (is_null($this->_configModel)) { + $object = Mage::getModel('core/config_data'); + $this->_configModel = Varien_Object_Cache::singleton()->save($object); + } + return Varien_Object_Cache::singleton()->load($this->_configModel); + + } + + public function importCollection($importId = null) { + $importTypes = array('website', 'root_category', 'group'); + $result = array(); + if (!is_null($importId)) { + $select = $this->_getReadAdapter()->select() + ->from(array('ref'=>$this->getTable('oscommerce_ref'))) + ->join( + array('type'=>$this->getTable('oscommerce_type')), + "type.type_id=ref.type_id AND type.type_code in ('".join("','",$importTypes)."')", + array('type.type_code')) + ->where("ref.import_id={$importId}"); + if ($results = $this->_getReadAdapter()->fetchAll($select)) { + foreach ($results as $result) { + $this->_importCollection[$result['type_code']] = $result['ref_id']; + } + } + + } + return $this->_importCollection; + } + + public function setImportModel(Mage_Oscommerce_Model_Oscommerce $model) + { + $this->_importModel = $model; + } + + public function getImportModel() + { + if ($this->_importModel) { + return $this->_importModel; + } + } + + public function getCollections($code) + { + if ($this->_importCollection) { + return $this->_importCollection; + } + return; + } + + /** + * Deleting oscommerce reference records + * + * @param integer $id + */ + public function deleteRecords($id = null) + { + if (!is_null($id) && $id > 0) { + if ($result = $this->_getReadAdapter() + ->fetchRow('SELECT * FROM '.$this->getTable('oscommerce_ref').' WHERE import_id='.$id)) { + $this->_getWriteAdapter()->raw_query('DELETE FROM '.$this->getTable('oscommerce_ref').' WHERE import_id='.$id); + } + } + } + + /** + * Formatting string + * + * @param string $input + * @param integer $number + * @return string + */ + protected function _formatStringTruncate($data, $number) + { + if (str_word_count($data, 0)>$number) { + $wordKey = str_word_count($data, 1); + $posKey = str_word_count($data, 2); + reset($posKey); + foreach ($wordKey as $key => &$value) { + $value=key($posKey); + next($posKey); + } + return substr($data, 0, $wordKey[$number]); + } else { + return $data; + } + } + + /** + * Getting current user ID + * + * @return string + */ + protected function _getCurrentUserId() + { + if (!$this->_currentUserId) { + $this->_currentUserId = Mage::getSingleton('admin/session')->getUser()->getId(); + $this->_logData['user_id'] = $this->_currentUserId; + } + return $this->_currentUserId; + } + + /** + * Getting oscommerce table with prefix + * + * @param string $table + * @return string + */ + function getOscTable($table) + { + return $this->_prefix.$table; + } + + /** + * Setting connection charset + * + * @param string $charset + */ + public function setConnectionCharset($charset) + { + $this->_connectionCharset = $charset; + } + + /** + * Getting connection charset, set deafult as utf8 + * if there is no predefine charset + * + * @return string + */ + public function getConnectionCharset() + { + if (!$this->_connectionCharset) { + $this->_connectionCharset = self::DEFAULT_FIELD_CHARSET; + } + return $this->_connectionCharset; + } + + public function resetConnectionCharset() + { + $charset = $this->getConnectionCharset(); + $this->_getForeignAdapter()->query("SET NAMES '{$charset}'"); + } + + + /** + * Setting dataCharset by user defined encoding charset + * + * @param string $charset + */ + public function setDataCharset($charset) + { + if (!is_null($charset)) { + $this->_dataCharset = $charset; + } + } + + /** + * Getting dataCharset + * + * @return string + */ + public function getDataCharset() + { + return $this->_dataCharset; + } + + /** + * Converting encoded charsets + * + * @param mixed $data + * @param array $notIncludedFields + * @return mixed + */ + public function convert($data, array $notIncludedFields = array()) + { + $charset = $this->getDataCharset(); + if (!is_null($charset) || $charset != self::DEFAULT_FIELD_CHARSET) { + if (is_array($data)) { + foreach($data as $field => $value) { + if (!in_array($field, $notIncludedFields)) { + $newValue = @iconv($charset, self::DEFAULT_FIELD_CHARSET, $value); + if (strlen($newValue)) { + $data[$field] = $newValue; + } + } + } + } else { + $newValue = @iconv($charset, self::DEFAULT_MAGENTO_CHARSET, $data); + if (strlen($newValue)) { + $data = $newValue; + } + } + } + return $data; + } + + /** + * Getting saveRows + * + * @return integer + */ + public function getSaveRows() + { + return $this->_saveRows; + } + + /** + * Resetting saveRows as zero + * + */ + protected function _resetSaveRows() + { + $this->_saveRows = 0; + } + + /** + * Adding error messages + * + * @param string $error + */ + protected function _addErrors($error) + { + if (isset($error)) $this->_errors[] = $error; + } + + /** + * Getting all errors + * + * @return array + */ + public function getErrors() + { + if (sizeof($this->_errors) > 0) { + return $this->_errors; + } + } + + /** + * Resetting error as empty array + * + */ + protected function _resetErrors() + { + $this->_errors = array(); + } +} diff --git a/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Collection.php b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Collection.php new file mode 100755 index 0000000000..6843da807c --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Collection.php @@ -0,0 +1,38 @@ + + */ +class Mage_Oscommerce_Model_Mysql4_Oscommerce_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('oscommerce/oscommerce'); + } +} diff --git a/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Order.php b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Order.php new file mode 100644 index 0000000000..637fb098c5 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Order.php @@ -0,0 +1,81 @@ + + */ +class Mage_Oscommerce_Model_Mysql4_Oscommerce_Order extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('oscommerce/oscommerce_order', 'osc_magento_id'); + } + + public function getProducts() + { + $order = Mage::registry('current_oscommerce_order'); + $result = array(); + if ($order && $order->getData() && $id = $order->getId()) + { + $select = $this->_getReadAdapter()->select(); + $select->from($this->getTable('oscommerce_order_products')) + ->where("osc_magento_id={$id}"); + $result = $this->_getReadAdapter()->fetchAll($select); + } + return $result; + } + + public function getTotal() + { + $order = Mage::registry('current_oscommerce_order'); + $result = array(); + if ($order && $order->getData() && $id = $order->getId()) + { + $select = $this->_getReadAdapter()->select(); + $select->from($this->getTable('oscommerce_order_total')) + ->where("osc_magento_id={$id}")->order('sort_order'); + $result = $this->_getReadAdapter()->fetchAll($select); + } + return $result; + } + + public function getComments() + { + $order = Mage::registry('current_oscommerce_order'); + $result = array(); + if ($order && $order->getData() && $id = $order->getId()) + { + $select = $this->_getReadAdapter()->select(); + $select->from($this->getTable('oscommerce_order_history')) + ->where("osc_magento_id={$id}"); + $result = $this->_getReadAdapter()->fetchAll($select); + } + return $result; + } +} + diff --git a/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Order/Collection.php b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Order/Collection.php new file mode 100644 index 0000000000..4673bc541a --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Mysql4/Oscommerce/Order/Collection.php @@ -0,0 +1,55 @@ + + */ +class Mage_Oscommerce_Model_Mysql4_Oscommerce_Order_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('oscommerce/oscommerce_order'); + } + + public function addOrderTotalField() + { + $this->_select + ->from(null, array('orders_total'=>new Zend_Db_Expr('FORMAT(main_table.orders_total,2)'))); + return $this; + } + + public function load($printQuery=false, $logQuery=false) + { + if ($this->isLoaded()) { + return $this; + } + $this->addOrderTotalField(); + parent::load($printQuery, $logQuery); + return $this; + } +} diff --git a/app/code/core/Mage/Oscommerce/Model/Oscommerce.php b/app/code/core/Mage/Oscommerce/Model/Oscommerce.php new file mode 100755 index 0000000000..3181e86713 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Oscommerce.php @@ -0,0 +1,87 @@ + + */ +class Mage_Oscommerce_Model_Oscommerce extends Mage_Core_Model_Abstract +{ + const DEFAULT_PORT = 3360; + const CONNECTION_TYPE = 'pdo_mysql'; + const CONNECTION_NAME = 'oscommerce_db'; + + protected function _construct() + { + $this->_init('oscommerce/oscommerce'); + } + + protected function _afterLoad() + { + parent::_afterLoad(); + Mage::getSingleton('oscommerce/config')->initForeignConnection($this->getData()); + +// if (Mage::app()->getRequest()->getActionName() == 'run') { +// $this->importStores(); +// } + + } + /** + * Get paypal session namespace + * + * @return Mage_Paypal_Model_Session + */ + public function getSession() + { + return Mage::getSingleton('oscommerce/session'); + } + + public function importStores() { + $this->getResource()->importStores($this); + } + + public function getImportTypeIdByCode($code = '') { + return $this->getResource()->getImportTypeIdByCode($code); + } + + public function loadOrders($customerId, $websiteId) + { + return $this->getResource()->loadOrders($customerId, $websiteId); + } + + public function loadOrderById($id) + { + return $this->getResource()->loadOrderById($id); + } + + public function deleteImportedRecords($id) + { + if (isset($id) && $id == $this->getId()) { + $this->getResource()->deleteRecords($id); + } + } +} diff --git a/app/code/core/Mage/Oscommerce/Model/Oscommerce/Order.php b/app/code/core/Mage/Oscommerce/Model/Oscommerce/Order.php new file mode 100644 index 0000000000..6a0bc6cd49 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Oscommerce/Order.php @@ -0,0 +1,55 @@ + + */ +class Mage_Oscommerce_Model_Oscommerce_Order extends Mage_Core_Model_Abstract +{ + + protected function _construct() + { + $this->_init('oscommerce/oscommerce_order'); + } + + public function getProducts() + { + return $this->getResource()->getProducts(); + } + + public function getTotal() + { + return $this->getResource()->getTotal(); + } + + public function getComments() + { + return $this->getResource()->getComments(); + } + +} diff --git a/app/code/core/Mage/Oscommerce/Model/Session.php b/app/code/core/Mage/Oscommerce/Model/Session.php new file mode 100644 index 0000000000..c660040a79 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/Model/Session.php @@ -0,0 +1,39 @@ + + */ +class Mage_Oscommerce_Model_Session extends Mage_Core_Model_Session_Abstract +{ + public function __construct() + { + $this->init('oscommerce'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/controllers/Adminhtml/ImportController.php b/app/code/core/Mage/Oscommerce/controllers/Adminhtml/ImportController.php new file mode 100644 index 0000000000..35d2b9de59 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/controllers/Adminhtml/ImportController.php @@ -0,0 +1,422 @@ + + */ +class Mage_Oscommerce_Adminhtml_ImportController extends Mage_Adminhtml_Controller_Action +{ + /** + * Initailization action of importController + */ + protected function _initAction() + { + $this->loadLayout(); + $this->_setActiveMenu('oscommerce/adminhtml_import'); + return $this; + } + + /** + * Initialization of importController + * + * @param idFieldnName string + * @return Mage_Oscommerce_Adminhtml_ImportController + */ + protected function _initImport($idFieldName = 'id') + { + $id = (int) $this->getRequest()->getParam($idFieldName); + $model = Mage::getModel('oscommerce/oscommerce'); + if ($id) { + $model->load($id); + } + + Mage::register('oscommerce_adminhtml_import', $model); + return $this; + } + + /** + * Index action of importController + */ + public function indexAction() + { + $this->_initAction(); + $this->_addContent( + $this->getLayout()->createBlock('oscommerce/adminhtml_import') + ); + $this->renderLayout(); + } + + /** + * Edit action of importController + */ + public function editAction() + { + $this->_initImport(); + $this->loadLayout(); + + $model = Mage::registry('oscommerce_adminhtml_import'); + $data = Mage::getSingleton('adminhtml/session')->getSystemConvertOscData(true); + + if (!empty($data)) { + $model->addData($data); + } + + $this->_initAction(); + $this->_addBreadcrumb + (Mage::helper('oscommerce')->__('Edit osCommerce Profile'), + Mage::helper('oscommerce')->__('Edit osCommerce Profile')); + /** + * Append edit tabs to left block + */ + $this->_addLeft($this->getLayout()->createBlock('oscommerce/adminhtml_import_edit_tabs')); + + $this->_addContent($this->getLayout()->createBlock('oscommerce/adminhtml_import_edit')); + + $this->renderLayout(); + } + + /** + * Create new action of importController + */ + public function newAction() + { + $this->_forward('edit'); + } + + /** + * Save action of + */ + public function saveAction() + { + if ($data = $this->getRequest()->getPost()) { + if (isset($data['send_subscription'])) { + $data['send_subscription'] = 1; + } else { + $data['send_subscription'] = 0; + } + + $this->_initImport('import_id'); + $model = Mage::registry('oscommerce_adminhtml_import'); + + // Prepare saving data + if (isset($data)) { + $model->addData($data); + } + +// if (empty($data['port'])) +// $data['port'] = Mage_Oscommerce_Model_Oscommerce::DEFAULT_PORT; + + try { + $model->save(); + + Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('oscommerce')->__('osCommerce Profile was successfully saved')); + } + catch (Exception $e){ + Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); + Mage::getSingleton('adminhtml/session')->setSystemConvertOscData($data); + $this->getResponse()->setRedirect($this->getUrl('*/*/edit', array('id'=>$model->getId()))); + return; + } + } + if ($this->getRequest()->getParam('continue')) { + $this->_redirect('*/*/edit', array('id'=>$model->getId())); + } else { + $this->_redirect('*/*'); + } + } + + public function batchRunAction() + { + @set_time_limit(0); + $this->_initImport('import_id'); + $importModel = Mage::registry('oscommerce_adminhtml_import'); + + if ($tablePrefix = $importModel->getTablePrefix()) { + $importModel->getResource()->setTablePrefix($tablePrefix); + } + + // Start setting data from sessions + if ($connCharset = $importModel->getSession()->getConnectionCharset()) { + $importModel->getResource()->setConnectionCharset($connCharset); + } + if ($dataCharset = $importModel->getSession()->getDataCharset()) { + $importModel->getResource()->setDataCharset($dataCharset); + } + if ($timezone = $importModel->getSession()->getTimezone()) { + $importModel->setTimezone($timezone); + } + if ($storeLocales = $importModel->getSession()->getStoreLocales()) { + $importModel->getResource()->setStoreLocales($storeLocales); + } + if ($isPoductWithCategories = $importModel->getSession()->getIsProductWithCategories()) { + $importModel->getResource()->setIsProductWithCategories($isPoductWithCategories); + } + // End setting data from sessions + + // Resetting connection charset + $importModel->getResource()->resetConnectionCharset(); + + $importModel->getResource()->setImportModel($importModel); + if ($collections = $importModel->getResource()->importCollection($importModel->getId())) { + if (isset($collections['website'])) { + $importModel->getResource()->getWebsiteModel()->load($collections['website']); + } + if (isset($collections['root_category'])) { + $importModel->getResource()->setRootCategory(clone $importModel->getResource()->getCategoryModel()->load($collections['root_category'])); + } + if (isset($collections['group'])) { + $importModel->getResource()->getStoreGroupModel()->load($collections['group']); + + } + } + + //$isUnderDefaultWebsite = $this->getRequest()->getParam('under_default_website') ? true: false; + $importType = $this->getRequest()->getParam('import_type'); + $importFrom = $this->getRequest()->getParam('from'); + $isImportDone = $this->getRequest()->getParam('is_done'); + switch($importType) { + case 'products': + $importModel->getResource()->importProducts($importFrom, true); + break; + case 'categories': + $importModel->getResource()->importCategories($importFrom, true); + if ($isImportDone == 'true') { + $importModel->getResource()->buildCategoryPath(); + } + break; + case 'customers': + $importModel->getResource()->importCustomers($importFrom, true, $importModel->getData('send_subscription')); + break; + case 'orders': + $importModel->getResource()->importOrders($importFrom, true); + break; + } + + $errors = $importModel->getResource()->getErrors(); + $result = array( + 'savedRows' => $importModel->getResource()->getSaveRows(), + 'errors' => ( $errors ? $errors: array()) + ); + $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result)); + } + + public function runAction() + { + @set_time_limit(0); + Mage::app()->cleanCache(); // Clean all cach + $this->_initImport(); + $importModel = Mage::registry('oscommerce_adminhtml_import'); + /** @var $importModel Mage_Oscommerce_Model_Oscommerce */ + $totalRecords = array(); + + // Start handling charsets + $connCharset = $this->getRequest()->getParam('connection_charset'); + if ($connCharset) { + $importModel->getSession()->setConnectionCharset($connCharset); + $importModel->getResource()->setConnectionCharset($connCharset); + } + $dataCharset = $this->getRequest()->getParam('data_charset'); + if ($dataCharset) { + $importModel->getSession()->setDataCharset($dataCharset); + $importModel->getResource()->setDataCharset($dataCharset); + } // End hanlding charsets + + $timezone = $this->getRequest()->getParam('timezone'); + $importModel->getSession()->setTimezone($timezone); + $importModel->getResource()->resetConnectionCharset(); + + if ($tablPrefix = $importModel->getTablePrefix()) { + $importModel->getResource()->setTablePrefix($tablPrefix); + } + + $importModel->getResource()->setImportModel($importModel); + $importModel->getResource()->importCollection($importModel->getId()); + + // Setting Locale for stores + $locales = explode("|",$this->getRequest()->getParam('store_locale')); + $storeLocales = array(); + if ($locales) foreach($locales as $locale) { + $localeCode = explode(':', $locale); + $storeLocales[$localeCode[0]] = $localeCode[1]; + } + + $importModel->getSession()->setStoreLocales($storeLocales); + $importModel->getResource()->setStoreLocales($storeLocales); + // End setting Locale for stores + + $websiteId = $this->getRequest()->getParam('website_id'); + $websiteCode = $this->getRequest()->getParam('website_code'); + $options = $this->getRequest()->getParam('import'); + + // Checking Website, StoreGroup and RootCategory + if (!$websiteId) { + $importModel->getResource()->setWebsiteCode($websiteCode); + $importModel->getResource()->createWebsite(); + } else { + $importModel->getResource()->createWebsite($websiteId); + } + // End checking Website, StoreGroup and RootCategory + + $importModel->getResource()->importStores(); + $importModel->getResource()->importTaxClasses(); + $importModel->getResource()->createOrderTables(); + + if (isset($options['categories'])) { + $importModel->getSession()->setIsProductWithCategories(true); + $totalRecords['categories'] = $importModel->getResource()->getCategoriesCount(); + } + if (isset($options['products'])) { + $totalRecords['products'] = $importModel->getResource()->getProductsCount(); + } + if (isset($options['customers'])) { + $totalRecords['customers'] = $importModel->getResource()->getCustomersCount(); + } + if (isset($options['customers']) && isset($options['orders'])) { + $totalRecords['orders'] = $importModel->getResource()->getOrdersCount(); + } + if ($totalRecords) { + $importModel->setTotalRecords($totalRecords); + Mage::unRegister('oscommerce_adminhtml_import'); + Mage::register('oscommerce_adminhtml_import', $importModel); + } + $this->getResponse()->setBody($this->getLayout()->createBlock('oscommerce/adminhtml_import_run')->toHtml()); + $this->getResponse()->sendResponse(); + } + + public function batchFinishAction() + { + if ($importId = $this->getRequest()->getParam('id')) { + $importModel = Mage::getModel('oscommerce/oscommerce')->load($importId); + /* @var $batchModel Mage_Dataflow_Model_Batch */ + + if ($importId = $importModel->getId()) { + $importModel->deleteImportedRecords($importId); +// $importModel->getSession()->unsStoreLocales(); +// $importModel->getSession()->unsIsProductWithCategories(); +// if ($importModel->getSession()->getTablePrefix()) { +// $importModel->getSession()->unsTablePrefix(); +// } + $importModel->getSession()->clear(); + } + } + } + + /** + * Delete osc action + */ + public function deleteAction() + { + $this->_initImport(); + $model = Mage::registry('oscommerce_adminhtml_import'); + if ($model->getId()) { + try { + $model->delete(); + Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('oscommerce')->__('osCommerce profile was deleted')); + } + catch (Exception $e){ + Mage::getSingleton('adminhtml/session')->addError($e->getMessage()); + } + } + $this->_redirect('*/*/'); + } + + /** + * Ajax checking store + * + */ + public function checkStoreAction() + { + $this->_initImport(); + $importModel = Mage::registry('oscommerce_adminhtml_import'); + $error = false; + if ($importModel->getId()) { + try { + $charset = $importModel->getResource()->getConnectionCharset(); + $defaultOscCharset = Mage_Oscommerce_Model_Mysql4_Oscommerce::DEFAULT_OSC_CHARSET; + $defaultMageCharset = Mage_Oscommerce_Model_Mysql4_Oscommerce::DEFAULT_MAGENTO_CHARSET; + + $stores = $importModel->getResource()->getOscStores(); + + $locales = Mage::app()->getLocale()->getOptionLocales(); + $options = ''; + foreach ($locales as $locale) { + $options .= ""; + } + $html = ''; + if ($stores) { + $html .= "\n"; + foreach ($stores as $store) { + $html .= " + + + + + getCanDisplayTotalRefunded()): ?> + + + + + + getCanDisplayTotalDue()): ?> + + + + + + */ + return $this; + } + + +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Block/Order/Invoice.php b/app/code/core/Mage/Sales/Block/Order/Invoice.php index da920969f8..2c73146049 100644 --- a/app/code/core/Mage/Sales/Block/Order/Invoice.php +++ b/app/code/core/Mage/Sales/Block/Order/Invoice.php @@ -31,7 +31,7 @@ * @package Mage_Sales * @author Magento Core Team */ -class Mage_Sales_Block_Order_Invoice extends Mage_Core_Block_Template +class Mage_Sales_Block_Order_Invoice extends Mage_Sales_Block_Order_Invoice_Items { protected function _construct() { diff --git a/app/code/core/Mage/Sales/Block/Order/Invoice/Items.php b/app/code/core/Mage/Sales/Block/Order/Invoice/Items.php index eb137e86aa..9b7ba601b5 100644 --- a/app/code/core/Mage/Sales/Block/Order/Invoice/Items.php +++ b/app/code/core/Mage/Sales/Block/Order/Invoice/Items.php @@ -43,11 +43,30 @@ public function getOrder() return Mage::registry('current_order'); } - public function getPrintInvoiceUrl($invoice){ + public function getPrintInvoiceUrl($invoice) + { return Mage::getUrl('*/*/printInvoice', array('invoice_id' => $invoice->getId())); } - public function getPrintAllInvoicesUrl($order){ + public function getPrintAllInvoicesUrl($order) + { return Mage::getUrl('*/*/printInvoice', array('order_id' => $order->getId())); } + + /** + * Get html of invoice totals block + * + * @param Mage_Sales_Model_Order_Invoice $invoice + * @return string + */ + public function getInvoiceTotalsHtml($invoice) + { + $html = ''; + $totals = $this->getChild('invoice_totals'); + if ($totals) { + $totals->setInvoice($invoice); + $html = $totals->toHtml(); + } + return $html; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Block/Order/Invoice/Totals.php b/app/code/core/Mage/Sales/Block/Order/Invoice/Totals.php new file mode 100644 index 0000000000..c07762d7b7 --- /dev/null +++ b/app/code/core/Mage/Sales/Block/Order/Invoice/Totals.php @@ -0,0 +1,73 @@ +_invoice === null) { + if ($this->hasData('invoice')) { + $this->_invoice = $this->_getData('invoice'); + } elseif (Mage::registry('current_invoice')) { + $this->_invoice = Mage::registry('current_invoice'); + } elseif ($this->getParentBlock()->getInvoice()) { + $this->_invoice = $this->getParentBlock()->getInvoice(); + } + } + return $this->_invoice; + } + + public function setInvoice($invoice) + { + $this->_invoice = $invoice; + return $this; + } + + /** + * Get totals source object + * + * @return Mage_Sales_Model_Order + */ + public function getSource() + { + return $this->getInvoice(); + } + + /** + * Initialize order totals array + * + * @return Mage_Sales_Block_Order_Totals + */ + protected function _initTotals() + { + parent::_initTotals(); + $this->removeTotal('base_grandtotal'); + return $this; + } + + +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Block/Order/Print/Creditmemo.php b/app/code/core/Mage/Sales/Block/Order/Print/Creditmemo.php index f0f9f77c59..b674df12d7 100644 --- a/app/code/core/Mage/Sales/Block/Order/Print/Creditmemo.php +++ b/app/code/core/Mage/Sales/Block/Order/Print/Creditmemo.php @@ -34,12 +34,6 @@ class Mage_Sales_Block_Order_Print_Creditmemo extends Mage_Sales_Block_Items_Abstract { - - public function __construct() - { - parent::__construct(); - } - protected function _prepareLayout() { if ($headBlock = $this->getLayout()->getBlock('head')) { @@ -79,8 +73,23 @@ public function getCreditmemo() protected function _prepareItem(Mage_Core_Block_Abstract $renderer) { $renderer->setPrintStatus(true); - return parent::_prepareItem($renderer); } -} + /** + * Get Creditmemo totals block html gor specific creditmemo + * + * @param Mage_Sales_Model_Order_Creditmemo $creditmemo + * @return string + */ + public function getTotalsHtml($creditmemo) + { + $totals = $this->getChild('creditmemo_totals'); + $html = ''; + if ($totals) { + $totals->setCreditmemo($creditmemo); + $html = $totals->toHtml(); + } + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Block/Order/Print/Invoice.php b/app/code/core/Mage/Sales/Block/Order/Print/Invoice.php index f902c025de..3e983abfdf 100644 --- a/app/code/core/Mage/Sales/Block/Order/Print/Invoice.php +++ b/app/code/core/Mage/Sales/Block/Order/Print/Invoice.php @@ -34,12 +34,6 @@ class Mage_Sales_Block_Order_Print_Invoice extends Mage_Sales_Block_Items_Abstract { - - public function __construct() - { - parent::__construct(); - } - protected function _prepareLayout() { if ($headBlock = $this->getLayout()->getBlock('head')) { @@ -79,8 +73,25 @@ public function getInvoice() protected function _prepareItem(Mage_Core_Block_Abstract $renderer) { $renderer->setPrintStatus(true); - return parent::_prepareItem($renderer); } + + /** + * Get html of invoice totlas block + * + * @param Mage_Sales_Model_Order_Invoice $invoice + * @return string + */ + public function getInvoiceTotalsHtml($invoice) + { + $html = ''; + $totals = $this->getChild('invoice_totals'); + if ($totals) { + $totals->setInvoice($invoice); + $html = $totals->toHtml(); + } + return $html; + } + } diff --git a/app/code/core/Mage/Sales/Block/Order/Tax.php b/app/code/core/Mage/Sales/Block/Order/Tax.php index 05b36f0b45..1c5fa765cc 100644 --- a/app/code/core/Mage/Sales/Block/Order/Tax.php +++ b/app/code/core/Mage/Sales/Block/Order/Tax.php @@ -27,6 +27,7 @@ /** * Sales order view tax block * + * @deprecated after 1.3.2.2 * @category Mage * @package Mage_Sales * @author Magento Core Team diff --git a/app/code/core/Mage/Sales/Block/Order/Totals.php b/app/code/core/Mage/Sales/Block/Order/Totals.php new file mode 100644 index 0000000000..3f55e98590 --- /dev/null +++ b/app/code/core/Mage/Sales/Block/Order/Totals.php @@ -0,0 +1,303 @@ + $totalObject + * ) + * + * @var array + */ + protected $_totals; + protected $_order = null; + + /** + * Initialize self totals and children blocks totals before html building + * + * @return Mage_Sales_Block_Order_Totals + */ + protected function _beforeToHtml() + { + $this->_initTotals(); + foreach ($this->getChild() as $child) { + if (method_exists($child, 'initTotals')) { + $child->initTotals(); + } + } + return parent::_beforeToHtml(); + } + + /** + * Get order object + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + if ($this->_order === null) { + if ($this->hasData('order')) { + $this->_order = $this->_getData('order'); + } elseif (Mage::registry('current_order')) { + $this->_order = Mage::registry('current_order'); + } elseif ($this->getParentBlock()->getOrder()) { + $this->_order = $this->getParentBlock()->getOrder(); + } + } + return $this->_order; + } + + public function setOrder($order) + { + $this->_order = $order; + return $this; + } + + /** + * Get totals source object + * + * @return Mage_Sales_Model_Order + */ + public function getSource() + { + return $this->getOrder(); + } + + /** + * Initialize order totals array + * + * @return Mage_Sales_Block_Order_Totals + */ + protected function _initTotals() + { + $this->_totals = array(); + $this->_totals['subtotal'] = new Varien_Object(array( + 'code' => 'subtotal', + 'value' => $this->getSource()->getSubtotal(), + 'label' => $this->__('Subtotal') + )); + + /** + * Add shipping + */ + if ($this->getSource()->getShippingAmount() || $this->getSource()->getShippingDescription()) + { + $this->_totals['shipping'] = new Varien_Object(array( + 'code' => 'shipping', + 'field' => 'shipping_amount', + 'value' => $this->getSource()->getShippingAmount(), + 'label' => $this->__('Shipping & Handling') + )); + } + + /** + * Add discount + */ + if (((float)$this->getSource()->getDiscountAmount()) != 0) { + if ($this->getSource()->getDiscountDescription()) { + $discountLabel = $this->__('Discount (%s)', $this->getSource()->getDiscountDescription()); + } else { + $discountLabel = $this->__('Discount'); + } + $this->_totals['discount'] = new Varien_Object(array( + 'code' => 'discount', + 'field' => 'discount_amount', + 'value' => $this->getSource()->getDiscountAmount(), + 'label' => $discountLabel + )); + } + + $this->_totals['grand_total'] = new Varien_Object(array( + 'code' => 'grand_total', + 'field' => 'grand_total', + 'strong'=> true, + 'value' => $this->getSource()->getGrandTotal(), + 'label' => $this->__('Grand Total') + )); + + /** + * Base grandtotal + */ + if ($this->getOrder()->isCurrencyDifferent()) { + $this->_totals['base_grandtotal'] = new Varien_Object(array( + 'code' => 'base_grandtotal', + 'value' => $this->getOrder()->formatBasePrice($this->getSource()->getBaseGrandTotal()), + 'label' => $this->__('Grand Total to be charged'), + 'is_formated' => true, + )); + } + return $this; + } + + /** + * Add new total to totals array after specific total or before last total by default + * + * @param Varien_Object $total + * @param null|string|last|first $after + * @return Mage_Sales_Block_Order_Totals + */ + public function addTotal(Varien_Object $total, $after=null) + { + if ($after !== null && $after != 'last' && $after != 'first') { + $totals = array(); + $added = false; + foreach ($this->_totals as $code => $item) { + $totals[$code] = $item; + if ($code == $after) { + $added = true; + $totals[$total->getCode()] = $total; + } + } + if (!$added) { + $last = array_pop($totals); + $totals[$total->getCode()] = $total; + $totals[$last->getCode()] = $last; + } + $this->_totals = $totals; + } elseif ($after=='last') { + $this->_totals[$total->getCode()] = $total; + } elseif ($after=='first') { + $totals = array($total->getCode()=>$total); + $this->_totals = array_merge($totals, $this->_totals); + } else { + $last = array_pop($this->_totals); + $this->_totals[$total->getCode()] = $total; + $this->_totals[$last->getCode()] = $last; + } + return $this; + } + + /** + * Add new total to totals array before specific total or after first total by default + * + * @param Varien_Object $total + * @param null|string $after + * @return Mage_Sales_Block_Order_Totals + */ + public function addTotalBefore(Varien_Object $total, $before=null) + { + if ($before !== null) { + if (isset($this->_totals[$before])) { + $totals = array(); + foreach ($this->_totals as $code => $item) { + if ($code == $before) { + $totals[$total->getCode()] = $total; + } + $totals[$code] = $item; + } + $this->_totals = $totals; + return $this; + } + } + $totals = array(); + $first = array_shift($this->_totals); + $totals[$first->getCode()] = $first; + $totals[$total->getCode()] = $total; + foreach ($this->_totals as $code => $item) { + $totals[$code] = $item; + } + $this->_totals = $totals; + return $this; + } + + /** + * Get Total object by code + * + * @@return Varien_Object + */ + public function getTotal($code) + { + if (isset($this->_totals[$code])) { + return $this->_totals[$code]; + } + return false; + } + + /** + * Delete total by specific + * + * @param string $code + * @return Mage_Sales_Block_Order_Totals + */ + public function removeTotal($code) + { + unset($this->_totals[$code]); + return $this; + } + + /** + * Apply sort orders to totals array. + * Array should have next structure + * array( + * $totalCode => $totalSortOrder + * ) + * + * + * @param array $order + * @return Mage_Sales_Block_Order_Totals + */ + public function applySortOrder($order) + { + return $this; + } + + /** + * get totals array for visualization + * + * @return array + */ + public function getTotals($area=null) + { + $totals = array(); + if ($area === null) { + $totals = $this->_totals; + } else { + $area = (string)$area; + foreach ($this->_totals as $total) { + $totalArea = (string) $total->getArea(); + if ($totalArea == $area) { + $totals[] = $total; + } + } + } + return $totals; + } + + /** + * Format total value based on order currency + * + * @param Varien_Object $total + * @return string + */ + public function formatValue($total) + { + if (!$total->getIsFormated()) { + return $this->getOrder()->formatPrice($total->getValue()); + } + return $total->getValue(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Convert/Order.php b/app/code/core/Mage/Sales/Model/Convert/Order.php index 55d721ff47..a21b18bba5 100644 --- a/app/code/core/Mage/Sales/Model/Convert/Order.php +++ b/app/code/core/Mage/Sales/Model/Convert/Order.php @@ -87,7 +87,6 @@ public function toQuote(Mage_Sales_Model_Order $order, $quote=null) // * Another data // */ // ->setCouponCode($order->getCouponCode()) -// ->setGiftcertCode($order->getGiftcertCode()) // ->setAppliedRuleIds($order->getAppliedRuleIds()); // //->collectTotals(); // @@ -116,7 +115,6 @@ public function toQuoteShippingAddress(Mage_Sales_Model_Order $order) // ->setTaxAmount($order->getTaxAmount()) // ->setDiscountAmount($order->getDiscountAmount()) // ->setShippingAmount($order->getShippingAmount()) -// ->setGiftcertAmount($order->getGiftcertAmount()) // ->setCustbalanceAmount($order->getCustbalanceAmount()) // ->setGrandTotal($order->getGrandTotal()) // @@ -124,7 +122,6 @@ public function toQuoteShippingAddress(Mage_Sales_Model_Order $order) // ->setBaseTaxAmount($order->getBaseTaxAmount()) // ->setBaseDiscountAmount($order->getBaseDiscountAmount()) // ->setBaseShippingAmount($order->getBaseShippingAmount()) -// ->setBaseGiftcertAmount($order->getBaseGiftcertAmount()) // ->setBaseCustbalanceAmount($order->getBaseCustbalanceAmount()) // ->setBaseGrandTotal($order->getBaseGrandTotal()); return $address; diff --git a/app/code/core/Mage/Sales/Model/Convert/Quote.php b/app/code/core/Mage/Sales/Model/Convert/Quote.php index 09d3cf5f2c..8a5b9ced04 100644 --- a/app/code/core/Mage/Sales/Model/Convert/Quote.php +++ b/app/code/core/Mage/Sales/Model/Convert/Quote.php @@ -96,7 +96,6 @@ public function toOrder(Mage_Sales_Model_Quote $quote, $order=null) // * Another data // */ // ->setCouponCode($quote->getCouponCode()) -// ->setGiftcertCode($quote->getGiftcertCode()) // ->setIsVirtual($quote->getIsVirtual()) // ->setIsMultiPayment($quote->getIsMultiPayment()) // ->setAppliedRuleIds($quote->getAppliedRuleIds()); @@ -131,7 +130,6 @@ public function addressToOrder(Mage_Sales_Model_Quote_Address $address, $order=n // ->setDiscountAmount($address->getDiscountAmount()) // ->setShippingAmount($address->getShippingAmount()) // ->setShippingTaxAmount($address->getShippingTaxAmount()) -// ->setGiftcertAmount($address->getGiftcertAmount()) // ->setCustbalanceAmount($address->getCustbalanceAmount()) // ->setGrandTotal($address->getGrandTotal()) // @@ -140,7 +138,6 @@ public function addressToOrder(Mage_Sales_Model_Quote_Address $address, $order=n // ->setBaseDiscountAmount($address->getBaseDiscountAmount()) // ->setBaseShippingAmount($address->getBaseShippingAmount()) // ->setBaseShippingTaxAmount($address->getBaseShippingTaxAmount()) -// ->setBaseGiftcertAmount($address->getBaseGiftcertAmount()) // ->setBaseCustbalanceAmount($address->getBaseCustbalanceAmount()) // ->setBaseGrandTotal($address->getBaseGrandTotal()); diff --git a/app/code/core/Mage/Sales/Model/Entity/Setup.php b/app/code/core/Mage/Sales/Model/Entity/Setup.php index 60de406f72..1f166bb471 100644 --- a/app/code/core/Mage/Sales/Model/Entity/Setup.php +++ b/app/code/core/Mage/Sales/Model/Entity/Setup.php @@ -290,7 +290,6 @@ public function getDefaultEntities() 'coupon_code' => array(), 'applied_rule_ids' => array(), - 'giftcert_code' => array(), 'global_currency_code' => array(), 'base_currency_code' => array(), @@ -311,7 +310,6 @@ public function getDefaultEntities() 'tax_amount' => array('type'=>'static'), 'shipping_amount' => array('type'=>'static'), 'discount_amount' => array('type'=>'static'), - 'giftcert_amount' => array('type'=>'decimal'), 'custbalance_amount'=> array('type'=>'decimal'), 'subtotal' => array('type'=>'static'), @@ -330,7 +328,6 @@ public function getDefaultEntities() 'base_tax_amount' => array('type'=>'static'), 'base_shipping_amount' => array('type'=>'static'), 'base_discount_amount' => array('type'=>'static'), - 'base_giftcert_amount' => array('type'=>'decimal'), 'base_custbalance_amount'=> array('type'=>'decimal'), 'base_subtotal' => array('type'=>'static'), diff --git a/app/code/core/Mage/Sales/Model/Mysql4/Setup.php b/app/code/core/Mage/Sales/Model/Mysql4/Setup.php index 4c2846469d..25fa721aac 100644 --- a/app/code/core/Mage/Sales/Model/Mysql4/Setup.php +++ b/app/code/core/Mage/Sales/Model/Mysql4/Setup.php @@ -343,7 +343,6 @@ public function getDefaultEntities() 'coupon_code' => array(), 'applied_rule_ids' => array(), - 'giftcert_code' => array(), 'global_currency_code' => array(), 'base_currency_code' => array(), @@ -364,7 +363,6 @@ public function getDefaultEntities() 'shipping_amount' => array('type'=>'static'), 'shipping_tax_amount' => array('type'=>'static'), 'discount_amount' => array('type'=>'static'), - 'giftcert_amount' => array('type'=>'decimal'), 'subtotal' => array('type'=>'static'), 'grand_total' => array('type'=>'static'), @@ -383,7 +381,6 @@ public function getDefaultEntities() 'base_shipping_amount' => array('type'=>'static'), 'base_shipping_tax_amount' => array('type'=>'static'), 'base_discount_amount' => array('type'=>'static'), - 'base_giftcert_amount' => array('type'=>'decimal'), 'base_subtotal' => array('type'=>'static'), 'base_grand_total' => array('type'=>'static'), diff --git a/app/code/core/Mage/Sales/Model/Order.php b/app/code/core/Mage/Sales/Model/Order.php index 88b8d3e5d0..0b17f7a7a0 100644 --- a/app/code/core/Mage/Sales/Model/Order.php +++ b/app/code/core/Mage/Sales/Model/Order.php @@ -1116,7 +1116,7 @@ public function getRealOrderId() } /** - * Retrieve order currency model instance + * Get currency model instance. Will be used currency with which order placed * * @return Mage_Directory_Model_Currency */ @@ -1129,7 +1129,7 @@ public function getOrderCurrency() } /** - * Retrieve formated price value includeing order rate + * Get formated price value including order currency rate to order website currency * * @param float $price * @param bool $addBrackets @@ -1137,7 +1137,12 @@ public function getOrderCurrency() */ public function formatPrice($price, $addBrackets = false) { - return $this->getOrderCurrency()->format($price, array(), true, $addBrackets); + return $this->formatPricePrecision($price, 2, $addBrackets); + } + + public function formatPricePrecision($price, $precision, $addBrackets = false) + { + return $this->getOrderCurrency()->formatPrecision($price, $precision, array(), true, $addBrackets); } /** @@ -1166,7 +1171,7 @@ public function getBaseCurrency() /** * Retrieve order website currency for working with base prices - * Deprecated method, please use getBaseCurrency instead. + * @deprecated please use getBaseCurrency instead. * * @return Mage_Directory_Model_Currency */ @@ -1177,7 +1182,12 @@ public function getStoreCurrency() public function formatBasePrice($price) { - return $this->getBaseCurrency()->format($price); + return $this->formatBasePricePrecision($price, 2); + } + + public function formatBasePricePrecision($price, $precision) + { + return $this->getBaseCurrency()->formatPrecision($price, $precision); } public function isCurrencyDifferent() @@ -1518,8 +1528,10 @@ public function prepareInvoice($qtys = array()) } else { if (isset($qtys[$orderItem->getId()])) { $qty = $qtys[$orderItem->getId()]; - } else { + } elseif (!count($qtys)) { $qty = $orderItem->getQtyToInvoice(); + } else { + continue; } } @@ -1554,8 +1566,10 @@ public function prepareShipment($qtys = array()) } else { if (isset($qtys[$orderItem->getId()])) { $qty = $qtys[$orderItem->getId()]; - } else { + } elseif (!count($qtys)) { $qty = $orderItem->getQtyToShip(); + } else { + continue; } } diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Item.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Item.php index aa27b0ff3a..2d71a340f5 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Item.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Item.php @@ -150,11 +150,40 @@ public function cancel() */ public function calcRowTotal() { - $rowTotal = $this->getOrderItem()->getRowTotal()/$this->getOrderItem()->getQtyOrdered()*$this->getQty(); - $baseRowTotal = $this->getOrderItem()->getBaseRowTotal()/$this->getOrderItem()->getQtyOrdered()*$this->getQty(); - - $this->setRowTotal($this->getCreditmemo()->getStore()->roundPrice($rowTotal)); - $this->setBaseRowTotal($this->getCreditmemo()->getStore()->roundPrice($baseRowTotal)); + $store = $this->getCreditmemo()->getStore(); + $orderItem = $this->getOrderItem(); + $orderItemQty = $orderItem->getQtyOrdered(); + + $rowTotal = $orderItem->getRowTotal(); + $baseRowTotal = $orderItem->getBaseRowTotal(); + $rowTotalInclTax = $orderItem->getRowTotalInclTax(); + $baseRowTotalInclTax= $orderItem->getBaseRowTotalInclTax(); + + $rowTotal = $rowTotal/$orderItemQty*$this->getQty(); + $baseRowTotal = $baseRowTotal/$orderItemQty*$this->getQty(); + + $this->setRowTotal($store->roundPrice($rowTotal)); + $this->setBaseRowTotal($store->roundPrice($baseRowTotal)); + + if ($rowTotalInclTax && $baseRowTotalInclTax) { + $this->setRowTotalInclTax($store->roundPrice($rowTotalInclTax/$orderItemQty*$this->getQty())); + $this->setBaseRowTotalInclTax($store->roundPrice($baseRowTotalInclTax/$orderItemQty*$this->getQty())); + } return $this; } + + /** + * Checking if the item is last + * + * @return bool + */ + public function isLast() + { + $orderItem = $this->getOrderItem(); + if ($this->getQty() == $orderItem->getQtyToRefund() && !$orderItem->getQtyToInvoice()) { + return true; + } + return false; + } + } \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Grand.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Grand.php index 22665f8868..3a74009440 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Grand.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Grand.php @@ -32,9 +32,6 @@ public function collect(Mage_Sales_Model_Order_Creditmemo $creditmemo) $grandTotal = $creditmemo->getGrandTotal(); $baseGrandTotal = $creditmemo->getBaseGrandTotal(); - $grandTotal+= $creditmemo->getShippingAmount(); - $baseGrandTotal+= $creditmemo->getBaseShippingAmount(); - $grandTotal+= $creditmemo->getAdjustmentPositive(); $baseGrandTotal+= $creditmemo->getBaseAdjustmentPositive(); diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Shipping.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Shipping.php new file mode 100644 index 0000000000..35863cc944 --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Shipping.php @@ -0,0 +1,71 @@ + + */ +class Mage_Sales_Model_Order_Creditmemo_Total_Shipping extends Mage_Sales_Model_Order_Creditmemo_Total_Abstract +{ + public function collect(Mage_Sales_Model_Order_Creditmemo $creditmemo) + { + $order = $creditmemo->getOrder(); + $allowedAmount = $order->getShippingAmount()-$order->getShippingRefunded(); + $baseAllowedAmount = $order->getBaseShippingAmount()-$order->getBaseShippingRefunded(); + + /** + * Check if shipping amount was specified (from invoice or another source) + */ + $baseShippingAmount = $creditmemo->getBaseShippingAmount(); + if ($baseShippingAmount) { + $baseShippingAmount = Mage::app()->getStore()->roundPrice($baseShippingAmount); + if ($baseShippingAmount<$baseAllowedAmount) { + $shippingAmount = $allowedAmount*$baseShippingAmount/$baseAllowedAmount; + $shippingAmount = Mage::app()->getStore()->roundPrice($shippingAmount); + } elseif ($baseShippingAmount==$baseAllowedAmount) { + $shippingAmount = $allowedAmount; + } else { + $baseAllowedAmount = $order->formatBasePrice($baseAllowedAmount); + Mage::throwException( + Mage::helper('sales')->__('Maximum shipping amount allowed to refound is: %s', $baseAllowedAmount) + ); + } + } else { + $baseShippingAmount = $baseAllowedAmount; + $shippingAmount = $allowedAmount; + } + + $creditmemo->setShippingAmount($shippingAmount); + $creditmemo->setBaseShippingAmount($baseShippingAmount); + + $creditmemo->setGrandTotal($creditmemo->getGrandTotal()+$shippingAmount); + $creditmemo->setBaseGrandTotal($creditmemo->getBaseGrandTotal()+$baseShippingAmount); + return $this; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Subtotal.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Subtotal.php index 5b1e3af701..9457c3ca50 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Subtotal.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Subtotal.php @@ -37,18 +37,26 @@ public function collect(Mage_Sales_Model_Order_Creditmemo $creditmemo) { $subtotal = 0; $baseSubtotal = 0; + $subtotalInclTax= 0; + $baseSubtotalInclTax = 0; foreach ($creditmemo->getAllItems() as $item) { $item->calcRowTotal(); + if ($item->getOrderItem()->isDummy()) { continue; } - $subtotal+= $item->getRowTotal(); - $baseSubtotal+= $item->getBaseRowTotal(); + + $subtotal += $item->getRowTotal(); + $baseSubtotal += $item->getBaseRowTotal(); + $subtotalInclTax+= $item->getRowTotalInclTax(); + $baseSubtotalInclTax += $item->getBaseRowTotalInclTax(); } $creditmemo->setSubtotal($subtotal); $creditmemo->setBaseSubtotal($baseSubtotal); + $creditmemo->setSubtotalInclTax($subtotalInclTax); + $creditmemo->setBaseSubtotalInclTax($baseSubtotalInclTax); $creditmemo->setGrandTotal($creditmemo->getGrandTotal() + $subtotal); $creditmemo->setBaseGrandTotal($creditmemo->getBaseGrandTotal() + $baseSubtotal); diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Tax.php index 435678d8f7..68b29cc03a 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -33,6 +33,7 @@ public function collect(Mage_Sales_Model_Order_Creditmemo $creditmemo) $baseShippingTaxAmount = 0; $totalTax = 0; $baseTotalTax = 0; + $order = $creditmemo->getOrder(); foreach ($creditmemo->getAllItems() as $item) { if ($item->getOrderItem()->isDummy()) { @@ -43,11 +44,20 @@ public function collect(Mage_Sales_Model_Order_Creditmemo $creditmemo) $orderItemQty = $item->getOrderItem()->getQtyOrdered(); if ($orderItemTax && $orderItemQty) { - $tax = $orderItemTax*$item->getQty()/$orderItemQty; - $baseTax = $baseOrderItemTax*$item->getQty()/$orderItemQty; - - $tax = $creditmemo->getStore()->roundPrice($tax); - $baseTax = $creditmemo->getStore()->roundPrice($baseTax); + /** + * Check item tax amount + */ + if ($item->isLast()) { + $tax = $orderItemTax - $item->getOrderItem()->getTaxRefunded(); + $baseTax = $baseOrderItemTax - $item->getOrderItem()->getTaxRefunded(); + } + else { + $tax = $orderItemTax*$item->getQty()/$orderItemQty; + $baseTax = $baseOrderItemTax*$item->getQty()/$orderItemQty; + + $tax = $creditmemo->getStore()->roundPrice($tax); + $baseTax = $creditmemo->getStore()->roundPrice($baseTax); + } $item->setTaxAmount($tax); $item->setBaseTaxAmount($baseTax); @@ -58,37 +68,36 @@ public function collect(Mage_Sales_Model_Order_Creditmemo $creditmemo) } if ($invoice = $creditmemo->getInvoice()) { - $totalTax += $invoice->getShippingTaxAmount(); - $baseTotalTax += $invoice->getBaseShippingTaxAmount(); - - $creditmemo->setShippingTaxAmount($invoice->getShippingTaxAmount()); - $creditmemo->setBaseShippingTaxAmount($invoice->getBaseShippingTaxAmount()); + $totalTax += $invoice->getShippingTaxAmount(); + $baseTotalTax += $invoice->getBaseShippingTaxAmount(); + $shippingTaxAmount = $invoice->getShippingTaxAmount(); + $baseShippingTaxAmount = $invoice->getBaseShippingTaxAmount(); } else { - $shippingAmount = $creditmemo->getOrder()->getBaseShippingAmount(); - $shippingRefundedAmount = $creditmemo->getOrder()->getBaseShippingRefunded(); + $orderShippingAmount = $order->getShippingAmount(); + $baseOrderShippingAmount = $order->getBaseShippingAmount(); + $baseOrderShippingRefundedAmount = $order->getBaseShippingRefunded(); $shippingTaxAmount = 0; $baseShippingTaxAmount = 0; - if (($shippingAmount - $shippingRefundedAmount) > $creditmemo->getShippingAmount()) { - $shippingTaxAmount = $creditmemo->getShippingAmount()*($creditmemo->getOrder()->getShippingTaxAmount()/$shippingAmount); - $baseShippingTaxAmount = $creditmemo->getBaseShippingAmount()*($creditmemo->getOrder()->getBaseShippingTaxAmount()/$shippingAmount); + if (($baseOrderShippingAmount - $baseOrderShippingRefundedAmount) > $creditmemo->getBaseShippingAmount()) { + $shippingTaxAmount = $creditmemo->getShippingAmount()*($order->getShippingTaxAmount()/$orderShippingAmount); + $baseShippingTaxAmount = $creditmemo->getBaseShippingAmount()*($order->getBaseShippingTaxAmount()/$baseOrderShippingAmount); $shippingTaxAmount = $creditmemo->getStore()->roundPrice($shippingTaxAmount); $baseShippingTaxAmount = $creditmemo->getStore()->roundPrice($baseShippingTaxAmount); - } elseif (($shippingAmount - $shippingRefundedAmount) == $creditmemo->getShippingAmount()) { - $shippingTaxAmount = $creditmemo->getOrder()->getShippingTaxAmount() - $creditmemo->getOrder()->getShippingTaxRefunded(); - $baseShippingTaxAmount = $creditmemo->getOrder()->getBaseShippingTaxAmount() - $creditmemo->getOrder()->getBaseShippingTaxRefunded(); + } elseif (($baseOrderShippingAmount - $baseOrderShippingRefundedAmount) == $creditmemo->getBaseShippingAmount()) { + $shippingTaxAmount = $order->getShippingTaxAmount() - $order->getShippingTaxRefunded(); + $baseShippingTaxAmount = $order->getBaseShippingTaxAmount() - $order->getBaseShippingTaxRefunded(); } $totalTax += $shippingTaxAmount; $baseTotalTax += $baseShippingTaxAmount; } + + $allowedTax = $order->getTaxAmount() - $order->getTaxRefunded(); + $allowedBaseTax = $order->getBaseTaxAmount() - $order->getBaseTaxRefunded();; - $tmpBaseTotalTax = $baseTotalTax - ($creditmemo->getOrder()->getBaseTaxRefunded() - $creditmemo->getOrder()->getBaseShippingTaxRefunded()); - - if ($tmpBaseTotalTax<0) { - $baseTotalTax = 0; - $totalTax = 0; - } + $totalTax = min($allowedTax, $totalTax); + $baseTotalTax = min($allowedBaseTax, $baseTotalTax); $creditmemo->setTaxAmount($totalTax); $creditmemo->setBaseTaxAmount($baseTotalTax); diff --git a/app/code/core/Mage/Sales/Model/Order/Invoice/Item.php b/app/code/core/Mage/Sales/Model/Order/Invoice/Item.php index cd35d457fa..68dac8f56d 100644 --- a/app/code/core/Mage/Sales/Model/Order/Invoice/Item.php +++ b/app/code/core/Mage/Sales/Model/Order/Invoice/Item.php @@ -174,11 +174,25 @@ public function cancel() */ public function calcRowTotal() { - $rowTotal = $this->getOrderItem()->getRowTotal()/$this->getOrderItem()->getQtyOrdered()*$this->getQty(); - $baseRowTotal = $this->getOrderItem()->getBaseRowTotal()/$this->getOrderItem()->getQtyOrdered()*$this->getQty(); + $store = $this->getInvoice()->getStore(); + $orderItem = $this->getOrderItem(); + $orderItemQty = $orderItem->getQtyOrdered(); - $this->setRowTotal($this->getInvoice()->getStore()->roundPrice($rowTotal)); - $this->setBaseRowTotal($this->getInvoice()->getStore()->roundPrice($baseRowTotal)); + $rowTotal = $orderItem->getRowTotal(); + $baseRowTotal = $orderItem->getBaseRowTotal(); + $rowTotalInclTax = $orderItem->getRowTotalInclTax(); + $baseRowTotalInclTax= $orderItem->getBaseRowTotalInclTax(); + + $rowTotal = $rowTotal/$orderItemQty*$this->getQty(); + $baseRowTotal = $baseRowTotal/$orderItemQty*$this->getQty(); + + $this->setRowTotal($store->roundPrice($rowTotal)); + $this->setBaseRowTotal($store->roundPrice($baseRowTotal)); + + if ($rowTotalInclTax && $baseRowTotalInclTax) { + $this->setRowTotalInclTax($store->roundPrice($rowTotalInclTax/$orderItemQty*$this->getQty())); + $this->setBaseRowTotalInclTax($store->roundPrice($baseRowTotalInclTax/$orderItemQty*$this->getQty())); + } return $this; } diff --git a/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Subtotal.php b/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Subtotal.php index c8d9d82c69..36b9b4882c 100644 --- a/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Subtotal.php +++ b/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Subtotal.php @@ -35,8 +35,10 @@ class Mage_Sales_Model_Order_Invoice_Total_Subtotal extends Mage_Sales_Model_Ord */ public function collect(Mage_Sales_Model_Order_Invoice $invoice) { - $subtotal = 0; - $baseSubtotal = 0; + $subtotal = 0; + $baseSubtotal = 0; + $subtotalInclTax= 0; + $baseSubtotalInclTax = 0; foreach ($invoice->getAllItems() as $item) { $item->calcRowTotal(); @@ -45,12 +47,16 @@ public function collect(Mage_Sales_Model_Order_Invoice $invoice) continue; } - $subtotal+= $item->getRowTotal(); - $baseSubtotal+= $item->getBaseRowTotal(); + $subtotal += $item->getRowTotal(); + $baseSubtotal += $item->getBaseRowTotal(); + $subtotalInclTax+= $item->getRowTotalInclTax(); + $baseSubtotalInclTax += $item->getBaseRowTotalInclTax(); } $invoice->setSubtotal($subtotal); $invoice->setBaseSubtotal($baseSubtotal); + $invoice->setSubtotalInclTax($subtotalInclTax); + $invoice->setBaseSubtotalInclTax($baseSubtotalInclTax); $invoice->setGrandTotal($invoice->getGrandTotal() + $subtotal); $invoice->setBaseGrandTotal($invoice->getBaseGrandTotal() + $baseSubtotal); diff --git a/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Tax.php b/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Tax.php index c71bacc59a..10af5688ab 100644 --- a/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Tax.php +++ b/app/code/core/Mage/Sales/Model/Order/Invoice/Total/Tax.php @@ -31,7 +31,7 @@ public function collect(Mage_Sales_Model_Order_Invoice $invoice) { $totalTax = 0; $baseTotalTax = 0; - + $order = $invoice->getOrder(); foreach ($invoice->getAllItems() as $item) { $orderItem = $item->getOrderItem(); $orderItemTax = $orderItem->getTaxAmount(); @@ -69,19 +69,24 @@ public function collect(Mage_Sales_Model_Order_Invoice $invoice) /** * Check shipping amount in previus invoices */ - foreach ($invoice->getOrder()->getInvoiceCollection() as $previusInvoice) { + foreach ($order->getInvoiceCollection() as $previusInvoice) { if ($previusInvoice->getShippingAmount() && !$previusInvoice->isCanceled()) { $includeShippingTax = false; } } if ($includeShippingTax) { - $totalTax += $invoice->getOrder()->getShippingTaxAmount(); - $baseTotalTax += $invoice->getOrder()->getBaseShippingTaxAmount(); - $invoice->setShippingTaxAmount($invoice->getOrder()->getShippingTaxAmount()); - $invoice->setBaseShippingTaxAmount($invoice->getOrder()->getBaseShippingTaxAmount()); + $totalTax += $order->getShippingTaxAmount(); + $baseTotalTax += $order->getBaseShippingTaxAmount(); + $invoice->setShippingTaxAmount($order->getShippingTaxAmount()); + $invoice->setBaseShippingTaxAmount($order->getBaseShippingTaxAmount()); } + $allowedTax = $order->getTaxAmount() - $order->getTaxInvoiced(); + $allowedBaseTax = $order->getBaseTaxAmount() - $order->getBaseTaxInvoiced();; + + $totalTax = min($allowedTax, $totalTax); + $baseTotalTax = min($allowedBaseTax, $baseTotalTax); $invoice->setTaxAmount($totalTax); $invoice->setBaseTaxAmount($baseTotalTax); 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 7a43a01410..4b1a899e19 100644 --- a/app/code/core/Mage/Sales/Model/Order/Pdf/Abstract.php +++ b/app/code/core/Mage/Sales/Model/Order/Pdf/Abstract.php @@ -56,6 +56,8 @@ abstract class Mage_Sales_Model_Order_Pdf_Abstract extends Varien_Object */ protected $_pdf; + protected $_defaultTotalModel = 'sales/order_pdf_total_default'; + /** * Retrieve PDF * @@ -166,7 +168,7 @@ protected function insertAddress(&$page, $store = null) protected function _formatAddress($address) { $return = array(); - foreach (split('\|', $address) as $str) { + foreach (explode('|', $address) as $str) { foreach (Mage::helper('core/string')->str_split($str, 65, true, true) as $part) { if (empty($part)) { continue; @@ -378,62 +380,60 @@ protected function _getTotalsList($source) { $totals = Mage::getConfig()->getNode('global/pdf/totals')->asArray(); usort($totals, array($this, '_sortTotalsList')); + $totalModels = array(); + foreach ($totals as $index => $totalInfo) { + if (!empty($totalInfo['model'])) { + $totalModel = Mage::getModel($totalInfo['model']); + if ($totalModel instanceof Mage_Sales_Model_Order_Pdf_Total_Default) { + $totalInfo['model'] = $totalModel; + } else { + Mage::throwException( + Mage::helper('sales')->__('Pdf total model should extend Mage_Sales_Model_Order_Pdf_Total_Default') + ); + } + } else { + $totalModel = Mage::getModel($this->_defaultTotalModel); + } + $totalModel->setData($totalInfo); + $totalModels[] = $totalModel; + } - return $totals; + return $totalModels; } protected function insertTotals($page, $source){ $order = $source->getOrder(); -// $font = $this->_setFontBold($page); - $totals = $this->_getTotalsList($source); - $lineBlock = array( 'lines' => array(), 'height' => 15 ); foreach ($totals as $total) { - $amount = $source->getDataUsingMethod($total['source_field']); - $displayZero = (isset($total['display_zero']) ? $total['display_zero'] : 0); - - if ($amount != 0 || $displayZero) { - $amount = $order->formatPriceTxt($amount); - - if (isset($total['amount_prefix']) && $total['amount_prefix']) { - $amount = "{$total['amount_prefix']}{$amount}"; + $total->setOrder($order) + ->setSource($source); + + if ($total->canDisplay()) { + foreach ($total->getTotalsForDisplay() as $totalData) { + $lineBlock['lines'][] = array( + array( + 'text' => $totalData['label'], + 'feed' => 475, + 'align' => 'right', + 'font_size' => $totalData['font_size'], + 'font' => 'bold' + ), + array( + 'text' => $totalData['amount'], + 'feed' => 565, + 'align' => 'right', + 'font_size' => $totalData['font_size'], + 'font' => 'bold' + ), + ); } - - $fontSize = (isset($total['font_size']) ? $total['font_size'] : 7); - //$page->setFont($font, $fontSize); - - $label = Mage::helper('sales')->__($total['title']) . ':'; - - $lineBlock['lines'][] = array( - array( - 'text' => $label, - 'feed' => 475, - 'align' => 'right', - 'font_size' => $fontSize, - 'font' => 'bold' - ), - array( - 'text' => $amount, - 'feed' => 565, - 'align' => 'right', - 'font_size' => $fontSize, - 'font' => 'bold' - ), - ); - -// $page->drawText($label, 475-$this->widthForStringUsingFontSize($label, $font, $fontSize), $this->y, 'UTF-8'); -// $page->drawText($amount, 565-$this->widthForStringUsingFontSize($amount, $font, $fontSize), $this->y, 'UTF-8'); -// $this->y -=15; } } -// echo '
      ';
      -//        var_dump($lineBlock);
      -
               $page = $this->drawLineBlocks($page, array($lineBlock));
               return $page;
           }
      diff --git a/app/code/core/Mage/Sales/Model/Order/Pdf/Total/Default.php b/app/code/core/Mage/Sales/Model/Order/Pdf/Total/Default.php
      new file mode 100644
      index 0000000000..e1e83597e0
      --- /dev/null
      +++ b/app/code/core/Mage/Sales/Model/Order/Pdf/Total/Default.php
      @@ -0,0 +1,76 @@
      + array(
      +     *      'amount'   => $amount,
      +     *      'label'    => $label,
      +     *      'font_size'=> $font_size
      +     *  )
      +     * )
      +     * @return array
      +     */
      +    public function getTotalsForDisplay()
      +    {
      +        $amount = $this->getOrder()->formatPriceTxt($this->getAmount());
      +        if ($this->getAmountPrefix()) {
      +            $amount = $this->getAmountPrefix().$amount;
      +        }
      +        $label = Mage::helper('sales')->__($this->getTitle()) . ':';
      +        $fontSize = $this->getFontSize() ? $this->getFontSize() : 7;
      +        $total = array(
      +            'amount'    => $amount,
      +            'label'     => $label,
      +            'font_size' => $fontSize
      +        );
      +        return array($total);
      +    }
      +
      +    /**
      +     * Check if we can display total information in PDF
      +     *
      +     * @return bool
      +     */
      +    public function canDisplay()
      +    {
      +        $amount = $this->getAmount();
      +        return $this->getDisplayZero() || ($amount != 0);
      +    }
      +
      +    /**
      +     * Get Total amount from source
      +     *
      +     * @return float
      +     */
      +    public function getAmount()
      +    {
      +        return $this->getSource()->getDataUsingMethod($this->getSourceField());
      +    }
      +}
      \ No newline at end of file
      diff --git a/app/code/core/Mage/Sales/Model/Order/Shipment/Api.php b/app/code/core/Mage/Sales/Model/Order/Shipment/Api.php
      index 22db54aef4..767d61b48d 100644
      --- a/app/code/core/Mage/Sales/Model/Order/Shipment/Api.php
      +++ b/app/code/core/Mage/Sales/Model/Order/Shipment/Api.php
      @@ -149,53 +149,27 @@ public function create($orderIncrementId, $itemsQty = array(), $comment = null,
                    $this->_fault('data_invalid', Mage::helper('sales')->__('Can not do shipment for order.'));
               }
       
      -        $convertor   = Mage::getModel('sales/convert_order');
      -        $shipment    = $convertor->toShipment($order);
                /* @var $shipment Mage_Sales_Model_Order_Shipment */
      -
      -        foreach ($order->getAllItems() as $orderItem) {
      -            if (!$orderItem->getQtyToShip()) {
      -                continue;
      -            }
      -            if ($orderItem->getIsVirtual()) {
      -                continue;
      -            }
      -            
      -            if ((!isset($itemsQty[$orderItem->getId()])) && (count($itemsQty))) {
      -                continue;
      -            }
      -            
      -            $item = $convertor->itemToShipmentItem($orderItem);
      -            if (isset($itemsQty[$orderItem->getId()])) {
      -                $qty = $itemsQty[$orderItem->getId()];
      +        $shipment = $order->prepareShipment($itemsQty);
      +        if ($shipment) {
      +            $shipment->register();
      +            $shipment->addComment($comment, $email && $includeComment);
      +            if ($email) {
      +                $shipment->setEmailSent(true);
                   }
      -            else {
      -                $qty = $orderItem->getQtyToShip();
      +            $shipment->getOrder()->setIsInProcess(true);
      +            try {
      +                $transactionSave = Mage::getModel('core/resource_transaction')
      +                    ->addObject($shipment)
      +                    ->addObject($shipment->getOrder())
      +                    ->save();
      +                $shipment->sendEmail($email, ($includeComment ? $comment : ''));
      +            } catch (Mage_Core_Exception $e) {
      +                $this->_fault('data_invalid', $e->getMessage());
                   }
      -            $item->setQty($qty);
      -        	$shipment->addItem($item);
      -        }
      -        $shipment->register();
      -        $shipment->addComment($comment, $email && $includeComment);
      -
      -        if ($email) {
      -            $shipment->setEmailSent(true);
      +            return $shipment->getIncrementId();
               }
      -
      -        $shipment->getOrder()->setIsInProcess(true);
      -
      -        try {
      -            $transactionSave = Mage::getModel('core/resource_transaction')
      -                ->addObject($shipment)
      -                ->addObject($shipment->getOrder())
      -                ->save();
      -
      -            $shipment->sendEmail($email, ($includeComment ? $comment : ''));
      -        } catch (Mage_Core_Exception $e) {
      -            $this->_fault('data_invalid', $e->getMessage());
      -        }
      -
      -        return $shipment->getIncrementId();
      +        return null;
           }
       
           /**
      diff --git a/app/code/core/Mage/Sales/Model/Order/Shipment/Api/V2.php b/app/code/core/Mage/Sales/Model/Order/Shipment/Api/V2.php
      index f935338d5f..a7a7eff646 100644
      --- a/app/code/core/Mage/Sales/Model/Order/Shipment/Api/V2.php
      +++ b/app/code/core/Mage/Sales/Model/Order/Shipment/Api/V2.php
      @@ -128,48 +128,28 @@ public function create($orderIncrementId, $itemsQty = array(), $comment = null,
                    $this->_fault('data_invalid', Mage::helper('sales')->__('Can not do shipment for order.'));
               }
       
      -        $convertor   = Mage::getModel('sales/convert_order');
               $shipment    = $convertor->toShipment($order);
                /* @var $shipment Mage_Sales_Model_Order_Shipment */
      -
      -        foreach ($order->getAllItems() as $orderItem) {
      -            if (!$orderItem->getQtyToShip()) {
      -                continue;
      -            }
      -            if ($orderItem->getIsVirtual()) {
      -                continue;
      -            }
      -            $item = $convertor->itemToShipmentItem($orderItem);
      -            if (isset($itemsQty[$orderItem->getId()])) {
      -                $qty = $itemsQty[$orderItem->getId()];
      +        $shipment = $order->prepareShipment($itemsQty);
      +        if ($shipment) {
      +            $shipment->register();
      +            $shipment->addComment($comment, $email && $includeComment);
      +            if ($email) {
      +                $shipment->setEmailSent(true);
                   }
      -            else {
      -                $qty = $orderItem->getQtyToShip();
      +            $shipment->getOrder()->setIsInProcess(true);
      +            try {
      +                $transactionSave = Mage::getModel('core/resource_transaction')
      +                    ->addObject($shipment)
      +                    ->addObject($shipment->getOrder())
      +                    ->save();
      +                $shipment->sendEmail($email, ($includeComment ? $comment : ''));
      +            } catch (Mage_Core_Exception $e) {
      +                $this->_fault('data_invalid', $e->getMessage());
                   }
      -            $item->setQty($qty);
      -            $shipment->addItem($item);
      -        }
      -        $shipment->register();
      -        $shipment->addComment($comment, $email && $includeComment);
      -
      -        if ($email) {
      -            $shipment->setEmailSent(true);
      -        }
      -
      -        $shipment->getOrder()->setIsInProcess(true);
      -
      -        try {
      -            $transactionSave = Mage::getModel('core/resource_transaction')
      -                ->addObject($shipment)
      -                ->addObject($shipment->getOrder())
      -                ->save();
      -
      -            $shipment->sendEmail($email, ($includeComment ? $comment : ''));
      -        } catch (Mage_Core_Exception $e) {
      -            $this->_fault('data_invalid', $e->getMessage());
      +            return $shipment->getIncrementId();
               }
      -
      -        return $shipment->getIncrementId();
      +        return null;
           }
       
           /**
      diff --git a/app/code/core/Mage/Sales/Model/Quote.php b/app/code/core/Mage/Sales/Model/Quote.php
      index 6e1b433383..fd717ffee5 100644
      --- a/app/code/core/Mage/Sales/Model/Quote.php
      +++ b/app/code/core/Mage/Sales/Model/Quote.php
      @@ -726,13 +726,18 @@ public function addProduct(Mage_Catalog_Model_Product $product, $request=null)
            * @param   Mage_Catalog_Model_Product $product
            * @return  Mage_Sales_Model_Quote_Item
            */
      -    protected function _addCatalogProduct(Mage_Catalog_Model_Product $product, $qty=1)
      +    protected function _addCatalogProduct(Mage_Catalog_Model_Product $product, $qty = 1)
           {
      -
               $item = $this->getItemByProduct($product);
               if (!$item) {
                   $item = Mage::getModel('sales/quote_item');
                   $item->setQuote($this);
      +            if (Mage::app()->getStore()->isAdmin()) {
      +                $item->setStoreId($this->getStore()->getId());
      +            }
      +            else {
      +                $item->setStoreId(Mage::app()->getStore()->getId());
      +            }
               }
       
               /**
      @@ -909,9 +914,6 @@ public function collectTotals()
                   $address->setSubtotal(0);
                   $address->setBaseSubtotal(0);
       
      -            $address->setSubtotalWithDiscount(0);
      -            $address->setBaseSubtotalWithDiscount(0);
      -
                   $address->setGrandTotal(0);
                   $address->setBaseGrandTotal(0);
       
      @@ -969,11 +971,20 @@ public function collectTotals()
       
           /**
            * Get all quote totals (sorted by priority)
      +     * Metchod process quote states isVirtual and isMultiShipping
            *
            * @return array
            */
           public function getTotals()
           {
      +        /**
      +         * If quote is virtual we are using totals of billing address because
      +         * all items assigned to it
      +         */
      +        if ($this->isVirtual()) {
      +            return $this->getBillingAddress()->getTotals();
      +        }
      +        
               $totals = $this->getShippingAddress()->getTotals();
               foreach ($this->getBillingAddress()->getTotals() as $code => $total) {
                   if (isset($totals[$code])) {
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address.php b/app/code/core/Mage/Sales/Model/Quote/Address.php
      index 6c617a6eb0..fc53b6d161 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Address.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address.php
      @@ -61,11 +61,11 @@ class Mage_Sales_Model_Quote_Address extends Mage_Customer_Model_Address_Abstrac
           protected $_rates = null;
       
           /**
      -     * Total models array
      +     * Total models collector
            *
      -     * @var array
      +     * @var Mage_Sales_Model_Quote_Address_Totla_Collector
            */
      -    protected $_totalModels;
      +    protected $_totalCollector = null;
       
           /**
            * Total data as array
      @@ -74,6 +74,9 @@ class Mage_Sales_Model_Quote_Address extends Mage_Customer_Model_Address_Abstrac
            */
           protected $_totals = array();
       
      +    protected $_totalAmounts = array();
      +    protected $_baseTotalAmounts = array();
      +
           /**
            * Initialize resource
            */
      @@ -240,7 +243,8 @@ public function getAllItems()
                       }
       
                       if (!$aItem->getQuoteItemImported()) {
      -                    if ($qItem = $this->getQuote()->getItemById($aItem->getQuoteItemId())) {
      +                    $qItem = $this->getQuote()->getItemById($aItem->getQuoteItemId());
      +                    if ($qItem) {
                               $this->addItem($aItem);
                               $aItem->importQuoteItem($qItem);
                           }
      @@ -253,12 +257,7 @@ public function getAllItems()
                       if ($qItem->isDeleted()) {
                           continue;
                       }
      -//                if ($this->getAddressType() == self::TYPE_BILLING && $qItem->getProduct()->getIsVirtual()) {
      -//                    $items[] = $qItem;
      -//                }
      -//                elseif ($this->getAddressType() == self::TYPE_SHIPPING && !$qItem->getProduct()->getIsVirtual()) {
      -//                    $items[] = $qItem;
      -//                }
      +
                       /**
                        * For virtual quote we assign all items to billing address
                        */
      @@ -312,7 +311,8 @@ public function getItemQty($itemId = 0)
                       $qty += $item->getQty();
                   }
               } else {
      -            if ($item = $this->getItemById($itemId)) {
      +            $item = $this->getItemById($itemId);
      +            if ($item) {
                       $qty = $item->getQty();
                   }
               }
      @@ -369,7 +369,8 @@ public function getItemByQuoteItemId($itemId)
            */
           public function removeItem($itemId)
           {
      -        if ($item = $this->getItemById($itemId)) {
      +        $item = $this->getItemById($itemId);
      +        if ($item) {
                   $item->isDeleted(true);
               }
               return $this;
      @@ -567,7 +568,9 @@ public function collectShippingRates()
       
               $this->removeAllShippingRates();
       
      -        if (!$this->getCountryId() && !$this->getPostcode()) {
      +        $havingOptionalZip = Mage::helper('directory')->getCountriesWithOptionalZip();
      +        $postcodeValid = $this->getPostcode() || in_array($this->getCountryId(), $havingOptionalZip);
      +        if (!$this->getCountryId() && !$postcodeValid) {
                   return $this;
               }
       
      @@ -636,30 +639,31 @@ public function collectShippingRates()
               return $this;
           }
       
      +    /**
      +     * Get totals collector model
      +     *
      +     * @return Mage_Sales_Model_Quote_Address_Total_Collector
      +     */
      +    public function getTotalCollector()
      +    {
      +        if ($this->_totalCollector === null) {
      +            $this->_totalCollector = Mage::getSingleton(
      +                'sales/quote_address_total_collector',
      +                array('store'=>$this->getQuote()->getStore())
      +            );
      +        }
      +        return $this->_totalCollector;
      +    }
      +
           /**
            * Retrieve total models
            *
      +     * @deprecated
            * @return array
            */
           public function getTotalModels()
           {
      -        if (!$this->_totalModels) {
      -            $totalsConfig = Mage::getConfig()->getNode('global/sales/quote/totals');
      -            $models = array();
      -            foreach ($totalsConfig->children() as $totalCode=>$totalConfig) {
      -                $sort = Mage::getStoreConfig('sales/totals_sort/'.$totalCode);
      -                while (isset($models[$sort])) {
      -                    $sort++;
      -                }
      -                $class = $totalConfig->getClassName();
      -                if ($class && ($model = Mage::getModel($class))) {
      -                    $models[$sort] = $model->setCode($totalCode);
      -                }
      -            }
      -            ksort($models);
      -            $this->_totalModels = $models;
      -        }
      -        return $this->_totalModels;
      +        return $this->getTotalCollector()->getRetrievers();
           }
       
           /**
      @@ -669,25 +673,21 @@ public function getTotalModels()
            */
           public function collectTotals()
           {
      -        foreach ($this->getTotalModels() as $model) {
      -            if (is_callable(array($model, 'collect'))) {
      -                $model->collect($this);
      -            }
      +        foreach ($this->getTotalCollector()->getCollectors() as $model) {
      +            $model->collect($this);
               }
               return $this;
           }
       
           /**
      -     * Retrieve totals as array
      +     * Get address totals as array
            *
            * @return array
            */
           public function getTotals()
           {
      -        foreach ($this->getTotalModels() as $model) {
      -            if (is_callable(array($model, 'fetch'))) {
      -                $model->fetch($this);
      -            }
      +        foreach ($this->getTotalCollector()->getRetrievers() as $model) {
      +            $model->fetch($this);
               }
               return $this->_totals;
           }
      @@ -706,6 +706,7 @@ public function addTotal($total)
               } elseif ($total instanceof Mage_Sales_Model_Quote_Total) {
                   $totalInstance = $total;
               }
      +        $totalInstance->setAddress($this);
               $this->_totals[$totalInstance->getCode()] = $totalInstance;
               return $this;
           }
      @@ -776,14 +777,16 @@ public function setAppliedTaxes($data)
            */
           public function setShippingAmount($value, $alreadyExclTax = false)
           {
      -        if (Mage::helper('tax')->shippingPriceIncludesTax()) {
      -            $includingTax = Mage::helper('tax')->getShippingPrice($value, true, $this, $this->getQuote()->getCustomerTaxClassId());
      -            if (!$alreadyExclTax) {
      -                $value = Mage::helper('tax')->getShippingPrice($value, false, $this, $this->getQuote()->getCustomerTaxClassId());
      -            }
      -            $this->setShippingTaxAmount($includingTax - $value);
      -        }
               return $this->setData('shipping_amount', $value);
      +//
      +//        if (Mage::helper('tax')->shippingPriceIncludesTax()) {
      +//            $includingTax = Mage::helper('tax')->getShippingPrice($value, true, $this, $this->getQuote()->getCustomerTaxClassId());
      +//            if (!$alreadyExclTax) {
      +//                $value = Mage::helper('tax')->getShippingPrice($value, false, $this, $this->getQuote()->getCustomerTaxClassId());
      +//            }
      +//            $this->setShippingTaxAmount($includingTax - $value);
      +//        }
      +//        return $this->setData('shipping_amount', $value);
           }
       
           /**
      @@ -795,13 +798,117 @@ public function setShippingAmount($value, $alreadyExclTax = false)
            */
           public function setBaseShippingAmount($value, $alreadyExclTax = false)
           {
      -        if (Mage::helper('tax')->shippingPriceIncludesTax()) {
      -            $includingTax = Mage::helper('tax')->getShippingPrice($value, true, $this, $this->getQuote()->getCustomerTaxClassId());
      -            if (!$alreadyExclTax) {
      -                $value = Mage::helper('tax')->getShippingPrice($value, false, $this, $this->getQuote()->getCustomerTaxClassId());
      -            }
      -            $this->setBaseShippingTaxAmount($includingTax - $value);
      -        }
               return $this->setData('base_shipping_amount', $value);
      +//
      +//        if (Mage::helper('tax')->shippingPriceIncludesTax()) {
      +//            $includingTax = Mage::helper('tax')->getShippingPrice($value, true, $this, $this->getQuote()->getCustomerTaxClassId());
      +//            if (!$alreadyExclTax) {
      +//                $value = Mage::helper('tax')->getShippingPrice($value, false, $this, $this->getQuote()->getCustomerTaxClassId());
      +//            }
      +//            $this->setBaseShippingTaxAmount($includingTax - $value);
      +//        }
      +//        return $this->setData('base_shipping_amount', $value);
      +    }
      +
      +    /**
      +     * Set total amount value
      +     *
      +     * @param   string $code
      +     * @param   float $amount
      +     * @return  Mage_Sales_Model_Quote_Address
      +     */
      +    public function setTotalAmount($code, $amount)
      +    {
      +        $this->_totalAmounts[$code] = $amount;
      +        if ($code != 'subtotal') {
      +            $code = $code.'_amount';
      +        }
      +        $this->setData($code, $amount);
      +        return $this;
      +    }
      +
      +    /**
      +     * Set total amount value in base store currency
      +     *
      +     * @param   string $code
      +     * @param   float $amount
      +     * @return  Mage_Sales_Model_Quote_Address
      +     */
      +    public function setBaseTotalAmount($code, $amount)
      +    {
      +        $this->_baseTotalAmounts[$code] = $amount;
      +        if ($code != 'subtotal') {
      +            $code = $code.'_amount';
      +        }
      +        $this->setData('base_'.$code, $amount);
      +        return $this;
      +    }
      +
      +    /**
      +     * Get total amount value by code
      +     *
      +     * @param   string $code
      +     * @return  float
      +     */
      +    public function getTotalAmount($code)
      +    {
      +        if (isset($this->_totalAmounts[$code])) {
      +            return  $this->_totalAmounts[$code];
      +        }
      +        return 0;
      +    }
      +
      +    /**
      +     * Get total amount value by code in base store curncy
      +     *
      +     * @param   string $code
      +     * @return  float
      +     */
      +    public function getBaseTotalAmount($code)
      +    {
      +        if (isset($this->_baseTotalAmounts[$code])) {
      +            return  $this->_baseTotalAmounts[$code];
      +        }
      +        return 0;
      +    }
      +
      +    /**
      +     * Get all total amount values
      +     *
      +     * @return array
      +     */
      +    public function getAllTotalAmounts()
      +    {
      +        return $this->_totalAmounts;
      +    }
      +
      +    /**
      +     * Get all total amount values in base currency
      +     *
      +     * @return array
      +     */
      +    public function getAllBaseTotalAmounts()
      +    {
      +        return $this->_baseTotalAmounts;
      +    }
      +
      +    /**
      +     * Get subtotal amount with applied discount in base currency
      +     *
      +     * @return float
      +     */
      +    public function getBaseSubtotalWithDiscount()
      +    {
      +        return $this->getBaseSubtotal()-$this->getBaseDiscountAmount();
      +    }
      +
      +    /**
      +     * Get subtotal amount with applied discount
      +     *
      +     * @return float
      +     */
      +    public function getSubtotalWithDiscount()
      +    {
      +        return $this->getSubtotal()-$this->getDiscountAmount();
           }
       }
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Abstract.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Abstract.php
      index bf23aebc8d..7336a398a2 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Abstract.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Abstract.php
      @@ -40,6 +40,7 @@ abstract class Mage_Sales_Model_Quote_Address_Total_Abstract
            * @var string
            */
           protected $_code;
      +    protected $_address = null;
       
           /**
            * Set total code code name
      @@ -64,13 +65,19 @@ public function getCode()
           }
       
           /**
      -     * Collect totals process
      +     * Collect totals process.
            *
            * @param Mage_Sales_Model_Quote_Address $address
            * @return Mage_Sales_Model_Quote_Address_Total_Abstract
            */
           public function collect(Mage_Sales_Model_Quote_Address $address)
           {
      +        $this->_setAddress($address);
      +        /**
      +         * Reset amounts
      +         */
      +        $this->_setAmount(0);
      +        $this->_setBaseAmount(0);
               return $this;
           }
       
      @@ -82,6 +89,102 @@ public function collect(Mage_Sales_Model_Quote_Address $address)
            */
           public function fetch(Mage_Sales_Model_Quote_Address $address)
           {
      +        $this->_setAddress($address);
               return array();
           }
      +
      +    /**
      +     * Set address shich can be used inside totals calculation
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Abstract
      +     */
      +    protected function _setAddress(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        $this->_address = $address;
      +        return $this;
      +    }
      +
      +    /**
      +     * Get quote address object
      +     *
      +     * @throw   Mage_Core_Exception if address not declared
      +     * @return  Mage_Sales_Model_Quote_Address
      +     */
      +    protected function _getAddress()
      +    {
      +        if ($this->_address === null) {
      +            Mage::throwException(
      +                Mage::helper('sales')->__('Address model is not defined')
      +            );
      +        }
      +        return $this->_address;
      +    }
      +
      +    /**
      +     * Set total model amount value to address
      +     *
      +     * @param   float $amount
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Abstract
      +     */
      +    protected function _setAmount($amount)
      +    {
      +        $this->_getAddress()->setTotalAmount($this->getCode(), $amount);
      +        return $this;
      +    }
      +
      +    /**
      +     * Set total model base amount value to address
      +     *
      +     * @param   float $amount
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Abstract
      +     */
      +    protected function _setBaseAmount($baseAmount)
      +    {
      +        $this->_getAddress()->setBaseTotalAmount($this->getCode(), $baseAmount);
      +        return $this;
      +    }
      +
      +    /**
      +     * Add total model amount value to address
      +     *
      +     * @param   float $amount
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Abstract
      +     */
      +    protected function _addAmount($amount)
      +    {
      +        $this->_getAddress()->setTotalAmount(
      +            $this->getCode(),
      +            $this->_getAddress()->getTotalAmount($this->getCode())+$amount
      +        );
      +        return $this;
      +    }
      +
      +    /**
      +     * Add total model base amount value to address
      +     *
      +     * @param   float $amount
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Abstract
      +     */
      +    protected function _addBaseAmount($baseAmount)
      +    {
      +        $this->_getAddress()->setBaseTotalAmount(
      +            $this->getCode(),
      +            $this->_getAddress()->getBaseTotalAmount($this->getCode())+$baseAmount
      +        );
      +        return $this;
      +    }
      +
      +    /**
      +     * Process model configuration array.
      +     * This method can be used for changing models apply sort order
      +     *
      +     * @param   array $config
      +     * @param   store $store
      +     * @return  array
      +     */
      +    public function processConfigArray($config, $store)
      +    {
      +        return $config;
      +    }
       }
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Collector.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Collector.php
      new file mode 100644
      index 0000000000..fadebe8a95
      --- /dev/null
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Collector.php
      @@ -0,0 +1,230 @@
      +_store = $options['store'];
      +        } else {
      +            $this->_store = Mage::app()->getStore();
      +        }
      +        $this->_initModels()
      +            ->_initCollectors()
      +            ->_initRetrievers();
      +    }
      +
      +    /**
      +     * Get total models array ordered for right calculation logic
      +     *
      +     * @return array
      +     */
      +    public function getCollectors()
      +    {
      +        return $this->_collectors;
      +    }
      +
      +    /**
      +     * Get total models array ordered for right display sequence
      +     *
      +     * @return array
      +     */
      +    public function getRetrievers()
      +    {
      +        return $this->_retrievers;
      +    }
      +
      +    /**
      +     * Initialize total models configuration and objects
      +     *
      +     * @return Mage_Sales_Model_Quote_Address_Total_Collector
      +     */
      +    protected function _initModels()
      +    {
      +        $totalsConfig = Mage::getConfig()->getNode('global/sales/quote/totals');
      +
      +        foreach ($totalsConfig->children() as $totalCode=>$totalConfig) {
      +            $class = $totalConfig->getClassName();
      +            if ($class) {
      +                $model = Mage::getModel($class);
      +                if ($model instanceof Mage_Sales_Model_Quote_Address_Total_Abstract) {
      +                    $model->setCode($totalCode);
      +                    $this->_modelsConfig[$totalCode]= $this->_prepareConfigArray($totalCode, $totalConfig);
      +                    $this->_modelsConfig[$totalCode]= $model->processConfigArray(
      +                        $this->_modelsConfig[$totalCode],
      +                        $this->_store
      +                    );
      +                    $this->_models[$totalCode]      = $model;
      +                } else {
      +                    Mage::throwException(
      +                        Mage::helper('sales')->__('Address total model should be extended from Mage_Sales_Model_Quote_Address_Total_Abstract')
      +                    );
      +                }
      +            }
      +        }
      +        return $this;
      +    }
      +
      +    /**
      +     * Prepare configuration array for total model
      +     *
      +     * @param   string $code
      +     * @param   Mage_Core_Model_Config_Element $totalConfig
      +     * @return  array
      +     */
      +    protected function _prepareConfigArray($code, $totalConfig)
      +    {
      +        $totalConfig = (array) $totalConfig;
      +        if (isset($totalConfig['before'])) {
      +            $totalConfig['before'] = explode(',',$totalConfig['before']);
      +        } else {
      +            $totalConfig['before'] = array();
      +        }
      +        if (isset($totalConfig['after'])) {
      +            $totalConfig['after'] = explode(',',$totalConfig['after']);
      +        } else {
      +            $totalConfig['after'] = array();
      +        }
      +        $totalConfig['_code'] = $code;
      +        return $totalConfig;
      +    }
      +
      +    /**
      +     * Aggregate before/after information from all items and sort totals based on this data
      +     *
      +     * @return array
      +     */
      +    protected function _getSortedCollectorCodes()
      +    {
      +        if (Mage::app()->useCache('config')) {
      +            $cachedData = Mage::app()->loadCache('sorted_quote_collectors');
      +            if ($cachedData) {
      +                return unserialize($cachedData);
      +            }
      +        }
      +        $configArray = $this->_modelsConfig;
      +        foreach ($configArray as $code => $data) {
      +            foreach ($data['before'] as $beforeCode) {
      +                if (!isset($configArray[$beforeCode])) {
      +                    continue;
      +                }
      +                $configArray[$code]['before'] = array_merge(
      +                    $configArray[$code]['before'], $configArray[$beforeCode]['before']
      +                );
      +                $configArray[$beforeCode]['after']  = array_merge(
      +                    $configArray[$beforeCode]['after'], array($code), $data['after']
      +                );
      +                $configArray[$beforeCode]['after']  = array_unique($configArray[$beforeCode]['after']);
      +            }
      +            foreach ($data['after'] as $afterCode) {
      +                if (!isset($configArray[$afterCode])) {
      +                    continue;
      +                }
      +                $configArray[$code]['after'] = array_merge(
      +                    $configArray[$code]['after'], $configArray[$afterCode]['after']
      +                );
      +                $configArray[$afterCode]['before'] = array_merge(
      +                    $configArray[$afterCode]['before'], array($code), $data['before']
      +                );
      +                $configArray[$afterCode]['before'] = array_unique($configArray[$afterCode]['before']);
      +            }
      +        }
      +        uasort($configArray, array($this, '_compareTotals'));
      +        $sortedCollectors = array_keys($configArray);
      +        if (Mage::app()->useCache('config')) {
      +            Mage::app()->saveCache(serialize($sortedCollectors), 'sorted_quote_collectors', array(
      +                Mage_Core_Model_Config::CACHE_TAG
      +            ));
      +        }
      +        return $sortedCollectors;
      +    }
      +
      +    /**
      +     * Initialize collectors array.
      +     * Collectors array is array of total models ordered based on configuration settings
      +     *
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Collector
      +     */
      +    protected function _initCollectors()
      +    {
      +        $sortedCodes = $this->_getSortedCollectorCodes();
      +        foreach ($sortedCodes as $code) {
      +            $this->_collectors[$code] = $this->_models[$code];
      +        }
      +        
      +        return $this;
      +    }
      +
      +    /**
      +     * uasort callback function
      +     *
      +     * @param   array $a
      +     * @param   array $b
      +     * @return  int
      +     */
      +    protected function _compareTotals($a, $b)
      +    {
      +        $aCode = $a['_code'];
      +        $bCode = $b['_code'];
      +        if (in_array($aCode, $b['after']) || in_array($bCode, $a['before'])) {
      +            $res = -1;
      +        } elseif (in_array($bCode, $a['after']) || in_array($aCode, $b['before'])) {
      +            $res = 1;
      +        } else {
      +            $res = 0;
      +        }
      +        return $res;
      +    }
      +
      +    /**
      +     * Initialize retrievers array
      +     *
      +     * @return Mage_Sales_Model_Quote_Address_Total_Collector
      +     */
      +    protected function _initRetrievers()
      +    {
      +        $sorts = Mage::getStoreConfig('sales/totals_sort', $this->_store);
      +        foreach ($sorts as $code => $sortOrder) {
      +            if (isset($this->_models[$code])) {
      +                $this->_retrievers[$sortOrder] = $this->_models[$code];
      +            }
      +        }
      +        ksort($this->_retrievers);
      +        $notSorted = array_diff(array_keys($this->_models), array_keys($sorts));
      +        foreach ($notSorted as $code) {
      +            $this->_retrievers[] = $this->_models[$code];
      +        }
      +        return $this;
      +    }
      +}
      \ No newline at end of file
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Discount.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Discount.php
      index f961e0a176..9f620f6400 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Discount.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Discount.php
      @@ -20,6 +20,8 @@
        *
        * @category   Mage
        * @package    Mage_Sales
      + * @deprecated Moved to SalesRule Module
      + * @deprecated after 1.3.2.2, functionality moved to salesRule module
        * @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)
        */
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Grand.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Grand.php
      index 0b893c222a..515c9d7462 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Grand.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Grand.php
      @@ -27,13 +27,38 @@
       
       class Mage_Sales_Model_Quote_Address_Total_Grand extends Mage_Sales_Model_Quote_Address_Total_Abstract
       {
      +    /**
      +     * Collect grand total address amount
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Grand
      +     */
      +    public function collect(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        $grandTotal     = $address->getGrandTotal();
      +        $baseGrandTotal = $address->getBaseGrandTotal();
      +
      +        $totals     = array_sum($address->getAllTotalAmounts());
      +        $baseTotals = array_sum($address->getAllBaseTotalAmounts());
      +
      +        $address->setGrandTotal($grandTotal+$totals);
      +        $address->setBaseGrandTotal($baseGrandTotal+$baseTotals);
      +        return $this;
      +    }
      +
      +    /**
      +     * Add grand total information to adderess
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Grand
      +     */
           public function fetch(Mage_Sales_Model_Quote_Address $address)
           {
               $address->addTotal(array(
      -            'code'=>$this->getCode(),
      -            'title'=>Mage::helper('sales')->__('Grand Total'),
      -            'value'=>$address->getGrandTotal(),
      -            'area'=>'footer',
      +            'code'  => $this->getCode(),
      +            'title' => Mage::helper('sales')->__('Grand Total'),
      +            'value' => $address->getGrandTotal(),
      +            'area'  => 'footer',
               ));
               return $this;
           }
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Shipping.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Shipping.php
      index 17eba71195..622358db48 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Shipping.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Shipping.php
      @@ -27,21 +27,34 @@
       
       class Mage_Sales_Model_Quote_Address_Total_Shipping extends Mage_Sales_Model_Quote_Address_Total_Abstract
       {
      +    public function __construct()
      +    {
      +        $this->setCode('shipping');
      +    }
      +
      +    /**
      +     * Collect totals information about shipping
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Shipping
      +     */
           public function collect(Mage_Sales_Model_Quote_Address $address)
           {
      +        parent::collect($address);
      +        
               $oldWeight = $address->getWeight();
               $address->setWeight(0);
      -        $address->setShippingAmount(0);
      -        $address->setBaseShippingAmount(0);
               $address->setFreeMethodWeight(0);
      +        $this->_setAmount(0)
      +            ->_setBaseAmount(0);
       
               $items = $address->getAllItems();
               if (!count($items)) {
                   return $this;
               }
       
      -        $method = $address->getShippingMethod();
      -        $freeAddress = $address->getFreeShipping();
      +        $method     = $address->getShippingMethod();
      +        $freeAddress= $address->getFreeShipping();
       
               $addressWeight      = $address->getWeight();
               $freeMethodWeight   = $address->getFreeMethodWeight();
      @@ -52,10 +65,10 @@ public function collect(Mage_Sales_Model_Quote_Address $address)
                   /**
                    * Skip if this item is virtual
                    */
      -
                   if ($item->getProduct()->isVirtual()) {
                       continue;
                   }
      +            
                   /**
                    * Children weight we calculate for parent
                    */
      @@ -68,11 +81,11 @@ public function collect(Mage_Sales_Model_Quote_Address $address)
                           if ($child->getProduct()->isVirtual()) {
                               continue;
                           }
      -                    $addressQty += $item->getQty()*$child->getQty();
      +                    $addressQty += $child->getTotalQty();
       
                           if (!$item->getProduct()->getWeightType()) {
                               $itemWeight = $child->getWeight();
      -                        $itemQty    = $item->getQty()*$child->getQty();
      +                        $itemQty    = $child->getTotalQty();
                               $rowWeight  = $itemWeight*$itemQty;
                               $addressWeight += $rowWeight;
                               if ($freeAddress || $child->getFreeShipping()===true) {
      @@ -141,8 +154,8 @@ public function collect(Mage_Sales_Model_Quote_Address $address)
       
               $address->collectShippingRates();
       
      -        $address->setShippingAmount(0);
      -        $address->setBaseShippingAmount(0);
      +        $this->_setAmount(0)
      +            ->_setBaseAmount(0);
       
               $method = $address->getShippingMethod();
       
      @@ -150,19 +163,23 @@ public function collect(Mage_Sales_Model_Quote_Address $address)
                   foreach ($address->getAllShippingRates() as $rate) {
                       if ($rate->getCode()==$method) {
                           $amountPrice = $address->getQuote()->getStore()->convertPrice($rate->getPrice(), false);
      -                    $address->setShippingAmount($amountPrice);
      -                    $address->setBaseShippingAmount($rate->getPrice());
      +                    $this->_setAmount($amountPrice);
      +                    $this->_setBaseAmount($rate->getPrice());
                           $address->setShippingDescription($rate->getCarrierTitle().' - '.$rate->getMethodTitle());
                           break;
                       }
                   }
               }
       
      -        $address->setGrandTotal($address->getGrandTotal() + $address->getShippingAmount());
      -        $address->setBaseGrandTotal($address->getBaseGrandTotal() + $address->getBaseShippingAmount());
               return $this;
           }
       
      +    /**
      +     * Add shipping totals information to address object
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Shipping
      +     */
           public function fetch(Mage_Sales_Model_Quote_Address $address)
           {
               $amount = $address->getShippingAmount();
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php
      index 4d8c0a392f..ad4ff6e3a7 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php
      @@ -35,13 +35,8 @@ class Mage_Sales_Model_Quote_Address_Total_Subtotal extends Mage_Sales_Model_Quo
            */
           public function collect(Mage_Sales_Model_Quote_Address $address)
           {
      -        /**
      -         * Reset subtotal information
      -         */
      -        $address->setSubtotal(0);
      -        $address->setBaseSubtotal(0);
      +        parent::collect($address);
               $address->setTotalQty(0);
      -        $address->setBaseTotalPriceIncTax(0);
       
               /**
                * Process address items
      @@ -57,8 +52,6 @@ public function collect(Mage_Sales_Model_Quote_Address $address)
               /**
                * Initialize grand totals
                */
      -        $address->setGrandTotal($address->getSubtotal());
      -        $address->setBaseGrandTotal($address->getBaseSubtotal());
               Mage::helper('sales')->checkQuoteAmount($address->getQuote(), $address->getSubtotal());
               Mage::helper('sales')->checkQuoteAmount($address->getQuote(), $address->getBaseSubtotal());
               return $this;
      @@ -104,13 +97,12 @@ protected function _initItem($address, $item)
                   );
                   $item->setPrice($finalPrice);
                   $item->calcRowTotal();
      -        }
      -        else if (!$quoteItem->getParentItem()) {
      +        } else if (!$quoteItem->getParentItem()) {
                   $finalPrice = $product->getFinalPrice($quoteItem->getQty());
                   $item->setPrice($finalPrice);
                   $item->calcRowTotal();
      -            $address->setSubtotal($address->getSubtotal() + $item->getRowTotal());
      -            $address->setBaseSubtotal($address->getBaseSubtotal() + $item->getBaseRowTotal());
      +            $this->_addAmount($item->getRowTotal());
      +            $this->_addBaseAmount($item->getBaseRowTotal());
                   $address->setTotalQty($address->getTotalQty() + $item->getQty());
               }
       
      @@ -142,12 +134,18 @@ protected function _removeItem($address, $item)
               return $this;
           }
       
      +    /**
      +     * Assign subtotal amount and label to address object
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_Sales_Model_Quote_Address_Total_Subtotal
      +     */
           public function fetch(Mage_Sales_Model_Quote_Address $address)
           {
               $address->addTotal(array(
      -            'code'=>$this->getCode(),
      -            'title'=>Mage::helper('sales')->__('Subtotal'),
      -            'value'=>$address->getSubtotal()
      +            'code'  => $this->getCode(),
      +            'title' => Mage::helper('sales')->__('Subtotal'),
      +            'value' => $address->getSubtotal()
               ));
               return $this;
           }
      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 69a4027ce1..37325ba362 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
      @@ -20,6 +20,7 @@
        *
        * @category   Mage
        * @package    Mage_Sales
      + * @deprecated after 1.3.2.2, functionality moved to tax module
        * @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)
        */
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php b/app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php
      index 1e9d524c78..f59dc2d213 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php
      @@ -44,6 +44,11 @@ abstract class Mage_Sales_Model_Quote_Item_Abstract extends Mage_Core_Model_Abst
            */
           abstract function getQuote();
       
      +    /**
      +     * Specify parent item id before saving data
      +     *
      +     * @return  Mage_Sales_Model_Quote_Item_Abstract
      +     */
           protected function _beforeSave()
           {
               parent::_beforeSave();
      @@ -121,8 +126,8 @@ public function setMessage($messages) {
           /**
            * Add message of quote item to array of messages
            *
      -     * @param string $message
      -     * @return Mage_Sales_Model_Quote_Item_Abstract
      +     * @param   string $message
      +     * @return  Mage_Sales_Model_Quote_Item_Abstract
            */
           public function addMessage($message)
           {
      @@ -133,7 +138,8 @@ public function addMessage($message)
           /**
            * Get messages array of quote item
            *
      -     * @return array
      +     * @param   bool $string flag for converting messages to string
      +     * @return  array | string
            */
           public function getMessage($string = true)
           {
      @@ -163,15 +169,14 @@ public function checkData()
               $this->setHasError(false);
               $this->unsMessage();
       
      -        $qty = $this->getData('qty');
      +        $qty = $this->_getData('qty');
      +        
               try {
                   $this->setQty($qty);
      -        }
      -        catch (Mage_Core_Exception $e){
      +        } catch (Mage_Core_Exception $e){
                   $this->setHasError(true);
                   $this->setMessage($e->getMessage());
      -        }
      -        catch (Exception $e){
      +        } catch (Exception $e){
                   $this->setHasError(true);
                   $this->setMessage(Mage::helper('sales')->__('Item qty declare error'));
               }
      @@ -182,7 +187,9 @@ public function checkData()
                   $this->setHasError(true);
                   $this->setMessage($e->getMessage());
                   $this->getQuote()->setHasError(true);
      -            $this->getQuote()->addMessage(Mage::helper('sales')->__('Some of the products below don\'t have all the required options. Please remove them and add again with all the required options.'));
      +            $this->getQuote()->addMessage(
      +                Mage::helper('sales')->__('Some of the products below don\'t have all the required options. Please remove them and add again with all the required options.')
      +            );
               } catch (Exception $e) {
                   $this->setHasError(true);
                   $this->setMessage(Mage::helper('sales')->__('Item options declare error'));
      @@ -193,6 +200,29 @@ public function checkData()
               return $this;
           }
       
      +    /**
      +     * Get original (not related with parent item) item quantity
      +     *
      +     * @return  int|float
      +     */
      +    public function getQty()
      +    {
      +        return $this->_getData('qty');
      +    }
      +
      +    /**
      +     * Get total item quantity (include parent item relation)
      +     *
      +     * @return  int|float
      +     */
      +    public function getTotalQty()
      +    {
      +        if ($this->getParentItem()) {
      +            return $this->getQty()*$this->getParentItem()->getQty();
      +        }
      +        return $this->getQty();
      +    }
      +
           /**
            * Calculate item row total price
            *
      @@ -200,31 +230,207 @@ public function checkData()
            */
           public function calcRowTotal()
           {
      -        $qty = $this->getQty();
      +        $qty        = $this->getTotalQty();
      +        $total      = $this->getCalculationPrice()*$qty;
      +        $baseTotal  = $this->getBaseCalculationPrice()*$qty;
       
      -        if ($this->getParentItem()) {
      -            $qty = $qty*$this->getParentItem()->getQty();
      +        $this->setRowTotal($this->getStore()->roundPrice($total));
      +        $this->setBaseRowTotal($this->getStore()->roundPrice($baseTotal));
      +        return $this;
      +    }
      +
      +    /**
      +     * Get item price used for quote calculation process.
      +     * This method get custom price (if ut defined) or original product final price
      +     *
      +     * @return float
      +     */
      +    public function getCalculationPrice()
      +    {
      +        $price = $this->_getData('calculation_price');
      +        if (is_null($price)) {
      +            if ($this->hasCustomPrice()) {
      +                $price = $this->getCustomPrice();
      +            }
      +            else {
      +                $price = $this->getOriginalPrice();
      +            }
      +            $this->setData('calculation_price', $price);
               }
      +        return $price;
      +    }
       
      -        if ($rowTotal = $this->getRowTotalExcTax()) {
      -            $baseTotal = $rowTotal;
      -            $total = $this->getStore()->convertPrice($baseTotal);
      +    /**
      +     * Get calculation price used for quote calculation in base currency.
      +     *
      +     * @return float
      +     */
      +    public function getBaseCalculationPrice()
      +    {
      +        if (!$this->hasBaseCalculationPrice()) {
      +            if ($this->hasCustomPrice()) {
      +                $price = (float) $this->getCustomPrice();
      +                if ($price) {
      +                    $rate = $this->getStore()->convertPrice($price) / $price;
      +                    $price = $price / $rate;
      +                }
      +            } else {
      +                $price = $this->getPrice();
      +            }
      +            $this->setBaseCalculationPrice($price);
               }
      -        else {
      -            $total      = $this->getCalculationPrice()*$qty;
      -            $baseTotal  = $this->getBaseCalculationPrice()*$qty;
      +        return $this->_getData('base_calculation_price');
      +    }
      +
      +    /**
      +     * Get original price (retrieved from product) for item.
      +     * Original price value is in current selected currency
      +     *
      +     * @return float
      +     */
      +    public function getOriginalPrice()
      +    {
      +        $price = $this->_getData('original_price');
      +        if (is_null($price)) {
      +            $price = $this->getStore()->convertPrice($this->getPrice());
      +            $this->setData('original_price', $price);
               }
      +        return $price;
      +    }
       
      -        $this->setRowTotal($this->getStore()->roundPrice($total));
      -        $this->setBaseRowTotal($this->getStore()->roundPrice($baseTotal));
      +    /**
      +     * Set original price to item (calculation price will be refreshed too)
      +     *
      +     * @param   float $price
      +     * @return  Mage_Sales_Model_Quote_Item_Abstract
      +     */
      +    public function setOriginalPrice($price)
      +    {
      +        $this->setCalculationPrice(null);
      +        return $this->setData('original_price', $price);
      +    }
      +
      +    /**
      +     * Get Original item price (got from product) in base website currency
      +     *
      +     * @return float
      +     */
      +    public function getBaseOriginalPrice()
      +    {
      +        return $this->getPrice();
      +    }
      +
      +    /**
      +     * Get item price (item price always exclude price)
      +     *
      +     * @return decimal
      +     */
      +    public function getPrice()
      +    {
      +        return $this->_getData('price');
      +    }
       
      +    /**
      +     * Specify custom item price (used in case whe we have apply not product price to item)
      +     *
      +     * @param   float $value
      +     * @return  Mage_Sales_Model_Quote_Item_Abstract
      +     */
      +    public function setCustomPrice($value)
      +    {
      +        return $this->setData('custom_price', $value);
      +    }
      +
      +    /**
      +     * Specify item price (base calculation price will be refreshed too)
      +     *
      +     * @param   float $value
      +     * @return  Mage_Sales_Model_Quote_Item_Abstract
      +     */
      +    public function setPrice($value)
      +    {
      +        $this->setBaseCalculationPrice(null);
      +        return $this->setData('price', $value);
      +    }
      +
      +    /**
      +     * Clone quote item
      +     *
      +     * @return Mage_Sales_Model_Quote_Item
      +     */
      +    public function __clone()
      +    {
      +        $this->setId(null);
      +        $this->_parentItem  = null;
      +        $this->_children    = array();
      +        $this->_messages    = array();
               return $this;
           }
      +
      +    /**
      +     * Checking if there children calculated or parent item
      +     * when we have parent quote item and its children
      +     *
      +     * @return bool
      +     */
      +    public function isChildrenCalculated() {
      +        if ($this->getParentItem()) {
      +            $calculate = $this->getParentItem()->getProduct()->getPriceType();
      +        } else {
      +            $calculate = $this->getProduct()->getPriceType();
      +        }
      +
      +        if ((null !== $calculate) && (int)$calculate === Mage_Catalog_Model_Product_Type_Abstract::CALCULATE_CHILD) {
      +            return true;
      +        }
      +        return false;
      +    }
      +
      +
      +    /**
      +     * Checking can we ship product separatelly (each child separately)
      +     * or each parent product item can be shipped only like one item
      +     *
      +     * @return bool
      +     */
      +    public function isShipSeparately() {
      +        if ($this->getParentItem()) {
      +            $shipmentType = $this->getParentItem()->getProduct()->getShipmentType();
      +        } else {
      +            $shipmentType = $this->getProduct()->getShipmentType();
      +        }
      +
      +        if ((null !== $shipmentType) && (int)$shipmentType === Mage_Catalog_Model_Product_Type_Abstract::SHIPMENT_SEPARATELY) {
      +            return true;
      +        }
      +        return false;
      +    }
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
       
           /**
            * Calculate item tax amount
            *
      -     * @return Mage_Sales_Model_Quote_Item
      +     * @deprecated logic moved to tax totals calculation model
      +     * @return  Mage_Sales_Model_Quote_Item
            */
           public function calcTaxAmount()
           {
      @@ -279,72 +485,16 @@ public function calcTaxAmount()
               return $this;
           }
       
      -    /**
      -     * Retrieve item price used for calculation
      -     *
      -     * @return unknown
      -     */
      -    public function getCalculationPrice()
      -    {
      -        $price = $this->getData('calculation_price');
      -        if (is_null($price)) {
      -            if ($this->hasCustomPrice()) {
      -                $price = $this->getCustomPrice();
      -            }
      -            else {
      -                $price = $this->getOriginalPrice();
      -            }
      -            $this->setData('calculation_price', $price);
      -        }
      -        return $price;
      -    }
      -
      -    /**
      -     * Retrieve calculation price in base currency
      -     *
      -     * @return unknown
      -     */
      -    public function getBaseCalculationPrice()
      -    {
      -        if (!$this->hasBaseCalculationPrice()) {
      -            if ($this->hasCustomPrice()) {
      -                if ($price = (float) $this->getCustomPrice()) {
      -                    $rate = $this->getStore()->convertPrice($price) / $price;
      -                    $price = $price / $rate;
      -                }
      -                else {
      -                    $price = $this->getCustomPrice();
      -                }
      -            } else {
      -                $price = $this->getPrice();
      -            }
      -            $this->setBaseCalculationPrice($price);
      -        }
      -        return $this->getData('base_calculation_price');
      -    }
      -
      -    /**
      -     * Retrieve original price (retrieved from product) for item
      -     *
      -     * @return float
      -     */
      -    public function getOriginalPrice()
      -    {
      -        $price = $this->getData('original_price');
      -        if (is_null($price)) {
      -            $price = $this->getStore()->convertPrice($this->getPrice());
      -            $this->setData('original_price', $price);
      -        }
      -        return $price;
      -    }
      -
           /**
            * Get item tax amount
            *
      -     * @return decimal
      +     * @deprecated
      +     * @return  decimal
            */
           public function getTaxAmount()
           {
      +        return $this->_getData('tax_amount');
      +
               $priceType = $this->getProduct()->getPriceType();
               if ($this->getHasChildren() && (null !== $priceType) && (int)$priceType === Mage_Catalog_Model_Product_Type_Abstract::CALCULATE_CHILD) {
                   $amount = 0;
      @@ -358,13 +508,17 @@ public function getTaxAmount()
               }
           }
       
      +
           /**
            * Get item base tax amount
            *
      +     * @deprecated
            * @return decimal
            */
           public function getBaseTaxAmount()
           {
      +        return $this->_getData('base_tax_amount');
      +        
               $priceType = $this->getProduct()->getPriceType();
               if ($this->getHasChildren() && (null !== $priceType) && (int)$priceType === Mage_Catalog_Model_Product_Type_Abstract::CALCULATE_CHILD) {
                   $baseAmount = 0;
      @@ -381,45 +535,9 @@ public function getBaseTaxAmount()
           /**
            * Get item price (item price always exclude price)
            *
      +     * @deprecated
            * @return decimal
            */
      -    public function getPrice()
      -    {
      -        $priceType = $this->getProduct()->getPriceType();
      -        if ($this->getHasChildren() && (null !== $priceType) && (int)$priceType === Mage_Catalog_Model_Product_Type_Abstract::CALCULATE_CHILD) {
      -            $price = $this->_getData('price');
      -            /*
      -            foreach ($this->getChildren() as $child) {
      -                $price+= $child->getPrice()*$child->getQty();
      -            }
      -            */
      -            return $price;
      -        }
      -        else {
      -            return $this->_getData('price');
      -        }
      -    }
      -
      -    public function setCustomPrice($value)
      -    {
      -        if (is_null($value)) {
      -            return $this->setData('custom_price', $value);
      -        }
      -
      -        $excludingTax = $this->_calculatePrice($value, Mage::helper('tax')->applyTaxOnCustomPrice());
      -        $this->setData('original_custom_price', $value);
      -        return $this->setData('custom_price', $excludingTax);
      -    }
      -
      -    public function setPrice($value)
      -    {
      -        $saveTaxes = true;
      -        if (Mage::helper('tax')->applyTaxOnCustomPrice() && $this->hasCustomPrice()) {
      -            $saveTaxes = false;
      -        }
      -        return $this->setData('price', $this->_calculatePrice($value, $saveTaxes));
      -    }
      -
           protected function _calculatePrice($value, $saveTaxes = true)
           {
               $store = $this->getQuote()->getStore();
      @@ -513,56 +631,4 @@ protected function _calculatePrice($value, $saveTaxes = true)
       
               return $value;
           }
      -
      -    /**
      -     * Clone quote item
      -     *
      -     * @return Mage_Sales_Model_Quote_Item
      -     */
      -    public function __clone()
      -    {
      -        $this->setId(null);
      -        $this->_parentItem  = null;
      -        $this->_children    = array();
      -        return $this;
      -    }
      -
      -    /**
      -     * Checking if there children calculated or parent item
      -     * when we have parent quote item and its children
      -     *
      -     * @return bool
      -     */
      -    public function isChildrenCalculated() {
      -        if ($this->getParentItem()) {
      -            $calculate = $this->getParentItem()->getProduct()->getPriceType();
      -        } else {
      -            $calculate = $this->getProduct()->getPriceType();
      -        }
      -
      -        if ((null !== $calculate) && (int)$calculate === Mage_Catalog_Model_Product_Type_Abstract::CALCULATE_CHILD) {
      -            return true;
      -        }
      -        return false;
      -    }
      -
      -
      -    /**
      -     * Checking can we ship product separatelly (each child separately)
      -     * or each parent product item can be shipped only like one item
      -     *
      -     * @return bool
      -     */
      -    public function isShipSeparately() {
      -        if ($this->getParentItem()) {
      -            $shipmentType = $this->getParentItem()->getProduct()->getShipmentType();
      -        } else {
      -            $shipmentType = $this->getProduct()->getShipmentType();
      -        }
      -
      -        if ((null !== $shipmentType) && (int)$shipmentType === Mage_Catalog_Model_Product_Type_Abstract::SHIPMENT_SEPARATELY) {
      -            return true;
      -        }
      -        return false;
      -    }
      -}
      \ No newline at end of file
      +}
      diff --git a/app/code/core/Mage/Sales/Model/Quote/Payment.php b/app/code/core/Mage/Sales/Model/Quote/Payment.php
      index 4cd8b95e3d..f1dbf5a901 100644
      --- a/app/code/core/Mage/Sales/Model/Quote/Payment.php
      +++ b/app/code/core/Mage/Sales/Model/Quote/Payment.php
      @@ -66,11 +66,13 @@ public function getQuote()
           }
       
           /**
      -     * Import data
      +     * Import data array to payment method object,
      +     * Method calls quote totals collect because payment method availability
      +     * can be related to quote totals
            *
      -     * @param array $data
      -     * @throws Mage_Core_Exception
      -     * @return Mage_Sales_Model_Quote_Payment
      +     * @param   array $data
      +     * @throws  Mage_Core_Exception
      +     * @return  Mage_Sales_Model_Quote_Payment
            */
           public function importData(array $data)
           {
      @@ -86,6 +88,12 @@ public function importData(array $data)
               $this->setMethod($data->getMethod());
               $method = $this->getMethodInstance();
       
      +        /**
      +         * Payment avalability related with quote totals.
      +         * We have recollect quote totals before checking
      +         */
      +        $this->getQuote()->collectTotals();
      +        
               if (!$method->isAvailable($this->getQuote())) {
                   Mage::throwException(Mage::helper('sales')->__('Requested Payment Method is not available'));
               }
      @@ -117,17 +125,31 @@ protected function _beforeSave()
               return parent::_beforeSave();
           }
       
      +    /**
      +     * Checkout redirect URL getter
      +     *
      +     * @return string
      +     */
           public function getCheckoutRedirectUrl()
           {
               $method = $this->getMethodInstance();
      -
      -        return $method ? $method->getCheckoutRedirectUrl() : false;
      +        if ($method) {
      +            return $method->getCheckoutRedirectUrl();
      +        }
      +        return '';
           }
       
      +    /**
      +     * Checkout order place redirect URL getter
      +     *
      +     * @return string
      +     */
           public function getOrderPlaceRedirectUrl()
           {
               $method = $this->getMethodInstance();
      -
      -        return $method ? $method->getOrderPlaceRedirectUrl() : false;
      +        if ($method) {
      +            return $method->getOrderPlaceRedirectUrl();
      +        }
      +        return '';
           }
      -}
      \ No newline at end of file
      +}
      diff --git a/app/code/core/Mage/Sales/doc/order.txt b/app/code/core/Mage/Sales/doc/order.txt
      index a8001294a2..678b8c17f6 100644
      --- a/app/code/core/Mage/Sales/doc/order.txt
      +++ b/app/code/core/Mage/Sales/doc/order.txt
      @@ -20,7 +20,6 @@
               billing_address_id
               shipping_address_id
               coupon_code
      -        giftcert_code
               weight
               shipping_method
               shipping_description
      @@ -28,7 +27,6 @@
               tax_amount
               shipping_amount
               discount_amount
      -        giftcert_amount
               custbalance_amount
               grand_total
               total_paid
      diff --git a/app/code/core/Mage/Sales/doc/quote.txt b/app/code/core/Mage/Sales/doc/quote.txt
      index 1774ccbd2a..d6b8988d23 100644
      --- a/app/code/core/Mage/Sales/doc/quote.txt
      +++ b/app/code/core/Mage/Sales/doc/quote.txt
      @@ -9,7 +9,6 @@
               billing_address_id
               converted_at
               coupon_code
      -        giftcert_code
               custbalance_amount
               global_currency_code
               base_currency_code
      diff --git a/app/code/core/Mage/Sales/etc/config.xml b/app/code/core/Mage/Sales/etc/config.xml
      index 67389fa6e0..d68e0d08b2 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.38
      +            0.9.40
               
           
           
      @@ -99,7 +99,6 @@
                       base_to_order_rate
       
                       *
      -                *
                       *
                       *
                       *
      @@ -117,7 +116,6 @@
                       *
                       *
                       *
      -                *
                       *
                       *
       
      @@ -126,7 +124,6 @@
                       *
                       *
                       *
      -                *
                       *
                       *
       
      @@ -219,7 +216,6 @@
                       **
       
                       *
      -                *
                       *
       
                       *
      @@ -232,14 +228,12 @@
                       *
                       *
                       *
      -                *
                       *
       
                       *
                       *
                       *
                       *
      -                *
                       *
       
                       *
      @@ -499,23 +493,11 @@
                               sales/quote_address_total_subtotal
                               grand_total
                           
      -                    
      -                        sales/quote_address_total_discount
      -                        subtotal
      -                        grand_total,shipping
      -                    
                           
                               sales/quote_address_total_shipping
      -                        subtotal,discount
      +                        subtotal,freeshipping
                               grand_total
                           
      -                    
      -                        sales/quote_address_total_tax
      -                        subtotal,shipping
      -                        grand_total
      -                        checkout/total_tax
      -                        adminhtml/sales_order_create_totals_tax
      -                    
                           
                               sales/quote_address_total_grand
                               subtotal
      @@ -626,6 +608,9 @@
                           
                               sales/order_creditmemo_total_subtotal
                           
      +                    
      +                        sales/order_creditmemo_total_shipping
      +                    
                           
                               sales/order_creditmemo_total_tax
                           
      @@ -666,13 +651,6 @@
                           0
                           200
                       
      -                
      -                    Tax
      -                    tax_amount
      -                    7
      -                    0
      -                    300
      -                
                       
                           Shipping & Handling
                           shipping_amount
      @@ -685,21 +663,21 @@
                           adjustment_positive
                           7
                           0
      -                    100
      +                    500
                       
                       
                           Adjustment Fee
                           adjustment_negative
                           7
                           0
      -                    500
      +                    600
                       
                       
                           Grand Total
                           grand_total
                           8
                           1
      -                    600
      +                    700
                       
                   
               
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.7.0.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.7.0.php
      index 8f190e0432..1a9583158d 100644
      --- a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.7.0.php
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.7.0.php
      @@ -68,22 +68,6 @@
       
       insert  into {$this->getTable('sales_discount_coupon')}(`coupon_id`,`coupon_code`,`discount_percent`,`discount_fixed`,`is_active`,`from_date`,`to_date`,`min_subtotal`,`limit_products`,`limit_categories`,`limit_attributes`) values (1,'test',10.0000,0.0000,1,'0000-00-00 00:00:00','0000-00-00 00:00:00',0.0000,'','','');
       
      -/*Table structure for table `sales_giftcert` */
      -
      --- DROP TABLE IF EXISTS {$this->getTable('sales_giftcert')};
      -
      -CREATE TABLE {$this->getTable('sales_giftcert')} (
      -  `giftcert_id` int(10) unsigned NOT NULL auto_increment,
      -  `giftcert_code` varchar(50) NOT NULL default '',
      -  `balance_amount` decimal(12,4) NOT NULL default '0.0000',
      -  PRIMARY KEY  (`giftcert_id`),
      -  UNIQUE KEY `gift_code` (`giftcert_code`)
      -) ENGINE=InnoDB DEFAULT CHARSET=utf8;
      -
      -/*Data for the table `sales_giftcert` */
      -
      -insert  into {$this->getTable('sales_giftcert')}(`giftcert_id`,`giftcert_code`,`balance_amount`) values (1,'test',20.0000);
      -
       /*Table structure for table `sales_invoice_entity` */
       
       -- DROP TABLE IF EXISTS {$this->getTable('sales_invoice_entity')};
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.8.11.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.8.11.php
      index 332686093b..374bda5e4f 100644
      --- a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.8.11.php
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-install-0.8.11.php
      @@ -64,15 +64,6 @@
         PRIMARY KEY  (`coupon_id`)
       ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
       
      --- DROP TABLE IF EXISTS `{$installer->getTable('sales_giftcert')}`;
      -CREATE TABLE `{$installer->getTable('sales_giftcert')}` (
      -  `giftcert_id` int(10) unsigned NOT NULL auto_increment,
      -  `giftcert_code` varchar(50) NOT NULL default '',
      -  `balance_amount` decimal(12,4) NOT NULL default '0.0000',
      -  PRIMARY KEY  (`giftcert_id`),
      -  UNIQUE KEY `gift_code` (`giftcert_code`)
      -) ENGINE=InnoDB DEFAULT CHARSET=utf8;
      -
       -- DROP TABLE IF EXISTS `{$installer->getTable('sales_order_entity')}`;
       CREATE TABLE `{$installer->getTable('sales_order_entity')}` (
         `entity_id` int(10) unsigned NOT NULL auto_increment,
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.14-0.8.15.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.14-0.8.15.php
      index f30a7f55dc..05111f5873 100644
      --- a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.14-0.8.15.php
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.14-0.8.15.php
      @@ -63,7 +63,6 @@
           `checkout_method` varchar(255) NOT NULL default '',
           `password_hash` varchar(255) NOT NULL default '',
           `coupon_code` varchar(255) NOT NULL default '',
      -    `giftcert_code` varchar(255) NOT NULL default '',
           `base_currency_code` varchar(255) NOT NULL default '',
           `store_currency_code` varchar(255) NOT NULL default '',
           `quote_currency_code` varchar(255) NOT NULL default '',
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.17-0.8.18.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.17-0.8.18.php
      index 2881a61b27..d6d565013f 100644
      --- a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.17-0.8.18.php
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.17-0.8.18.php
      @@ -50,7 +50,6 @@
           change `checkout_method` `checkout_method` varchar (255)  NULL  COLLATE utf8_general_ci ,
           change `password_hash` `password_hash` varchar (255)  NULL  COLLATE utf8_general_ci ,
           change `coupon_code` `coupon_code` varchar (255)  NULL  COLLATE utf8_general_ci ,
      -    change `giftcert_code` `giftcert_code` varchar (255)  NULL  COLLATE utf8_general_ci ,
           change `base_currency_code` `base_currency_code` varchar (255)  NULL  COLLATE utf8_general_ci ,
           change `store_currency_code` `store_currency_code` varchar (255)  NULL  COLLATE utf8_general_ci ,
           change `quote_currency_code` `quote_currency_code` varchar (255)  NULL  COLLATE utf8_general_ci ,
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.29-0.9.0.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.29-0.9.0.php
      index 543c8f1780..f05839bd0d 100644
      --- a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.29-0.9.0.php
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.8.29-0.9.0.php
      @@ -349,6 +349,5 @@
       
       DROP TABLE IF EXISTS {$this->getTable('sales_counter')};
       DROP TABLE IF EXISTS {$this->getTable('sales_discount_coupon')};
      -DROP TABLE IF EXISTS {$this->getTable('sales_giftcert')};
       ");
       $installer->endSetup();
      \ No newline at end of file
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.38-0.9.39.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.38-0.9.39.php
      new file mode 100644
      index 0000000000..acdc8c4cc7
      --- /dev/null
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.38-0.9.39.php
      @@ -0,0 +1,49 @@
      +startSetup();
      +$installer->getConnection()->addColumn($installer->getTable('sales/quote_item'),
      +    'store_id', 'smallint(5) unsigned default null AFTER `product_id`');
      +$installer->getConnection()->addConstraint('FK_SALES_QUOTE_ITEM_STORE',
      +    $installer->getTable('sales/quote_item'), 'store_id',
      +    $installer->getTable('core/store'), 'store_id',
      +    'set null', 'cascade'
      +);
      +$installer->getConnection()->addColumn($installer->getTable('sales/order_item'),
      +    'store_id', 'smallint(5) unsigned default null AFTER `quote_item_id`');
      +$installer->getConnection()->addConstraint('FK_SALES_ORDER_ITEM_STORE',
      +    $installer->getTable('sales/order_item'), 'store_id',
      +    $installer->getTable('core/store'), 'store_id',
      +    'set null', 'cascade'
      +);
      +$installer->addAttribute('quote_item', 'redirect_url', array(
      +    'type'  => 'varchar',
      +));
      +$installer->endSetup();
      diff --git a/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.39-0.9.40.php b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.39-0.9.40.php
      new file mode 100644
      index 0000000000..336ff8b27d
      --- /dev/null
      +++ b/app/code/core/Mage/Sales/sql/sales_setup/mysql4-upgrade-0.9.39-0.9.40.php
      @@ -0,0 +1,32 @@
      +startSetup();
      +$this->removeAttribute('order', 'giftcert_code');
      +$this->removeAttribute('order', 'giftcert_amount');
      +$this->removeAttribute('order', 'base_giftcert_amount');
      +$this->endSetup();
      +
      diff --git a/app/code/core/Mage/SalesRule/Model/Mysql4/Rule.php b/app/code/core/Mage/SalesRule/Model/Mysql4/Rule.php
      index fd20e96f2e..4adb97a875 100644
      --- a/app/code/core/Mage/SalesRule/Model/Mysql4/Rule.php
      +++ b/app/code/core/Mage/SalesRule/Model/Mysql4/Rule.php
      @@ -65,4 +65,66 @@ public function getCustomerUses($rule, $customerId)
                   ->where('customer_id=?', $customerId);
               return $read->fetchOne($select);
           }
      +
      +    /**
      +     * Save rule labels for different store views
      +     *
      +     * @param   int $ruleId
      +     * @param   array $labels
      +     * @return  Mage_SalesRule_Model_Mysql4_Rule
      +     */
      +    public function saveStoreLabels($ruleId, $labels)
      +    {
      +        $delete = array();
      +        $save = array();
      +        $table = $this->getTable('salesrule/label');
      +        $adapter = $this->_getWriteAdapter();
      +        
      +        foreach ($labels as $storeId => $label) {
      +            if (Mage::helper('core/string')->strlen($label)) {
      +                $data = array('rule_id' => $ruleId, 'store_id' => $storeId, 'label' => $label);
      +                $adapter->insertOnDuplicate($table, $data, array('label'));
      +            } else {
      +                $delete[] = $storeId;
      +            }
      +        }
      +
      +        if (!empty($delete)) {
      +            $adapter->delete($table,
      +                $adapter->quoteInto('rule_id=? AND ', $ruleId) . $adapter->quoteInto('store_id IN (?)', $delete)
      +            );
      +        }
      +        return $this;
      +    }
      +
      +    /**
      +     * Get all existing rule labels
      +     *
      +     * @param   int $ruleId
      +     * @return  array
      +     */
      +    public function getStoreLabels($ruleId)
      +    {
      +        $select = $this->_getReadAdapter()->select()
      +            ->from($this->getTable('salesrule/label'), array('store_id', 'label'))
      +            ->where('rule_id=?', $ruleId);
      +        return $this->_getReadAdapter()->fetchPairs($select);
      +    }
      +
      +    /**
      +     * Get rule label by specific store id
      +     *
      +     * @param   int $ruleId
      +     * @param   int $storeId
      +     * @return  string
      +     */
      +    public function getStoreLabel($ruleId, $storeId)
      +    {
      +        $select = $this->_getReadAdapter()->select()
      +            ->from($this->getTable('salesrule/label'), 'label')
      +            ->where('rule_id=?', $ruleId)
      +            ->where('store_id IN(?)', array($storeId, 0))
      +            ->order('store_id DESC');
      +        return $this->_getReadAdapter()->fetchOne($select);
      +    }
       }
      \ No newline at end of file
      diff --git a/app/code/core/Mage/SalesRule/Model/Observer.php b/app/code/core/Mage/SalesRule/Model/Observer.php
      index 9ab232c475..310394a297 100644
      --- a/app/code/core/Mage/SalesRule/Model/Observer.php
      +++ b/app/code/core/Mage/SalesRule/Model/Observer.php
      @@ -29,6 +29,13 @@ class Mage_SalesRule_Model_Observer
       {
           protected $_validator;
       
      +    /**
      +     * Get quote item validator/processor object
      +     *
      +     * @deprecated
      +     * @param   Varien_Event $event
      +     * @return  Mage_SalesRule_Model_Validator
      +     */
           public function getValidator($event)
           {
               if (!$this->_validator) {
      @@ -38,6 +45,12 @@ public function getValidator($event)
               return $this->_validator;
           }
       
      +    /**
      +     * Process quote item (apply discount to item)
      +     *
      +     * @deprecated process call movet to total model
      +     * @param Varien_Event_Observer $observer
      +     */
           public function sales_quote_address_discount_item($observer)
           {
               $this->getValidator($observer->getEvent())
      diff --git a/app/code/core/Mage/SalesRule/Model/Quote/Discount.php b/app/code/core/Mage/SalesRule/Model/Quote/Discount.php
      new file mode 100644
      index 0000000000..81cc04e072
      --- /dev/null
      +++ b/app/code/core/Mage/SalesRule/Model/Quote/Discount.php
      @@ -0,0 +1,152 @@
      +setCode('discount');
      +        $this->_calculator = Mage::getSingleton('salesrule/validator');
      +    }
      +
      +    /**
      +     * Collect address discount amount
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_SalesRule_Model_Quote_Discount
      +     */
      +    public function collect(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        parent::collect($address);
      +        $quote = $address->getQuote();
      +        $store = Mage::app()->getStore($quote->getStoreId());
      +
      +        $items = $address->getAllItems();
      +        if (!count($items)) {
      +            return $this;
      +        }
      +
      +        $eventArgs = array(
      +            'website_id'        => $store->getWebsiteId(),
      +            'customer_group_id' => $quote->getCustomerGroupId(),
      +            'coupon_code'       => $quote->getCouponCode(),
      +        );
      +
      +        $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode());
      +        $address->setDiscountDescription(array());
      +        
      +        foreach ($items as $item) {
      +            if ($item->getNoDiscount()) {
      +                $item->setDiscountAmount(0);
      +                $item->setBaseDiscountAmount(0);
      +            }
      +            else {
      +                /**
      +                 * Child item discount we calculate for parent
      +                 */
      +                if ($item->getParentItemId()) {
      +                    continue;
      +                }
      +
      +                $eventArgs['item'] = $item;
      +                Mage::dispatchEvent('sales_quote_address_discount_item', $eventArgs);
      +                
      +                if ($item->getHasChildren() && $item->isChildrenCalculated()) {
      +                    foreach ($item->getChildren() as $child) {
      +                        $this->_calculator->process($child);
      +                        $eventArgs['item'] = $child;
      +                        Mage::dispatchEvent('sales_quote_address_discount_item', $eventArgs);
      +                        $this->_aggregateItemDiscount($child);
      +                    }
      +                } else {
      +                    $this->_calculator->process($item);
      +                    $this->_aggregateItemDiscount($item);
      +                }
      +            }
      +        }
      +
      +        /**
      +         * Process shipping amount discount
      +         */
      +        $address->setShippingDiscountAmount(0);
      +        $address->setBaseShippingDiscountAmount(0);
      +        if ($address->getShippingAmount()) {
      +            $this->_calculator->processShippingAmount($address);
      +            $this->_addAmount(-$address->getShippingDiscountAmount());
      +            $this->_addBaseAmount(-$address->getBaseShippingDiscountAmount());
      +        }
      +
      +        $this->_calculator->prepareDescription($address);
      +        return $this;
      +    }
      +
      +    /**
      +     * Aggregate item discount information to address data and related properties
      +     *
      +     * @param   Mage_Sales_Model_Quote_Item_Abstract $item
      +     * @return  Mage_SalesRule_Model_Quote_Discount
      +     */
      +    protected function _aggregateItemDiscount($item)
      +    {
      +        $this->_addAmount(-$item->getDiscountAmount());
      +        $this->_addBaseAmount(-$item->getBaseDiscountAmount());
      +        return $this;
      +    }
      +
      +    /**
      +     * Add discount total information to address
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_SalesRule_Model_Quote_Discount
      +     */
      +    public function fetch(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        $amount = $address->getDiscountAmount();
      +        
      +        if ($amount!=0) {
      +            $description = $address->getDiscountDescription();
      +            if ($description) {
      +                $title = Mage::helper('sales')->__('Discount (%s)', $description);
      +            } else {
      +                $title = Mage::helper('sales')->__('Discount');
      +            }
      +            $address->addTotal(array(
      +                'code'  => $this->getCode(),
      +                'title' => $title,
      +                'value' => $amount
      +            ));
      +        }
      +        return $this;
      +    }
      +}
      \ No newline at end of file
      diff --git a/app/code/core/Mage/SalesRule/Model/Quote/Freeshipping.php b/app/code/core/Mage/SalesRule/Model/Quote/Freeshipping.php
      new file mode 100644
      index 0000000000..0982a063c9
      --- /dev/null
      +++ b/app/code/core/Mage/SalesRule/Model/Quote/Freeshipping.php
      @@ -0,0 +1,103 @@
      +setCode('discount');
      +        $this->_calculator = Mage::getSingleton('salesrule/validator');
      +    }
      +
      +    /**
      +     * Collect information about free shipping for all address items
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_SalesRule_Model_Quote_Freeshipping
      +     */
      +    public function collect(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        parent::collect($address);
      +        $quote = $address->getQuote();
      +        $store = Mage::app()->getStore($quote->getStoreId());
      +
      +        $address->setFreeShipping(0);
      +        $items = $address->getAllItems();
      +        
      +        if (!count($items)) {
      +            return $this;
      +        }
      +        $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode());
      +        
      +        foreach ($items as $item) {
      +            if ($item->getNoDiscount()) {
      +                $item->setFreeShipping(false);
      +            } else {
      +                /**
      +                 * Child item discount we calculate for parent
      +                 */
      +                if ($item->getParentItemId()) {
      +                    continue;
      +                }
      +                $this->_calculator->processFreeShipping($item);
      +                if ($item->getHasChildren() && $item->isChildrenCalculated()) {
      +                    foreach ($item->getChildren() as $child) {
      +                        $this->_calculator->processFreeShipping($child);
      +                        /**
      +                         * Parent free shipping we apply to all children
      +                         */
      +                        if ($item->getFreeShipping()) {
      +                            $child->setFreeShipping($item->getFreeShipping());
      +                        }
      +
      +                    }
      +                }
      +            }
      +        }
      +        return $this;
      +    }
      +
      +   /**
      +    * Add information about free shipping for all address items to address object
      +    * By default we not present such information
      +    *
      +    * @param   Mage_Sales_Model_Quote_Address $address
      +    * @return  Mage_SalesRule_Model_Quote_Freeshipping
      +    */
      +    public function fetch(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        return $this;
      +    }
      +
      +}
      \ No newline at end of file
      diff --git a/app/code/core/Mage/SalesRule/Model/Rule.php b/app/code/core/Mage/SalesRule/Model/Rule.php
      index 432efeb399..740ae254d5 100644
      --- a/app/code/core/Mage/SalesRule/Model/Rule.php
      +++ b/app/code/core/Mage/SalesRule/Model/Rule.php
      @@ -30,6 +30,8 @@ class Mage_SalesRule_Model_Rule extends Mage_Rule_Model_Rule
           const FREE_SHIPPING_ITEM = 1;
           const FREE_SHIPPING_ADDRESS = 2;
       
      +    protected $_labels = array();
      +
           protected function _construct()
           {
               parent::_construct();
      @@ -37,9 +39,15 @@ protected function _construct()
               $this->setIdFieldName('rule_id');
           }
       
      +    /**
      +     * Check unique coupon code before saving and clear coupon related cache
      +     *
      +     * @return Mage_SalesRule_Model_Rule
      +     */
           protected function _beforeSave()
           {
      -        if($coupon = $this->getCouponCode()) {
      +        $coupon = $this->getCouponCode();
      +        if($coupon) {
                   $this->getResource()->addUniqueField(array(
                       'field' => 'coupon_code',
                       'title' => Mage::helper('salesRule')->__('Coupon with the same code')
      @@ -51,13 +59,18 @@ protected function _beforeSave()
               return parent::_beforeSave();
           }
       
      -
      +    /**
      +     * Delete rule coupon code related cache
      +     *
      +     * @return Mage_SalesRule_Model_Rule
      +     */
           protected function _beforeDelete()
           {
      -        if ($coupon = $this->getCouponCode()) {
      +        $coupon = $this->getCouponCode();
      +        if ($coupon) {
                   Mage::app()->cleanCache('salesrule_coupon_'.$coupon);
               }
      -        parent::_beforeDelete();
      +        return parent::_beforeDelete();
           }
       
           public function getConditionsInstance()
      @@ -83,16 +96,24 @@ public function toString($format='')
               return $str;
           }
       
      +    /**
      +     * Initialize rule model data from array
      +     *
      +     * @param   array $rule
      +     * @return  Mage_SalesRule_Model_Rule
      +     */
           public function loadPost(array $rule)
           {
               $arr = $this->_convertFlatToRecursive($rule);
      -		if (isset($arr['conditions'])) {
      -    		$this->getConditions()->setConditions(array())->loadArray($arr['conditions'][1]);
      -		}
      -		if (isset($arr['actions'])) {
      -    		$this->getActions()->setActions(array())->loadArray($arr['actions'][1], 'actions');
      -		}
      -
      +        if (isset($arr['conditions'])) {
      +            $this->getConditions()->setConditions(array())->loadArray($arr['conditions'][1]);
      +        }
      +        if (isset($arr['actions'])) {
      +            $this->getActions()->setActions(array())->loadArray($arr['actions'][1], 'actions');
      +        }
      +        if (isset($rule['store_labels'])) {
      +            $this->setStoreLabels($rule['store_labels']);
      +        }
           	return $this;
           }
       
      @@ -116,56 +137,59 @@ public function toArray(array $arrAttributes = array())
       
               return $out;
           }
      -    /*
      -    public function processProduct(Mage_Sales_Model_Product $product)
      +
      +    public function getResourceCollection()
           {
      -        $this->validateProduct($product) && $this->updateProduct($product);
      -        return $this;
      +        return Mage::getResourceModel('salesrule/rule_collection');
           }
       
      -    public function validateProduct(Mage_Sales_Model_Product $product)
      +    /**
      +     * Save rula labels after rule save
      +     *
      +     * @return Mage_SalesRule_Model_Rule
      +     */
      +    protected function _afterSave()
           {
      -        if (!$this->getIsCollectionValidated()) {
      -            $env = $this->getEnv();
      -            $result = $result && $this->getIsActive()
      -                && (strtotime($this->getStartAt()) <= $env->getNow())
      -                && (strtotime($this->getExpireAt()) >= $env->getNow())
      -                && ($this->getCustomerRegistered()==2 || $this->getCustomerRegistered()==$env->getCustomerRegistered())
      -                && ($this->getCustomerNewBuyer()==2 || $this->getCustomerNewBuyer()==$env->getCustomerNewBuyer())
      -                && $this->getConditions()->validateProduct($product);
      -        } else {
      -            $result = $this->getConditions()->validateProduct($product);
      +        if ($this->hasStoreLabels()) {
      +            $this->_getResource()->saveStoreLabels($this->getId(), $this->getStoreLabels());
               }
      -
      -        return $result;
      +        return parent::_afterSave();
           }
       
      -    public function updateProduct(Mage_Sales_Model_Product $product)
      +    /**
      +     * Get Rule label for specific store
      +     *
      +     * @param   store $store
      +     * @return  string | false
      +     */
      +    public function getStoreLabel($store=null)
           {
      -        $this->getActions()->updateProduct($product);
      -        return $this;
      +        $storeId = Mage::app()->getStore($store)->getId();
      +        if ($this->hasStoreLabels()) {
      +            $labels = $this->_getData('store_labels');
      +            if (isset($labels[$storeId])) {
      +                return $labels[$storeId];
      +            } elseif ($labels[0]) {
      +                return $labels[0];
      +            }
      +            return false;
      +        } elseif (!isset($this->_labels[$storeId])) {
      +            $this->_labels[$storeId] = $this->_getResource()->getStoreLabel($this->getId(), $storeId);
      +        }
      +        return $this->_labels[$storeId];
           }
      -    */
      -    public function getResourceCollection()
      +
      +    /**
      +     * Get all existing rule labels
      +     *
      +     * @return array
      +     */
      +    public function getStoreLabels()
           {
      -        return Mage::getResourceModel('salesrule/rule_collection');
      +        if (!$this->hasStoreLabels()) {
      +            $labels = $this->_getResource()->getStoreLabels($this->getId());
      +            $this->setStoreLabels($labels);
      +        }
      +        return $this->_getData('store_labels');
           }
      -
      -//    protected function _afterSave()
      -//    {
      -//        $this->_getResource()->updateRuleProductData($this);
      -//        parent::_afterSave();
      -//    }
      -//
      -//    public function validate(Varien_Object $quote)
      -//    {
      -//
      -//        if ($this->getUsesPerCustomer() && $quote->getCustomer()) {
      -//            $customerUses = $this->_getResource()->getCustomerUses($this, $quote->getCustomerId());
      -//            if ($customerUses >= $this->getUsesPerCustomer()) {
      -//                return false;
      -//            }
      -//        }
      -//        return parent::validate($quote);
      -//    }
       }
      diff --git a/app/code/core/Mage/SalesRule/Model/Validator.php b/app/code/core/Mage/SalesRule/Model/Validator.php
      index b3991609d9..4f56703cca 100644
      --- a/app/code/core/Mage/SalesRule/Model/Validator.php
      +++ b/app/code/core/Mage/SalesRule/Model/Validator.php
      @@ -43,6 +43,9 @@ class Mage_SalesRule_Model_Validator extends Mage_Core_Model_Abstract
            */
           protected $_rules;
       
      +    protected $_roundingDeltas = array();
      +    protected $_baseRoundingDeltas = array();
      +
           protected function _construct()
           {
               parent::_construct();
      @@ -51,133 +54,214 @@ protected function _construct()
       
           /**
            * Init validator
      +     * Init process load collection of rules for specific website,
      +     * customer group and coupon code
            *
      -     * @param int $websiteId
      -     * @param int $customerGroupId
      -     * @param string $couponCode
      -     * @return Mage_SalesRule_Model_Validator
      +     * @param   int $websiteId
      +     * @param   int $customerGroupId
      +     * @param   string $couponCode
      +     * @return  Mage_SalesRule_Model_Validator
            */
           public function init($websiteId, $customerGroupId, $couponCode)
           {
               $this->setWebsiteId($websiteId)
      -           ->setCustomerGroupId($customerGroupId)
      -           ->setCouponCode($couponCode);
      -
      -        $this->_rules = Mage::getResourceModel('salesrule/rule_collection')
      -            ->setValidationFilter($websiteId, $customerGroupId, $couponCode)
      -            ->load();
      +            ->setCustomerGroupId($customerGroupId)
      +            ->setCouponCode($couponCode);
       
      +        $key = $websiteId . '_' . $customerGroupId . '_' . $couponCode;
      +        if (!isset($this->_rules[$key])) {
      +            $this->_rules[$key] = Mage::getResourceModel('salesrule/rule_collection')
      +                ->setValidationFilter($websiteId, $customerGroupId, $couponCode)
      +                ->load();
      +        }
               return $this;
           }
       
      -    public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
      +    /**
      +     * Get rules collection for current object state
      +     *
      +     * @return Mage_SalesRule_Model_Mysql4_Rule_Collection
      +     */
      +    protected function _getRules()
           {
      -        $item->setFreeShipping(false);
      -        $item->setDiscountAmount(0);
      -        $item->setBaseDiscountAmount(0);
      -        $item->setDiscountPercent(0);
      +        $key = $this->getWebsiteId() . '_' . $this->getCustomerGroupId() . '_' . $this->getCouponCode();
      +        return $this->_rules[$key];
      +    }
       
      -        $quote = $item->getQuote();
      +    /**
      +     * Get address object which can be used for discount calculation
      +     *
      +     * @param   Mage_Sales_Model_Quote_Item_Abstract $item
      +     * @return  Mage_Sales_Model_Quote_Address
      +     */
      +    protected function _getAddress(Mage_Sales_Model_Quote_Item_Abstract $item)
      +    {
               if ($item instanceof Mage_Sales_Model_Quote_Address_Item) {
                   $address = $item->getAddress();
      -        } elseif ($quote->isVirtual()) {
      -            $address = $quote->getBillingAddress();
      +        } elseif ($item->getQuote()->isVirtual()) {
      +            $address = $item->getQuote()->getBillingAddress();
               } else {
      -            $address = $quote->getShippingAddress();
      +            $address = $item->getQuote()->getShippingAddress();
               }
      +        return $address;
      +    }
       
      -        $customerId = $quote->getCustomerId();
      -        $ruleCustomer = Mage::getModel('salesrule/rule_customer');
      -        $appliedRuleIds = array();
      -
      -        foreach ($this->_rules as $rule) {
      -            /* @var $rule Mage_SalesRule_Model_Rule */
      +    /**
      +     * Check if rule can be applied for specific address/quote/customer
      +     *
      +     * @param   Mage_SalesRule_Model_Rule $rule
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  bool
      +     */
      +    protected function _canProcessRule($rule, $address)
      +    {
      +        if (!$rule->hasIsValid()) {
                   /**
      -             * already tried to validate and failed
      +             * too many times used in general
                    */
      -            if ($rule->getIsValid() === false) {
      -                continue;
      +            if ($rule->getUsesPerCoupon() && ($rule->getTimesUsed() >= $rule->getUsesPerCoupon())) {
      +                $rule->setIsValid(false);
      +                return false;
                   }
      -
      -            if ($rule->getIsValid() !== true) {
      -                /**
      -                 * too many times used in general
      -                 */
      -                if ($rule->getUsesPerCoupon() && ($rule->getTimesUsed() >= $rule->getUsesPerCoupon())) {
      -                    $rule->setIsValid(false);
      -                    continue;
      -                }
      -                /**
      -                 * too many times used for this customer
      -                 */
      -                $ruleId = $rule->getId();
      -                if ($ruleId && $rule->getUsesPerCustomer()) {
      -                    $ruleCustomer->loadByCustomerRule($customerId, $ruleId);
      -                    if ($ruleCustomer->getId()) {
      -                        if ($ruleCustomer->getTimesUsed() >= $rule->getUsesPerCustomer()) {
      -                            continue;
      -                        }
      +            /**
      +             * too many times used for this customer
      +             */
      +            $ruleId = $rule->getId();
      +            if ($ruleId && $rule->getUsesPerCustomer()) {
      +                $customerId     = $address->getQuote()->getCustomerId();
      +                $ruleCustomer   = Mage::getModel('salesrule/rule_customer');
      +                $ruleCustomer->loadByCustomerRule($customerId, $ruleId);
      +                if ($ruleCustomer->getId()) {
      +                    if ($ruleCustomer->getTimesUsed() >= $rule->getUsesPerCustomer()) {
      +                        $rule->setIsValid(false);
      +                        return false;
                           }
                       }
      -                $rule->afterLoad();
      -                /**
      -                 * quote does not meet rule's conditions
      -                 */
      -                if (!$rule->validate($address)) {
      -                    $rule->setIsValid(false);
      -                    continue;
      -                }
      -                /**
      -                 * passed all validations, remember to be valid
      -                 */
      -                $rule->setIsValid(true);
                   }
      -
      +            $rule->afterLoad();
                   /**
      -             * although the rule is valid, this item is not marked for action
      +             * quote does not meet rule's conditions
                    */
      +            if (!$rule->validate($address)) {
      +                $rule->setIsValid(false);
      +                return false;
      +            }
      +            /**
      +             * passed all validations, remember to be valid
      +             */
      +            $rule->setIsValid(true);
      +        }
      +        return $rule->getIsValid();
      +
      +    }
      +
      +    /**
      +     * Quote item free shipping ability check
      +     * This process not affect information about applied rules, coupon code etc.
      +     * This information will be added during discount amounts processing
      +     *
      +     * @param   Mage_Sales_Model_Quote_Item_Abstract $item
      +     * @return  Mage_SalesRule_Model_Validator
      +     */
      +    public function processFreeShipping(Mage_Sales_Model_Quote_Item_Abstract $item)
      +    {
      +        $address = $this->_getAddress($item);
      +        $item->setFreeShipping(false);
      +        
      +        foreach ($this->_getRules() as $rule) {
      +            /* @var $rule Mage_SalesRule_Model_Rule */
      +            if (!$this->_canProcessRule($rule, $address)) {
      +                continue;
      +            }
      +
                   if (!$rule->getActions()->validate($item)) {
                       continue;
                   }
      -            $qty = $item->getQty();
      -            if ($item->getParentItem()) {
      -                $qty*= $item->getParentItem()->getQty();
      +
      +            switch ($rule->getSimpleFreeShipping()) {
      +                case Mage_SalesRule_Model_Rule::FREE_SHIPPING_ITEM:
      +                    $item->setFreeShipping($rule->getDiscountQty() ? $rule->getDiscountQty() : true);
      +                    break;
      +
      +                case Mage_SalesRule_Model_Rule::FREE_SHIPPING_ADDRESS:
      +                    $address->setFreeShipping(true);
      +                    break;
      +            }
      +            if ($rule->getStopRulesProcessing()) {
      +                break;
      +            }
      +        }
      +        return $this;
      +    }
      +
      +    /**
      +     * Quote item discount calculation process
      +     *
      +     * @param   Mage_Sales_Model_Quote_Item_Abstract $item
      +     * @return  Mage_SalesRule_Model_Validator
      +     */
      +    public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
      +    {
      +        $item->setDiscountAmount(0);
      +        $item->setBaseDiscountAmount(0);
      +        $item->setDiscountPercent(0);
      +        $quote      = $item->getQuote();
      +        $address    = $this->_getAddress($item);
      +        $itemPrice  = $item->getDiscountCalculationPrice();
      +        if ($itemPrice !== null) {
      +            $baseItemPrice = $item->getBaseDiscountCalculationPrice();
      +        } else {
      +            $itemPrice = $item->getCalculationPrice();
      +            $baseItemPrice = $item->getBaseCalculationPrice();
      +        }
      +
      +        $appliedRuleIds = array();
      +        foreach ($this->_getRules() as $rule) {
      +            /* @var $rule Mage_SalesRule_Model_Rule */
      +            if (!$this->_canProcessRule($rule, $address)) {
      +                continue;
      +            }
      +            
      +            if (!$rule->getActions()->validate($item)) {
      +                continue;
                   }
      +            $qty = $item->getTotalQty();
                   $qty = $rule->getDiscountQty() ? min($qty, $rule->getDiscountQty()) : $qty;
                   $rulePercent = min(100, $rule->getDiscountAmount());
      +
                   $discountAmount = 0;
                   $baseDiscountAmount = 0;
                   switch ($rule->getSimpleAction()) {
                       case 'to_percent':
                           $rulePercent = max(0, 100-$rule->getDiscountAmount());
                           //no break;
      -
                       case 'by_percent':
      -                    if ($step = $rule->getDiscountStep()) {
      +                    $step = $rule->getDiscountStep();
      +                    if ($step) {
                               $qty = floor($qty/$step)*$step;
                           }
      -                    $discountAmount    = ($qty*$item->getCalculationPrice() - $item->getDiscountAmount()) * $rulePercent/100;
      -                    $baseDiscountAmount= ($qty*$item->getBaseCalculationPrice() - $item->getBaseDiscountAmount()) * $rulePercent/100;
      +                    $discountAmount    = ($qty*$itemPrice - $item->getDiscountAmount()) * $rulePercent/100;
      +                    $baseDiscountAmount= ($qty*$baseItemPrice - $item->getBaseDiscountAmount()) * $rulePercent/100;
       
                           if (!$rule->getDiscountQty() || $rule->getDiscountQty()>$qty) {
                               $discountPercent = min(100, $item->getDiscountPercent()+$rulePercent);
                               $item->setDiscountPercent($discountPercent);
                           }
                           break;
      -
                       case 'to_fixed':
                           $quoteAmount = $quote->getStore()->convertPrice($rule->getDiscountAmount());
      -                    $discountAmount    = $qty*($item->getCalculationPrice()-$quoteAmount);
      -                    $baseDiscountAmount= $qty*($item->getBaseCalculationPrice()-$rule->getDiscountAmount());
      +                    $discountAmount    = $qty*($itemPrice-$quoteAmount);
      +                    $baseDiscountAmount= $qty*($baseItemPrice-$rule->getDiscountAmount());
                           break;
       
                       case 'by_fixed':
      -                    if ($step = $rule->getDiscountStep()) {
      +                    $step = $rule->getDiscountStep();
      +                    if ($step) {
                               $qty = floor($qty/$step)*$step;
                           }
      -                    $quoteAmount = $quote->getStore()->convertPrice($rule->getDiscountAmount());
      -                    $discountAmount    = $qty*$quoteAmount;
      -                    $baseDiscountAmount= $qty*$rule->getDiscountAmount();
      +                    $quoteAmount        = $quote->getStore()->convertPrice($rule->getDiscountAmount());
      +                    $discountAmount     = $qty*$quoteAmount;
      +                    $baseDiscountAmount = $qty*$rule->getDiscountAmount();
                           break;
       
                       case 'cart_fixed':
      @@ -186,9 +270,12 @@ public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
                               $cartRules[$rule->getId()] = $rule->getDiscountAmount();
                           }
                           if ($cartRules[$rule->getId()] > 0) {
      -                        $quoteAmount = $quote->getStore()->convertPrice($cartRules[$rule->getId()]);
      -                        $discountAmount = min($item->getRowTotal(), $quoteAmount);
      -                        $baseDiscountAmount = min($item->getBaseRowTotal(), $cartRules[$rule->getId()]);
      +                        $quoteAmount        = $quote->getStore()->convertPrice($cartRules[$rule->getId()]);
      +                        /**
      +                         * We can't use row total here because row total not include tax
      +                         */
      +                        $discountAmount     = min($itemPrice*$qty, $quoteAmount);
      +                        $baseDiscountAmount = min($baseItemPrice*$qty, $cartRules[$rule->getId()]);
                               $cartRules[$rule->getId()] -= $baseDiscountAmount;
                           }
                           $address->setCartFixedRules($cartRules);
      @@ -211,8 +298,8 @@ public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
                                   break;
                               }
                           }
      -                    $discountAmount    = $free*$item->getCalculationPrice();
      -                    $baseDiscountAmount= $free*$item->getBaseCalculationPrice();
      +                    $discountAmount    = $free*$itemPrice;
      +                    $baseDiscountAmount= $free*$baseItemPrice;
                           break;
                   }
       
      @@ -232,35 +319,126 @@ public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
                   $discountAmount = $result->getDiscountAmount();
                   $baseDiscountAmount = $result->getBaseDiscountAmount();
       
      -            $discountAmount     = $quote->getStore()->roundPrice($discountAmount);
      -            $baseDiscountAmount = $quote->getStore()->roundPrice($baseDiscountAmount);
      -            $discountAmount     = min($item->getDiscountAmount()+$discountAmount, $item->getRowTotal());
      -            $baseDiscountAmount = min($item->getBaseDiscountAmount()+$baseDiscountAmount, $item->getBaseRowTotal());
      +            $percentKey = $item->getDiscountPercent();
      +            /**
      +             * Process "delta" rounding
      +             */
      +            if ($percentKey) {
      +                $delta      = isset($this->_roundingDeltas[$percentKey]) ? $this->_roundingDeltas[$percentKey] : 0;
      +                $baseDelta  = isset($this->_baseRoundingDeltas[$percentKey]) ? $this->_baseRoundingDeltas[$percentKey] : 0;
      +                $discountAmount+= $delta;
      +                $baseDiscountAmount+=$baseDelta;
      +
      +                $this->_roundingDeltas[$percentKey]     = $discountAmount - $quote->getStore()->roundPrice($discountAmount);
      +                $this->_baseRoundingDeltas[$percentKey] = $baseDiscountAmount - $quote->getStore()->roundPrice($baseDiscountAmount);
      +                $discountAmount = $quote->getStore()->roundPrice($discountAmount);
      +                $baseDiscountAmount = $quote->getStore()->roundPrice($baseDiscountAmount);
      +            } else {
      +                $discountAmount     = $quote->getStore()->roundPrice($discountAmount);
      +                $baseDiscountAmount = $quote->getStore()->roundPrice($baseDiscountAmount);
      +            }
      +
      +            /**
      +             * We can't use row total here because row total not include tax
      +             * Discount can be applied on price included tax
      +             */
      +            $discountAmount     = min($item->getDiscountAmount()+$discountAmount, $itemPrice*$qty);
      +            $baseDiscountAmount = min($item->getBaseDiscountAmount()+$baseDiscountAmount, $baseItemPrice*$qty);
       
                   $item->setDiscountAmount($discountAmount);
                   $item->setBaseDiscountAmount($baseDiscountAmount);
       
      -            switch ($rule->getSimpleFreeShipping()) {
      -                case Mage_SalesRule_Model_Rule::FREE_SHIPPING_ITEM:
      -                    $item->setFreeShipping($rule->getDiscountQty() ? $rule->getDiscountQty() : true);
      +            $appliedRuleIds[$rule->getRuleId()] = $rule->getRuleId();
      +
      +            if ($rule->getCouponCode() && ( strtolower($rule->getCouponCode()) == strtolower($this->getCouponCode()))) {
      +                $address->setCouponCode($this->getCouponCode());
      +            }
      +            $this->_addDiscountDescription($address, $rule);
      +            if ($rule->getStopRulesProcessing()) {
      +                break;
      +            }
      +        }
      +        $item->setAppliedRuleIds(join(',',$appliedRuleIds));
      +        $address->setAppliedRuleIds($this->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds));
      +        $quote->setAppliedRuleIds($this->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds));
      +        return $this;
      +    }
      +
      +    /**
      +     * Apply discounts to shipping amount
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @return  Mage_SalesRule_Model_Validator
      +     */
      +    public function processShippingAmount(Mage_Sales_Model_Quote_Address $address)
      +    {
      +        $shippingAmount     = $address->getShippingAmountForDiscount();
      +        if ($shippingAmount!==null) {
      +            $baseShippingAmount = $address->getBaseShippingAmountForDiscount();
      +        } else {
      +            $shippingAmount     = $address->getShippingAmount();
      +            $baseShippingAmount = $address->getBaseShippingAmount();
      +        }
      +        $quote              = $address->getQuote();
      +        $appliedRuleIds = array();
      +        foreach ($this->_getRules() as $rule) {
      +            /* @var $rule Mage_SalesRule_Model_Rule */
      +            if (!$rule->getApplyToShipping() || !$this->_canProcessRule($rule, $address)) {
      +                continue;
      +            }
      +            
      +            $discountAmount = 0;
      +            $baseDiscountAmount = 0;
      +            $rulePercent = min(100, $rule->getDiscountAmount());
      +            switch ($rule->getSimpleAction()) {
      +                case 'to_percent':
      +                    $rulePercent = max(0, 100-$rule->getDiscountAmount());
      +                case 'by_percent':
      +                    $discountAmount    = ($shippingAmount - $address->getShippingDiscountAmount()) * $rulePercent/100;
      +                    $baseDiscountAmount= ($baseShippingAmount - $address->getBaseShippingDiscountAmount()) * $rulePercent/100;
      +                    $discountPercent = min(100, $address->getShippingDiscountPercent()+$rulePercent);
      +                    $address->setShippingDiscountPercent($discountPercent);
      +                    break;
      +                case 'to_fixed':
      +                    $quoteAmount = $quote->getStore()->convertPrice($rule->getDiscountAmount());
      +                    $discountAmount    = $shippingAmount-$quoteAmount;
      +                    $baseDiscountAmount= $baseShippingAmount-$rule->getDiscountAmount();
      +                    break;
      +                case 'by_fixed':
      +                    $quoteAmount        = $quote->getStore()->convertPrice($rule->getDiscountAmount());
      +                    $discountAmount     = $quoteAmount;
      +                    $baseDiscountAmount = $rule->getDiscountAmount();
                           break;
       
      -                case Mage_SalesRule_Model_Rule::FREE_SHIPPING_ADDRESS:
      -                    $address->setFreeShipping(true);
      +                case 'cart_fixed':
      +                    $cartRules = $address->getCartFixedRules();
      +                    if (!isset($cartRules[$rule->getId()])) {
      +                        $cartRules[$rule->getId()] = $rule->getDiscountAmount();
      +                    }
      +                    if ($cartRules[$rule->getId()] > 0) {
      +                        $quoteAmount        = $quote->getStore()->convertPrice($cartRules[$rule->getId()]);
      +                        $discountAmount     = min($shippingAmount, $quoteAmount);
      +                        $baseDiscountAmount = min($baseShippingAmount, $cartRules[$rule->getId()]);
      +                        $cartRules[$rule->getId()] -= $baseDiscountAmount;
      +                    }
      +                    $address->setCartFixedRules($cartRules);
                           break;
                   }
       
      +            $discountAmount     = min($address->getShippingDiscountAmount()+$discountAmount, $shippingAmount);
      +            $baseDiscountAmount = min($address->getBaseShippingDiscountAmount()+$baseDiscountAmount, $baseShippingAmount);
      +            $address->setShippingDiscountAmount($discountAmount);
      +            $address->setBaseShippingDiscountAmount($baseDiscountAmount);
                   $appliedRuleIds[$rule->getRuleId()] = $rule->getRuleId();
       
                   if ($rule->getCouponCode() && ( strtolower($rule->getCouponCode()) == strtolower($this->getCouponCode()))) {
                       $address->setCouponCode($this->getCouponCode());
                   }
      -
      +            $this->_addDiscountDescription($address, $rule);
                   if ($rule->getStopRulesProcessing()) {
                       break;
                   }
               }
      -        $item->setAppliedRuleIds(join(',',$appliedRuleIds));
               $address->setAppliedRuleIds($this->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds));
               $quote->setAppliedRuleIds($this->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds));
               return $this;
      @@ -280,4 +458,49 @@ public function mergeIds($a1, $a2, $asString=true)
               }
               return $a;
           }
      +
      +    /**
      +     * Add rule discount description label to address object
      +     *
      +     * @param   Mage_Sales_Model_Quote_Address $address
      +     * @param   Mage_SalesRule_Model_Rule $rule
      +     * @return  Mage_SalesRule_Model_Validator
      +     */
      +    protected function _addDiscountDescription($address, $rule)
      +    {
      +        $description = $address->getDiscountDescriptionArray();
      +        $ruleLabel = $rule->getStoreLabel($address->getQuote()->getStore());
      +        $label = '';
      +        if ($ruleLabel) {
      +            $label = $ruleLabel;
      +        } elseif ($rule->getCouponCode()) {
      +            $label = $rule->getCouponCode();
      +        }
      +        
      +        if (!empty($label)) {
      +            $description[$rule->getId()] = $label;
      +        }
      +        $address->setDiscountDescriptionArray($description);
      +        return $this;
      +    }
      +
      +    /**
      +     * Convert address discount description array to string
      +     *
      +     * @param Mage_Sales_Model_Quote_Address $address
      +     * @param string $separator
      +     * @return Mage_SalesRule_Model_Validator
      +     */
      +    public function prepareDescription($address, $separator=', ')
      +    {
      +        $description = $address->getDiscountDescriptionArray();
      +
      +        if (is_array($description) && !empty($description)) {
      +            $description = implode($separator, $description);
      +        } else {
      +            $description = '';
      +        }
      +        $address->setDiscountDescription($description);
      +        return $this;
      +    }
       }
      diff --git a/app/code/core/Mage/SalesRule/etc/config.xml b/app/code/core/Mage/SalesRule/etc/config.xml
      index 0fca08fa66..1c81493168 100644
      --- a/app/code/core/Mage/SalesRule/etc/config.xml
      +++ b/app/code/core/Mage/SalesRule/etc/config.xml
      @@ -28,7 +28,7 @@
       
           
               
      -            0.7.7
      +            0.7.9
               
           
           
      @@ -45,12 +45,9 @@
                   
                       Mage_SalesRule_Model_Mysql4
                       
      -                    
      -                        
      " . $importModel->getResource()->convert($store['name']) . " Store"; + $html .= "oscommerce_import
      + + + oscommerce_import_type
      +
      + + oscommerce_ref
      +
      + + catalog_product_website
      +
      + + catalog_category_entity
      +
      + + oscommerce_orders
      +
      + + oscommerce_orders_products
      +
      + + oscommerce_orders_total
      +
      + + oscommerce_orders_status_history
      +
      + + + + + + Mage_Oscommerce_Block + + + + + + Mage_Oscommerce + + + core_setup + + + + + core_write + + + + + core_read + + + + + pdo_mysql + mysql4 + 1 + + + + + + + + admin + + Mage_Oscommerce + oscommerce + + + + + + + + + + + + osCommerce + oscommerce/adminhtml_import + + + + + + + + + osCommerce Orders + oscommerce/adminhtml_order + 100 + + + + + + + + + + + + osCommerce Orders + 100 + + + + + + + + + osCommerce + 100 + + + + + + + + + + + + + + singleton + admin/observer + actionPreDispatchAdmin + + + + + + + + + + standard + + Mage_Oscommerce + oscommerce + + + + + + + oscommerce.xml + + + + + + + + + 10 + + + + diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-install-0.8.0.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-install-0.8.0.php new file mode 100755 index 0000000000..360f23ffed --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-install-0.8.0.php @@ -0,0 +1,72 @@ +startSetup(); + +$installer->run(" + +-- DROP TABLE IF EXISTS `{$this->getTable('oscommerce_import')}`; + +CREATE TABLE `{$this->getTable('oscommerce_import')}` ( + `import_id` int(10) unsigned NOT NULL auto_increment, + `name` varchar(255) default NULL, + `created_at` datetime default NULL, + `updated_at` datetime default NULL, + `host` varchar(255) NOT NULL, + `port` int(5) NOT NULL, + `db_name` varchar(255) default NULL, + `db_user` varchar(255) default NULL, + `db_password` varchar(255) default NULL, + `db_type` varchar(32) default NULL, + PRIMARY KEY (`import_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `{$this->getTable('oscommerce_import_type')}` ( + `type_id` int(2) unsigned NOT NULL auto_increment, + `type_code` varchar(32) NOT NULL, + `type_name` varchar(255) NOT NULL, + PRIMARY KEY (`type_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; + +insert into `{$this->getTable('oscommerce_import_type')}`(`type_id`,`type_code`,`type_name`) values (1,'store','Store'),(2,'category','Category'),(3,'product','Product'), (4,'customer','Customer'),(5,'order','Order'); + +CREATE TABLE `{$this->getTable('oscommerce_ref')}` ( + `id` int(10) unsigned NOT NULL auto_increment, + `import_id` int(10) NOT NULL, + `type_id` int(10) NOT NULL, + `value` int(10) NOT NULL, + `ref_id` int(10) NOT NULL, + `created_at` datetime default NULL, + `user_id` int(10) default NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.0-0.8.1.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.0-0.8.1.php new file mode 100755 index 0000000000..24d8dc8e04 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.0-0.8.1.php @@ -0,0 +1,47 @@ +startSetup(); + +$installer->run(" + +DROP TABLE IF EXISTS `{$this->getTable('oscommerce_import_type')}`; + +CREATE TABLE `{$this->getTable('oscommerce_import_type')}` ( + `type_id` int(2) unsigned NOT NULL auto_increment, + `type_code` varchar(32) NOT NULL, + `type_name` varchar(255) NOT NULL, + PRIMARY KEY (`type_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +insert into `{$this->getTable('oscommerce_import_type')}`(`type_code`,`type_name`) values ('website', 'Website'),('store','Store'),('category','Category'),('product','Product'), ('customer','Customer'),('order','Order'); + +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.1-0.8.2.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.1-0.8.2.php new file mode 100644 index 0000000000..a8fa808971 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.1-0.8.2.php @@ -0,0 +1,41 @@ +startSetup(); + +$installer->run(" + +truncate `{$this->getTable('oscommerce_import_type')}`; +insert into `{$this->getTable('oscommerce_import_type')}`(`type_code`,`type_name`) values ('website', 'Website'),('store','Store'),('category','Category'),('product','Product'), ('customer','Customer'),('order','Order'),('group','Store Group'), ('taxclass', 'Product Tax Class'); + +ALTER TABLE `{$this->getTable('oscommerce_import')}` ADD table_prefix VARCHAR(32) DEFAULT NULL; + +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.2-0.8.3.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.2-0.8.3.php new file mode 100644 index 0000000000..b07a605a4c --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.2-0.8.3.php @@ -0,0 +1,125 @@ +startSetup(); + +$installer->run(" +CREATE TABLE IF NOT EXISTS `{$this->getTable('oscommerce_orders')}` ( + `osc_magento_id` int(11) NOT NULL auto_increment, + `orders_id` int(11) NOT NULL, + `customers_id` int(11) NOT NULL default '0', + `magento_customers_id` int(11) NOT NULL default '0', + `import_id` int(11) NOT NULL default '0', + `website_id` int(11) NOT NULL default '0', + `customers_name` varchar(64) NOT NULL default '', + `customers_company` varchar(32) default NULL, + `customers_street_address` varchar(64) NOT NULL default '', + `customers_suburb` varchar(32) default NULL, + `customers_city` varchar(32) NOT NULL default '', + `customers_postcode` varchar(10) NOT NULL default '', + `customers_state` varchar(32) default NULL, + `customers_country` varchar(32) NOT NULL default '', + `customers_telephone` varchar(32) NOT NULL default '', + `customers_email_address` varchar(96) NOT NULL default '', + `customers_address_format_id` int(5) NOT NULL default '0', + `delivery_name` varchar(64) NOT NULL default '', + `delivery_company` varchar(32) default NULL, + `delivery_street_address` varchar(64) NOT NULL default '', + `delivery_suburb` varchar(32) default NULL, + `delivery_city` varchar(32) NOT NULL default '', + `delivery_postcode` varchar(10) NOT NULL default '', + `delivery_state` varchar(32) default NULL, + `delivery_country` varchar(32) NOT NULL default '', + `delivery_address_format_id` int(5) NOT NULL default '0', + `billing_name` varchar(64) NOT NULL default '', + `billing_company` varchar(32) default NULL, + `billing_street_address` varchar(64) NOT NULL default '', + `billing_suburb` varchar(32) default NULL, + `billing_city` varchar(32) NOT NULL default '', + `billing_postcode` varchar(10) NOT NULL default '', + `billing_state` varchar(32) default NULL, + `billing_country` varchar(32) NOT NULL default '', + `billing_address_format_id` int(5) NOT NULL default '0', + `payment_method` varchar(255) NOT NULL default '', + `cc_type` varchar(20) default NULL, + `cc_owner` varchar(64) default NULL, + `cc_number` varchar(32) default NULL, + `cc_expires` varchar(4) default NULL, + `last_modified` datetime default NULL, + `date_purchased` datetime default NULL, + `orders_status` int(5) NOT NULL default '0', + `orders_date_finished` datetime default NULL, + `currency` char(3) default NULL, + `currency_value` decimal(14,6) default NULL, + `currency_symbol` char(3) default NULL, + PRIMARY KEY (`osc_magento_id`), + KEY `idx_orders_customers_id` (`customers_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `{$this->getTable('oscommerce_orders_products')}` ( + `orders_products_id` int(11) NOT NULL auto_increment, + `osc_magento_id` int(11) NOT NULL default '0', + `products_id` int(11) NOT NULL default '0', + `products_model` varchar(12) default NULL, + `products_name` varchar(64) NOT NULL default '', + `products_price` decimal(15,4) NOT NULL default '0.0000', + `final_price` decimal(15,4) NOT NULL default '0.0000', + `products_tax` decimal(7,4) NOT NULL default '0.0000', + `products_quantity` int(2) NOT NULL default '0', + PRIMARY KEY (`orders_products_id`), + KEY `idx_orders_products_osc_magento_id` (`osc_magento_id`), + KEY `idx_orders_products_products_id` (`products_id`) +) ENGINE=MyISAM CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `{$this->getTable('oscommerce_orders_total')}` ( + `orders_total_id` int(10) unsigned NOT NULL auto_increment, + `osc_magento_id` int(11) NOT NULL default '0', + `title` varchar(255) NOT NULL default '', + `text` varchar(255) NOT NULL default '', + `value` decimal(15,4) NOT NULL default '0.0000', + `class` varchar(32) NOT NULL default '', + `sort_order` int(11) NOT NULL default '0', + PRIMARY KEY (`orders_total_id`), + KEY `idx_orders_total_osc_magento_id` (`osc_magento_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `{$this->getTable('oscommerce_orders_status_history')}`( + `orders_status_history_id` int(11) NOT NULL auto_increment, + `osc_magento_id` int(11) NOT NULL default '0', + `orders_status_id` int(5) NOT NULL default '0', + `date_added` datetime NOT NULL default '0000-00-00 00:00:00', + `customer_notified` int(1) default '0', + `comments` text, + PRIMARY KEY (`orders_status_history_id`), + KEY `idx_orders_status_history_osc_magento_id` (`osc_magento_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +"); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.3-0.8.4.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.3-0.8.4.php new file mode 100644 index 0000000000..c2c9902924 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.3-0.8.4.php @@ -0,0 +1,36 @@ +startSetup(); + +$installer->run(" + ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD orders_total decimal(14,6) default NULL; +"); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.4-0.8.5.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.4-0.8.5.php new file mode 100644 index 0000000000..a6f951002c --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.4-0.8.5.php @@ -0,0 +1,37 @@ +startSetup(); + +$installer->run(" + ALTER TABLE `{$this->getTable('oscommerce_orders')}` MODIFY orders_status varchar(32) default NULL; + ALTER TABLE `{$this->getTable('oscommerce_orders_status_history')}` ADD orders_status varchar(32) default NULL; +"); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.5-0.8.6.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.5-0.8.6.php new file mode 100644 index 0000000000..857fda4840 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.5-0.8.6.php @@ -0,0 +1,37 @@ +startSetup(); + +$installer->run(" + +insert into `{$this->getTable('oscommerce_import_type')}`(`type_code`,`type_name`) values ('root_category', 'Root Category'); +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.6-0.8.7.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.6-0.8.7.php new file mode 100644 index 0000000000..191fd2f3d5 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.6-0.8.7.php @@ -0,0 +1,39 @@ +startSetup(); + +$installer->run(" +ALTER TABLE `{$this->getTable('oscommerce_orders')}` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; +ALTER TABLE `{$this->getTable('oscommerce_orders_products')}` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; +ALTER TABLE `{$this->getTable('oscommerce_orders_status_history')}` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; +ALTER TABLE `{$this->getTable('oscommerce_orders_total')}` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.7-0.8.8.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.7-0.8.8.php new file mode 100644 index 0000000000..220f46444b --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.7-0.8.8.php @@ -0,0 +1,41 @@ +startSetup(); + +$installer->run(" +ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD purchased_year INT(4) DEFAULT NULL; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD purchased_month INT(2) DEFAULT NULL; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD purchased_day INT(2) DEFAULT NULL; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD modified_year INT(4) DEFAULT NULL; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD modified_month INT(2) DEFAULT NULL; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` ADD modified_day INT(2) DEFAULT NULL; +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.8-0.8.9.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.8-0.8.9.php new file mode 100644 index 0000000000..3bedb45beb --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.8-0.8.9.php @@ -0,0 +1,41 @@ +startSetup(); + +$installer->run(" +ALTER TABLE `{$this->getTable('oscommerce_orders')}` DROP COLUMN purchased_year; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` DROP COLUMN purchased_month; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` DROP COLUMN purchased_day; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` DROP COLUMN modified_year; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` DROP COLUMN modified_month; +ALTER TABLE `{$this->getTable('oscommerce_orders')}` DROP COLUMN modified_day; +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.9-0.8.10.php b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.9-0.8.10.php new file mode 100644 index 0000000000..92981409a2 --- /dev/null +++ b/app/code/core/Mage/Oscommerce/sql/oscommerce_setup/mysql4-upgrade-0.8.9-0.8.10.php @@ -0,0 +1,38 @@ +startSetup(); + +$installer->run(" + +ALTER TABLE `{$this->getTable('oscommerce_import')}` ADD send_subscription BOOL NOT NULL DEFAULT '0'; + +"); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Paybox/Block/Adminhtml/Cart/Type.php b/app/code/core/Mage/Paybox/Block/Adminhtml/Cart/Type.php new file mode 100644 index 0000000000..224ba5dc5b --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/Adminhtml/Cart/Type.php @@ -0,0 +1,46 @@ + + */ +class Mage_Paybox_Block_Adminhtml_Cart_Type extends Mage_Adminhtml_Block_System_Config_Form_Field +{ + protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) + { + $additional = Mage::getSingleton('core/layout')->createBlock('paybox/adminhtml_cart_type_select') + ->setTemplate('paybox/adminhtml/cart/type/select.phtml') + ->setDependHtmlId($element->getHtmlId()) + ->toHtml(); + + return parent::_getElementHtml($element).$additional; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Block/Adminhtml/Cart/Type/Select.php b/app/code/core/Mage/Paybox/Block/Adminhtml/Cart/Type/Select.php new file mode 100644 index 0000000000..7c26150f40 --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/Adminhtml/Cart/Type/Select.php @@ -0,0 +1,56 @@ + + */ +class Mage_Paybox_Block_Adminhtml_Cart_Type_Select extends Mage_Adminhtml_Block_Template +{ + /** + * Enter description here... + * + * @return Mage_Paybox_Model_System + */ + public function getModel() + { + return Mage::getModel('paybox/system'); + } + + public function getParentHtmlId() + { + return substr($this->getDependHtmlId(), 0, strrpos($this->getDependHtmlId(), 'typecarte')) . 'typepaiement'; + } + + public function getJsonCartTypes() + { + return $this->getModel()->getJsonCartTypes(); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Block/Direct/Form.php b/app/code/core/Mage/Paybox/Block/Direct/Form.php new file mode 100644 index 0000000000..b024b9881a --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/Direct/Form.php @@ -0,0 +1,42 @@ + + */ +class Mage_Paybox_Block_Direct_Form extends Mage_Payment_Block_Form_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('paybox/direct/form.phtml'); + } + +} diff --git a/app/code/core/Mage/Paybox/Block/Direct/Info.php b/app/code/core/Mage/Paybox/Block/Direct/Info.php new file mode 100644 index 0000000000..13ecdc8a90 --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/Direct/Info.php @@ -0,0 +1,48 @@ + + */ +class Mage_Paybox_Block_Direct_Info extends Mage_Payment_Block_Info_Cc +{ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('paybox/direct/info.phtml'); + } + + public function toPdf() + { + $this->setTemplate('paybox/direct/pdf/info.phtml'); + return $this->toHtml(); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Block/System/Error.php b/app/code/core/Mage/Paybox/Block/System/Error.php new file mode 100644 index 0000000000..c33836758b --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/System/Error.php @@ -0,0 +1,93 @@ + + */ +class Mage_Paybox_Block_System_Error extends Mage_Core_Block_Template +{ + + /** + * Error messages desciptions + * + * @var array + */ + protected $_pbxErrorsDesc; + + /** + * Enter description here... + * + * @return Mage_Checkout_Model_Session + */ + public function getCheckout() + { + return Mage::getSingleton('checkout/session'); + } + + public function getErrorMessage() + { + if (empty($this->_pbxErrorsDesc)) { + $this->_pbxErrorsDesc = array( + '-1' => 'Error in reading the parameters via stdin (POST method) (error in http reception)', + '-2' => 'Error in memory allocation. Not enough memory available on the trader\'s server', + '-3' => 'Error in reading the parameters QUERY_STRING or CONTENT_LENGTH. (http error)', + '-4' => 'PBX_RETOUR, PBX_ANNULE, PBX_REFUSE or PBX_EFFECTUE are too long (<150 characters)', + '-5' => 'Error in opening the file (if PBX_MODE contains 3) : local file non-existent, not found or access error', + '-6' => 'Error in file format (if PBX_MODE contains 3) : local file badly formed, empty or lines are badly formatted', + '-7' => 'A compulsory variable is missing (PBX_SITE, PBX_RANG, PBX_IDENTIFIANT, PBX_TOTAL, PBX_CMD, etc.)', + '-8' => 'One of the numerical variables contains a non-numerical character (site, rank, identifier, amount, currency etc.)', + '-9' => 'PBX_SITE contains a site number which does not consist of exactly 7 characters', + '-10' => 'PBX_RANG contains a rank number which does not consist of exactly 2 characters', + '-11' => 'PBX_TOTAL has more than 10 or fewer than 3 numerical characters', + '-12' => 'PBX_LANGUE or PBX_DEVISE contains a code which does not contain exactly 3 characters', + '-13' => 'PBX_CMD is empty or contains a reference longer than 250 characters', + '-14' => '', + '-15' => '', + '-16' => 'PBX_PORTEUR does not contain a valid e-mail address', + '-17' => 'Error of coherence (multi-baskets) : Reserved Future Usage', + ); + } + $msg = Mage::helper('paybox')->__($this->_pbxErrorsDesc[$this->getCheckout()->getPayboxErrorNumber()]); + $this->getCheckout()->unsPayboxErrorNumber(); + return $msg; + } + + /** + * Get continue shopping url + * + * @return string + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart', array('_secure' => true)); + } + +} diff --git a/app/code/core/Mage/Paybox/Block/System/Failure.php b/app/code/core/Mage/Paybox/Block/System/Failure.php new file mode 100644 index 0000000000..76cff37597 --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/System/Failure.php @@ -0,0 +1,61 @@ + + */ +class Mage_Paybox_Block_System_Failure extends Mage_Core_Block_Template +{ + /** + * Enter description here... + * + * @return Mage_Checkout_Model_Session + */ + public function getCheckout() + { + return Mage::getSingleton('checkout/session'); + } + + public function getErrorMessage() + { + $msg = Mage::helper('paybox')->__($this->getCheckout()->getPayboxErrorMessage()); + $this->getCheckout()->unsPayboxErrorMessage(); + return $msg; + } + + /** + * Get continue shopping url + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart', array('_secure' => true)); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Block/System/Form.php b/app/code/core/Mage/Paybox/Block/System/Form.php new file mode 100644 index 0000000000..57b5b2309a --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/System/Form.php @@ -0,0 +1,42 @@ + + */ +class Mage_Paybox_Block_System_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('paybox/system/form.phtml'); + parent::_construct(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Block/System/Redirect.php b/app/code/core/Mage/Paybox/Block/System/Redirect.php new file mode 100644 index 0000000000..36bce602ce --- /dev/null +++ b/app/code/core/Mage/Paybox/Block/System/Redirect.php @@ -0,0 +1,57 @@ + + */ +class Mage_Paybox_Block_System_Redirect extends Mage_Core_Block_Abstract +{ + protected function _toHtml() + { + $system = $this->getOrder()->getPayment()->getMethodInstance(); + + $form = new Varien_Data_Form(); + $form->setAction(Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB).$system->getPayboxFile()) + ->setId('paybox_system_checkout') + ->setName('paybox_system_checkout') + ->setMethod('POST') + ->setUseContainer(true); + foreach ($system->getFormFields() as $field=>$value) { + $form->addField($field, 'hidden', array('name'=>$field, 'value'=>$value)); + } + $html = ''; + $html.= $this->__('You will be redirected to Paybox in a few seconds.'); + $html.= $form->toHtml(); + $html.= ''; + $html.= ''; + + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Helper/Data.php b/app/code/core/Mage/Paybox/Helper/Data.php new file mode 100644 index 0000000000..3cd1a7a7ba --- /dev/null +++ b/app/code/core/Mage/Paybox/Helper/Data.php @@ -0,0 +1,37 @@ + + */ +class Mage_Paybox_Helper_Data extends Mage_Core_Helper_Abstract +{ + +} diff --git a/app/code/core/Mage/Paybox/Model/Api/Debug.php b/app/code/core/Mage/Paybox/Model/Api/Debug.php new file mode 100644 index 0000000000..d3ba960cc4 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Paybox_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('paybox/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Direct.php b/app/code/core/Mage/Paybox/Model/Direct.php new file mode 100644 index 0000000000..844967baa6 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Direct.php @@ -0,0 +1,506 @@ + + */ +class Mage_Paybox_Model_Direct extends Mage_Payment_Model_Method_Cc +{ + /** + * Paybox direct payment actions + */ + const PBX_PAYMENT_ACTION_ATHORIZE = '00001'; + const PBX_PAYMENT_ACTION_DEBIT = '00002'; + const PBX_PAYMENT_ACTION_ATHORIZE_CAPTURE = '00003'; + const PBX_PAYMENT_ACTION_CANCELLATION = '00005'; + const PBX_PAYMENT_ACTION_REFUND = '00004'; + + const PBX_VERSION = '00103'; + + /** + * ECL(Electronic Commerce Indicator). + * Type of ordering items. Need for some banks. + * 024 - request by internet + */ + const PBX_ACTIVITE_VALUE = '024'; + + protected $_code = 'paybox_direct'; + + protected $_isGateway = true; + protected $_canAuthorize = true; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = true; + protected $_canVoid = false; + protected $_canUseInternal = true; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = true; + protected $_canSaveCc = true; + + protected $_formBlockType = 'paybox/direct_form'; + protected $_infoBlockType = 'paybox/direct_info'; + + protected $_order; + protected $_currenciesNumbers; + protected $_questionNumberModel; + + /** + * Return paybox gateway url. + * If $recallNumber > 0 (primary url is not available) return url of backup gateway + * + * @param integer $recallNumber + * @return string + */ + public function getPayboxUrl($recallNumber) + { + $path = 'pbx_url'; + if ($recallNumber) { + $path = 'pbx_backupurl'; + } + return $this->getConfigData($path); + } + + /** + * Get Payment Action of Paybox Direct, + * changed to Paybox specification + * + * @return string + */ + public function getPaymentAction() + { + $paymentAction = $this->getConfigData('payment_action'); + switch ($paymentAction) { + case self::ACTION_AUTHORIZE: + return self::PBX_PAYMENT_ACTION_ATHORIZE; + break; + case self::ACTION_AUTHORIZE_CAPTURE: + return self::PBX_PAYMENT_ACTION_ATHORIZE_CAPTURE; + break; + default: + return self::PBX_PAYMENT_ACTION_ATHORIZE; + break; + } + } + + /** + * Return site number of account (TPE) + * + * @return string + */ + public function getSiteNumber() + { + return $this->getConfigData('pbx_site'); + } + + /** + * Return rang number of account + * + * @return string + */ + public function getRang() + { + return $this->getConfigData('pbx_rang'); + } + + /** + * Return Cle number of account + * + * @return string + */ + public function getCleNumber() + { + return $this->getConfigData('pbx_cle'); + } + + /** + * Return currency number in ISO4217 format + * + * @return string + */ + public function getCurrencyNumb() + { + $currencyCode = $this->getPayment()->getOrder()->getBaseCurrencyCode(); + if (!$this->_currenciesNumbers) { + $this->_currenciesNumbers = simplexml_load_file(Mage::getBaseDir().'/app/code/core/Mage/Paybox/etc/currency.xml'); + } + if ($this->_currenciesNumbers->$currencyCode) { + return (string)$this->_currenciesNumbers->$currencyCode; + } + } + + /** + * Return model of Question Number + * + * @return Mage_Paybox_Model_Question_Number + */ + public function getQuestionNumberModel() + { + if (!$this->_questionNumberModel) { + $accountHash = md5($this->getSiteNumber().$this->getRang()); + $this->_questionNumberModel = Mage::getModel('paybox/question_number')->load($accountHash, 'account_hash'); + } + return $this->_questionNumberModel; + } + + /** + * Return Debug Flag + * + * @return string + */ + public function getDebugFlag() + { + return $this->getConfigData('debug_flag'); + } + + public function authorize(Varien_Object $payment, $amount) + { + parent::authorize($payment, $amount); + + $this->setAmount($amount) + ->setPayment($payment); + + if ($this->callDoDirectPayment()!==false) { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()) + ->setPayboxRequestNumber($this->getRequestNumber()) + ->setPayboxQuestionNumber($this->getQuestionNumber()); + } else { + $e = $this->getError(); + if (isset($e['message'])) { + $message = Mage::helper('paybox')->__('There has been an error processing your payment. ') . $e['message']; + } else { + $message = Mage::helper('paybox')->__('There has been an error processing your payment. Please try later or contact us for help.'); + } + Mage::throwException($message); + } + + return $this; + } + + public function capture(Varien_Object $payment, $amount) + { + parent::capture($payment, $amount); + + $this->setAmount($amount) + ->setPayment($payment); + + if ($payment->getLastTransId()) {//if after authorize + $result = $this->callDoDebitPayment()!==false; + } else {//authorize+capture (debit) + $result = $this->callDoDirectPayment()!==false; + } + + if ($result) { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()) + ->setPayboxRequestNumber($this->getRequestNumber()); + } else { + $e = $this->getError(); + if (isset($e['message'])) { + $message = Mage::helper('paybox')->__('There has been an error processing your payment. ') . $e['message']; + } else { + $message = Mage::helper('paybox')->__('There has been an error processing your payment. Please try later or contact us for help.'); + } + Mage::throwException($message); + } + + return $this; + } + + public function cancel(Varien_Object $payment) + { + $payment->setStatus(self::STATUS_DECLINED); + return $this; + } + + public function refund(Varien_Object $payment, $amount) + { + parent::refund($payment, $amount); + + $error = false; + if($payment->getRefundTransactionId() && $amount>0) { + $this->setTransactionId($payment->getRefundTransactionId()) + ->setPayment($payment) + ->setAmount($amount); + + if ($this->callDoRefund()!==false) { + $payment->setStatus(self::STATUS_SUCCESS) + ->setCcTransId($this->getTransactionId()); + } else { + $payment->setStatus(self::STATUS_ERROR); + $e = $this->getError(); + if (isset($e['message'])) { + $error = $e['message']; + } else { + $error = Mage::helper('paybox')->__('Error in refunding the payment'); + } + } + } else { + $payment->setStatus(self::STATUS_ERROR); + $error = Mage::helper('paybox')->__('Error in refunding the payment'); + } + if ($error !== false) { + Mage::throwException($error); + } + + return $this; + } + + /** + * Building array of params for direct payment + * + * @return bool | array + */ + public function callDoDirectPayment() + { + $payment = $this->getPayment(); + $requestStr = ''; + + $tmpArr = array( + 'VERSION' => self::PBX_VERSION, + 'DATEQ' => Mage::getModel('core/date')->date('dmYHis'), + 'TYPE' => $this->getPaymentAction(), + 'NUMQUESTION' => $this->getQuestionNumberModel()->getNextQuestionNumber(), + 'SITE' => $this->getSiteNumber(), + 'RANG' => $this->getRang(), + 'CLE' => $this->getCleNumber(), + 'IDENTIFIANT' => '', + 'MONTANT' => ($this->getAmount()*100), + 'DEVISE' => $this->getCurrencyNumb(), + 'REFERENCE' => base64_encode($payment->getOrder()->getRealOrderId()), + 'PORTEUR' => $payment->getCcNumber(), + 'DATEVAL' => Mage::getModel('core/date')->date('my', mktime(0,0,0,$payment->getCcExpMonth(),1,$payment->getCcExpYear())), + 'CVV' => $payment->getCcCid(), + 'ACTIVITE' => self::PBX_ACTIVITE_VALUE, + ); + + foreach ($tmpArr as $param=>$value) { + $requestStr .= $param . '=' . $value . '&'; + } + $requestStr = substr($requestStr, 0, -1); + + $resultArr = $this->call($requestStr); + + if ($resultArr === false) { + return false; + } + + $this->getQuestionNumberModel() + ->increaseQuestionNumber(); + + $this->setTransactionId($resultArr['NUMTRANS']); + $this->setRequestNumber($resultArr['NUMAPPEL']); + $this->setQuestionNumber($resultArr['NUMQUESTION']); + + return $resultArr; + } + + /** + * Building array of params for debit (after authorize) + * + * @return bool | array + */ + public function callDoDebitPayment() + { + $payment = $this->getPayment(); + $requestStr = ''; + + $tmpArr = array( + 'VERSION' => self::PBX_VERSION, + 'DATEQ' => Mage::getModel('core/date')->date('dmYHis'), + 'TYPE' => self::PBX_PAYMENT_ACTION_DEBIT, + 'NUMQUESTION' => $payment->getPayboxQuestionNumber(), + 'SITE' => $this->getSiteNumber(), + 'RANG' => $this->getRang(), + 'CLE' => $this->getCleNumber(), + 'MONTANT' => ($this->getAmount()*100), + 'DEVISE' => (string)$this->getCurrencyNumb(), + 'REFERENCE' => base64_encode($payment->getOrder()->getRealOrderId()), + 'NUMAPPEL' => $payment->getPayboxRequestNumber(), + 'NUMTRANS' => $payment->getLastTransId(), + ); + + foreach ($tmpArr as $param=>$value) { + $requestStr .= $param . '=' . $value . '&'; + } + $requestStr = substr($requestStr, 0, -1); + + $resultArr = $this->call($requestStr); + + if ($resultArr === false) { + return false; + } + + $this->setTransactionId($resultArr['NUMTRANS']); + + return $resultArr; + } + + /** + * Building array of params for refund + * + * @return bool | array + */ + public function callDoRefund() + { + $payment = $this->getPayment(); + $requestStr = ''; + + $tmpArr = array( + 'VERSION' => self::PBX_VERSION, + 'DATEQ' => Mage::getModel('core/date')->date('dmYHis'), + 'TYPE' => self::PBX_PAYMENT_ACTION_REFUND, + 'NUMQUESTION' => $this->getQuestionNumberModel()->getNextQuestionNumber(), + 'SITE' => $this->getSiteNumber(), + 'RANG' => $this->getRang(), + 'CLE' => $this->getCleNumber(), + 'MONTANT' => ($this->getAmount()*100), + 'DEVISE' => (string)$this->getCurrencyNumb(), + 'REFERENCE' => base64_encode($payment->getOrder()->getRealOrderId()), + 'PORTEUR' => $payment->getCcNumber(), + 'DATEVAL' => Mage::getModel('core/date')->date('my', mktime(0,0,0,$payment->getCcExpMonth(),1,$payment->getCcExpYear())), + 'NUMAPPEL' => '', + 'NUMTRANS' => '', + ); + + foreach ($tmpArr as $param=>$value) { + $requestStr .= $param . '=' . $value . '&'; + } + $requestStr = substr($requestStr, 0, -1); + + $resultArr = $this->call($requestStr); + + if ($resultArr === false) { + return false; + } + + $this->getQuestionNumberModel() + ->increaseQuestionNumber(); + + $this->setTransactionId($resultArr['NUMTRANS']); + + return $resultArr; + } + + /** + * Making a call to gateway + * + * @param string $requestStr + * @return bool | array + */ + public function call($requestStr) + { + if ($this->getDebugFlag()) { + $debug = Mage::getModel('paybox/api_debug') + ->setRequestBody($requestStr) + ->save(); + } + $recall = true; + $recallCounter = 0; + while ($recall && $recallCounter < 3) { + $recall = false; + $this->unsError(); + + $http = new Varien_Http_Adapter_Curl(); + $config = array('timeout' => 30); + $http->setConfig($config); + $http->write(Zend_Http_Client::POST, $this->getPayboxUrl($recallCounter), '1.1', array(), $requestStr); + $response = $http->read(); + + $response = preg_split('/^\r?$/m', $response, 2); + $response = trim($response[1]); + + if ($http->getErrno()) { + $http->close(); + if ($this->getDebugFlag()) { + $debug->setResponseBody($response)->save(); + } + $this->setError(array( + 'message' => $http->getError() + )); + return false; + } + $http->close(); + + $parsedResArr = $this->parseResponseStr($response); + + //primary gateway is down, need to recall to backup gateway + if ($parsedResArr['CODEREPONSE'] == '00001' || + $parsedResArr['CODEREPONSE'] == '00097' || + $parsedResArr['CODEREPONSE'] == '00098' + ) { + $recallCounter++; + $recall = true; + } + } + + if ($this->getDebugFlag()) { + $debug->setResponseBody($response)->save(); + } + + //if backup gateway was down too + if ($recall) { + $this->setError(array( + 'message' => Mage::helper('paybox')->__('Paybox payment gateway is not available right now') + )); + return false; + } + + if ($parsedResArr['CODEREPONSE'] == '00000') { + return $parsedResArr; + } + + if (isset($parsedResArr['COMMENTAIRE'])) { + $this->setError(array( + 'message' => $parsedResArr['CODEREPONSE'] . ':' . $parsedResArr['COMMENTAIRE'] + )); + } + + return false; + } + + /** + * Parsing response string + * + * @param string $str + * @return array + */ + public function parseResponseStr($str) + { + $tmpResponseArr = explode('&', $str); + $responseArr = array(); + foreach ($tmpResponseArr as $response) { + $paramValue = explode('=', $response); + $responseArr[$paramValue[0]] = $paramValue[1]; + } + + return $responseArr; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Paybox/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..c0f530df34 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Mysql4/Api/Debug.php @@ -0,0 +1,40 @@ + + */ +class Mage_Paybox_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('paybox/api_debug', 'debug_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Paybox/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..b59bdde53c --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,40 @@ + + */ +class Mage_Paybox_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('paybox/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Mysql4/Question/Number.php b/app/code/core/Mage/Paybox/Model/Mysql4/Question/Number.php new file mode 100644 index 0000000000..a80a7fbf64 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Mysql4/Question/Number.php @@ -0,0 +1,41 @@ + + */ +class Mage_Paybox_Model_Mysql4_Question_Number extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('paybox/question_number', 'account_id'); + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Mysql4/Setup.php b/app/code/core/Mage/Paybox/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..d1ea0e9213 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Mysql4/Setup.php @@ -0,0 +1,37 @@ + + */ +class Mage_Paybox_Model_Mysql4_Setup extends Mage_Sales_Model_Mysql4_Setup +{ + +} diff --git a/app/code/core/Mage/Paybox/Model/Question/Number.php b/app/code/core/Mage/Paybox/Model/Question/Number.php new file mode 100644 index 0000000000..096420d329 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Question/Number.php @@ -0,0 +1,95 @@ + + */ +class Mage_Paybox_Model_Question_Number extends Mage_Core_Model_Abstract +{ + /** + * Max value of question number + */ + const MAX_QUESTION_NUMBER_VALUE = 2147483647; + + protected $_accountHash; + + protected function _construct() + { + $this->_init('paybox/question_number'); + } + + public function load($id, $field=null) + { + $this->_accountHash = $id; + return parent::load($id, $field); + } + + protected function _afterLoad() + { + //need to create new record (with default data) if it first time using of paybox direct + if (!$this->getAccountHash()) { + $this->setAccountHash($this->_accountHash); + $this->setIncrementValue(1); + $this->save(); + } + unset($this->_accountHash); + + //need to set default value of question number if it reach max value + if ($this->getIncrementValue() >= self::MAX_QUESTION_NUMBER_VALUE) { + $this->setResetDate('CURRENT_TIMESTAMP') + ->setIncrementValue(1); + } + + return parent::_afterLoad(); + } + + /** + * Return next number formated to paybox specification + * + * @return string + */ + public function getNextQuestionNumber() + { + $questionNumber = $this->getIncrementValue()+1; + return sprintf('%010d', $questionNumber); + } + + /** + * Increase question number and save it after successful transaction + * + * @return Mage_Paybox_Model_Question_Number + */ + public function increaseQuestionNumber() + { + $this->setIncrementValue($this->getIncrementValue()+1) + ->save(); + return $this; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Source/CartType.php b/app/code/core/Mage/Paybox/Model/Source/CartType.php new file mode 100644 index 0000000000..0f4566f69d --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/CartType.php @@ -0,0 +1,61 @@ + + */ +class Mage_Paybox_Model_Source_CartType +{ + /** + * Enter description here... + * + * @return Mage_Paybox_Model_System + */ + public function getModel() + { + return Mage::getModel('paybox/system'); + } + + public function toOptionArray() + { + $cartTypesArr = array(); + $tmpArr = $this->getModel()->getCartTypesByPayment($this->getModel()->getPaymentType()); + + foreach ($tmpArr as $code => $name) { + $cartTypesArr[] = array( + 'value' => $code, + 'label' => $name + ); + } + + return $cartTypesArr; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Source/Cctype.php b/app/code/core/Mage/Paybox/Model/Source/Cctype.php new file mode 100644 index 0000000000..92628504b5 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/Cctype.php @@ -0,0 +1,40 @@ + + */ +class Mage_Paybox_Model_Source_Cctype extends Mage_Payment_Model_Source_Cctype +{ + public function getAllowedTypes() + { + return array('VI', 'MC', 'AE', 'OT'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Source/Language.php b/app/code/core/Mage/Paybox/Model/Source/Language.php new file mode 100644 index 0000000000..2dd1e95d18 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/Language.php @@ -0,0 +1,50 @@ + + */ +class Mage_Paybox_Model_Source_Language +{ + public function toOptionArray() + { + return array( +// array('value' => Mage_Paypal_Model_Api_Abstract::PAYMENT_TYPE_AUTH, 'label' => Mage::helper('paypal')->__('Authorization')), + array('value' => 'FRA', 'label' => Mage::helper('paybox')->__('FRA (French)')), + array('value' => 'GBR', 'label' => Mage::helper('paybox')->__('GBR (English)')), + array('value' => 'ESP', 'label' => Mage::helper('paybox')->__('ESP (Spanish)')), + array('value' => 'ITA', 'label' => Mage::helper('paybox')->__('ITA (Italian)')), + array('value' => 'DEU', 'label' => Mage::helper('paybox')->__('DEU (German)')), + array('value' => 'NLD', 'label' => Mage::helper('paybox')->__('NLD (Dutch)')), + array('value' => 'SWE', 'label' => Mage::helper('paybox')->__('SWE (Swedish)')), + ); + } +} diff --git a/app/code/core/Mage/Paybox/Model/Source/ManagementMode.php b/app/code/core/Mage/Paybox/Model/Source/ManagementMode.php new file mode 100644 index 0000000000..e88f064105 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/ManagementMode.php @@ -0,0 +1,48 @@ + + */ +class Mage_Paybox_Model_Source_ManagementMode +{ + public function toOptionArray() + { + return array( +// array('value' => Mage_Paypal_Model_Api_Abstract::PAYMENT_TYPE_AUTH, 'label' => Mage::helper('paypal')->__('Authorization')), + array('value' => 'A', 'label' => Mage::helper('paybox')->__('Mode A')), + array('value' => 'B', 'label' => Mage::helper('paybox')->__('Mode B')), + array('value' => 'C', 'label' => Mage::helper('paybox')->__('Mode C')), + array('value' => 'D', 'label' => Mage::helper('paybox')->__('Mode D')), + array('value' => 'E', 'label' => Mage::helper('paybox')->__('Mode E')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Source/MethodCall.php b/app/code/core/Mage/Paybox/Model/Source/MethodCall.php new file mode 100644 index 0000000000..7629263003 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/MethodCall.php @@ -0,0 +1,45 @@ + + */ +class Mage_Paybox_Model_Source_MethodCall +{ + public function toOptionArray() + { + return array( +// array('value' => Mage_Paypal_Model_Api_Abstract::PAYMENT_TYPE_AUTH, 'label' => Mage::helper('paypal')->__('Authorization')), + array('value' => 'GET', 'label' => Mage::helper('paybox')->__('GET')), + array('value' => 'POST', 'label' => Mage::helper('paybox')->__('POST')), + ); + } +} diff --git a/app/code/core/Mage/Paybox/Model/Source/PaymentAction.php b/app/code/core/Mage/Paybox/Model/Source/PaymentAction.php new file mode 100644 index 0000000000..5491fcda82 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/PaymentAction.php @@ -0,0 +1,43 @@ + + */ +class Mage_Paybox_Model_Source_PaymentAction +{ + public function toOptionArray() + { + return array( + array('value' => Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE, 'label' => Mage::helper('paybox')->__('Authorization')), + array('value' => Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE_CAPTURE, 'label' => Mage::helper('paybox')->__('Payment')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Source/PaymentMode.php b/app/code/core/Mage/Paybox/Model/Source/PaymentMode.php new file mode 100644 index 0000000000..deac42ec87 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/PaymentMode.php @@ -0,0 +1,45 @@ + + */ +class Mage_Paybox_Model_Source_PaymentMode +{ + public function toOptionArray() + { + return array( +// array('value' => Mage_Paypal_Model_Api_Abstract::PAYMENT_TYPE_AUTH, 'label' => Mage::helper('paypal')->__('Authorization')), + array('value' => 1, 'label' => Mage::helper('paybox')->__('HTML form')), + array('value' => 4, 'label' => Mage::helper('paybox')->__('Command Line Mode')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/Source/PaymentType.php b/app/code/core/Mage/Paybox/Model/Source/PaymentType.php new file mode 100644 index 0000000000..e39ccfcf4c --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/Source/PaymentType.php @@ -0,0 +1,48 @@ + + */ +class Mage_Paybox_Model_Source_PaymentType +{ + public function toOptionArray() + { + return array( + array('value' => '', 'label' => Mage::helper('paybox')->__('--Please Select--')), + array('value' => Mage_Paybox_Model_System::PBX_PAYMENT_TYPE_CARTE, 'label' => Mage::helper('paybox')->__('CARTE')), + array('value' => Mage_Paybox_Model_System::PBX_PAYMENT_TYPE_SYMPASS, 'label' => Mage::helper('paybox')->__('SYMPASS')), + array('value' => Mage_Paybox_Model_System::PBX_PAYMENT_TYPE_PAYNOVA , 'label' => Mage::helper('paybox')->__('PAYNOVA')), + array('value' => Mage_Paybox_Model_System::PBX_PAYMENT_TYPE_TERMINEO, 'label' => Mage::helper('paybox')->__('TERMINEO')), + array('value' => Mage_Paybox_Model_System::PBX_PAYMENT_TYPE_PAYPAL, 'label' => Mage::helper('paybox')->__('PAYPAL')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/Model/System.php b/app/code/core/Mage/Paybox/Model/System.php new file mode 100644 index 0000000000..21a43190a9 --- /dev/null +++ b/app/code/core/Mage/Paybox/Model/System.php @@ -0,0 +1,529 @@ + + */ +class Mage_Paybox_Model_System extends Mage_Payment_Model_Method_Abstract +{ + /** + * Paybox const variables + */ + const PBX_FORM_HTML_METHOD = 1; + const PBX_COMMAND_LINE_METHOD = 4; + + const PBX_METHOD_CALL = 'POST'; + + const PBX_PAYMENT_ACTION_ATHORIZE = 'O'; + const PBX_PAYMENT_ACTION_ATHORIZE_CAPTURE = 'N'; + + const PBX_PAYMENT_TYPE_CARTE = 'CARTE'; + const PBX_PAYMENT_TYPE_SYMPASS = 'SYMPASS'; + const PBX_PAYMENT_TYPE_PAYNOVA = 'PAYNOVA'; + const PBX_PAYMENT_TYPE_TERMINEO = 'TERMINEO'; + const PBX_PAYMENT_TYPE_PAYPAL = 'PAYPAL'; + + const PBX_CARTE_TYPE_CB = 'CB'; + const PBX_CARTE_TYPE_VISA = 'VISA'; + const PBX_CARTE_TYPE_EUROCARDMASTERCARD = 'EUROCARD_MASTERCARD'; + const PBX_CARTE_TYPE_ECARD = 'E_CARD'; + const PBX_CARTE_TYPE_AMEX = 'AMEX'; + const PBX_CARTE_TYPE_DINERS = 'DINERS'; + const PBX_CARTE_TYPE_JCB = 'JCB'; + const PBX_CARTE_TYPE_AURORE = 'AURORE'; + const PBX_CARTE_TYPE_PAYNOVA = 'PAYNOVA'; + const PBX_CARTE_TYPE_TERMINEO = 'TERMINEO'; + const PBX_CARTE_TYPE_PAYPAL = 'PAYPAL'; + + protected $_code = 'paybox_system'; + + protected $_isGateway = false; + protected $_canAuthorize = true; + protected $_canCapture = false; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + protected $_formBlockType = 'paybox/system_form'; + + protected $_order; + protected $_cartTypes; + protected $_currenciesNumbers; + + /** + * Get order model + * + * @return Mage_Sales_Model_Order + */ + public function getOrder() + { + if (!$this->_order) { + $paymentInfo = $this->getInfoInstance(); + $this->_order = Mage::getModel('sales/order') + ->loadByIncrementId($paymentInfo->getOrder()->getRealOrderId()); + } + return $this->_order; + } + + /** + * Set order + * + * @param Mage_Sales_Model_Order $order + */ + public function setOrder(Mage_Sales_Model_Order $order) + { + $this->_order = $order; + return $this; + } + + /** + * Get cart types for all payment types + * or for given payment type + * + * @param string $paymentType + * @return array + */ + protected function _getCartTypes($paymentType = null) + { + if (!$this->_cartTypes) { + $this->_cartTypes = array( + self::PBX_PAYMENT_TYPE_CARTE => array( + 'none' => Mage::helper('paybox')->__('Customer Choise'), + self::PBX_CARTE_TYPE_CB => Mage::helper('paybox')->__('CB'), + self::PBX_CARTE_TYPE_VISA => Mage::helper('paybox')->__('VISA'), + self::PBX_CARTE_TYPE_EUROCARDMASTERCARD => Mage::helper('paybox')->__('EUROCARD & MASTERCARD'), + self::PBX_CARTE_TYPE_ECARD => Mage::helper('paybox')->__('E CARD'), + self::PBX_CARTE_TYPE_AMEX => Mage::helper('paybox')->__('AMEX'), + self::PBX_CARTE_TYPE_DINERS => Mage::helper('paybox')->__('DINERS'), + self::PBX_CARTE_TYPE_JCB => Mage::helper('paybox')->__('JCB'), + self::PBX_CARTE_TYPE_AURORE => Mage::helper('paybox')->__('AURORE'), + ), + self::PBX_PAYMENT_TYPE_SYMPASS => array( + 'none' => Mage::helper('paybox')->__('Customer Choise'), + self::PBX_CARTE_TYPE_CB => Mage::helper('paybox')->__('CB'), + self::PBX_CARTE_TYPE_VISA => Mage::helper('paybox')->__('VISA'), + self::PBX_CARTE_TYPE_EUROCARDMASTERCARD => Mage::helper('paybox')->__('EUROCARD & MASTERCARD'), + self::PBX_CARTE_TYPE_ECARD => Mage::helper('paybox')->__('E CARD'), + self::PBX_CARTE_TYPE_AMEX => Mage::helper('paybox')->__('AMEX'), + self::PBX_CARTE_TYPE_DINERS => Mage::helper('paybox')->__('DINERS'), + self::PBX_CARTE_TYPE_JCB => Mage::helper('paybox')->__('JCB'), + self::PBX_CARTE_TYPE_AURORE => Mage::helper('paybox')->__('AURORE'), + ), + self::PBX_PAYMENT_TYPE_PAYNOVA => array( + self::PBX_CARTE_TYPE_PAYNOVA => Mage::helper('paybox')->__('PAYNOVA'), + ), + self::PBX_PAYMENT_TYPE_TERMINEO => array( + self::PBX_CARTE_TYPE_TERMINEO => Mage::helper('paybox')->__('TERMINEO'), + ), + self::PBX_PAYMENT_TYPE_PAYPAL => array( + self::PBX_CARTE_TYPE_PAYPAL => Mage::helper('paybox')->__('PAYPAL'), + ) + ); + } + + if (!is_null($paymentType)) { + if (isset($this->_cartTypes[$paymentType])) { + return $this->_cartTypes[$paymentType]; + } + } + + return $this->_cartTypes; + } + + /** + * Get cart types by given payment + * + * @param string $paymentType + * @return array + */ + public function getCartTypesByPayment($paymentType) + { + if ($paymentType == '') { + return array(); + } + return $this->_getCartTypes($paymentType); + } + + /** + * Get all cart types in JSON format + * + * @return string + */ + public function getJsonCartTypes() + { + return Mage::helper('core')->jsonEncode($this->_getCartTypes()); + } + + /** + * Get payment method + * + * @return string + */ + public function getPaymentMethod() + { + return $this->getConfigData('pbx_mode'); + } + + /** + * Get name of executable file + * + * @return string + */ + public function getPayboxFile() + { + return $this->getConfigData('pbx_file'); + } + + /** + * Get Payment type + * + * @return string + */ + public function getPaymentType() + { + return $this->getConfigData('pbx_typepaiement'); + } + + /** + * Get Payment Action of Paybox System changed to Paybox specification + * + * @return string + */ + public function getPaymentAction() + { + $paymentAction = $this->getConfigData('pbx_autoseule'); + switch ($paymentAction) { + case self::ACTION_AUTHORIZE: + return self::PBX_PAYMENT_ACTION_ATHORIZE; + break; + case self::ACTION_AUTHORIZE_CAPTURE: + return self::PBX_PAYMENT_ACTION_ATHORIZE_CAPTURE; + break; + default: + return self::PBX_PAYMENT_ACTION_ATHORIZE; + break; + } + } + + /** + * Get cart type + * + * @return string + */ + public function getCartType() + { + return $this->getConfigData('pbx_typecarte'); + } + + /** + * Get Site number (TPE) + * + * @return string + */ + public function getSiteNumber() + { + return $this->getConfigData('pbx_site'); + } + + /** + * Get Rang number + * + * @return string + */ + public function getRang() + { + return $this->getConfigData('pbx_rang'); + } + + /** + * Get Identifiant number + * + * @return string + */ + public function getIdentifiant() + { + return $this->getConfigData('pbx_identifiant'); + } + + /** + * Get currency number in ISO4217 format + * + * @return string + */ + public function getCurrencyNumber() + { + $currencyCode = $this->getOrder()->getBaseCurrencyCode(); + if (!$this->_currenciesNumbers) { + $this->_currenciesNumbers = simplexml_load_file(Mage::getBaseDir().'/app/code/core/Mage/Paybox/etc/currency.xml'); + } + if ($this->_currenciesNumbers->$currencyCode) { + return (string)$this->_currenciesNumbers->$currencyCode; + } + } + + /** + * Get language of interface of payment defined in config + * + * @return string + */ + public function getLanguage() + { + return $this->getConfigData('pbx_langue'); + } + + /** + * Get api urls if they defined in config + * + * @return unknown + */ + public function getApiUrls() + { + $fielldsArr = array(); + if (($primary = trim($this->getConfigData('pbx_paybox'))) != '') { + $fielldsArr['PBX_PAYBOX'] = $primary; + } + + if (($backup1 = trim($this->getConfigData('pbx_backup1'))) != '') { + $fielldsArr['PBX_BACKUP1'] = $backup1; + } + + if (($backup2 = trim($this->getConfigData('pbx_backup2'))) != '') { + $fielldsArr['PBX_BACKUP2'] = $backup2; + } + + if (($backup3 = trim($this->getConfigData('pbx_backup3'))) != '') { + $fielldsArr['PBX_BACKUP3'] = $backup3; + } + + return $fielldsArr; + } + + /** + * Get timeouts for api urls if timeouts diferent from default + * + * @return array + */ + public function getTimeouts() + { + $fielldsArr = array(); + if (($timeout = trim($this->getConfigData('pbx_timeout'))) != '') { + $fielldsArr['PBX_TIMEOUT'] = $timeout; + } + + if (($timeout1 = trim($this->getConfigData('pbx_timeout1'))) != '') { + $fielldsArr['PBX_TIMEOUT1'] = $timeout1; + } + + if (($timeout2 = trim($this->getConfigData('pbx_timeout2'))) != '') { + $fielldsArr['PBX_TIMEOUT2'] = $timeout2; + } + + if (($timeout3 = trim($this->getConfigData('pbx_timeout3'))) != '') { + $fielldsArr['PBX_TIMEOUT3'] = $timeout3; + } + + return $fielldsArr; + } + + /** + * Get params from config for HTML form mode + * + * @return array + */ + public function getManagementMode() + { + $fieldsArr = array(); + if (($text = trim($this->getConfigData('pbx_txt'))) != '') { + $fieldsArr['PBX_TXT'] = $text; + } + + if (($wait = trim($this->getConfigData('pbx_wait'))) != '') { + $fieldsArr['PBX_WAIT'] = $wait; + } + + if (($boutpi = trim($this->getConfigData('pbx_boutpi')))) { + $fieldsArr['PBX_BOUTPI'] = $boutpi; + } + + if (($bkgd = trim($this->getConfigData('pbx_bkgd'))) != '') { + $fieldsArr['PBX_BKGD'] = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA).'payment/paybox/bkgd/' . $bkgd; + } + + $fieldsArr['PBX_OUTPUT'] = $this->getConfigData('pbx_output'); + + return $fieldsArr; + } + + /** + * Get ping flag (commandline mode) + * + * @return unknown + */ + public function getPingFlag() + { + return $this->getConfigData('pbx_ping'); + } + + /** + * Get ping port (commandline mode) + * + * @return string + */ + public function getPingPort() + { + return $this->getConfigData('pbx_port'); + } + + /** + * Get debug flag + * + * @return string + */ + public function getDebugFlag() + { + return $this->getConfigData('debug_flag'); + } + + public function getOrderPlaceRedirectUrl() + { + if ($this->getPaymentMethod() == self::PBX_FORM_HTML_METHOD) { + return Mage::getUrl('paybox/system/redirect', array('_secure' => true)); + } else { + return Mage::getUrl('paybox/system/commandline', array('_secure' => true)); + } + } + + /** + * Building array of params to send + * + * @return array + */ + public function getFormFields() + { + $fieldsArr = array(); + + $fieldsArr = array( + 'PBX_MODE' => $this->getPaymentMethod(), + 'PBX_SITE' => $this->getSiteNumber(),//'1999888', + 'PBX_RANG' => $this->getRang(),//'99', + 'PBX_IDENTIFIANT' => $this->getIdentifiant(),//'2', + 'PBX_TOTAL' => ($this->getOrder()->getBaseGrandTotal()*100), + 'PBX_DEVISE' => $this->getCurrencyNumber(), + 'PBX_CMD' => $this->getOrder()->getRealOrderId(), + 'PBX_PORTEUR' => $this->getOrder()->getCustomerEmail(), + 'PBX_RETOUR' => 'amount:M;ref:R;auto:A;trans:T;error:E', + 'PBX_EFFECTUE' => Mage::getUrl('paybox/system/success', array('_secure' => true)), + 'PBX_REFUSE' => Mage::getUrl('paybox/system/refuse', array('_secure' => true)), + 'PBX_ANNULE' => Mage::getUrl('paybox/system/decline', array('_secure' => true)), + 'PBX_AUTOSEULE' => $this->getPaymentAction(), + 'PBX_LANGUE' => $this->getLanguage(), + 'PBX_ERREUR' => Mage::getUrl('paybox/system/error', array('_secure' => true)), + 'PBX_TYPEPAIEMENT' => $this->getPaymentType(), + 'PBX_TYPECARTE' => $this->getCartType(), + 'PBX_RUF1' => self::PBX_METHOD_CALL, + ); + + if (count($apiUrls = $this->getApiUrls())) { + $fieldsArr = array_merge($fieldsArr, $this->getApiUrls()); + } + if (count($timeouts = $this->getTimeouts())) { + $fieldsArr = array_merge($fieldsArr, $this->getTimeouts()); + } + + if ($this->getPaymentMethod() == self::PBX_FORM_HTML_METHOD) { + $fieldsArr = array_merge($fieldsArr, $this->getManagementMode()); + } + + if ($this->getPaymentMethod() == self::PBX_COMMAND_LINE_METHOD && $this->getPingFlag()) { + $tmpFieldsArr['PBX_PING'] = '1'; + if (($pingPort = trim($this->getPingPort())) != '') { + $tmpFieldsArr['PING_PORT'] = $pingPort; + } + + $fieldsArr = array_merge($fieldsArr, $tmpFieldsArr); + } + + if ($this->getDebugFlag()) { + $debug = Mage::getModel('paybox/api_debug') + ->setRealOrderId($this->getOrder()->getRealOrderId()) + ->setRequestBody(print_r($fieldsArr, 1)) + ->save(); + } + + return $fieldsArr; + } + + /** + * Checking response + * + * @param array $response + * @return bool + */ + public function checkResponse($response) + { + if ($this->getDebugFlag()) { + $debug = Mage::getModel('paybox/api_debug') + ->load($response['ref'], 'real_order_id') + ->setResponseBody(print_r($response, 1)) + ->save(); + } + + if (isset($response['error'], $response['amount'], + $response['ref'], $response['trans']) + ) { + return true; + } + return false; + } + + public function capture(Varien_Object $payment, $amount) + { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + return $this; + } + + public function authorize(Varien_Object $payment, $amount) + { + $payment->setStatus(self::STATUS_APPROVED) + ->setLastTransId($this->getTransactionId()); + + return $this; + } + + public function cancel(Varien_Object $payment) + { + $payment->setStatus(self::STATUS_DECLINED); + return $this; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/controllers/SystemController.php b/app/code/core/Mage/Paybox/controllers/SystemController.php new file mode 100644 index 0000000000..b454e68d46 --- /dev/null +++ b/app/code/core/Mage/Paybox/controllers/SystemController.php @@ -0,0 +1,364 @@ + + */ +class Mage_Paybox_SystemController extends Mage_Core_Controller_Front_Action +{ + protected $_payboxResponse = null; + + protected $_responseStatus = false; + + /** + * seting response after returning from paybox + * + * @param array $response + * @return object $this + */ + protected function setPayboxResponse($response) + { + if (count($response)) { + $this->_payboxResponse = $response; + } + return $this; + } + + /** + * Get System Model + * + * @return Mage_Paybox_Model_System + */ + public function getModel() + { + return Mage::getSingleton('paybox/system'); + } + + /** + * Get Checkout Singleton + * + * @return Mage_Checkout_Model_Session + */ + public function getCheckout() + { + return Mage::getSingleton('checkout/session'); + } + + /** + * Redirect action. Redirect customer to Paybox + * + */ + public function redirectAction() + { + $session = $this->getCheckout(); + $session->setPayboxQuoteId($session->getQuoteId()); + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($session->getLastRealOrderId()); + $order->addStatusToHistory($order->getStatus(), $this->__('Customer was redirected to Paybox')); + $order->save(); + + $session->setPayboxOrderId(Mage::helper('core')->encrypt($session->getLastRealOrderId())); + $session->setPayboxPaymentAction( + $order->getPayment()->getMethodInstance()->getPaymentAction() + ); + + $this->getResponse()->setBody( + $this->getLayout() + ->createBlock('paybox/system_redirect') + ->setOrder($order) + ->toHtml() + ); + + $session->unsQuoteId(); + } + + /** + * Customer returning to this action if payment was successe + */ + public function successAction() + { + $this->setPayboxResponse($this->getRequest()->getParams()); + if ($this->_checkResponse()) { + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($this->_payboxResponse['ref']); + + if (!$order->getId()) { + Mage::throwException($this->__('There are no order.')); + } + + if (Mage::helper('core')->decrypt($this->getCheckout()->getPayboxOrderId()) != $this->_payboxResponse['ref']) { + Mage::throwException($this->__('Order is not match.')); + } + $this->getCheckout()->unsPayboxOrderId(); + + if (($order->getBaseGrandTotal()*100) != $this->_payboxResponse['amount']) { + Mage::throwException($this->__('Amount is not match.')); + } + + if ($this->_payboxResponse['error'] == '00000') { + $order->addStatusToHistory($order->getStatus(), $this->__('Customer successfully returned from Paybox')); + + $redirectTo = 'checkout/onepage/success'; + if ($this->getCheckout()->getPayboxPaymentAction() == Mage_Paybox_Model_System::PBX_PAYMENT_ACTION_ATHORIZE_CAPTURE) { + $this->getCheckout()->unsPayboxPaymentAction(); + $order->getPayment() + ->getMethodInstance() + ->setTransactionId($this->_payboxResponse['trans']); + if ($this->_createInvoice($order)) { + $order->addStatusToHistory($order->getStatus(), $this->__('Invoice was create successfully')); + } else { + $order->addStatusToHistory($order->getStatus(), $this->__('Cann\'t create invoice')); + $redirectTo = '*/*/failure'; + } + } + + $session = $this->getCheckout(); + $session->setQuoteId($session->getPayboxQuoteId(true)); + $session->getQuote()->setIsActive(false)->save(); + $session->unsPayboxQuoteId(); + } else { + $redirectTo = '*/*/failure'; + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), $this->__('Customer was rejected by Paybox')); + } + + $order->sendNewOrderEmail(); + $order->save(); + + $this->_redirect($redirectTo); + } else { + $this->norouteAction(); + return; + } + } + + /** + * Action when payment was declined by Paybox + */ + public function refuseAction() + { + $this->setPayboxResponse($this->getRequest()->getParams()); + if ($this->_checkResponse()) { + $this->getCheckout()->unsPayboxQuoteId(); + $this->getCheckout()->setPayboxErrorMessage('Order was canceled by Paybox'); + + $order = Mage::getModel('sales/order') + ->loadByIncrementId($this->_payboxResponse['ref']); + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), $this->__('Customer was refuse by Paybox')); + $order->save(); + + $this->_redirect('*/*/failure'); + } else { + $this->norouteAction(); + return; + } + } + + /** + * Action when customer cancele payment or press button to back to shop + */ + public function declineAction() + { + $this->setPayboxResponse($this->getRequest()->getParams()); + if ($this->_checkResponse()) { + + $order = Mage::getModel('sales/order') + ->loadByIncrementId($this->_payboxResponse['ref']); + $order->cancel(); + $order->addStatusToHistory($order->getStatus(), $this->__('Order was canceled by customer')); + $order->save(); + + $session = $this->getCheckout(); + $session->setQuoteId($session->getPayboxQuoteId(true)); + $session->getQuote()->setIsActive(false)->save(); + $session->unsPayboxQuoteId(); + + $this->_redirect('checkout/cart'); + } else { + $this->norouteAction(); + return; + } + } + + /** + * Redirect action. Redirect to Paybox using commandline mode + * + */ + public function commandlineAction() + { + $session = $this->getCheckout(); + $session->setPayboxQuoteId($session->getQuoteId()); + + $order = Mage::getModel('sales/order') + ->loadByIncrementId($this->getCheckout()->getLastRealOrderId()); + $order->addStatusToHistory( + $order->getStatus(), $this->__('Customer was redirected to Paybox using \'command line\' mode') + ); + $order->save(); + + $session->setPayboxOrderId(Mage::helper('core')->encrypt($session->getLastRealOrderId())); + $session->setPayboxPaymentAction( + $order->getPayment()->getMethodInstance()->getPaymentAction() + ); + + $session->unsQuoteId(); + + $payment = $order->getPayment()->getMethodInstance(); + $fieldsArr = $payment->getFormFields(); + $paramStr = ''; + foreach ($fieldsArr as $k => $v) { + $paramStr .= $k.'='.$v.' '; + } + + $paramStr = str_replace(';', '\;', $paramStr); + $result = shell_exec(Mage::getBaseDir().'/'.$this->getModel()->getPayboxFile().' '.$paramStr); + + if (isset($fieldsArr['PBX_PING']) && $fieldsArr['PBX_PING'] == '1') { + $fieldsArr['PBX_PING'] = '0'; + $fieldsArr['PBX_PAYBOX'] = trim(substr($result, strpos($result, 'http'))); + $paramStr = ''; + foreach ($fieldsArr as $k => $v) { + $paramStr .= $k.'='.$v.' '; + } + + $paramStr = str_replace(';', '\;', $paramStr); + $result = shell_exec(Mage::getBaseDir().'/'.$this->getModel()->getPayboxFile().' '.$paramStr); + } + + $this->loadLayout(false); + $this->getResponse()->setBody($result); + $this->renderLayout(); + } + + /** + * Error action. If request params to Paybox has mistakes + * + */ + public function errorAction() + { + if (!$this->getCheckout()->getPayboxQuoteId()) { + $this->norouteAction(); + return; + } + + $session = $this->getCheckout(); + $session->setQuoteId($session->getPayboxQuoteId(true)); + $session->getQuote()->setIsActive(false)->save(); + $session->unsPayboxQuoteId(); + + if (!$this->getRequest()->getParam('NUMERR')) { + $this->norouteAction(); + return; + } + + $this->loadLayout(); + + $this->getCheckout() + ->setPayboxErrorNumber( + $this->getRequest()->getParam('NUMERR') + ); + + $this->renderLayout(); + } + + /** + * Failure action. + * Displaying information if customer was redirecting to cancel or decline actions + * + */ + public function failureAction() + { + if (!$this->getCheckout()->getPayboxErrorMessage()) { + $this->norouteAction(); + return; + } + + $this->loadLayout(); + $this->renderLayout(); + } + + /** + * Checking response and Paybox session variables + * + * @return unknown + */ + protected function _checkResponse() + { + if (!$this->getCheckout()->getPayboxQuoteId()) { + $this->norouteAction(); + return; + } + + if (!$this->getCheckout()->getPayboxOrderId()) { + $this->norouteAction(); + return; + } + + if (!$this->getCheckout()->getPayboxPaymentAction()) { + $this->norouteAction(); + return; + } + + if (!$this->_payboxResponse) { + return false; + } + + //check for valid response + if ($this->getModel()->checkResponse($this->_payboxResponse)) { + return true; + } + + return true; + } + + /** + * Creating invoice + * + * @param Mage_Sales_Model_Order $order + * @return bool + */ + protected function _createInvoice(Mage_Sales_Model_Order $order) + { + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/etc/config.xml b/app/code/core/Mage/Paybox/etc/config.xml new file mode 100644 index 0000000000..a8407c1809 --- /dev/null +++ b/app/code/core/Mage/Paybox/etc/config.xml @@ -0,0 +1,133 @@ + + + + + + 0.1.3 + + + + + + Mage_Paybox_Model + paybox_mysql4 + + + Mage_Paybox_Model_Mysql4 + + paybox_api_debug
      + paybox_question_number
      +
      +
      +
      + + + + Mage_Paybox + Mage_Paybox_Model_Mysql4_Setup + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Paybox_Block + +
      + + + + standard + + Mage_Paybox + paybox + + + + + + + + Mage_Paybox.csv + + + + + + + + + + + Mage_Paybox.csv + + + + + + + + + 1 + + + + authorize + E + FRA + paybox/system + Paybox System + processing + 0 + + + + + + authorize + paybox/direct + Paybox Direct + 1 + processing + AE,VI,MC,DI + 0 + + + +
      diff --git a/app/code/core/Mage/Paybox/etc/currency.xml b/app/code/core/Mage/Paybox/etc/currency.xml new file mode 100644 index 0000000000..7f8fc2000c --- /dev/null +++ b/app/code/core/Mage/Paybox/etc/currency.xml @@ -0,0 +1,209 @@ + + + + + 971 + 008 + 012 + 840 + 978 + 973 + 951 + 032 + 051 + 533 + 036 + 944 + 044 + 048 + 050 + 052 + 974 + 084 + 952 + 060 + 356 + 064 + 068 + 984 + 977 + 072 + 578 + 986 + 096 + 975 + 108 + 116 + 950 + 124 + 132 + 136 + 152 + 156 + 170 + 174 + 976 + 554 + 188 + 191 + 192 + 203 + 208 + 262 + 214 + 818 + 222 + 232 + 233 + 230 + 238 + 242 + 953 + 270 + 981 + 936 + 292 + 320 + 324 + 328 + 332 + 340 + 344 + 348 + 352 + 360 + 960 + 364 + 368 + 376 + 388 + 392 + 400 + 398 + 404 + 408 + 410 + 414 + 417 + 418 + 428 + 422 + 710 + 426 + 430 + 434 + 756 + 440 + 446 + 807 + 969 + 454 + 458 + 462 + 478 + 480 + 484 + 979 + 498 + 496 + 504 + 943 + 104 + 516 + 524 + 532 + 558 + 566 + 512 + 586 + 590 + 598 + 600 + 604 + 608 + 985 + 634 + 946 + 643 + 646 + 654 + 882 + 678 + 682 + 941 + 690 + 694 + 702 + 703 + 090 + 706 + 144 + 938 + 968 + 748 + 752 + 948 + 947 + 760 + 901 + 972 + 834 + 764 + 776 + 780 + 788 + 949 + 795 + 800 + 980 + 784 + 826 + 998 + 997 + 858 + 940 + 860 + 548 + 937 + 704 + 886 + 894 + 716 + 959 + 955 + 956 + 957 + 958 + 964 + 962 + 961 + Nil + Nil + 963 + 999 + diff --git a/app/code/core/Mage/Paybox/etc/system.xml b/app/code/core/Mage/Paybox/etc/system.xml new file mode 100644 index 0000000000..8f19c7f2e6 --- /dev/null +++ b/app/code/core/Mage/Paybox/etc/system.xml @@ -0,0 +1,459 @@ + + + + + + + + + text + 101 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + + text + 5 + 1 + 1 + 0 + + + + select + paybox/source_paymentMode + 10 + 1 + 1 + 0 + + + + select + paybox/source_paymentAction + 15 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 20 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 30 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 40 + 1 + 1 + 0 + + + + Only if you HTML From mode selected + select + paybox/source_managementMode + 50 + 1 + 1 + 0 + + + + Only if you HTML From mode selected + text + 60 + 1 + 1 + 0 + + + + Only if you HTML From mode selected + text + 70 + 1 + 1 + 0 + + + + Only if you HTML From mode selected + image + adminhtml/system_config_backend_image + payment/paybox/bkgd + payment/paybox/bkgd + 80 + 1 + 1 + 0 + + + + Only if you HTML From mode selected + text + 90 + 1 + 1 + 0 + + + + select + paybox/source_language + 100 + 1 + 1 + 0 + + + + select + paybox/source_paymentType + 105 + 1 + 1 + 0 + + + + select + paybox/adminhtml_cart_type + paybox/source_cartType + 108 + 1 + 1 + 0 + + + + Specify the URL of the primary payment server if different from default + text + 110 + 1 + 1 + 0 + + + + Time-out (in seconds) to check the avalaibility of the primary payment server (Default 8 seconds) + text + 115 + 1 + 1 + 0 + + + + Specify the URL of the primary backup payment server if different from default + text + 120 + 1 + 1 + 0 + + + + Time-out (in seconds) to check the avalaibility of the primary backup payment server (Default 10 seconds) + text + 125 + 1 + 1 + 0 + + + + Specify the URL of the second backup payment server if different from default + text + 130 + 1 + 1 + 0 + + + + Time-out (in seconds) to check the avalaibility of the second backup payment server (Default 20 seconds) + text + 135 + 1 + 1 + 0 + + + + Specify the URL of the third backup payment server if different from default + text + 140 + 1 + 1 + 0 + + + + Time-out (in seconds) to check the avalaibility of the third backup payment server (Default 20 seconds) + text + 145 + 1 + 1 + 0 + + + + Only with 'Command Line' mode + select + adminhtml/system_config_source_yesno + 160 + 1 + 1 + 0 + + + + TCP Port used to check the availability of a payment server if Ping Flag enabled (Default port is 443) + text + 170 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 300 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>310</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + select + adminhtml/system_config_source_order_status_processing + 320 + 1 + 1 + 0 + + + + text + 500 + 1 + 1 + 0 + + + + allowspecific + 330 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 340 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + + + text + 101 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 20 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 30 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 40 + 1 + 1 + 0 + + + + text + 50 + 1 + 1 + 0 + + + + text + 60 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 70 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>80</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + select + adminhtml/system_config_source_order_status_processing + 90 + 1 + 1 + 0 + + + + select + paybox/source_paymentAction + 95 + 1 + 1 + 0 + + + + multiselect + paybox/source_cctype + 100 + 1 + 1 + 0 + + + + allowspecific + 110 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 120 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 500 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-install-0.1.0.php new file mode 100644 index 0000000000..cdfc810e60 --- /dev/null +++ b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-install-0.1.0.php @@ -0,0 +1,47 @@ +startSetup(); + +$installer->run(" + +DROP TABLE IF EXISTS `{$this->getTable('paybox/api_debug')}`; +CREATE TABLE `{$this->getTable('paybox/api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `real_order_id` varchar(50)NOT NULL default '', + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.0-0.1.1.php b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.0-0.1.1.php new file mode 100644 index 0000000000..dd3e7058c6 --- /dev/null +++ b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.0-0.1.1.php @@ -0,0 +1,33 @@ +startSetup(); + +$installer->addAttribute('order_payment', 'paybox_request_number', array()); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.1-0.1.2.php b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.1-0.1.2.php new file mode 100644 index 0000000000..56139488ab --- /dev/null +++ b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.1-0.1.2.php @@ -0,0 +1,44 @@ +startSetup(); + +$installer->run(" + +DROP TABLE IF EXISTS `{$this->getTable('paybox/question_number')}`; +CREATE TABLE `{$this->getTable('paybox/question_number')}` ( + `account_id` int(10) unsigned NOT NULL auto_increment, + `account_hash` varchar(50) NOT NULL default '', + `increment_value` int(10) unsigned NOT NULL default 1, + `reset_date` timestamp NOT NULL default CURRENT_TIMESTAMP, + PRIMARY KEY (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +"); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.2-0.1.3.php b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.2-0.1.3.php new file mode 100644 index 0000000000..1b425ad932 --- /dev/null +++ b/app/code/core/Mage/Paybox/sql/paybox_setup/mysql4-upgrade-0.1.2-0.1.3.php @@ -0,0 +1,33 @@ +startSetup(); + +$installer->addAttribute('order_payment', 'paybox_question_number', array()); + +$installer->endSetup(); \ No newline at end of file diff --git a/app/code/core/Mage/Paygate/Model/Authorizenet.php b/app/code/core/Mage/Paygate/Model/Authorizenet.php index ea1298c895..361c78afe8 100644 --- a/app/code/core/Mage/Paygate/Model/Authorizenet.php +++ b/app/code/core/Mage/Paygate/Model/Authorizenet.php @@ -102,53 +102,49 @@ public function canUseForCurrency($currencyCode) /** * Send authorize request to gateway * - * @param Varien_Object $payment - * @param decimal $amount - * @return Mage_Paygate_Model_Authorizenet + * @param Varien_Object $payment + * @param decimal $amount + * @return Mage_Paygate_Model_Authorizenet + * @throws Mage_Core_Exception */ public function authorize(Varien_Object $payment, $amount) { - $error = false; - - if($amount>0){ - $payment->setAnetTransType(self::REQUEST_TYPE_AUTH_ONLY); - $payment->setAmount($amount); - - $request= $this->_buildRequest($payment); - $result = $this->_postRequest($request); - - $payment->setCcApproval($result->getApprovalCode()) - ->setLastTransId($result->getTransactionId()) - ->setCcTransId($result->getTransactionId()) - ->setCcAvsStatus($result->getAvsResultCode()) - ->setCcCidStatus($result->getCardCodeResponseCode()); - - switch ($result->getResponseCode()) { - case self::RESPONSE_CODE_APPROVED: - $payment->setStatus(self::STATUS_APPROVED); - break; - case self::RESPONSE_CODE_DECLINED: - $error = Mage::helper('paygate')->__('Payment authorization transaction has been declined.'); - break; - default: - $error = Mage::helper('paygate')->__('Payment authorization error.'); - break; - } - }else{ - $error = Mage::helper('paygate')->__('Invalid amount for authorization.'); + if ($amount <= 0) { + Mage::throwException(Mage::helper('paygate')->__('Invalid amount for authorization.')); } + $payment->setAnetTransType(self::REQUEST_TYPE_AUTH_ONLY); + $payment->setAmount($amount); + + $request= $this->_buildRequest($payment); + $result = $this->_postRequest($request); - if ($error !== false) { - Mage::throwException($error); + $payment->setCcApproval($result->getApprovalCode()) + ->setLastTransId($result->getTransactionId()) + ->setCcTransId($result->getTransactionId()) + ->setCcAvsStatus($result->getAvsResultCode()) + ->setCcCidStatus($result->getCardCodeResponseCode()); + + switch ($result->getResponseCode()) { + case self::RESPONSE_CODE_APPROVED: + $payment->setStatus(self::STATUS_APPROVED); + return $this; + case self::RESPONSE_CODE_DECLINED: + Mage::throwException(Mage::helper('paygate')->__('Payment authorization transaction has been declined.')); + default: + Mage::throwException(Mage::helper('paygate')->__('Payment authorization error.')); } - return $this; } - + /** + * Send capture request to gateway + * + * @param Varien_Object $payment + * @param decimal $amount + * @return Mage_Paygate_Model_Authorizenet + * @throws Mage_Core_Exception + */ public function capture(Varien_Object $payment, $amount) { - $error = false; - if ($payment->getCcTransId()) { $payment->setAnetTransType(self::REQUEST_TYPE_PRIOR_AUTH_CAPTURE); } else { @@ -164,68 +160,50 @@ public function capture(Varien_Object $payment, $amount) $payment->setStatus(self::STATUS_APPROVED); //$payment->setCcTransId($result->getTransactionId()); $payment->setLastTransId($result->getTransactionId()); + return $this; } - else { - if ($result->getResponseReasonText()) { - $error = $result->getResponseReasonText(); - } - else { - $error = Mage::helper('paygate')->__('Error in capturing the payment'); - } - } - - if ($error !== false) { - Mage::throwException($error); + if ($result->getResponseReasonText()) { + Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText())); } - - return $this; + Mage::throwException(Mage::helper('paygate')->__('Error in capturing the payment')); } /** - * void + * Void the payment through gateway * - * @author Magento Core Team - * @access public - * @param string $payment Varien_Object object - * @return Mage_Payment_Model_Abstract + * @param Varien_Object $payment + * @return Mage_Paygate_Model_Authorizenet + * @throws Mage_Core_Exception */ public function void(Varien_Object $payment) { - $error = false; - if($payment->getVoidTransactionId()){ + if ($payment->getVoidTransactionId()) { $payment->setAnetTransType(self::REQUEST_TYPE_VOID); $request = $this->_buildRequest($payment); $request->setXTransId($payment->getVoidTransactionId()); $result = $this->_postRequest($request); - if($result->getResponseCode()==self::RESPONSE_CODE_APPROVED){ + if ($result->getResponseCode()==self::RESPONSE_CODE_APPROVED) { $payment->setStatus(self::STATUS_SUCCESS ); + return $this; } - else{ - $payment->setStatus(self::STATUS_ERROR); - $error = $result->getResponseReasonText(); - } - }else{ $payment->setStatus(self::STATUS_ERROR); - $error = Mage::helper('paygate')->__('Invalid transaction id'); + Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText())); } - if ($error !== false) { - Mage::throwException($error); - } - return $this; + $payment->setStatus(self::STATUS_ERROR); + Mage::throwException(Mage::helper('paygate')->__('Invalid transaction id')); } /** * refund the amount with transaction id * - * @access public * @param string $payment Varien_Object object - * @return Mage_Payment_Model_Abstract + * @return Mage_Paygate_Model_Authorizenet + * @throws Mage_Core_Exception */ public function refund(Varien_Object $payment, $amount) { - $error = false; - if ($payment->getRefundTransactionId() && $amount>0) { + if ($payment->getRefundTransactionId() && $amount > 0) { $payment->setAnetTransType(self::REQUEST_TYPE_CREDIT); $request = $this->_buildRequest($payment); $request->setXTransId($payment->getRefundTransactionId()); @@ -233,18 +211,11 @@ public function refund(Varien_Object $payment, $amount) if ($result->getResponseCode()==self::RESPONSE_CODE_APPROVED) { $payment->setStatus(self::STATUS_SUCCESS); - } else { - $error = $result->getResponseReasonText(); + return $this; } - - } else { - $error = Mage::helper('paygate')->__('Error in refunding the payment'); + Mage::throwException($this->_wrapGatewayError($result->getResponseReasonText())); } - - if ($error !== false) { - Mage::throwException($error); - } - return $this; + Mage::throwException(Mage::helper('paygate')->__('Error in refunding the payment')); } /** @@ -408,9 +379,7 @@ protected function _postRequest(Varien_Object $request) ->setResultDump(print_r($result->getData(),1)) ->save(); } - Mage::throwException( - Mage::helper('paygate')->__('Gateway request error: %s', $e->getMessage()) - ); + Mage::throwException($this->_wrapGatewayError($e->getMessage())); } $responseBody = $response->getBody(); @@ -450,4 +419,15 @@ protected function _postRequest(Varien_Object $request) return $result; } + + /** + * Gateway response wrapper + * + * @param string $text + * @return string + */ + protected function _wrapGatewayError($text) + { + return Mage::helper('paygate')->__('Gateway error: %s', $text); + } } diff --git a/app/code/core/Mage/Paygate/Model/Payflow/Pro.php b/app/code/core/Mage/Paygate/Model/Payflow/Pro.php index 1aaca5cc14..d31aec6a02 100644 --- a/app/code/core/Mage/Paygate/Model/Payflow/Pro.php +++ b/app/code/core/Mage/Paygate/Model/Payflow/Pro.php @@ -129,6 +129,21 @@ public function authorize(Varien_Object $payment, $amount) return $this; } + /** + * Check capture availability + * To avoid capture already voided transactions, allow only one capture thus the method + * cannot make capture partially + * + * @return bool + */ + public function canCapture() + { + if ($this->getInfoInstance()->getOrder()->getBaseSubtotalInvoiced() > 0) { + return false; + } + return true; + } + public function capture(Varien_Object $payment, $amount) { $error = false; diff --git a/app/code/core/Mage/Payment/Model/Info.php b/app/code/core/Mage/Payment/Model/Info.php index d3c01caf95..709ae496cd 100644 --- a/app/code/core/Mage/Payment/Model/Info.php +++ b/app/code/core/Mage/Payment/Model/Info.php @@ -73,7 +73,7 @@ public function getMethodInstance() } else { return $this->_getData('method_instance'); } - Mage::throwException(Mage::helper('payment')->__('Can not retrieve payment method instance')); + Mage::throwException(Mage::helper('payment')->__('Cannot retrieve payment method instance')); } /** diff --git a/app/code/core/Mage/Payment/Model/Method/Cc.php b/app/code/core/Mage/Payment/Model/Method/Cc.php index a7b6c0f822..0311ccbf2d 100644 --- a/app/code/core/Mage/Payment/Model/Method/Cc.php +++ b/app/code/core/Mage/Payment/Model/Method/Cc.php @@ -144,7 +144,7 @@ public function validate() if (!$info->getCcCid() || !$regExp || !preg_match($regExp ,$info->getCcCid())){ $errorMsg = $this->_getHelper()->__('Please enter a valid credit card verification number.'); } - } + } if($errorMsg){ Mage::throwException($errorMsg); @@ -170,7 +170,7 @@ public function getVerificationRegEx() 'MC' => '/^[0-9]{3}$/', // Master Card 'AE' => '/^[0-9]{4}$/', // American Express 'DI' => '/^[0-9]{3}$/', // Discovery - 'SS' => '/^[0-9]{4}$/', + 'SS' => '/^[0-9]{3,4}$/', 'OT' => '/^[0-9]{3,4}$/' ); return $verificationExpList; @@ -184,7 +184,7 @@ protected function _validateExpDate($expYear, $expMonth) } return true; } - + public function OtherCcType($type) { return $type=='OT'; diff --git a/app/code/core/Mage/Paypal/Model/Standard.php b/app/code/core/Mage/Paypal/Model/Standard.php index e77e3d8453..a5c7e2e50a 100644 --- a/app/code/core/Mage/Paypal/Model/Standard.php +++ b/app/code/core/Mage/Paypal/Model/Standard.php @@ -389,12 +389,6 @@ public function ipnPostSubmit() } */ - // get from config order status to be set - $newOrderStatus = $this->getConfigData('order_status', $order->getStoreId()); - if (empty($newOrderStatus)) { - $newOrderStatus = $order->getStatus(); - } - /* if payer_status=verified ==> transaction in sale mode if transactin in sale mode, we need to create an invoice @@ -426,6 +420,10 @@ public function ipnPostSubmit() ); } } else { + $newOrderStatus = $this->getConfigData('order_status', $order->getStoreId()); + if (empty($newOrderStatus)) { + $newOrderStatus = true; + } $order->setState( Mage_Sales_Model_Order::STATE_PROCESSING, $newOrderStatus, Mage::helper('paypal')->__('Received IPN verification'), diff --git a/app/code/core/Mage/PaypalUk/Block/Direct/Form.php b/app/code/core/Mage/PaypalUk/Block/Direct/Form.php index 499054d453..a5c0d36d70 100644 --- a/app/code/core/Mage/PaypalUk/Block/Direct/Form.php +++ b/app/code/core/Mage/PaypalUk/Block/Direct/Form.php @@ -19,26 +19,21 @@ * needs please refer to http://www.magentocommerce.com for more information. * * @category Mage - * @package Mage_Paypal + * @package Mage_PaypalUk * @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) */ - +/** + * UK domestic cards specific information block + */ class Mage_PaypalUk_Block_Direct_Form extends Mage_Payment_Block_Form_Cc { - protected function _construct() - { - parent::_construct(); - $this->setTemplate('paypaluk/direct/form.phtml'); - } - protected function _getDirect() { return Mage::getSingleton('paypaluk/direct'); } - /** * Retrieve availables credit card types * @@ -93,4 +88,19 @@ public function hasSsCardType() return false; } + /** + * Add UK domestic cards additional fields as child block + * + * Forks a clone, but with a different form + * + * @return Mage_PaypalUk_Block_Direct_Form + */ + public function _beforeToHtml() + { + $child = clone $this; + $this->setChild('uk_domestic', + $child->setTemplate('paypaluk/direct/form.phtml') + ); + return parent::_beforeToHtml(); + } } \ No newline at end of file diff --git a/app/code/core/Mage/PaypalUk/controllers/ExpressController.php b/app/code/core/Mage/PaypalUk/controllers/ExpressController.php index caff3eacda..858b27d311 100644 --- a/app/code/core/Mage/PaypalUk/controllers/ExpressController.php +++ b/app/code/core/Mage/PaypalUk/controllers/ExpressController.php @@ -71,6 +71,11 @@ public function shortcutAction() $this->getResponse()->setRedirect($this->getExpress()->getRedirectUrl()); } + public function editAction() + { + $this->getResponse()->setRedirect($this->getExpress()->getApi()->getPaypalUrl()); + } + /* * when a user click on cancel on paypal need to redirect them to shopping cart */ diff --git a/app/code/core/Mage/PaypalUk/etc/config.xml b/app/code/core/Mage/PaypalUk/etc/config.xml index 917a58142a..8199705649 100644 --- a/app/code/core/Mage/PaypalUk/etc/config.xml +++ b/app/code/core/Mage/PaypalUk/etc/config.xml @@ -78,7 +78,7 @@ SS - Switch/Solo + Maestro/Solo 40 diff --git a/app/code/core/Mage/Protx/Block/Standard/Failure.php b/app/code/core/Mage/Protx/Block/Standard/Failure.php new file mode 100644 index 0000000000..79250b8a17 --- /dev/null +++ b/app/code/core/Mage/Protx/Block/Standard/Failure.php @@ -0,0 +1,56 @@ + + */ + +class Mage_Protx_Block_Standard_Failure extends Mage_Core_Block_Template +{ + /** + * Return StatusDetail field value from Response + * + * @return string + */ + public function getErrorMessage () + { + $error = Mage::getSingleton('checkout/session')->getErrorMessage(); + Mage::getSingleton('checkout/session')->unsErrorMessage(); + return $error; + } + + /** + * Get continue shopping url + */ + public function getContinueShoppingUrl() + { + return Mage::getUrl('checkout/cart'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Block/Standard/Form.php b/app/code/core/Mage/Protx/Block/Standard/Form.php new file mode 100644 index 0000000000..eae3318aec --- /dev/null +++ b/app/code/core/Mage/Protx/Block/Standard/Form.php @@ -0,0 +1,43 @@ + + */ + +class Mage_Protx_Block_Standard_Form extends Mage_Payment_Block_Form +{ + protected function _construct() + { + $this->setTemplate('protx/standard/form.phtml'); + parent::_construct(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Block/Standard/Redirect.php b/app/code/core/Mage/Protx/Block/Standard/Redirect.php new file mode 100644 index 0000000000..fffc3518cb --- /dev/null +++ b/app/code/core/Mage/Protx/Block/Standard/Redirect.php @@ -0,0 +1,58 @@ + + */ + +class Mage_Protx_Block_Standard_Redirect extends Mage_Core_Block_Abstract +{ + protected function _toHtml() + { + $standard = Mage::getModel('protx/standard'); + $form = new Varien_Data_Form(); + $form->setAction($standard->getProtxUrl()) + ->setId('protx_standard_checkout') + ->setName('protx_standard_checkout') + ->setMethod('POST') + ->setUseContainer(true); + foreach ($standard->setOrder($this->getOrder())->getStandardCheckoutFormFields() as $field => $value) { + $form->addField($field, 'hidden', array('name' => $field, 'value' => $value)); + } + $html = ''; + $html.= $this->__('You will be redirected to protx in a few seconds.'); + $html.= $form->toHtml(); + $html.= ''; + $html.= ''; + + return $html; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Helper/Data.php b/app/code/core/Mage/Protx/Helper/Data.php new file mode 100644 index 0000000000..01b46bd4b2 --- /dev/null +++ b/app/code/core/Mage/Protx/Helper/Data.php @@ -0,0 +1,38 @@ + + */ +class Mage_Protx_Helper_Data extends Mage_Core_Helper_Abstract +{ + +} diff --git a/app/code/core/Mage/Protx/Model/Api/Debug.php b/app/code/core/Mage/Protx/Model/Api/Debug.php new file mode 100644 index 0000000000..18fa88127c --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Api/Debug.php @@ -0,0 +1,41 @@ + + */ +class Mage_Protx_Model_Api_Debug extends Mage_Core_Model_Abstract +{ + protected function _construct() + { + $this->_init('protx/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Model/Config.php b/app/code/core/Mage/Protx/Model/Config.php new file mode 100644 index 0000000000..874ea9eda5 --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Config.php @@ -0,0 +1,179 @@ + + */ + +class Mage_Protx_Model_Config extends Varien_Object +{ + const MODE_SIMULATOR = 'SIMULATOR'; + const MODE_TEST = 'TEST'; + const MODE_LIVE = 'LIVE'; + + const PAYMENT_TYPE_PAYMENT = 'PAYMENT'; + const PAYMENT_TYPE_DEFERRED = 'DEFERRED'; + const PAYMENT_TYPE_AUTHENTICATE = 'AUTHENTICATE'; + const PAYMENT_TYPE_AUTHORISE = 'AUTHORISE'; + + + /** + * Return config var + * + * @param string Var key + * @param string Default value for non-existing key + * @return mixed + */ + public function getConfigData($key, $default=false) + { + if (!$this->hasData($key)) { + $value = Mage::getStoreConfig('payment/protx_standard/'.$key); + if (is_null($value) || false===$value) { + $value = $default; + } + $this->setData($key, $value); + } + return $this->getData($key); + } + + /** + * Return Protocol version + * + * @return string Protocol version + */ + public function getVersion () + { + return '2.22'; + } + + /** + * Return Store description sent to Protx + * + * @return string Description + */ + public function getDescription () + { + return $this->getConfigData('description'); + } + + /** + * Return Protx registered merchant account name + * + * @return string Merchant account name + */ + public function getVendorName () + { + return $this->getConfigData('vendor_name'); + } + + /** + * Return Protx merchant password + * + * @return string Merchant password + */ + public function getVendorPassword () + { + return $this->getConfigData('vendor_password'); + } + + /** + * Return preferred payment type (see SELF::PAYMENT_TYPE_* constants) + * + * @return string payment type + */ + public function getPaymentType () + { + return $this->getConfigData('payment_action'); + } + + /** + * Return working mode (see SELF::MODE_* constants) + * + * @return string Working mode + */ + public function getMode () + { + return $this->getConfigData('mode'); + } + + /** + * Return new order status + * + * @return string New order status + */ + public function getNewOrderStatus () + { + return $this->getConfigData('order_status'); + } + + /** + * Return debug flag + * + * @return boolean Debug flag (0/1) + */ + public function getDebug () + { + return $this->getConfigData('debug_flag'); + } + + /** + * Return key for simple XOR crypt, using Vendor encrypted password by Protx + * + * @return string Key for simple XOR crypt + */ + public function getCryptKey () + { + return $this->getVendorPassword(); + } + + /** + * Returns status of vendore notification + * + * @return bool + */ + public function getVendorNotification() + { + return $this->getConfigData('vendor_notification'); + } + + /** + * Returns status of vendore email + * + * @return bool + */ + public function getVendorEmail() + { + if ($email = $this->getConfigData('vendor_email')) { + return $email; + } else { + return Mage::getStoreConfig('trans_email/ident_general/email'); + } + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Model/Mysql4/Api/Debug.php b/app/code/core/Mage/Protx/Model/Mysql4/Api/Debug.php new file mode 100644 index 0000000000..0d2e690ee2 --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Mysql4/Api/Debug.php @@ -0,0 +1,42 @@ + + */ + +class Mage_Protx_Model_Mysql4_Api_Debug extends Mage_Core_Model_Mysql4_Abstract +{ + protected function _construct() + { + $this->_init('protx/api_debug', 'transaction_id'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Model/Mysql4/Api/Debug/Collection.php b/app/code/core/Mage/Protx/Model/Mysql4/Api/Debug/Collection.php new file mode 100644 index 0000000000..ecdaea09bf --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Mysql4/Api/Debug/Collection.php @@ -0,0 +1,41 @@ + + */ +class Mage_Protx_Model_Mysql4_Api_Debug_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract +{ + protected function _construct() + { + $this->_init('protx/api_debug'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Model/Mysql4/Setup.php b/app/code/core/Mage/Protx/Model/Mysql4/Setup.php new file mode 100644 index 0000000000..6cfe88156d --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Mysql4/Setup.php @@ -0,0 +1,39 @@ + + */ + +class Mage_Protx_Model_Mysql4_Setup extends Mage_Sales_Model_Mysql4_Setup +{ + +} diff --git a/app/code/core/Mage/Protx/Model/Session.php b/app/code/core/Mage/Protx/Model/Session.php new file mode 100644 index 0000000000..90f3f5f940 --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Session.php @@ -0,0 +1,42 @@ + + */ + +class Mage_Protx_Model_Session extends Mage_Core_Model_Session_Abstract +{ + public function __construct() + { + $this->init('protx'); + } +} diff --git a/app/code/core/Mage/Protx/Model/Source/ModeAction.php b/app/code/core/Mage/Protx/Model/Source/ModeAction.php new file mode 100644 index 0000000000..e8165b89c5 --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Source/ModeAction.php @@ -0,0 +1,49 @@ + + */ + +class Mage_Protx_Model_Source_ModeAction +{ + public function toOptionArray() + { + return array( + array('value' => Mage_Protx_Model_Config::MODE_SIMULATOR, 'label' => Mage::helper('protx')->__('Simulator')), + array('value' => Mage_Protx_Model_Config::MODE_TEST, 'label' => Mage::helper('protx')->__('Test')), + array('value' => Mage_Protx_Model_Config::MODE_LIVE, 'label' => Mage::helper('protx')->__('Live')), + ); + } +} + + + diff --git a/app/code/core/Mage/Protx/Model/Source/PaymentAction.php b/app/code/core/Mage/Protx/Model/Source/PaymentAction.php new file mode 100644 index 0000000000..2db2366dce --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Source/PaymentAction.php @@ -0,0 +1,46 @@ + + */ + +class Mage_Protx_Model_Source_PaymentAction +{ + public function toOptionArray() + { + return array( + array('value' => Mage_Protx_Model_Config::PAYMENT_TYPE_PAYMENT, 'label' => Mage::helper('protx')->__('PAYMENT')), + array('value' => Mage_Protx_Model_Config::PAYMENT_TYPE_DEFERRED, 'label' => Mage::helper('protx')->__('DEFERRED')), + array('value' => Mage_Protx_Model_Config::PAYMENT_TYPE_AUTHENTICATE, 'label' => Mage::helper('protx')->__('AUTHENTICATE')), + ); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/Model/Standard.php b/app/code/core/Mage/Protx/Model/Standard.php new file mode 100644 index 0000000000..ed08bf96f9 --- /dev/null +++ b/app/code/core/Mage/Protx/Model/Standard.php @@ -0,0 +1,456 @@ + + */ +class Mage_Protx_Model_Standard extends Mage_Payment_Model_Method_Abstract +{ + protected $_code = 'protx_standard'; + protected $_formBlockType = 'protx/standard_form'; + + protected $_isGateway = false; + protected $_canAuthorize = true; + protected $_canCapture = true; + protected $_canCapturePartial = false; + protected $_canRefund = false; + protected $_canVoid = false; + protected $_canUseInternal = false; + protected $_canUseCheckout = true; + protected $_canUseForMultishipping = false; + + protected $_order = null; + + + /** + * Get Config model + * + * @return object Mage_Protx_Model_Config + */ + public function getConfig() + { + return Mage::getSingleton('protx/config'); + } + + /** + * Return debug flag + * + * @return boolean + */ + public function getDebug () + { + return $this->getConfig()->getDebug(); + } + + /** + * Returns Target URL + * + * @return string Target URL + */ + public function getProtxUrl () + { + switch ($this->getConfig()->getMode()) { + case Mage_Protx_Model_Config::MODE_LIVE: + $url = 'https://ukvps.protx.com/vspgateway/service/vspform-register.vsp'; + break; + case Mage_Protx_Model_Config::MODE_TEST: + $url = 'https://ukvpstest.protx.com/vspgateway/service/vspform-register.vsp'; + break; + default: // simulator mode + $url = 'https://ukvpstest.protx.com/VSPSimulator/VSPFormGateway.asp'; + break; + } + return $url; + } + + /** + * Return URL for Protx success response + * + * @return string URL + */ + protected function getSuccessURL () + { + return Mage::getUrl('protx/standard/successresponse'); + } + + /** + * Return URL for Protx failure response + * + * @return string URL + */ + protected function getFailureURL () + { + return Mage::getUrl('protx/standard/failureresponse'); + } + + /** + * Transaction unique ID sent to Protx and sent back by Protx for order restore + * Using created order ID + * + * @return string Transaction unique number + */ + protected function getVendorTxCode () + { + return $this->getOrder()->getRealOrderId(); + } + + /** + * Returns cart formatted + * String format: + * Number of lines:Name1:Quantity1:CostNoTax1:Tax1:CostTax1:Total1:Name2:Quantity2:CostNoTax2... + * + * @return string Formatted cart items + */ + protected function getFormattedCart () + { + $items = $this->getOrder()->getAllItems(); + $resultParts = array(); + $totalLines = 0; + if ($items) { + foreach($items as $item) { + if ($item->getParentItem()) { + continue; + } + $quantity = $item->getQtyOrdered(); + + $cost = sprintf('%.2f', $item->getBasePrice() - $item->getBaseDiscountAmount()); + $tax = sprintf('%.2f', $item->getBaseTaxAmount()); + $costPlusTax = sprintf('%.2f', $cost + $tax/$quantity); + + $totalCostPlusTax = sprintf('%.2f', $quantity * $cost + $tax); + + $resultParts[] = str_replace(':', ' ', $item->getName()); + $resultParts[] = $quantity; + $resultParts[] = $cost; + $resultParts[] = $tax; + $resultParts[] = $costPlusTax; + $resultParts[] = $totalCostPlusTax; + $totalLines++; //counting actual formatted items + } + } + + // add delivery + $shipping = $this->getOrder()->getBaseShippingAmount(); + if ((int)$shipping > 0) { + $totalLines++; + $resultParts = array_merge($resultParts, array('Shipping','','','','',sprintf('%.2f', $shipping))); + } + + $result = $totalLines . ':' . implode(':', $resultParts); + return $result; + } + + /** + * Format Crypted string with all order data for request to Protx + * + * @return string Crypted string + */ + protected function getCrypted () + { + $order = $this->getOrder(); + if (!($order instanceof Mage_Sales_Model_Order)) { + Mage::throwException($this->_getHelper()->__('Cannot retrieve order object')); + } + + $shipping = $order->getShippingAddress(); + $billing = $order->getBillingAddress(); + + $amount = $order->getBaseGrandTotal(); + + $currency = $order->getBaseCurrencyCode(); + + $queryPairs = array(); + + $transactionId = $this->getVendorTxCode(); + $queryPairs['VendorTxCode'] = $transactionId; + + + $queryPairs['Amount'] = sprintf('%.2f', $amount); + $queryPairs['Currency'] = $currency; + + // Up to 100 chars of free format description + $description = $this->getConfig()->getDescription() != '' + ? $this->getConfig()->getDescription() + : Mage::app()->getStore()->getName() . ' ' . ' payment'; + $queryPairs['Description'] = $description; + + $queryPairs['SuccessURL'] = $this->getSuccessURL(); + $queryPairs['FailureURL'] = $this->getFailureURL(); + + $queryPairs['CustomerName'] = $billing->getFirstname().' '.$billing->getLastname(); + $queryPairs['CustomerEMail'] = $order->getCustomerEmail(); + $queryPairs['ContactNumber'] = $billing->getTelephone(); + $queryPairs['ContactFax'] = $billing->getFax(); + + if ($this->getConfig()->getVendorNotification()) { + $queryPairs['VendorEMail'] = $this->getConfig()->getVendorEmail(); + } else { + $queryPairs['VendorEMail'] = ''; + } + + $queryPairs['eMailMessage'] = ''; + + $queryPairs['BillingAddress'] = $billing->format('oneline'); + $queryPairs['BillingPostCode'] = $billing->getPostcode(); + + if ($shipping) { + $queryPairs['DeliveryAddress'] = $shipping->getFormated(); + $queryPairs['DeliveryPostCode'] = $shipping->getPostcode(); + } else { + $queryPairs['DeliveryAddress'] = ''; + $queryPairs['DeliveryPostCode'] = ''; + } + + $queryPairs['Basket'] = $this->getFormattedCart(); + + // For charities registered for Gift Aid + $queryPairs['AllowGiftAid'] = '0'; + + /** + * Allow fine control over AVS/CV2 checks and rules by changing this value. 0 is Default + * It can be changed dynamically, per transaction, if you wish. See the VSP Server Protocol document + */ + if ($this->getConfig()->getPaymentType() !== Mage_Protx_Model_Config::PAYMENT_TYPE_AUTHENTICATE) { + $queryPairs['ApplyAVSCV2'] = '0'; + } + + /** + * Allow fine control over 3D-Secure checks and rules by changing this value. 0 is Default + * It can be changed dynamically, per transaction, if you wish. See the VSP Server Protocol document + */ + $queryPairs['Apply3DSecure'] = '0'; + + if ($this->getDebug()) { + Mage::getModel('protx/api_debug') + ->setRequestBody($this->getProtxUrl()."\n".print_r($queryPairs,1)) + ->save(); + } + + // Encrypt the plaintext string for inclusion in the hidden field + $result = $this->arrayToCrypt($queryPairs); + return $result; + } + + /** + * Form block description + * + * @return object + */ + public function createFormBlock($name) + { + $block = $this->getLayout()->createBlock('protx/form_standard', $name); + $block->setMethod($this->_code); + $block->setPayment($this->getPayment()); + return $block; + } + + /** + * Return Order Place Redirect URL + * + * @return string Order Redirect URL + */ + public function getOrderPlaceRedirectUrl() + { + return Mage::getUrl('protx/standard/redirect'); + } + + /** + * Return encrypted string with simple XOR algorithm + * + * @param string String to be encrypted + * @return string Encrypted string + */ + protected function simpleXOR ($string) + { + $result = ''; + $cryptKey = $this->getConfig()->getCryptKey(); + + if (!$cryptKey) { + return $string; + } + + // Initialise key array + $keyList = array(); + + // Convert $cryptKey into array of ASCII values + for($i = 0; $i < strlen($cryptKey); $i++){ + $keyList[$i] = ord(substr($cryptKey, $i, 1)); + } + + // Step through string a character at a time + for($i = 0; $i < strlen($string); $i++) { + /** + * Get ASCII code from string, get ASCII code from key (loop through with MOD), + * XOR the two, get the character from the result + * % is MOD (modulus), ^ is XOR + */ + $result .= chr(ord(substr($string, $i, 1)) ^ ($keyList[$i % strlen($cryptKey)])); + } + return $result; + } + + /** + * Extract possible response values into array from query string + * + * @param string Query string i.e. var1=value1&var2=value3... + * @return array + */ + protected function getToken($queryString) { + + // List the possible tokens + $Tokens = array( + "Status", + "StatusDetail", + "VendorTxCode", + "VPSTxId", + "TxAuthNo", + "Amount", + "AVSCV2", + "AddressResult", + "PostCodeResult", + "CV2Result", + "GiftAid", + "3DSecureStatus", + "CAVV" + ); + + // Initialise arrays + $output = array(); + $resultArray = array(); + + // Get the next token in the sequence + $c = count($Tokens); + for ($i = $c - 1; $i >= 0 ; $i--){ + // Find the position in the string + $start = strpos($queryString, $Tokens[$i]); + // If it's present + if ($start !== false){ + // Record position and token name + $resultArray[$i]['start'] = $start; + $resultArray[$i]['token'] = $Tokens[$i]; + } + } + + // Sort in order of position + sort($resultArray); + + // Go through the result array, getting the token values + $c = count($resultArray); + for ($i = 0; $i < $c; $i++){ + // Get the start point of the value + $valueStart = $resultArray[$i]['start'] + strlen($resultArray[$i]['token']) + 1; + // Get the length of the value + if ($i == $c-1) { + $output[$resultArray[$i]['token']] = substr($queryString, $valueStart); + } else { + $valueLength = $resultArray[$i+1]['start'] - $resultArray[$i]['start'] - strlen($resultArray[$i]['token']) - 2; + $output[$resultArray[$i]['token']] = substr($queryString, $valueStart, $valueLength); + } + + } + + return $output; + } + + /** + * Convert array (key => value, key => value, ...) to crypt string + * + * @param array Array to be converted + * @return string Crypt string + */ + public function arrayToCrypt ($array) + { + $parts = array(); + if (is_array($array)) { + foreach ($array as $k => $v) { + $parts[] = $k . '=' . $v; + } + } + $result = implode('&', $parts); + $result = $this->simpleXOR($result); + $result = $this->base64Encode($result); + return $result; + } + + /** + * Reverse arrayToCrypt + * + * @param string Crypt string + * @return array + */ + public function cryptToArray ($crypted) + { + $decoded = $this->base64Decode($crypted); + $uncrypted = $this->simpleXOR($decoded); + $tokens = $this->getToken($uncrypted); + return $tokens; + } + + /** + * Custom base64_encode() + * + * @param String + * @return String + */ + protected function base64Encode($plain) + { + return base64_encode($plain); + } + + /** + * Custom base64_decode() + * + * @param String + * @return String + */ + protected function base64Decode($scrambled) + { + // Fix plus to space conversion issue + $scrambled = str_replace(" ","+",$scrambled); + return base64_decode($scrambled); + } + + /** + * Return Standard Checkout Form Fields for request to Protx + * + * @return array Array of hidden form fields + */ + public function getStandardCheckoutFormFields () + { + $fields = array( + 'VPSProtocol' => $this->getConfig()->getVersion(), + 'TxType' => $this->getConfig()->getPaymentType(), + 'Vendor' => $this->getConfig()->getVendorName(), + 'Crypt' => $this->getCrypted() + ); + return $fields; + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/controllers/StandardController.php b/app/code/core/Mage/Protx/controllers/StandardController.php new file mode 100644 index 0000000000..3d0e81df4a --- /dev/null +++ b/app/code/core/Mage/Protx/controllers/StandardController.php @@ -0,0 +1,279 @@ + + */ +class Mage_Protx_StandardController extends Mage_Core_Controller_Front_Action +{ + public $isValidResponse = false; + + /** + * Get singleton with protx strandard + * + * @return object Mage_Protx_Model_Standard + */ + public function getStandard() + { + return Mage::getSingleton('protx/standard'); + } + + /** + * Get Config model + * + * @return object Mage_Protx_Model_Config + */ + public function getConfig() + { + return $this->getStandard()->getConfig(); + } + + /** + * Return debug flag + * + * @return boolean + */ + public function getDebug () + { + return $this->getStandard()->getDebug(); + } + + /** + * When a customer chooses Protx on Checkout/Payment page + * + */ + public function redirectAction() + { + $session = Mage::getSingleton('checkout/session'); + $session->setProtxStandardQuoteId($session->getQuoteId()); + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($session->getLastRealOrderId()); + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('protx')->__('Customer was redirected to Protx') + ); + $order->save(); + + $this->getResponse() + ->setBody($this->getLayout() + ->createBlock('protx/standard_redirect') + ->setOrder($order) + ->toHtml()); + + $session->unsQuoteId(); + } + + /** + * Success response from Protx + */ + public function successResponseAction() + { + $this->preResponse(); + + if (!$this->isValidResponse) { + $this->_redirect(''); + return ; + } + + $transactionId = $this->responseArr['VendorTxCode']; + + if ($this->getDebug()) { + Mage::getModel('protx/api_debug') + ->setResponseBody(print_r($this->responseArr,1)) + ->save(); + } + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($transactionId); + + if (!$order->getId()) { + /* + * need to have logic when there is no order with the order id from protx + */ + return false; + } + + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('protx')->__('Customer successfully returned from Protx') + ); + + $order->sendNewOrderEmail(); + + $this->responseArr['Amount'] = str_replace(',', '', $this->responseArr['Amount']); + + if (sprintf('%.2f', $this->responseArr['Amount']) != sprintf('%.2f', $order->getBaseGrandTotal())) { + // cancel order + $order->cancel(); + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('protx')->__('Order total amount does not match protx gross total amount') + ); + } else { + $order->getPayment()->setTransactionId($this->responseArr['VPSTxId']); + + if ($this->getConfig()->getPaymentType() == Mage_Protx_Model_Config::PAYMENT_TYPE_PAYMENT) { + if ($this->saveInvoice($order)) { + $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true); + } else { + $newOrderStatus = $this->getConfig()->getNewOrderStatus() ? + $this->getConfig()->getNewOrderStatus() : Mage_Sales_Model_Order::STATE_NEW; + } + } else { + $order->addStatusToHistory( + $order->getStatus(), + Mage::helper('protx')->__($this->responseArr['StatusDetail']) + ); + } + } + + $order->save(); + + $session = Mage::getSingleton('checkout/session'); + $session->setQuoteId($session->getProtxStandardQuoteId(true)); + Mage::getSingleton('checkout/session')->getQuote()->setIsActive(false)->save(); + $this->_redirect('checkout/onepage/success'); + } + + /** + * Save invoice for order + * + * @param Mage_Sales_Model_Order $order + * @return boolean Can save invoice or not + */ + protected function saveInvoice (Mage_Sales_Model_Order $order) + { + if ($order->canInvoice()) { + $invoice = $order->prepareInvoice(); + + $invoice->register()->capture(); + Mage::getModel('core/resource_transaction') + ->addObject($invoice) + ->addObject($invoice->getOrder()) + ->save(); + return true; + } + + return false; + } + + /** + * Failure response from Protx + */ + public function failureResponseAction () + { + $this->preResponse(); + + if (!$this->isValidResponse) { + $this->_redirect(''); + return ; + } + + $transactionId = $this->responseArr['VendorTxCode']; + + if ($this->getDebug()) { + Mage::getModel('protx/api_debug') + ->setResponseBody(print_r($this->responseArr,1)) + ->save(); + } + + $order = Mage::getModel('sales/order'); + $order->loadByIncrementId($transactionId); + + if (!$order->getId()) { + /** + * need to have logic when there is no order with the order id from protx + */ + return false; + } + + // cancel order in anyway + $order->cancel(); + + $session = Mage::getSingleton('checkout/session'); + $session->setQuoteId($session->getProtxStandardQuoteId(true)); + + // Customer clicked CANCEL Butoon + if ($this->responseArr['Status'] == 'ABORT') { + $history = Mage::helper('protx')->__('Order '.$order->getId().' was canceled by customer'); + $redirectTo = 'checkout/cart'; + } else { + $history = Mage::helper('protx')->__($this->responseArr['StatusDetail']); + $session->setErrorMessage($this->responseArr['StatusDetail']); + $redirectTo = 'protx/standard/failure'; + } + + $history = Mage::helper('protx')->__('Customer was returned from Protx.') . ' ' . $history; + $order->addStatusToHistory($order->getStatus(), $history); + $order->save(); + + $this->_redirect($redirectTo); + } + + /** + * Expected GET HTTP Method + */ + protected function preResponse () + { + $responseCryptString = $this->getRequest()->crypt; + + if ($responseCryptString != '') { + $rArr = $this->getStandard()->cryptToArray($responseCryptString); + $ok = is_array($rArr) + && isset($rArr['Status']) && $rArr['Status'] != '' + && isset($rArr['VendorTxCode']) && $rArr['VendorTxCode'] != '' + && isset($rArr['Amount']) && $rArr['Amount'] != ''; + + if ($ok) { + $this->responseArr = $rArr; + $this->isValidResponse = true; + } + } + } + + /** + * Failure Action + */ + public function failureAction () + { + $session = Mage::getSingleton('checkout/session'); + + if (!$session->getErrorMessage()) { + $this->_redirect('checkout/cart'); + return; + } + + $this->loadLayout(); + $this->_initLayoutMessages('protx/session'); + $this->renderLayout(); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Protx/etc/config.xml b/app/code/core/Mage/Protx/etc/config.xml new file mode 100644 index 0000000000..2274dee2c5 --- /dev/null +++ b/app/code/core/Mage/Protx/etc/config.xml @@ -0,0 +1,122 @@ + + + + + + 0.1.0 + + + + + + Mage_Protx_Model + protx_mysql4 + + + Mage_Protx_Model_Mysql4 + + protx_api_debug
      +
      +
      +
      + + + + Mage_Protx + + + core_setup + + + + + core_write + + + + + core_read + + + + + Mage_Protx_Block + +
      + + + /protx/standard + + + + standard + + Mage_Protx + protx + + + + + + + + Mage_Protx.csv + + + + + + + + protx.xml + + + + + + + + + + Mage_Protx.csv + + + + + + + + + Payment + protx/standard + Protx Standard + 0 + + + +
      diff --git a/app/code/core/Mage/Protx/etc/system.xml b/app/code/core/Mage/Protx/etc/system.xml new file mode 100644 index 0000000000..4b5e76a3e7 --- /dev/null +++ b/app/code/core/Mage/Protx/etc/system.xml @@ -0,0 +1,167 @@ + + + + + + + + + text + 101 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 1 + 0 + + + + select + protx/source_paymentAction + 2 + 1 + 1 + 0 + + + <label>Title</label> + <frontend_type>text</frontend_type> + <sort_order>3</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + Store name will be used if left empty + text + 3 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_order_status_processing + 4 + 1 + 1 + 0 + + + + text + 5 + 1 + 1 + 0 + + + + text + 6 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 7 + 1 + 1 + 0 + + + + 'General contact' email will be used if left empty + text + 8 + 1 + 1 + 0 + + + + select + protx/source_modeAction + 9 + 1 + 1 + 0 + + + + select + adminhtml/system_config_source_yesno + 10 + 1 + 1 + 0 + + + + allowspecific + 50 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 51 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 100 + 1 + 1 + 0 + + + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Protx/sql/protx_setup/mysql4-install-0.1.0.php b/app/code/core/Mage/Protx/sql/protx_setup/mysql4-install-0.1.0.php new file mode 100644 index 0000000000..b8d74b89c6 --- /dev/null +++ b/app/code/core/Mage/Protx/sql/protx_setup/mysql4-install-0.1.0.php @@ -0,0 +1,46 @@ +startSetup(); + +$installer->run(" +CREATE TABLE `{$this->getTable('protx_api_debug')}` ( + `debug_id` int(10) unsigned NOT NULL auto_increment, + `transaction_id` varchar(255) NOT NULL default '', + `debug_at` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `request_body` text, + `response_body` text, + PRIMARY KEY (`debug_id`), + KEY `debug_at` (`debug_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + "); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Reports/Block/Product/Abstract.php b/app/code/core/Mage/Reports/Block/Product/Abstract.php index 2ab0387f3e..561f956bf8 100644 --- a/app/code/core/Mage/Reports/Block/Product/Abstract.php +++ b/app/code/core/Mage/Reports/Block/Product/Abstract.php @@ -20,20 +20,47 @@ * * @category Mage * @package Mage_Reports - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + +/** + * Reports Recently Products Abstract Block + * + * @category Mage + * @package Mage_Reports + * @author Magento Core Team + */ abstract class Mage_Reports_Block_Product_Abstract extends Mage_Catalog_Block_Product_Abstract { - protected $_eventTypeId = 0; + /** + * Product Index model name + * + * @var string + */ + protected $_indexName; + + /** + * Product Index model instance + * + * @var Mage_Reports_Model_Product_Index_Abstract + */ + protected $_indexModel; + + /** + * Product Index Collection + * + * @var Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract + */ + protected $_collection; /** - * Retrieve page size (count) + * Retrieve page size * * @return int */ - protected function getPageSize() + public function getPageSize() { if ($this->hasData('page_size')) { return $this->getData('page_size'); @@ -42,7 +69,8 @@ protected function getPageSize() } /** - * Obtain product ids, that must not be included in collection + * Retrieve product ids, that must not be included in collection + * * @return array */ protected function _getProductsToSkip() @@ -51,36 +79,68 @@ protected function _getProductsToSkip() } /** - * Get products collection and apply recent events log to it + * Retrieve Product Index model instance * - * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection + * @return Mage_Reports_Model_Product_Index_Abstract */ - protected function _getRecentProductsCollection() + protected function _getModel() { - // get products collection and apply status and visibility filter - $collection = $this->_addProductAttributesAndPrices(Mage::getModel('catalog/product')->getCollection()) - ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes()) - ->addUrlRewrite() - ->setPageSize($this->getPageSize()) - ->setCurPage(1) - ; - Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection); - Mage::getSingleton('catalog/product_visibility')->addVisibleInSiteFilterToCollection($collection); + if (is_null($this->_indexModel)) { + if (is_null($this->_indexName)) { + Mage::throwException(Mage::helper('reports')->__('Index model name must be defined')); + } - // apply events log to collection with required parameters - $skip = $this->_getProductsToSkip(); - $subtype = 0; - if (Mage::getSingleton('customer/session')->isLoggedIn()) { - $subjectId = Mage::getSingleton('customer/session')->getCustomer()->getId(); - } else { - $subjectId = Mage::getSingleton('log/visitor')->getId(); - $subtype = 1; + $this->_indexModel = Mage::getModel($this->_indexName); } - Mage::getResourceSingleton('reports/event')->applyLogToCollection($collection, $this->_eventTypeId, $subjectId, $subtype, $skip); - foreach ($collection as $product) { - $product->setDoNotUseCategoryId(true); + return $this->_indexModel; + } + + /** + * Retrieve Index Product Collection + * + * @return Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract + */ + public function getItemsCollection() + { + if (is_null($this->_collection)) { + $attributes = Mage::getSingleton('catalog/config')->getProductAttributes(); + + $this->_collection = $this->_getModel() + ->getCollection() + ->addAttributeToSelect($attributes) + ->addIndexFilter() + ->excludeProductIds($this->_getModel()->getExcludeProductIds()) + ->setAddedAtOrder(); + + Mage::getSingleton('catalog/product_visibility') + ->addVisibleInSiteFilterToCollection($this->_collection); } - return $collection; + + return $this->_collection; + } + + /** + * Retrieve count of product index items + * + * @return int + */ + public function getCount() + { + if (!$this->_getModel()->getCount()) { + return 0; + } + return $this->getItemsCollection()->count(); + } + + /** + * Get products collection and apply recent events log to it + * + * @deprecated + * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection + */ + protected function _getRecentProductsCollection() + { + return $this->getItemsCollection(); } } diff --git a/app/code/core/Mage/Reports/Block/Product/Compared.php b/app/code/core/Mage/Reports/Block/Product/Compared.php index 0f9f0458c2..08e9d1d8eb 100644 --- a/app/code/core/Mage/Reports/Block/Product/Compared.php +++ b/app/code/core/Mage/Reports/Block/Product/Compared.php @@ -20,7 +20,7 @@ * * @category Mage * @package Mage_Reports - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ @@ -29,21 +29,25 @@ * * @category Mage * @package Mage_Reports - * @author Magento Core Team + * @author Magento Core Team */ - class Mage_Reports_Block_Product_Compared extends Mage_Reports_Block_Product_Abstract { const XML_PATH_RECENTLY_COMPARED_COUNT = 'catalog/recently_products/compared_count'; - protected $_eventTypeId = Mage_Reports_Model_Event::EVENT_PRODUCT_COMPARE; + /** + * Compared Product Index model name + * + * @var string + */ + protected $_indexName = 'reports/product_index_compared'; /** * Retrieve page size (count) * * @return int */ - protected function getPageSize() + public function getPageSize() { if ($this->hasData('page_size')) { return $this->getData('page_size'); @@ -51,55 +55,19 @@ protected function getPageSize() return Mage::getStoreConfig(self::XML_PATH_RECENTLY_COMPARED_COUNT); } - /** - * Retrieve Product Ids to skip - * - * @return array - */ - protected function _getProductsToSkip() - { - $ids = array(); - if (Mage::helper('catalog/product_compare')->getItemCount()) { - foreach (Mage::helper('catalog/product_compare')->getItemCollection() as $_item) { - $ids[] = $_item->getId(); - } - if (($product = Mage::registry('product')) && $product->getId()) { - $ids[] = $product->getId(); - } - } - return $ids; - } - - /** - * Check session has compared products - * - * @return bool - */ - protected function _hasComparedProductsBefore() - { - return Mage::getSingleton('reports/session')->getData('compared_products'); - } - /** * Prepare to html - * check has compared products + * Check has compared products * * @return string */ protected function _toHtml() { - if (!$this->_hasComparedProductsBefore()) { + if (!$this->getCount()) { return ''; } - $collection = $this->_getRecentProductsCollection(); - $hasProducts = (bool)count($collection); - if (is_null($this->_hasComparedProductsBefore())) { - Mage::getSingleton('reports/session')->setData('compared_products', $hasProducts); - } - if ($hasProducts) { - $this->setRecentlyComparedProducts($collection); - } + $this->setRecentlyComparedProducts($this->getItemsCollection()); return parent::_toHtml(); } diff --git a/app/code/core/Mage/Reports/Block/Product/Viewed.php b/app/code/core/Mage/Reports/Block/Product/Viewed.php index e66a09a748..12fdb99946 100644 --- a/app/code/core/Mage/Reports/Block/Product/Viewed.php +++ b/app/code/core/Mage/Reports/Block/Product/Viewed.php @@ -20,7 +20,7 @@ * * @category Mage * @package Mage_Reports - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ @@ -29,21 +29,25 @@ * * @category Mage * @package Mage_Reports - * @author Magento Core Team + * @author Magento Core Team */ - class Mage_Reports_Block_Product_Viewed extends Mage_Reports_Block_Product_Abstract { const XML_PATH_RECENTLY_VIEWED_COUNT = 'catalog/recently_products/viewed_count'; - protected $_eventTypeId = Mage_Reports_Model_Event::EVENT_PRODUCT_VIEW; + /** + * Viewed Product Index model name + * + * @var string + */ + protected $_indexName = 'reports/product_index_viewed'; /** * Retrieve page size (count) * * @return int */ - protected function getPageSize() + public function getPageSize() { if ($this->hasData('page_size')) { return $this->getData('page_size'); @@ -51,30 +55,6 @@ protected function getPageSize() return Mage::getStoreConfig(self::XML_PATH_RECENTLY_VIEWED_COUNT); } - /** - * Retrieve Product Ids to skip - * - * @return array - */ - protected function _getProductsToSkip() - { - $ids = array(); - if (($product = Mage::registry('product')) && $product->getId()) { - $ids = (int)$product->getId(); - } - return $ids; - } - - /** - * Check session has viewed products - * - * @return bool - */ - protected function _hasViewedProductsBefore() - { - return Mage::getSingleton('reports/session')->getData('viewed_products'); - } - /** * Prepare to html * check has viewed products @@ -83,18 +63,11 @@ protected function _hasViewedProductsBefore() */ protected function _toHtml() { - if (!$this->_hasViewedProductsBefore()) { + if (!$this->getCount()) { return ''; } - $collection = $this->_getRecentProductsCollection(); - $hasProducts = (bool)count($collection); - if (is_null($this->_hasViewedProductsBefore())) { - Mage::getSingleton('reports/session')->setData('viewed_products', $hasProducts); - } - if ($hasProducts) { - $this->setRecentlyViewedProducts($collection); - } + $this->setRecentlyViewedProducts($this->getItemsCollection()); return parent::_toHtml(); } diff --git a/app/code/core/Mage/Reports/Helper/Data.php b/app/code/core/Mage/Reports/Helper/Data.php index fd2d63d9d8..89fd2da7d2 100644 --- a/app/code/core/Mage/Reports/Helper/Data.php +++ b/app/code/core/Mage/Reports/Helper/Data.php @@ -29,5 +29,4 @@ */ class Mage_Reports_Helper_Data extends Mage_Core_Helper_Abstract { - } diff --git a/app/code/core/Mage/Reports/Model/Event/Observer.php b/app/code/core/Mage/Reports/Model/Event/Observer.php index e4f1546b2a..5d8cf75698 100644 --- a/app/code/core/Mage/Reports/Model/Event/Observer.php +++ b/app/code/core/Mage/Reports/Model/Event/Observer.php @@ -20,20 +20,31 @@ * * @category Mage * @package Mage_Reports - * @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + /** - * Report event observer model + * Reports Event observer model * * @category Mage * @package Mage_Reports - * @author Magento Core Team + * @author Magento Core Team */ - class Mage_Reports_Model_Event_Observer { + /** + * Abstract Event obeserver logic + * + * Save event + * + * @param int $eventTypeId + * @param int $objectId + * @param int $subjectId + * @param int $subtype + * @return Mage_Reports_Model_Event_Observer + */ protected function _event($eventTypeId, $objectId, $subjectId = null, $subtype = 0) { if (is_null($subjectId)) { @@ -60,48 +71,137 @@ protected function _event($eventTypeId, $objectId, $subjectId = null, $subtype = return $this; } - public function customerLogin(Varien_Event_Observer $observer) { + /** + * Customer login action + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ + public function customerLogin(Varien_Event_Observer $observer) + { if (!Mage::getSingleton('customer/session')->isLoggedIn()) { return $this; } - $customer = Mage::getSingleton('customer/session')->getCustomer(); - $visitorId = Mage::getSingleton('log/visitor')->getId(); - $customerId = $customer->getId(); + + $visitorId = Mage::getSingleton('log/visitor')->getId(); + $customerId = Mage::getSingleton('customer/session')->getCustomerId(); $eventModel = Mage::getModel('reports/event'); $eventModel->updateCustomerType($visitorId, $customerId); + + Mage::getModel('reports/product_index_compared') + ->updateCustomerFromVisitor() + ->calculate(); + Mage::getModel('reports/product_index_viewed') + ->updateCustomerFromVisitor() + ->calculate(); + + return $this; + } + + /** + * Customer logout processing + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ + public function customerLogout(Varien_Event_Observer $observer) + { + Mage::getModel('reports/product_index_compared') + ->purgeVisitorByCustomer() + ->calculate(); + Mage::getModel('reports/product_index_viewed') + ->purgeVisitorByCustomer() + ->calculate(); + return $this; } + /** + * View Catalog Product action + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ public function catalogProductView(Varien_Event_Observer $observer) { - Mage::getSingleton('reports/session')->setData('viewed_products', true); - return $this->_event( - Mage_Reports_Model_Event::EVENT_PRODUCT_VIEW, - $observer->getEvent()->getProduct()->getId() - ); + $productId = $observer->getEvent()->getProduct()->getId(); + + Mage::getModel('reports/product_index_viewed') + ->setProductId($productId) + ->save() + ->calculate(); + + return $this->_event(Mage_Reports_Model_Event::EVENT_PRODUCT_VIEW, $productId); } + /** + * Send Product link to friends action + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ public function sendfriendProduct(Varien_Event_Observer $observer) { - return $this->_event( - Mage_Reports_Model_Event::EVENT_PRODUCT_SEND, + return $this->_event(Mage_Reports_Model_Event::EVENT_PRODUCT_SEND, $observer->getEvent()->getProduct()->getId() ); } + /** + * Remove Product from Compare Products action + * + * Reset count of compared products cache + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ public function catalogProductCompareRemoveProduct(Varien_Event_Observer $observer) { - Mage::getSingleton('reports/session')->setData('compared_products', null); + Mage::getModel('reports/product_index_compared')->calculate(); + + return $this; + } + + /** + * Remove All Products from Compare Products + * + * Reset count of compared products cache + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ + public function catalogProductCompareClear(Varien_Event_Observer $observer) + { + Mage::getModel('reports/product_index_compared')->calculate(); + + return $this; } + /** + * Add Product to Compare Products List action + * + * Reset count of compared products cache + * + * @param Varien_Event_Observer $observer + * @return unknown + */ public function catalogProductCompareAddProduct(Varien_Event_Observer $observer) { - Mage::getSingleton('reports/session')->setData('compared_products', true); - return $this->_event( - Mage_Reports_Model_Event::EVENT_PRODUCT_COMPARE, - $observer->getEvent()->getProduct()->getId() - ); + $productId = $observer->getEvent()->getProduct()->getId(); + + Mage::getModel('reports/product_index_compared') + ->setProductId($productId) + ->save() + ->calculate(); + + return $this->_event(Mage_Reports_Model_Event::EVENT_PRODUCT_COMPARE, $productId); } + /** + * Add product to shopping cart action + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ public function checkoutCartAddProduct(Varien_Event_Observer $observer) { $quoteItem = $observer->getEvent()->getItem(); @@ -112,34 +212,49 @@ public function checkoutCartAddProduct(Varien_Event_Observer $observer) return $this; } + /** + * Add product to wishlist action + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ public function wishlistAddProduct(Varien_Event_Observer $observer) { - return $this->_event( - Mage_Reports_Model_Event::EVENT_PRODUCT_TO_WISHLIST, + return $this->_event(Mage_Reports_Model_Event::EVENT_PRODUCT_TO_WISHLIST, $observer->getEvent()->getProduct()->getId() ); } + /** + * Share customer wishlist action + * + * @param Varien_Event_Observer $observer + * @return Mage_Reports_Model_Event_Observer + */ public function wishlistShare(Varien_Event_Observer $observer) { - return $this->_event( - Mage_Reports_Model_Event::EVENT_WISHLIST_SHARE, + return $this->_event(Mage_Reports_Model_Event::EVENT_WISHLIST_SHARE, $observer->getEvent()->getWishlist()->getId() ); } /** - * event clean + * Clean events by old visitors + * + * @see Global Log Clean Settings * * @param Varien_Event_Observer $observer * @return Mage_Reports_Model_Event_Observer */ public function eventClean(Varien_Event_Observer $observer) { - $event = Mage::getModel('reports/event'); /* @var $event Mage_Reports_Model_Event */ + $event = Mage::getModel('reports/event'); $event->clean(); + Mage::getModel('reports/product_index_compared')->clean(); + Mage::getModel('reports/product_index_viewed')->clean(); + return $this; } -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Order/Collection.php b/app/code/core/Mage/Reports/Model/Mysql4/Order/Collection.php index 2114fa4824..33e2249895 100644 --- a/app/code/core/Mage/Reports/Model/Mysql4/Order/Collection.php +++ b/app/code/core/Mage/Reports/Model/Mysql4/Order/Collection.php @@ -114,7 +114,7 @@ public function getDateRange($range, $customStart, $customEnd, $returnObjects = break; case '1m': - $dateStart->setDay(1); + $dateStart->setDay(Mage::getStoreConfig('reports/dashboard/mtd_start')); break; case 'custom': @@ -123,13 +123,15 @@ public function getDateRange($range, $customStart, $customEnd, $returnObjects = break; case '1y': - $dateStart->setMonth(1); - $dateStart->setDay(1); - break; case '2y': - $dateStart->setMonth(1); - $dateStart->setDay(1); - $dateStart->subYear(1); + $startMonthDay = explode(',', Mage::getStoreConfig('reports/dashboard/ytd_start')); + $startMonth = isset($startMonthDay[0]) ? (int)$startMonthDay[0] : 1; + $startDay = isset($startMonthDay[1]) ? (int)$startMonthDay[1] : 1; + $dateStart->setMonth($startMonth); + $dateStart->setDay($startDay); + if ($range == '2y') { + $dateStart->subYear(1); + } break; } diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Abstract.php b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Abstract.php new file mode 100644 index 0000000000..3936b91f41 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Abstract.php @@ -0,0 +1,155 @@ + + */ +abstract class Mage_Reports_Model_Mysql4_Product_Index_Abstract extends Mage_Core_Model_Mysql4_Abstract +{ + /** + * Fields List for update in forsedSave + * + * @var array + */ + protected $_fieldsForUpdate = array('store_id', 'added_at'); + + /** + * Update Customer from visitor (Customer loggin) + * + * @param Mage_Reports_Model_Product_Index_Abstract $object + * @return Mage_Reports_Model_Mysql4_Product_Index_Abstract + */ + public function updateCustomerFromVisitor(Mage_Reports_Model_Product_Index_Abstract $object) + { + /** + * Do nothing if customer not logged in + */ + if (!$object->getCustomerId()) { + return $this; + } + + $select = $this->_getWriteAdapter()->select() + ->from($this->getMainTable()) + ->where('visitor_id=?', $object->getVisitorId()); + $rowSet = $select->query()->fetchAll(); + foreach ($rowSet as $row) { + $select = $this->_getWriteAdapter()->select() + ->from($this->getMainTable()) + ->where('customer_id=?', $object->getCustomerId()) + ->where('product_id=?', $row['product_id']); + $idx = $this->_getWriteAdapter()->fetchRow($select); + + if ($idx) { + $this->_getWriteAdapter()->delete($this->getMainTable(), + $this->_getWriteAdapter()->quoteInto('index_id=?', $row['index_id']) + ); + $this->_getWriteAdapter()->update($this->getMainTable(), array( + 'visitor_id' => $object->getVisitorId(), + 'store_id' => $object->getStoreId(), + 'added_at' => now(), + ), $this->_getWriteAdapter()->quoteInto('index_id=?', $idx['index_id'])); + } + else { + $this->_getWriteAdapter()->update($this->getMainTable(), array( + 'customer_id' => $object->getCustomerId(), + 'store_id' => $object->getStoreId(), + 'added_at' => now() + ), $this->_getWriteAdapter()->quoteInto('index_id=?', $row['index_id'])); + } + } + + return $this; + } + + /** + * Purge visitor data by customer (logout) + * + * @param Mage_Reports_Model_Product_Index_Abstract $object + * @return Mage_Reports_Model_Mysql4_Product_Index_Abstract + */ + public function purgeVisitorByCustomer(Mage_Reports_Model_Product_Index_Abstract $object) + { + if (!$object->getCustomerId()) { + return $this; + } + + $where = $this->_getWriteAdapter()->quoteInto('customer_id=?', $object->getCustomerId()); + $bind = array( + 'visitor_id' => 0, + ); + + $this->_getWriteAdapter()->update($this->getMainTable(), $bind, $where); + + return $this; + } + + /** + * Save Product Index data (forsed save) + * + * @param Mage_Reports_Model_Product_Index_Abstract $object + * @return Mage_Reports_Model_Mysql4_Product_Index_Abstract + */ + public function save(Mage_Core_Model_Abstract $object) + { + return $this->forsedSave($object); + } + + /** + * Clean index (visitor) + * + * @return Mage_Reports_Model_Mysql4_Product_Index_Abstract + */ + public function clean() + { + while (true) { + $select = $this->_getReadAdapter()->select() + ->from(array('main_table' => $this->getMainTable()), array($this->getIdFieldName())) + ->joinLeft( + array('visitor_table' => $this->getTable('log/visitor')), + 'main_table.visitor_id = visitor_table.visitor_id', + array()) + ->where('main_table.visitor_id > 0') + ->where('visitor_table.visitor_id IS NULL') + ->limit(100); + $indexIds = $this->_getReadAdapter()->fetchCol($select); + + if (!$indexIds) { + break; + } + + $this->_getWriteAdapter()->delete( + $this->getMainTable(), + $this->_getWriteAdapter()->quoteInto($this->getIdFieldName() . ' IN(?)', $indexIds) + ); + } + return $this; + } +} diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Collection/Abstract.php b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Collection/Abstract.php new file mode 100644 index 0000000000..e3d31e0ca8 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Collection/Abstract.php @@ -0,0 +1,116 @@ + + */ +abstract class Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract + extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection +{ + /** + * Retrieve Product Index table name + * + * @return string + */ + abstract protected function _getTableName(); + + /** + * Add Viewed Products Index to Collection + * + * @return Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract + */ + public function addIndexFilter() + { + $this->joinTable( + array('idx_table' => $this->_getTableName()), + 'product_id=entity_id', + array( + 'product_id' => 'product_id', + 'item_store_id' => 'store_id', + 'added_at' => 'added_at' + ), + $this->_getWhereCondition() + ); + + $this->_productLimitationFilters['store_table'] = 'idx_table'; + + $this->setFlag('url_data_object', true); + $this->setFlag('do_not_use_category_id', true); + + return $this; + } + + /** + * Retrieve Where Condition to Index table + * + * @return array + */ + protected function _getWhereCondition() + { + $condition = array(); + + if (Mage::getSingleton('customer/session')->isLoggedIn()) { + $condition['customer_id'] = Mage::getSingleton('customer/session')->getCustomerId(); + } + else { + $condition['visitor_id'] = Mage::getSingleton('log/visitor')->getId(); + } + + return $condition; + } + + /** + * Add order by "added at" + * + * @param string $dir + * @return Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract + */ + public function setAddedAtOrder($dir = 'desc') + { + $this->setOrder("added_at", $dir); + return $this; + } + + /** + * Add exclude Product Ids + * + * @param int|array $productIds + * @return Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract + */ + public function excludeProductIds($productIds) + { + if (empty($productIds)) { + return $this; + } + $this->getSelect()->where('idx_table.product_id NOT IN(?)', $productIds); + return $this; + } +} diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Compared.php b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Compared.php new file mode 100644 index 0000000000..298ae0e9f5 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Compared.php @@ -0,0 +1,45 @@ + + */ +class Mage_Reports_Model_Mysql4_Product_Index_Compared extends Mage_Reports_Model_Mysql4_Product_Index_Abstract +{ + /** + * Initialize connection and main resource table + * + */ + protected function _construct() + { + $this->_init('reports/compared_product_index', 'index_id'); + } +} diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Compared/Collection.php b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Compared/Collection.php new file mode 100644 index 0000000000..074d3d8dde --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Compared/Collection.php @@ -0,0 +1,46 @@ + + */ +class Mage_Reports_Model_Mysql4_Product_Index_Compared_Collection extends Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract +{ + /** + * Retrieve Product Index table name + * + * @return string + */ + protected function _getTableName() + { + return $this->getTable('reports/compared_product_index'); + } +} \ No newline at end of file diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Viewed.php b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Viewed.php new file mode 100644 index 0000000000..f5c8718627 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Viewed.php @@ -0,0 +1,45 @@ + + */ +class Mage_Reports_Model_Mysql4_Product_Index_Viewed extends Mage_Reports_Model_Mysql4_Product_Index_Abstract +{ + /** + * Initialize connection and main resource table + * + */ + protected function _construct() + { + $this->_init('reports/viewed_product_index', 'index_id'); + } +} diff --git a/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Viewed/Collection.php b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Viewed/Collection.php new file mode 100644 index 0000000000..551aa92db7 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Mysql4/Product/Index/Viewed/Collection.php @@ -0,0 +1,46 @@ + + */ +class Mage_Reports_Model_Mysql4_Product_Index_Viewed_Collection extends Mage_Reports_Model_Mysql4_Product_Index_Collection_Abstract +{ + /** + * Retrieve Product Index table name + * + * @return string + */ + protected function _getTableName() + { + return $this->getTable('reports/viewed_product_index'); + } +} diff --git a/app/code/core/Mage/Reports/Model/Product/Index/Abstract.php b/app/code/core/Mage/Reports/Model/Product/Index/Abstract.php new file mode 100644 index 0000000000..1173a66cd4 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Product/Index/Abstract.php @@ -0,0 +1,220 @@ + + */ +abstract class Mage_Reports_Model_Product_Index_Abstract extends Mage_Core_Model_Abstract +{ + /** + * Cache key name for Count of product index + * + * @var string + */ + protected $_countCacheKey; + + /** + * Prepare customer/visitor, store data before save + * + * @return Mage_Reports_Model_Product_Index_Abstract + */ + protected function _beforeSave() + { + parent::_beforeSave(); + + if (!$this->hasVisitorId()) { + $this->setVisitorId($this->getVisitorId()); + } + if (!$this->hasCustomerId()) { + $this->setCustomerId($this->getCustomerId()); + } + if (!$this->hasStoreId()) { + $this->setStoreId($this->getStoreId()); + } + if (!$this->hasAddedAt()) { + $this->setAddedAt(now()); + } + + return $this; + } + + /** + * Retrieve visitor id + * + * if don't exists return current visitor id + * + * @return int + */ + public function getVisitorId() + { + if ($this->hasData('visitor_id')) { + return $this->getData('visitor_id'); + } + return Mage::getSingleton('log/visitor')->getId(); + } + + /** + * Retrieve customer id + * + * if customer don't logged in return null + * + * @return int + */ + public function getCustomerId() + { + if ($this->hasData('customer_id')) { + return $this->getData('customer_id'); + } + return Mage::getSingleton('customer/session')->getCustomerId(); + } + + /** + * Retrieve store id + * + * default return current store id + * + * @return int + */ + public function getStoreId() + { + if ($this->hasData('store_id')) { + return $this->getData('store_id'); + } + return Mage::app()->getStore()->getId(); + } + + /** + * Retrieve resource instance wrapper + * + * @return Mage_Reports_Model_Mysql4_Product_Index_Abstract + */ + protected function _getResource() + { + return parent::_getResource(); + } + + /** + * On customer loggin merge visitor/customer index + * + * @return Mage_Reports_Model_Product_Index_Abstract + */ + public function updateCustomerFromVisitor() + { + $this->_getResource()->updateCustomerFromVisitor($this); + return $this; + } + + /** + * Purge visitor data by customer (logout) + * + * @return Mage_Reports_Model_Product_Index_Abstract + */ + public function purgeVisitorByCustomer() + { + $this->_getResource()->purgeVisitorByCustomer($this); + return $this; + } + + /** + * Retrieve Reports Session instance + * + * @return Mage_Reports_Model_Session + */ + protected function _getSession() + { + return Mage::getSingleton('reports/session'); + } + + /** + * Calculate count of product index items cache + * + * @return Mage_Reports_Model_Product_Index_Abstract + */ + public function calculate() + { + if (!$this->_getSession()->hasData($this->_countCacheKey)) { + $count = 0; + } + else { + $collection = $this->getCollection() + ->addIndexFilter() + ->excludeProductIds($this->getExcludeProductIds()); + + Mage::getSingleton('catalog/product_visibility') + ->addVisibleInSiteFilterToCollection($collection); + + $count = $collection->getSize(); + } + + $this->_getSession()->setData($this->_countCacheKey, $count); + + return $this; + } + + /** + * Retrieve Exclude Product Ids List for Collection + * + * @return array + */ + public function getExcludeProductIds() + { + return array(); + } + + /** + * Retrieve count of product index items + * + * @return int + */ + public function getCount() + { + if (!$this->_countCacheKey) { + return 0; + } + + if (!$this->_getSession()->hasData($this->_countCacheKey)) { + $this->calculate(); + } + + return $this->_getSession()->getData($this->_countCacheKey); + } + + /** + * Clean index (visitors) + * + * @return Mage_Reports_Model_Product_Index_Abstract + */ + public function clean() + { + $this->_getResource()->clean($this); + return $this; + } +} diff --git a/app/code/core/Mage/Reports/Model/Product/Index/Compared.php b/app/code/core/Mage/Reports/Model/Product/Index/Compared.php new file mode 100644 index 0000000000..562d94c3ee --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Product/Index/Compared.php @@ -0,0 +1,77 @@ + + */ +class Mage_Reports_Model_Product_Index_Compared extends Mage_Reports_Model_Product_Index_Abstract +{ + /** + * Cache key name for Count of product index + * + * @var string + */ + protected $_countCacheKey = 'product_index_compared_count'; + + /** + * Initialize resource model + * + */ + protected function _construct() + { + $this->_init('reports/product_index_compared'); + } + + /** + * Retrieve Exclude Product Ids List for Collection + * + * @return array + */ + public function getExcludeProductIds() + { + $productIds = array(); + + /* @var $helper Mage_Catalog_Helper_Product_Compare */ + $helper = Mage::helper('catalog/product_compare'); + + if ($helper->hasItems()) { + foreach ($helper->getItemCollection() as $_item) { + $productIds[] = $_item->getEntityId(); + } + } + + if (Mage::registry('current_product')) { + $productIds[] = Mage::registry('current_product')->getId(); + } + + return array_unique($productIds); + } +} diff --git a/app/code/core/Mage/Reports/Model/Product/Index/Viewed.php b/app/code/core/Mage/Reports/Model/Product/Index/Viewed.php new file mode 100644 index 0000000000..69e4226bd0 --- /dev/null +++ b/app/code/core/Mage/Reports/Model/Product/Index/Viewed.php @@ -0,0 +1,68 @@ + + */ +class Mage_Reports_Model_Product_Index_Viewed extends Mage_Reports_Model_Product_Index_Abstract +{ + /** + * Cache key name for Count of product index + * + * @var string + */ + protected $_countCacheKey = 'product_index_compared_count'; + + /** + * Initialize resource model + * + */ + protected function _construct() + { + $this->_init('reports/product_index_viewed'); + } + + /** + * Retrieve Exclude Product Ids List for Collection + * + * @return array + */ + public function getExcludeProductIds() + { + $productIds = array(); + + if (Mage::registry('current_product')) { + $productIds[] = Mage::registry('current_product')->getId(); + } + + return $productIds; + } +} diff --git a/app/code/core/Mage/Reports/Model/Session.php b/app/code/core/Mage/Reports/Model/Session.php index c4d7ce2d12..eef34c9e15 100644 --- a/app/code/core/Mage/Reports/Model/Session.php +++ b/app/code/core/Mage/Reports/Model/Session.php @@ -1,41 +1,45 @@ - - */ -class Mage_Reports_Model_Session extends Mage_Core_Model_Session_Abstract -{ - public function __construct() - { - $this->init('reports'); - } -} \ No newline at end of file + + */ +class Mage_Reports_Model_Session extends Mage_Core_Model_Session_Abstract +{ + /** + * Initialize session name space + * + */ + public function __construct() + { + $this->init('reports'); + } +} diff --git a/app/code/core/Mage/Reports/etc/config.xml b/app/code/core/Mage/Reports/etc/config.xml index 8315d4f153..2a0aae3760 100644 --- a/app/code/core/Mage/Reports/etc/config.xml +++ b/app/code/core/Mage/Reports/etc/config.xml @@ -28,7 +28,7 @@ - 0.7.7 + 0.7.8 @@ -46,6 +46,12 @@ report_event_types
      + + report_compared_product_index
      +
      + + report_viewed_product_index
      +
      @@ -70,80 +76,7 @@ - - - - - singleton - reports/event_observer - catalogProductCompareRemoveProduct - - - - - - - singleton - reports/event_observer - customerLogin - - - - - - - singleton - reports/event_observer - catalogProductView - - - - - - - singleton - reports/event_observer - sendfriendProduct - - - - - - - singleton - reports/event_observer - catalogProductCompareAddProduct - - - - - - - singleton - reports/event_observer - checkoutCartAddProduct - - - - - - - singleton - reports/event_observer - wishlistAddProduct - - - - - - - singleton - reports/event_observer - wishlistShare - - - @@ -423,6 +356,17 @@ + + + + + + Reports + + + + + @@ -445,9 +389,101 @@ + + + + + singleton + reports/event_observer + catalogProductCompareRemoveProduct + + + + + + + singleton + reports/event_observer + customerLogin + + + + + + + singleton + reports/event_observer + customerLogout + + + + + + + singleton + reports/event_observer + catalogProductView + + + + + + + singleton + reports/event_observer + sendfriendProduct + + + + + + + singleton + reports/event_observer + catalogProductCompareAddProduct + + + + + + + singleton + reports/event_observer + catalogProductCompareClear + + + + + + + singleton + reports/event_observer + checkoutCartAddProduct + + + + + + + singleton + reports/event_observer + wishlistAddProduct + + + + + + + singleton + reports/event_observer + wishlistShare + + + + - + website @@ -455,5 +491,11 @@ 5 + + + 1,1 + 1 + +
      diff --git a/app/code/core/Mage/Reports/etc/system.xml b/app/code/core/Mage/Reports/etc/system.xml index 82bbb915ff..0b0b1838da 100644 --- a/app/code/core/Mage/Reports/etc/system.xml +++ b/app/code/core/Mage/Reports/etc/system.xml @@ -66,5 +66,45 @@ + + + general + text + 1000 + 1 + 0 + 0 + + + + text + 1 + 1 + 0 + 0 + + + + select + adminhtml/report_config_form_field_ytdStart + 1 + 1 + 0 + 0 + + + + select + adminhtml/report_config_form_field_mtdStart + Select day of the month + 2 + 1 + 0 + 0 + + + + + \ No newline at end of file diff --git a/app/code/core/Mage/Reports/sql/reports_setup/mysql4-upgrade-0.7.7-0.7.8.php b/app/code/core/Mage/Reports/sql/reports_setup/mysql4-upgrade-0.7.7-0.7.8.php new file mode 100644 index 0000000000..5845ee48ac --- /dev/null +++ b/app/code/core/Mage/Reports/sql/reports_setup/mysql4-upgrade-0.7.7-0.7.8.php @@ -0,0 +1,71 @@ +startSetup(); +$installer->run(" + +CREATE TABLE `{$installer->getTable('reports/viewed_product_index')}` ( + `index_id` bigint(20) unsigned NOT NULL auto_increment, + `visitor_id` int(10) unsigned NOT NULL, + `customer_id` int(10) unsigned default NULL, + `product_id` int(10) unsigned NOT NULL, + `store_id` smallint(5) unsigned default NULL, + `added_at` datetime NOT NULL, + PRIMARY KEY (`index_id`), + UNIQUE KEY `UNQ_BY_VISITOR` (`visitor_id`,`product_id`), + UNIQUE KEY `UNQ_BY_CUSTOMER` (`customer_id`,`product_id`), + KEY `IDX_STORE` (`store_id`), + KEY `IDX_SORT_ADDED_AT` (`added_at`), + KEY `product_id` (`product_id`), + CONSTRAINT `FK_REPORT_VIEWED_PRODUCT_INDEX_STORE` FOREIGN KEY (`store_id`) REFERENCES `{$installer->getTable('core/store')}` (`store_id`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `FK_REPORT_VIEWED_PRODUCT_INDEX_CUSTOMER` FOREIGN KEY (`customer_id`) REFERENCES `{$installer->getTable('customer/entity')}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_REPORT_VIEWED_PRODUCT_INDEX_PRODUCT` FOREIGN KEY (`product_id`) REFERENCES `{$installer->getTable('catalog/product')}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `{$installer->getTable('reports/compared_product_index')}` ( + `index_id` bigint(20) unsigned NOT NULL auto_increment, + `visitor_id` int(10) unsigned NOT NULL, + `customer_id` int(10) unsigned default NULL, + `product_id` int(10) unsigned NOT NULL, + `store_id` smallint(5) unsigned default NULL, + `added_at` datetime NOT NULL, + PRIMARY KEY (`index_id`), + UNIQUE KEY `UNQ_BY_VISITOR` (`visitor_id`,`product_id`), + UNIQUE KEY `UNQ_BY_CUSTOMER` (`customer_id`,`product_id`), + KEY `IDX_STORE` (`store_id`), + KEY `IDX_SORT_ADDED_AT` (`added_at`), + KEY `product_id` (`product_id`), + CONSTRAINT `FK_REPORT_COMPARED_PRODUCT_INDEX_STORE` FOREIGN KEY (`store_id`) REFERENCES `{$installer->getTable('core/store')}` (`store_id`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `FK_REPORT_COMPARED_PRODUCT_INDEX_CUSTOMER` FOREIGN KEY (`customer_id`) REFERENCES `{$installer->getTable('customer/entity')}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_REPORT_COMPARED_PRODUCT_INDEX_PRODUCT` FOREIGN KEY (`product_id`) REFERENCES `{$installer->getTable('catalog/product')}` (`entity_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +"); +$installer->endSetup(); diff --git a/app/code/core/Mage/Review/controllers/ProductController.php b/app/code/core/Mage/Review/controllers/ProductController.php index 138cb4ea88..87dd9231fb 100644 --- a/app/code/core/Mage/Review/controllers/ProductController.php +++ b/app/code/core/Mage/Review/controllers/ProductController.php @@ -68,7 +68,7 @@ public function preDispatch() * * @return Mage_Catalog_Model_Product */ - protected function _initProduct() + protected function _initProduct() { Mage::dispatchEvent('review_controller_product_init_before', array('controller_action'=>$this)); $categoryId = (int) $this->getRequest()->getParam('category', false); @@ -195,6 +195,7 @@ public function viewAction() { $this->loadLayout(); $this->_initLayoutMessages('review/session'); + $this->_initLayoutMessages('catalog/session'); $this->renderLayout(); } diff --git a/app/code/core/Mage/Rss/Block/Catalog/New.php b/app/code/core/Mage/Rss/Block/Catalog/New.php index daeb2aa628..422922b384 100644 --- a/app/code/core/Mage/Rss/Block/Catalog/New.php +++ b/app/code/core/Mage/Rss/Block/Catalog/New.php @@ -93,6 +93,15 @@ protected function _toHtml() public function addNewItemXmlCallback($args) { $product = $args['product']; + + $product->setAllowedInRss(true); + Mage::dispatchEvent('rss_catalog_new_xml_callback', $args); + + if (!$product->getAllowedInRss()) { + //Skip adding product to RSS + return; + } + //$product->unsetData()->load($args['row']['entity_id']); $product->setData($args['row']); $final_price = $product->getFinalPrice(); diff --git a/app/code/core/Mage/Rss/Block/Catalog/NotifyStock.php b/app/code/core/Mage/Rss/Block/Catalog/NotifyStock.php index 04f77bbeb3..9676c28d5a 100644 --- a/app/code/core/Mage/Rss/Block/Catalog/NotifyStock.php +++ b/app/code/core/Mage/Rss/Block/Catalog/NotifyStock.php @@ -58,7 +58,7 @@ protected function _toHtml() $_configManageStock = (int)Mage::getStoreConfigFlag(Mage_CatalogInventory_Model_Stock_Item::XML_PATH_MANAGE_STOCK); $stockItemWhere = "({{table}}.low_stock_date is not null) " . " and ({{table}}.low_stock_date>'0000-00-00') " - . " and IF({{table}}.use_config_manage_stock=1," . $_configManageStock . ",{{table}}.manage_stock)=1"; + . " and IF({{table}}.use_config_manage_stock=1," . $_configManageStock . ",{{table}}.manage_stock) <= 1"; $product = Mage::getModel('catalog/product'); $collection = $product->getCollection() diff --git a/app/code/core/Mage/Rss/Block/Catalog/Special.php b/app/code/core/Mage/Rss/Block/Catalog/Special.php index a02a1c9ab5..6fa5c48191 100644 --- a/app/code/core/Mage/Rss/Block/Catalog/Special.php +++ b/app/code/core/Mage/Rss/Block/Catalog/Special.php @@ -141,6 +141,21 @@ protected function _toHtml() public function addSpecialXmlCallback($args) { + /* + * RSS state object + */ + $product = new Varien_Object(); + //Product is allowed for RSS initially + $product->setAllowedInRss(true); + $args['product'] = $product; + + Mage::dispatchEvent('rss_catalog_special_xml_callback', $args); + + if (!$product->getAllowedInRss()) { + //Skip adding product to RSS + return; + } + //echo "
      ";
       //print_r($args['row']);
              $row = $args['row'];
      diff --git a/app/code/core/Mage/Rss/Block/Catalog/Tag.php b/app/code/core/Mage/Rss/Block/Catalog/Tag.php
      index ed1b4a51b2..1bf0b34726 100644
      --- a/app/code/core/Mage/Rss/Block/Catalog/Tag.php
      +++ b/app/code/core/Mage/Rss/Block/Catalog/Tag.php
      @@ -75,6 +75,15 @@ protected function _toHtml()
           public function addTaggedItemXml($args)
           {
               $product = $args['product'];
      +
      +        $product->setAllowedInRss(true);
      +        Mage::dispatchEvent('rss_catalog_tagged_item_xml_callback', $args);
      +
      +        if (!$product->getAllowedInRss()) {
      +            //Skip adding product to RSS
      +            return;
      +        }
      +
               $product->unsetData()->load($args['row']['entity_id']);
               $description = ''.
               ''.
      diff --git a/app/code/core/Mage/Rss/Block/Wishlist.php b/app/code/core/Mage/Rss/Block/Wishlist.php
      index f5db3797dd..86bc51018c 100644
      --- a/app/code/core/Mage/Rss/Block/Wishlist.php
      +++ b/app/code/core/Mage/Rss/Block/Wishlist.php
      @@ -20,85 +20,142 @@
        *
        * @category   Mage
        * @package    Mage_Rss
      - * @copyright  Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
      + * @copyright  Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
        * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
        */
       
       /**
      - * Review form block
      + * Customer Shared Wishlist Rss Block
        *
        * @category   Mage
        * @package    Mage_Rss
      - * @author      Magento Core Team 
      + * @author     Magento Core Team 
        */
      -class Mage_Rss_Block_Wishlist extends Mage_Core_Block_Template
      +class Mage_Rss_Block_Wishlist extends Mage_Wishlist_Block_Abstract
       {
      -    protected function _toHtml()
      +    /**
      +     * Customer instance
      +     *
      +     * @var Mage_Customer_Model_Customer
      +     */
      +    protected $_customer;
      +
      +    /**
      +     * Retrieve Wishlist model
      +     *
      +     * @return Mage_Wishlist_Model_Wishlist
      +     */
      +    protected function _getWishlist()
           {
      -        $descrpt = Mage::helper('core')->urlDecode($this->getRequest()->getParam('data'));
      -        $data = explode(',',$descrpt);
      -        $cid = (int)$data[0];
      +        if (is_null($this->_wishlist)) {
      +            $this->_wishlist = Mage::getModel('wishlist/wishlist');
      +            if ($this->_getCustomer()->getId()) {
      +                $this->_wishlist->loadByCustomer($this->_getCustomer());
      +            }
      +        }
      +        return $this->_wishlist;
      +    }
       
      -        $rssObj = Mage::getModel('rss/rss');
      +    /**
      +     * Retrieve Customer instance
      +     *
      +     * @return Mage_Customer_Model_Customer
      +     */
      +    protected function _getCustomer()
      +    {
      +        if (is_null($this->_customer)) {
      +            $this->_customer = Mage::getModel('customer/customer');
       
      -        if ($cid) {
      -            $customer = Mage::getModel('customer/customer')->load($cid);
      -            if ($customer && $customer->getId()) {
      +            $params = Mage::helper('core')->urlDecode($this->getRequest()->getParam('data'));
      +            $data   = explode(',', $params);
      +            $cId    = abs(intval($data[0]));
      +            if ($cId) {
      +                $this->_customer->load($cId);
      +            }
      +        }
      +
      +        return $this->_customer;
      +    }
       
      -                $wishlist = Mage::getModel('wishlist/wishlist')
      -                ->loadByCustomer($customer, true);
      +    /**
      +     * Render block HTML
      +     *
      +     * @return string
      +     */
      +    protected function _toHtml()
      +    {
      +        /* @var $rssObj Mage_Rss_Model_Rss */
      +        $rssObj = Mage::getModel('rss/rss');
       
      -                $newurl = Mage::getUrl('wishlist/shared/index',array('code'=>$wishlist->getSharingCode()));
      -                $title = Mage::helper('rss')->__('%s\'s Wishlist',$customer->getName());
      -                $lang = Mage::getStoreConfig('general/locale/code');
      +        if ($this->_getWishlist()->getId()) {
      +            $newUrl = Mage::getUrl('wishlist/shared/index', array(
      +                'code'  => $this->_getWishlist()->getSharingCode()
      +            ));
       
      -                $data = array('title' => $title,
      -                    'description' => $title,
      -                    'link'        => $newurl,
      -                    'charset'     => 'UTF-8',
      -                    'language'    => $lang
      -                );
      -                $rssObj->_addHeader($data);
      +            $title  = Mage::helper('rss')->__('%s\'s Wishlist', $this->_getCustomer()->getName());
      +            $lang   = Mage::getStoreConfig('general/locale/code');
       
      -                $collection = $wishlist->getProductCollection()
      -                            ->addAttributeToSelect('url_key')
      -                            ->addAttributeToSelect('name')
      -                            ->addAttributeToSelect('price')
      -                            ->addAttributeToSelect('thumbnail')
      -                            ->addAttributeToFilter('store_id', array('in'=> $wishlist->getSharedStoreIds()))
      -                            ->load();
      +            $rssObj->_addHeader(array(
      +                'title'         => $title,
      +                'description'   => $title,
      +                'link'          => $newUrl,
      +                'charset'       => 'UTF-8',
      +                'language'      => $lang
      +            ));
       
      -                $product = Mage::getModel('catalog/product');
      -                foreach($collection as $item){
      -                    $product->unsetData()->load($item->getProductId());
      -                    $description = '
      '. - ''. - ''. - '
      '. - $product->getDescription(). - '

      Price:'.Mage::helper('core')->currency($product->getPrice()). - ($product->getPrice() != $product->getFinalPrice() ? ' Special Price:'. Mage::helper('core')->currency($product->getFinalPrice()) : ''). - ($item->getDescription() && $item->getDescription() != Mage::helper('wishlist')->defaultCommentString() ? '

      Comment: '.$item->getDescription().'

      ' : ''). - '

      '; - $data = array( - 'title' => $product->getName(), - 'link' => $product->getProductUrl(), - 'description' => $description, - ); - $rssObj->_addEntry($data); + /* @var $product Mage_Catalog_Model_Product */ + foreach ($this->getWishlistItems() as $product) { + $description = '' + . '
      ' . $this->htmlEscape($product->getShortDescription()) . '

      '; + if ($product->getPrice() != $product->getFinalPrice()) { + $description .= Mage::helper('catalog')->__('Regular Price:') . ' ' + . Mage::helper('core')->currency($product->getPrice()) . ' ' + . Mage::helper('catalog')->__('Special Price:') . ' ' + . Mage::helper('core')->currency($product->getFinalPrice()).''; + } + else { + $description .= Mage::helper('catalog')->__('Price:') . ' ' + . Mage::helper('core')->currency($product->getFinalPrice()); + } + $description .= '

      '; + if ($this->hasDescription($product)) { + $description .= '

      ' . Mage::helper('wishlist')->__('Comment:') + . ' ' . $this->getEscapedDescription($product) . '

      '; } - } + $description .= '

      '; - } else { - $data = array('title' => Mage::helper('rss')->__('Cannot retrieve the wishlist'), - 'description' => Mage::helper('rss')->__('Cannot retrieve the wishlist'), - 'link' => Mage::getUrl(), - 'charset' => 'UTF-8', - ); - $rssObj->_addHeader($data); + $rssObj->_addEntry(array( + 'title' => $product->getName(), + 'link' => $this->getProductUrl($product), + 'description' => $description, + )); + } + } + else { + $rssObj->_addHeader(array( + 'title' => Mage::helper('rss')->__('Cannot retrieve the wishlist'), + 'description' => Mage::helper('rss')->__('Cannot retrieve the wishlist'), + 'link' => Mage::getUrl(), + 'charset' => 'UTF-8', + )); } + return $rssObj->createRssXml(); } -} \ No newline at end of file + /** + * Retrieve Product View URL + * + * @param Mage_Catalog_Model_Product $product + * @param array $additional + * @return string + */ + public function getProductUrl($product, $additional = array()) + { + $additional['_rss'] = true; + return parent::getProductUrl($product, $additional); + } +} diff --git a/app/code/core/Mage/Rule/Model/Rule.php b/app/code/core/Mage/Rule/Model/Rule.php index 86eb0c0682..4ddb5a6014 100644 --- a/app/code/core/Mage/Rule/Model/Rule.php +++ b/app/code/core/Mage/Rule/Model/Rule.php @@ -244,10 +244,13 @@ protected function _afterLoad() $this->getActions()->loadArray($actionsArr); } - $this->setWebsiteIds(explode(',',$this->getWebsiteIds())); + $websiteIds = $this->getWebsiteIds(); + if (is_string($websiteIds)) { + $this->setWebsiteIds(explode(',', $websiteIds)); + } $groupIds = $this->getCustomerGroupIds(); if (is_string($groupIds)) { - $this->setCustomerGroupIds(explode(',',$groupIds)); + $this->setCustomerGroupIds(explode(',', $groupIds)); } } diff --git a/app/code/core/Mage/Sales/Block/Order/Creditmemo.php b/app/code/core/Mage/Sales/Block/Order/Creditmemo.php index 2fa4862151..000cec8a28 100644 --- a/app/code/core/Mage/Sales/Block/Order/Creditmemo.php +++ b/app/code/core/Mage/Sales/Block/Order/Creditmemo.php @@ -31,7 +31,7 @@ * @package Mage_Sales * @author Magento Core Team */ -class Mage_Sales_Block_Order_Creditmemo extends Mage_Core_Block_Template +class Mage_Sales_Block_Order_Creditmemo extends Mage_Sales_Block_Order_Creditmemo_Items { protected function _construct() { diff --git a/app/code/core/Mage/Sales/Block/Order/Creditmemo/Items.php b/app/code/core/Mage/Sales/Block/Order/Creditmemo/Items.php index e7ab02804d..2bf085999b 100644 --- a/app/code/core/Mage/Sales/Block/Order/Creditmemo/Items.php +++ b/app/code/core/Mage/Sales/Block/Order/Creditmemo/Items.php @@ -43,11 +43,30 @@ public function getOrder() return Mage::registry('current_order'); } - public function getPrintCreditmemoUrl($creditmemo){ + public function getPrintCreditmemoUrl($creditmemo) + { return Mage::getUrl('*/*/printCreditmemo', array('creditmemo_id' => $creditmemo->getId())); } - public function getPrintAllCreditmemosUrl($order){ + public function getPrintAllCreditmemosUrl($order) + { return Mage::getUrl('*/*/printCreditmemo', array('order_id' => $order->getId())); } + + /** + * Get creditmemo totals block html + * + * @param Mage_Sales_Model_Order_Creditmemo $creditmemo + * @return string + */ + public function getTotalsHtml($creditmemo) + { + $totals = $this->getChild('creditmemo_totals'); + $html = ''; + if ($totals) { + $totals->setCreditmemo($creditmemo); + $html = $totals->toHtml(); + } + return $html; + } } \ No newline at end of file diff --git a/app/code/core/Mage/Sales/Block/Order/Creditmemo/Totals.php b/app/code/core/Mage/Sales/Block/Order/Creditmemo/Totals.php new file mode 100644 index 0000000000..99c262960e --- /dev/null +++ b/app/code/core/Mage/Sales/Block/Order/Creditmemo/Totals.php @@ -0,0 +1,109 @@ +_creditmemo === null) { + if ($this->hasData('creditmemo')) { + $this->_creditmemo = $this->_getData('creditmemo'); + } elseif (Mage::registry('current_creditmemo')) { + $this->_creditmemo = Mage::registry('current_creditmemo'); + } elseif ($this->getParentBlock()->getCreditmemo()) { + $this->_creditmemo = $this->getParentBlock()->getCreditmemo(); + } + } + return $this->_creditmemo; + } + + public function setCreditmemo($creditmemo) + { + $this->_creditmemo = $creditmemo; + return $this; + } + + /** + * Get totals source object + * + * @return Mage_Sales_Model_Order + */ + public function getSource() + { + return $this->getCreditmemo(); + } + + /** + * Initialize order totals array + * + * @return Mage_Sales_Block_Order_Totals + */ + protected function _initTotals() + { + parent::_initTotals(); + $this->removeTotal('base_grandtotal'); + if ((float) $this->getSource()->getAdjustmentPositive()) { + $total = new Varien_Object(array( + 'code' => 'adjustment_positive', + 'value' => $this->getSource()->getAdjustmentPositive(), + 'label' => $this->__('Adjustment Refund') + )); + $this->addTotal($total); + } + if ((float) $this->getSource()->getAdjustmentNegative()) { + $total = new Varien_Object(array( + 'code' => 'adjustment_negative', + 'value' => $this->getSource()->getAdjustmentNegative(), + 'label' => $this->__('Adjustment Fee') + )); + $this->addTotal($total); + } + /** + getCanDisplayTotalPaid()): ?> +
      __('Total Paid') ?>formatPrice($_creditmemo->getTotalPaid()) ?>
      __('Total Refunded') ?>formatPrice($_creditmemo->getTotalRefunded()) ?>
      __('Total Due') ?>formatPrice($_creditmemo->getTotalDue()) ?>
      salesrule
      - - - salesrule_customer
      -
      + salesrule
      + salesrule_customer
      + @@ -58,6 +55,7 @@ Mage_SalesRule + Mage_Sales_Model_Mysql4_Setup core_setup @@ -74,17 +72,14 @@ - + + + * + * + * + + - - - - singleton - salesrule/observer - sales_quote_address_discount_item - - - @@ -95,6 +90,22 @@ + + + + + salesrule/quote_freeshipping + subtotal + shipping + + + salesrule/quote_discount + subtotal,shipping + grand_total + + + + diff --git a/app/code/core/Mage/SalesRule/sql/salesrule_setup/mysql4-upgrade-0.7.7-0.7.8.php b/app/code/core/Mage/SalesRule/sql/salesrule_setup/mysql4-upgrade-0.7.7-0.7.8.php new file mode 100644 index 0000000000..fd6f5b15e7 --- /dev/null +++ b/app/code/core/Mage/SalesRule/sql/salesrule_setup/mysql4-upgrade-0.7.7-0.7.8.php @@ -0,0 +1,39 @@ +addAttribute('quote_address', 'discount_description', array('type'=>'varchar')); +$installer->addAttribute('quote_address', 'shipping_discount_amount', array('type'=>'decimal')); +$installer->addAttribute('quote_address', 'base_shipping_discount_amount', array('type'=>'decimal')); + + +$installer->addAttribute('order', 'discount_description', array('type'=>'varchar')); +$installer->addAttribute('order', 'shipping_discount_amount', array('type'=>'decimal')); +$installer->addAttribute('order', 'base_shipping_discount_amount', array('type'=>'decimal')); diff --git a/app/code/core/Mage/SalesRule/sql/salesrule_setup/mysql4-upgrade-0.7.8-0.7.9.php b/app/code/core/Mage/SalesRule/sql/salesrule_setup/mysql4-upgrade-0.7.8-0.7.9.php new file mode 100644 index 0000000000..f9d5d765d3 --- /dev/null +++ b/app/code/core/Mage/SalesRule/sql/salesrule_setup/mysql4-upgrade-0.7.8-0.7.9.php @@ -0,0 +1,48 @@ +getConnection()->addColumn( + $this->getTable('salesrule'), + 'apply_to_shipping', + "tinyint(1) unsigned not null default '0' after simple_free_shipping" +); + +$installer->run(" +CREATE TABLE `{$this->getTable('salesrule/label')}` ( + `label_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `rule_id` int(10) unsigned NOT NULL, + `store_id` smallint(5) unsigned NOT NULL, + `label` varchar(255) DEFAULT NULL, + PRIMARY KEY (`label_id`), + UNIQUE KEY `IDX_RULE_STORE` (`rule_id`,`store_id`), + KEY `FK_SALESRULE_LABEL_STORE` (`store_id`), + KEY `FK_SALESRULE_LABEL_RULE` (`rule_id`), + CONSTRAINT `FK_SALESRULE_LABEL_RULE` FOREIGN KEY (`rule_id`) REFERENCES `{$this->getTable('salesrule')}` (`rule_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_SALESRULE_LABEL_STORE` FOREIGN KEY (`store_id`) REFERENCES `{$this->getTable('core/store')}` (`store_id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +"); \ No newline at end of file diff --git a/app/code/core/Mage/Sendfriend/Block/Send.php b/app/code/core/Mage/Sendfriend/Block/Send.php index 5d90eff7de..a93e2c0dc2 100644 --- a/app/code/core/Mage/Sendfriend/Block/Send.php +++ b/app/code/core/Mage/Sendfriend/Block/Send.php @@ -18,61 +18,147 @@ * 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_Sendfriend - * @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_Sendfriend + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ +/** + * Email to a Friend Block + * + * @category Mage + * @package Mage_Sendfriend + * @author Magento Core Team + */ class Mage_Sendfriend_Block_Send extends Mage_Core_Block_Template { - /** + /** * Retrieve username for form field * * @return string */ public function getUserName() { - if ($name = $this->getFormData()->getData('sender/name')) { - return $name; + $name = $this->getFormData()->getData('sender/name'); + if (!empty($name)) { + return trim($name); + } + + /* @var $session Mage_Customer_Model_Session */ + $session = Mage::getSingleton('customer/session'); + + if ($session->isLoggedIn()) { + return $session->getCustomer()->getName(); } - $firstName =(string)Mage::getSingleton('customer/session')->getCustomer()->getFirstname(); - $lastName = (string)Mage::getSingleton('customer/session')->getCustomer()->getLastname(); - return $firstName.' '.$lastName; + + return ''; } + /** + * Retrieve sender email address + * + * @return string + */ public function getEmail() { - if ($email = $this->getFormData()->getData('sender/email')) { - return $email; + $email = $this->getFormData()->getData('sender/email'); + if (!empty($email)) { + return trim($email); + } + + /* @var $session Mage_Customer_Model_Session */ + $session = Mage::getSingleton('customer/session'); + + if ($session->isLoggedIn()) { + return $session->getCustomer()->getEmail(); } - return (string) Mage::getSingleton('customer/session')->getCustomer()->getEmail(); + + return ''; } + /** + * Retrieve Message text + * + * @return string + */ + public function getMessage() + { + return $this->getFormData()->getData('sender/message'); + } + + /** + * Retrieve Form data or empty Varien_Object + * + * @return Varien_Object + */ public function getFormData() { $data = $this->getData('form_data'); - if (is_null($data)) { - $data = new Varien_Object(Mage::getSingleton('catalog/session')->getFormData(true)); - $this->setFormData($data); + if (!$data instanceof Varien_Object) { + $data = new Varien_Object(); + $this->setData('form_data', $data); } + return $data; } + /** + * Set Form data array + * + * @param array $data + * @return Mage_Sendfriend_Block_Send + */ + public function setFormData($data) + { + if (is_array($data)) { + $this->setData('form_data', new Varien_Object($data)); + } + + return $this; + } + + /** + * Retrieve Current Product Id + * + * @return int + */ public function getProductId() { - return $this->getRequest()->getParam('id'); + return $this->getRequest()->getParam('id', null); } + /** + * Retrieve current category id for product + * + * @return int + */ public function getCategoryId() { return $this->getRequest()->getParam('cat_id', null); } - public function getMaxRecipients() - { - $sendToFriendModel = Mage::registry('send_to_friend_model'); - return $sendToFriendModel->getMaxRecipients(); - } -} \ No newline at end of file + /** + * Retrieve Max Recipients + * + * @return int + */ + public function getMaxRecipients() + { + return Mage::helper('sendfriend')->getMaxRecipients(); + } + + /** + * Retrieve Send URL for Form Action + * + * @return string + */ + public function getSendUrl() + { + return Mage::getUrl('*/*/sendmail', array( + 'id' => $this->getProductId(), + 'cat_id' => $this->getCategoryId() + )); + } +} diff --git a/app/code/core/Mage/Sendfriend/Helper/Data.php b/app/code/core/Mage/Sendfriend/Helper/Data.php index edf127f8a4..1328ad3da7 100644 --- a/app/code/core/Mage/Sendfriend/Helper/Data.php +++ b/app/code/core/Mage/Sendfriend/Helper/Data.php @@ -18,13 +18,118 @@ * 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_Sedfriend + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + +/** + * Sendfriend Data Helper + * + * @category Mage + * @package Mage_Sedfriend + * @author Magento Core Team + */ class Mage_Sendfriend_Helper_Data extends Mage_Core_Helper_Abstract { + const XML_PATH_ENABLED = 'sendfriend/email/enabled'; + const XML_PATH_ALLOW_FOR_GUEST = 'sendfriend/email/allow_guest'; + const XML_PATH_MAX_RECIPIENTS = 'sendfriend/email/max_recipients'; + const XML_PATH_MAX_PER_HOUR = 'sendfriend/email/max_per_hour'; + const XML_PATH_LIMIT_BY = 'sendfriend/email/check_by'; + const XML_PATH_EMAIL_TEMPLATE = 'sendfriend/email/template'; + + const COOKIE_NAME = 'stf'; + + const CHECK_IP = 1; + const CHECK_COOKIE = 0; + + /** + * Check is enabled Module + * + * @param int $store + * @return bool + */ + public function isEnabled($store = null) + { + return Mage::getStoreConfigFlag(self::XML_PATH_ENABLED, $store); + } + + /** + * Check allow send email for guest + * + * @param int $store + * @return bool + */ + public function isAllowForGuest($store = null) + { + return Mage::getStoreConfigFlag(self::XML_PATH_ALLOW_FOR_GUEST, $store); + } + + /** + * Retrieve Max Recipients + * + * @param int $store + * @return int + */ + public function getMaxRecipients($store = null) + { + return (int)Mage::getStoreConfig(self::XML_PATH_MAX_RECIPIENTS, $store); + } + + /** + * Retrieve Max Products Sent in 1 Hour + * + * @param int $store + * @return int + */ + public function getMaxEmailPerPeriod($store = null) + { + return (int)Mage::getStoreConfig(self::XML_PATH_MAX_PER_HOUR, $store); + } + + /** + * Retrieve Limitation Period in seconds (1 hour) + * + * @return int + */ + public function getPeriod() + { + return 3600; + } + + /** + * Retrieve Limit Sending By + * + * @param int $store + * @return int + */ + public function getLimitBy($store = null) + { + return (int)Mage::getStoreConfig(self::XML_PATH_LIMIT_BY, $store); + } + + /** + * Retrieve Email Template + * + * @param int $store + * @return mixed + */ + public function getEmailTemplate($store = null) + { + return Mage::getStoreConfig(self::XML_PATH_EMAIL_TEMPLATE, $store); + } + /** + * Retrieve Key Name for Cookie + * + * @see self::COOKIE_NAME + * @return string + */ + public function getCookieName() + { + return self::COOKIE_NAME; + } } diff --git a/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend.php b/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend.php index b06ffb263a..a81ca34c32 100644 --- a/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend.php +++ b/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend.php @@ -18,36 +18,63 @@ * 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_Sendfriend - * @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_Sendfriend + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + +/** + * SendFriend Log Resource Model + * + * @category Mage + * @package Mage_Sendfriend + * @author Magento Core Team + */ class Mage_Sendfriend_Model_Mysql4_Sendfriend extends Mage_Core_Model_Mysql4_Abstract { + /** + * Initialize connection and table + * + */ protected function _construct() { $this->_init('sendfriend/sendfriend', 'log_id'); } - public function getSendCount($model, $ip, $startTime) + /** + * Retrieve Sended Emails By Ip + * + * @param Mage_Sendfriend_Model_Sendfriend $object + * @param int $ip + * @param int $startTime + * @return int + */ + public function getSendCount($object, $ip, $startTime) { $select = $this->_getReadAdapter()->select() - ->from(array('main_table' => $this->getTable('sendfriend')), new Zend_Db_Expr('count(*)')) - ->where('main_table.ip = ?', $ip) - ->where('main_table.time >= ?', $startTime); + ->from( + array('main_table' => $this->getTable('sendfriend')), + array('count' => new Zend_Db_Expr('count(*)'))) + ->where('ip=?', $ip) + ->where('time>=?', $startTime); - $data = $this->_getReadAdapter()->fetchRow($select); - - return $data['count(*)']; + $row = $this->_getReadAdapter()->fetchRow($select); + return $row['count']; } - public function deleteLogsBefore($time) - { - $deleted = $this->_getWriteAdapter() - ->delete($this->getTable('sendfriend'), $this->_getWriteAdapter()->quoteInto('time < ?', $time)); + /** + * Delete Old logs + * + * @param int $time + * @return Mage_Sendfriend_Model_Mysql4_Sendfriend + */ + public function deleteLogsBefore($time) + { + $cond = $this->_getWriteAdapter()->quoteInto('time_getWriteAdapter()->delete($this->getMainTable(), $cond); return $this; - } -} \ No newline at end of file + } +} diff --git a/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend/Collection.php b/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend/Collection.php index 743333ff58..383d04007d 100644 --- a/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend/Collection.php +++ b/app/code/core/Mage/Sendfriend/Model/Mysql4/Sendfriend/Collection.php @@ -18,16 +18,28 @@ * 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_Sendfriend - * @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_Sendfriend + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + +/** + * Sendfriend log resource collection + * + * @category Mage + * @package Mage_Sendfriend + * @author Magento Core Team + */ class Mage_Sendfriend_Model_Mysql4_Sendfriend_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract { - protected function _construct() - { - $this->_init('sendfriend/sendfriend'); - } -} \ No newline at end of file + /** + * Init resource collection + * + */ + protected function _construct() + { + $this->_init('sendfriend/sendfriend'); + } +} diff --git a/app/code/core/Mage/Sendfriend/Model/Mysql4/Setup.php b/app/code/core/Mage/Sendfriend/Model/Mysql4/Setup.php index 6df2bb5d79..47df5c4196 100644 --- a/app/code/core/Mage/Sendfriend/Model/Mysql4/Setup.php +++ b/app/code/core/Mage/Sendfriend/Model/Mysql4/Setup.php @@ -18,12 +18,20 @@ * 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_Sendfriend - * @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_Sendfriend + * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + +/** + * Sendfriend module Setup Model + * + * @category Mage + * @package Mage_Sendfriend + * @author Magento Core Team + */ class Mage_Sendfriend_Model_Mysql4_Setup extends Mage_Core_Model_Resource_Setup { } diff --git a/app/code/core/Mage/Sendfriend/Model/Observer.php b/app/code/core/Mage/Sendfriend/Model/Observer.php new file mode 100644 index 0000000000..2892c5df0b --- /dev/null +++ b/app/code/core/Mage/Sendfriend/Model/Observer.php @@ -0,0 +1,48 @@ + + */ +class Mage_Sendfriend_Model_Observer +{ + /** + * Register Sendfriend Model in global registry + * + * @param Varien_Event_Observer $observer + * @return Mage_Sendfriend_Model_Observer + */ + public function register(Varien_Event_Observer $observer) + { + Mage::getModel('sendfriend/sendfriend')->register(); + return $this; + } +} diff --git a/app/code/core/Mage/Sendfriend/Model/Sendfriend.php b/app/code/core/Mage/Sendfriend/Model/Sendfriend.php index ff3a980464..530e0bd899 100644 --- a/app/code/core/Mage/Sendfriend/Model/Sendfriend.php +++ b/app/code/core/Mage/Sendfriend/Model/Sendfriend.php @@ -27,69 +27,100 @@ class Mage_Sendfriend_Model_Sendfriend extends Mage_Core_Model_Abstract { /** - * XML configuration paths + * Recipient Names + * + * @var array */ - const XML_PATH_SENDFRIEND_EMAIL_TEMPLATE = 'sendfriend/email/template'; + protected $_names = array(); - protected $_names = array(); - protected $_emails = array(); - protected $_sender = array(); - protected $_ip = 0; - protected $_product = null; + /** + * Recipient Emails + * + * @var array + */ + protected $_emails = array(); - protected $_period = 3600; // hour + /** + * Sender data array + * + * @var array + */ + protected $_sender = array(); - protected $_cookieName = 'stf'; + /** + * Product Instance + * + * @var Mage_Catalog_Model_Product + */ + protected $_product; + /** + * Initialize resource model + * + */ protected function _construct() { $this->_init('sendfriend/sendfriend'); } - public function toOptionArray() + /** + * Retrieve Data Helper + * + * @return Mage_Sendfriend_Helper_Data + */ + protected function _getHelper() { - if(!$collection = Mage::registry('config_system_email_template')) { - $collection = Mage::getResourceModel('core/email_template_collection') - ->load(); + return Mage::helper('sendfriend'); + } - Mage::register('config_system_email_template', $collection); - } - $options = $collection->toOptionArray(); - array_unshift($options, array('value'=>'', 'label'=>'')); - return $options; + /** + * Retrieve Option Array + * + * @deprecated It Is a not Source model + * @return array + */ + public function toOptionArray() + { + return array(); } public function send() { - $translate = Mage::getSingleton('core/translate'); /* @var $translate Mage_Core_Model_Translate */ + $translate = Mage::getSingleton('core/translate'); $translate->setTranslateInline(false); - $errors = array(); + /* @var $mailTemplate Mage_Core_Model_Email_Template */ + $mailTemplate = Mage::getModel('core/email_template'); - $this->_emailModel = Mage::getModel('core/email_template'); - $message = nl2br(htmlspecialchars($this->_sender['message'])); + $message = nl2br(htmlspecialchars($this->getSender()->getMessage())); $sender = array( - 'name' => strip_tags($this->_sender['name']), - 'email' => strip_tags($this->_sender['email']) - ); - - foreach($this->_emails as $key => $email) { - $this->_emailModel->setDesignConfig(array('area'=>'frontend', 'store'=>$this->getStoreId())) - ->sendTransactional( - Mage::getStoreConfig(self::XML_PATH_SENDFRIEND_EMAIL_TEMPLATE), + 'name' => $this->_getHelper()->htmlEscape($this->getSender()->getName()), + 'email' => $this->_getHelper()->htmlEscape($this->getSender()->getEmail()) + ); + + $mailTemplate->setDesignConfig(array( + 'area' => 'frontend', + 'store' => Mage::app()->getStore()->getId() + )); + + foreach ($this->getRecipients()->getEmails() as $k => $email) { + $name = $this->getRecipients()->getNames($k); + $mailTemplate->sendTransactional( + $this->getTemplate(), $sender, $email, - $this->_names[$key], + $name, array( - 'name' => $this->_names[$key], + 'name' => $name, 'email' => $email, - 'product_name' => $this->_product->getName(), - 'product_url' => $this->_product->getProductUrl(), + 'product_name' => $this->getProduct()->getName(), + 'product_url' => $this->getProduct()->getUrlInStore(), 'message' => $message, - 'sender_name' => strip_tags($this->_sender['name']), - 'sender_email' => strip_tags($this->_sender['email']), - 'product_image' => Mage::helper('catalog/image')->init($this->_product, 'small_image')->resize(75), + 'sender_name' => $sender['name'], + 'sender_email' => $sender['email'], + 'product_image' => Mage::helper('catalog/image')->init($this->getProduct(), + 'small_image')->resize(75), ) ); } @@ -99,87 +130,246 @@ public function send() return $this; } + /** + * Validate Form data + * + * @return bool|array + */ public function validate() { $errors = array(); - $helper = Mage::helper('sendfriend'); - if (empty($this->_sender['name'])) { - $errors[] = $helper->__('Sender name can\'t be empty'); + $name = $this->getSender()->getName(); + if (empty($name)) { + $errors[] = Mage::helper('sendfriend')->__('Sender name can\'t be empty'); + } + + $email = $this->getSender()->getEmail(); + if (empty($email) OR !Zend_Validate::is($email, 'EmailAddress')) { + $errors[] = Mage::helper('sendfriend')->__('Invalid sender email'); } - if (!isset($this->_sender['email']) || !Zend_Validate::is($this->_sender['email'], 'EmailAddress')) { - $errors[] = $helper->__('Invalid sender email'); + $message = $this->getSender()->getMessage(); + if (empty($message)) { + $errors[] = Mage::helper('sendfriend')->__('Message can\'t be empty'); } - if (empty($this->_sender['message'])) { - $errors[] = $helper->__('Message can\'t be empty'); + if (!$this->getRecipients()->getEmails()) { + $errors[] = Mage::helper('sendfriend')->__('You have to specify at least one recipient'); } - foreach ($this->_emails as $email) { + // validate recipients email addresses + foreach ($this->getRecipients()->getEmails() as $email) { if (!Zend_Validate::is($email, 'EmailAddress')) { - $errors[] = $helper->__('You input invalid email address for recipient'); + $errors[] = Mage::helper('sendfriend')->__('You input invalid email address for recipient'); break; } } - if (!$this->canEmailToFriend()) { - $errors[] = $helper->__('You cannot email this product to a friend'); - } + switch ($this->_getHelper()->getLimitBy()) { + case Mage_Sendfriend_Helper_Data::CHECK_COOKIE: + $amount = $this->_amountByCookies(); + break; - if ($this->_getSendToFriendCheckType()) { - $amount = $this->_amountByCookies(); - } else { - $amount = $this->_amountByIp(); + case Mage_Sendfriend_Helper_Data::CHECK_IP: + $amount = $this->_amountByIp(); + break; + default: + $amount = 0; + break; } if ($amount >= $this->getMaxSendsToFriend()){ - $errors[] = $helper->__('You have exceeded limit of %d sends in an hour', $this->getMaxSendsToFriend()); + $errors[] = Mage::helper('sendfriend')->__('You have exceeded limit of %d sends in an hour', $this->getMaxSendsToFriend()); } $maxRecipients = $this->getMaxRecipients(); - if (count($this->_emails) > $maxRecipients) { - $errors[] = $helper->__('You cannot send more than %d emails at a time', $this->getMaxRecipients()); + if (count($this->getRecipients()->getEmails()) > $maxRecipients) { + $errors[] = Mage::helper('sendfriend')->__('You cannot send more than %d emails at a time', $this->getMaxRecipients()); } - if (count($this->_emails) < 1) { - $errors[] = $helper->__('You have to specify at least one recipient'); + if (empty($errors)) { + return true; } - if (!$this->getTemplate()){ - $errors[] = $helper->__('Email template is not specified by administrator'); - } + return $errors; + } + /** + * Set cookie instance + * + * @param Mage_Core_Model_Cookie $product + * @return Mage_Sendfriend_Model_Sendfriend + */ + public function setCookie($cookie) + { + return $this->setData('_cookie', $cookie); + } - if (empty($errors)) { - return true; + /** + * Retrieve Cookie instance + * + * @throws Mage_Core_Exception + * @return Mage_Core_Model_Cookie + */ + public function getCookie() + { + $cookie = $this->_getData('_cookie'); + if (!$cookie instanceof Mage_Core_Model_Cookie) { + Mage::throwException(Mage::helper('sendfriend')->__('Please define correct Cookie instance')); } - return $errors; + return $cookie; } - public function setIp($ip) + /** + * Set Visitor Remote Address + * + * @param int $ipAddr the IP address on Long Format + * @return Mage_Sendfriend_Model_Sendfriend + */ + public function setRemoteAddr($ipAddr) { - $this->_ip = $ip; + Mage::log(array(__METHOD__, $ipAddr)); + $this->setData('_remote_addr', $ipAddr); + return $this; } + /** + * Retrieve Visitor Remote Address + * + * @return int + */ + public function getRemoteAddr() + { + Mage::log(array(__METHOD__, $this->_getData('_remote_addr'))); + return $this->_getData('_remote_addr'); + } + + /** + * Set Recipients + * + * @param array $recipients + * @return Mage_Sendfriend_Model_Sendfriend + */ public function setRecipients($recipients) { - $this->_emails = array_unique($recipients['email']); - $this->_names = $recipients['name']; + // validate array + if (!is_array($recipients) OR !isset($recipients['email']) + OR !isset($recipients['name']) OR !is_array($recipients['email']) + OR !is_array($recipients['name'])) { + return $this; + } + + $emails = array(); + $names = array(); + foreach ($recipients['email'] as $k => $email) { + if (!isset($emails[$email]) && isset($recipients['name'][$k])) { + $emails[$email] = true; + $names[] = $recipients['name'][$k]; + } + } + + if ($emails) { + $emails = array_keys($emails); + } + + return $this->setData('_recipients', new Varien_Object(array( + 'emails' => $emails, + 'names' => $names + ))); + } + + /** + * Retrieve Recipients object + * + * @return Varien_Object + */ + public function getRecipients() + { + $recipients = $this->_getData('_recipients'); + if (!$recipients instanceof Varien_Object) { + $recipients = new Varien_Object(array( + 'emails' => array(), + 'names' => array() + )); + $this->setData('_recipients', $recipients); + } + return $recipients; } - public function setProduct($product){ - $this->_product = $product; + /** + * Set product instance + * + * @param Mage_Catalog_Model_Product $product + * @return Mage_Sendfriend_Model_Sendfriend + */ + public function setProduct($product) + { + return $this->setData('_product', $product); + } + + /** + * Retrieve Product instance + * + * @throws Mage_Core_Exception + * @return Mage_Catalog_Model_Product + */ + public function getProduct() + { + $product = $this->_getData('_product'); + if (!$product instanceof Mage_Catalog_Model_Product) { + Mage::throwException(Mage::helper('sendfriend')->__('Please define correct Product instance')); + } + return $product; + } + + /** + * Set Sender Information array + * + * @param array $sender + * @return Mage_Sendfriend_Model_Sendfriend + */ + public function setSender($sender) + { + if (!is_array($sender)) { + Mage::helper('sendfriend')->__('Invalid Sender information'); + } + + return $this->setData('_sender', new Varien_Object($sender)); } - public function setSender($sender){ - $this->_sender = $sender; + /** + * Retrieve Sender Information Object + * + * @throws Mage_Core_Exception + * @return Varien_Object + */ + public function getSender() + { + $sender = $this->_getData('_sender'); + if (!$sender instanceof Varien_Object) { + Mage::throwException(Mage::helper('sendfriend')->__('Please define correct Sender information')); + } + return $sender; } - public function getSendCount($ip, $startTime) + /** + * Retrieve Send count by IP + * + * @param int $ip + * @param int $startTime + * @return int + */ + public function getSendCount($ip = null, $startTime = null) { - $count = $this->_getResource()->getSendCount($this, $ip, $startTime); - return $count; + if (is_null($ip)) { + $ip = $this->getRemoteAddr(); + } + if (is_null($startTime)) { + $startTime = time() - $this->_getHelper()->getPeriod(); + } + + return $this->_getResource()->getSendCount($this, $ip, $startTime); } /** @@ -189,17 +379,17 @@ public function getSendCount($ip, $startTime) */ public function getMaxSendsToFriend() { - return max(0, (int) Mage::getStoreConfig('sendfriend/email/max_per_hour')); + return $this->_getHelper()->getMaxEmailPerPeriod(); } /** - * Get current "Send to friend" template + * Get current Email "Send to friend" template * * @return string */ public function getTemplate() { - return Mage::getStoreConfig('sendfriend/email/template'); + return $this->_getHelper()->getEmailTemplate(); } /** @@ -209,7 +399,7 @@ public function getTemplate() */ public function getMaxRecipients() { - return max(0, (int) Mage::getStoreConfig('sendfriend/email/max_recipients')); + return $this->_getHelper()->getMaxRecipients(); } /** @@ -219,84 +409,63 @@ public function getMaxRecipients() */ public function canEmailToFriend() { - if (!Mage::getStoreConfig('sendfriend/email/enabled')) { - return false; - } - if (!Mage::getStoreConfig('sendfriend/email/allow_guest') - && !Mage::getSingleton('customer/session')->isLoggedIn()) { - return false; - } - return true; - } - - /** - * Get check type for "Send to Friend" function - * - * @return integer - */ - private function _getSendToFriendCheckType() - { - return max(0, (int) Mage::getStoreConfig('sendfriend/email/check_by')); + return $this->_getHelper()->isEnabled(); } /** * Retrieve amount by cookie + * * @return int */ - private function _amountByCookies() + protected function _amountByCookies() { + $cookie = $this->_getHelper()->getCookieName(); + $time = time(); $newTimes = array(); - $oldTimes = Mage::app()->getCookie() - ->get($this->_cookieName); - if ($oldTimes){ + $oldTimes = $this->getCookie()->get($cookie); + if ($oldTimes) { $oldTimes = explode(',', $oldTimes); - foreach ($oldTimes as $time){ - if (is_numeric($time) && $time >= time()-$this->_period){ - $newTimes[] = $time; + foreach ($oldTimes as $oldTime) { + $periodTime = $time - $this->_getHelper()->getPeriod(); + if (is_numeric($oldTime) AND $oldTime >= $periodTime) { + $newTimes[] = $oldTime; } } } + $amount = count($newTimes); + $newTimes[] = $time; - $newTimes[] = time(); - Mage::app()->getCookie() - ->set($this->_cookieName, implode(',', $newTimes), $this->_period); + $this->getCookie()->set($cookie, implode(',', $newTimes)); return $amount; } /** * Retrieve amount by IP address - * + * * @return int */ - private function _amountByIp() + protected function _amountByIp() { - $this->_deleteLogsBefore(time() - $this->_period); + $time = time(); + $period = $this->_getHelper()->getPeriod(); + + // delete expired logs + $this->_getResource()->deleteLogsBefore($time - $period); $amount = $this->getSendCount($this->_ip, time() - $this->_period); - $this->setData(array('ip'=>$this->_ip, 'time'=>time())); - $this->save(); + $this->setIp($this->getRemoteAddr()) + ->setTime($time) + ->save(); 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 - * + * Register self in global register with name send_to_friend_model + * * @return Mage_Sendfriend_Model_Sendfriend */ public function register() @@ -306,4 +475,4 @@ public function register() } return $this; } -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Sendfriend/controllers/ProductController.php b/app/code/core/Mage/Sendfriend/controllers/ProductController.php index 5c09575199..32e08876ba 100644 --- a/app/code/core/Mage/Sendfriend/controllers/ProductController.php +++ b/app/code/core/Mage/Sendfriend/controllers/ProductController.php @@ -24,24 +24,66 @@ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ +/** + * Email to a Friend Product Controller + * + * @category Mage + * @package Mage_Sedfriend + * @author Magento Core Team + */ class Mage_Sendfriend_ProductController extends Mage_Core_Controller_Front_Action { /** - * Initialize product instance + * Predispatch: check is enable module + * If allow only for customer - redirect to login page + * + * @return Mage_Sendfriend_ProductController + */ + public function preDispatch() + { + parent::preDispatch(); + + /* @var $helper Mage_Sendfriend_Helper_Data */ + $helper = Mage::helper('sendfriend'); + /* @var $session Mage_Customer_Model_Session */ + $session = Mage::getSingleton('customer/session'); + + if (!$helper->isEnabled()) { + $this->_forward('noRoute'); + return $this; + } + + if (!$helper->isAllowForGuest() && !$session->authenticate($this)) { + $this->setFlag('', self::FLAG_NO_DISPATCH, true); + if ($this->getRequest()->getActionName() == 'sendemail') { + $session->setBeforeAuthUrl(Mage::getUrl('*/*/send', array( + '_current' => true + ))); + Mage::getSingleton('catalog/session') + ->setSendfriendFormData($this->getRequest()->getPost()); + } + } + + return $this; + } + + /** + * Initialize Product Instance * * @return Mage_Catalog_Model_Product */ protected function _initProduct() { - $productId = (int) $this->getRequest()->getParam('id'); + $productId = (int)$this->getRequest()->getParam('id'); if (!$productId) { return false; } $product = Mage::getModel('catalog/product') ->load($productId); - if (!$product->getId()) { + if (!$product->getId() || !$product->isVisibleInCatalog()) { return false; } + Mage::register('product', $product); return $product; } @@ -51,94 +93,114 @@ protected function _initProduct() * * @return Mage_Sendfriend_Model_Sendfriend */ - protected function _initSendToFriendModel(){ - $sendToFriendModel = Mage::getModel('sendfriend/sendfriend'); - Mage::register('send_to_friend_model', $sendToFriendModel); - return $sendToFriendModel; - } + protected function _initSendToFriendModel() + { + $model = Mage::getModel('sendfriend/sendfriend'); + $model->setRemoteAddr(Mage::helper('core/http')->getRemoteAddr(true)); + $model->setCookie(Mage::app()->getCookie()); - public function sendAction(){ - $product = $this->_initProduct(); - $this->_initSendToFriendModel(); + Mage::register('send_to_friend_model', $model); - if (!$product || !$product->isVisibleInCatalog()) { - $this->_forward('noRoute'); - return; - } + return $model; + } - $productHelper = Mage::helper('catalog/product'); - $sendToFriendModel = Mage::registry('send_to_friend_model'); + /** + * Show Send to a Friend Form + * + */ + public function sendAction() + { + $product = $this->_initProduct(); + $model = $this->_initSendToFriendModel(); - /** - * check if user is allowed to send product to a friend - */ - if (!$sendToFriendModel->canEmailToFriend()) { - Mage::getSingleton('catalog/session')->addError( - $this->__('You cannot email this product to a friend') - ); - $this->_redirectReferer($product->getProductUrl()); + if (!$product) { + $this->_forward('noRoute'); return; } - $maxSendsToFriend = $sendToFriendModel->getMaxSendsToFriend(); - if ($maxSendsToFriend){ + if ($model->getMaxSendsToFriend()) { Mage::getSingleton('catalog/session')->addNotice( - $this->__('You cannot send more than %d times in an hour', $maxSendsToFriend) + $this->__('You cannot send more than %d times in an hour', $model->getMaxSendsToFriend()) ); } $this->loadLayout(); $this->_initLayoutMessages('catalog/session'); + + Mage::dispatchEvent('sendfriend_product', array('product' => $product)); + $data = Mage::getSingleton('catalog/session')->getSendfriendFormData(); + if ($data) { + Mage::getSingleton('catalog/session')->setSendfriendFormData(true); + $block = $this->getLayout()->getBlock('sendfriend.send'); + if ($block) { + $block->setFormData($data); + } + } + $this->renderLayout(); - Mage::dispatchEvent('sendfriend_product', array('product'=>$product)); } + /** + * Send Email Post Action + * + */ public function sendmailAction() { - $product = $this->_initProduct(); - $sendToFriendModel = $this->_initSendToFriendModel(); - $data = $this->getRequest()->getPost(); + if (!$this->_validateFormKey()) { + return $this->_redirect('*/*/send', array('_current' => true)); + } - if (!$product || !$product->isVisibleInCatalog() || !$data) { + $product = $this->_initProduct(); + $model = $this->_initSendToFriendModel(); + $data = $this->getRequest()->getPost(); + + if (!$product || !$data) { $this->_forward('noRoute'); return; } $categoryId = $this->getRequest()->getParam('cat_id', null); - if ($categoryId && $category = Mage::getModel('catalog/category')->load($categoryId)) { + if ($categoryId) { + $category = Mage::getModel('catalog/category') + ->load($categoryId); + $product->setCategory($category); Mage::register('current_category', $category); } - $sendToFriendModel->setSender($this->getRequest()->getPost('sender')); - $sendToFriendModel->setRecipients($this->getRequest()->getPost('recipients')); - $sendToFriendModel->setIp(Mage::getSingleton('log/visitor')->getRemoteAddr()); - $sendToFriendModel->setProduct($product); + $model->setSender($this->getRequest()->getPost('sender')); + $model->setRecipients($this->getRequest()->getPost('recipients')); + $model->setProduct($product); try { - $validateRes = $sendToFriendModel->validate(); - if (true === $validateRes) { - $sendToFriendModel->send(); + $validate = $model->validate(); + if ($validate === true) { + $model->send(); Mage::getSingleton('catalog/session')->addSuccess($this->__('Link to a friend was sent.')); $this->_redirectSuccess($product->getProductUrl()); return; } else { - Mage::getSingleton('catalog/session')->setFormData($data); - if (is_array($validateRes)) { - foreach ($validateRes as $errorMessage) { - Mage::getSingleton('catalog/session')->addError($errorMessage); + if (is_array($validate)) { + foreach ($validate as $errorMessage) { + Mage::getSingleton('catalog/session')->addError($errorMessage); } - } else { + } + else { Mage::getSingleton('catalog/session')->addError($this->__('Some problems with data.')); } } - } catch (Mage_Core_Exception $e) { + } + catch (Mage_Core_Exception $e) { Mage::getSingleton('catalog/session')->addError($e->getMessage()); - } catch (Exception $e) { + } + catch (Exception $e) { Mage::getSingleton('catalog/session') ->addException($e, $this->__('Some emails was not sent')); } - $this->_redirectError(Mage::getURL('*/*/send',array('id'=>$product->getId()))); + // save form data + Mage::getSingleton('catalog/session')->setSendfriendFormData($data); + + $this->_redirectError(Mage::getURL('*/*/send', array('_current' => true))); } -} \ No newline at end of file +} diff --git a/app/code/core/Mage/Sendfriend/etc/config.xml b/app/code/core/Mage/Sendfriend/etc/config.xml index 156b2db85b..255e62463e 100644 --- a/app/code/core/Mage/Sendfriend/etc/config.xml +++ b/app/code/core/Mage/Sendfriend/etc/config.xml @@ -26,52 +26,52 @@ */ --> - - - 0.7.2 - - - - - - Mage_Sendfriend_Model - sendfriend_mysql4 - - - Mage_Sendfriend_Model_Mysql4 - sendfriend_mysql4 - - - sendfriend_log
      -
      -
      -
      -
      - - - - Mage_Sendfriend - Mage_Sendfriend_Model_Mysql4_Setup - - - core_setup - - - - - core_write - - - - - core_read - - - - - Mage_Sendfriend_Block - -