Klevu_SmartSearch - Version 1.1.13

Version Notes

- Attributes change

Download this release

Release Info

Developer Klevu
Extension Klevu_SmartSearch
Version 1.1.13
Comparing to
See all releases


Version 1.1.13

Files changed (194) hide show
  1. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Attribute/Mappings.php +98 -0
  2. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Automatic/Attribute/Mappings.php +23 -0
  3. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Html/Select.php +8 -0
  4. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Image/Button.php +49 -0
  5. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Image/Log.php +49 -0
  6. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Store/Level/Label.php +23 -0
  7. app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Sync/Button.php +48 -0
  8. app/code/community/Klevu/Search/Block/Adminhtml/Form/Information.php +28 -0
  9. app/code/community/Klevu/Search/Block/Adminhtml/Notifications.php +24 -0
  10. app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Config/Button.php +86 -0
  11. app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Configure/Attributes.php +50 -0
  12. app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Configure/Store.php +48 -0
  13. app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Configure/User.php +22 -0
  14. app/code/community/Klevu/Search/Block/Catalog/Product/Tracking.php +26 -0
  15. app/code/community/Klevu/Search/Helper/Api.php +210 -0
  16. app/code/community/Klevu/Search/Helper/Compat.php +51 -0
  17. app/code/community/Klevu/Search/Helper/Config.php +642 -0
  18. app/code/community/Klevu/Search/Helper/Data.php +197 -0
  19. app/code/community/Klevu/Search/Model/Api/Action.php +118 -0
  20. app/code/community/Klevu/Search/Model/Api/Action/Addrecords.php +232 -0
  21. app/code/community/Klevu/Search/Model/Api/Action/Adduser.php +32 -0
  22. app/code/community/Klevu/Search/Model/Api/Action/Addwebstore.php +56 -0
  23. app/code/community/Klevu/Search/Model/Api/Action/Debuginfo.php +24 -0
  24. app/code/community/Klevu/Search/Model/Api/Action/Deleterecords.php +12 -0
  25. app/code/community/Klevu/Search/Model/Api/Action/Gettimezone.php +14 -0
  26. app/code/community/Klevu/Search/Model/Api/Action/Getuserdetail.php +28 -0
  27. app/code/community/Klevu/Search/Model/Api/Action/Idsearch.php +70 -0
  28. app/code/community/Klevu/Search/Model/Api/Action/Producttracking.php +75 -0
  29. app/code/community/Klevu/Search/Model/Api/Action/Removetestmode.php +24 -0
  30. app/code/community/Klevu/Search/Model/Api/Action/Startsession.php +37 -0
  31. app/code/community/Klevu/Search/Model/Api/Action/Updaterecords.php +12 -0
  32. app/code/community/Klevu/Search/Model/Api/Request.php +178 -0
  33. app/code/community/Klevu/Search/Model/Api/Request/Get.php +32 -0
  34. app/code/community/Klevu/Search/Model/Api/Request/Post.php +32 -0
  35. app/code/community/Klevu/Search/Model/Api/Request/Xml.php +127 -0
  36. app/code/community/Klevu/Search/Model/Api/Response.php +107 -0
  37. app/code/community/Klevu/Search/Model/Api/Response/Data.php +37 -0
  38. app/code/community/Klevu/Search/Model/Api/Response/Empty.php +24 -0
  39. app/code/community/Klevu/Search/Model/Api/Response/Invalid.php +60 -0
  40. app/code/community/Klevu/Search/Model/Api/Response/Message.php +28 -0
  41. app/code/community/Klevu/Search/Model/Api/Response/Search.php +73 -0
  42. app/code/community/Klevu/Search/Model/Api/Response/Timezone.php +16 -0
  43. app/code/community/Klevu/Search/Model/Catalog/Model/Config.php +31 -0
  44. app/code/community/Klevu/Search/Model/CatalogSearch/Layer/Filter/Attribute.php +104 -0
  45. app/code/community/Klevu/Search/Model/CatalogSearch/Layer/Filter/Category.php +103 -0
  46. app/code/community/Klevu/Search/Model/CatalogSearch/Layer/Filter/Price.php +93 -0
  47. app/code/community/Klevu/Search/Model/CatalogSearch/Resource/Fulltext/Collection.php +463 -0
  48. app/code/community/Klevu/Search/Model/CatalogSearch/Resource/Layer/Filter/Attribute.php +20 -0
  49. app/code/community/Klevu/Search/Model/Config/Log/Level.php +15 -0
  50. app/code/community/Klevu/Search/Model/Cron.php +13 -0
  51. app/code/community/Klevu/Search/Model/Notification.php +8 -0
  52. app/code/community/Klevu/Search/Model/Observer.php +137 -0
  53. app/code/community/Klevu/Search/Model/Order/Sync.php +364 -0
  54. app/code/community/Klevu/Search/Model/Product/Sync.php +1969 -0
  55. app/code/community/Klevu/Search/Model/Resource/Notification.php +8 -0
  56. app/code/community/Klevu/Search/Model/Resource/Notification/Collection.php +8 -0
  57. app/code/community/Klevu/Search/Model/Session.php +8 -0
  58. app/code/community/Klevu/Search/Model/Sync.php +156 -0
  59. app/code/community/Klevu/Search/Model/System/Config/Source/Additional/Attributes.php +15 -0
  60. app/code/community/Klevu/Search/Model/System/Config/Source/Boosting/Attribute.php +62 -0
  61. app/code/community/Klevu/Search/Model/System/Config/Source/Frequency.php +37 -0
  62. app/code/community/Klevu/Search/Model/System/Config/Source/Landingoptions.php +19 -0
  63. app/code/community/Klevu/Search/Model/System/Config/Source/Log/Level.php +19 -0
  64. app/code/community/Klevu/Search/Model/System/Config/Source/Product/Attributes.php +36 -0
  65. app/code/community/Klevu/Search/Model/System/Config/Source/Yesnoforced.php +18 -0
  66. app/code/community/Klevu/Search/Test/Config/Base.php +13 -0
  67. app/code/community/Klevu/Search/Test/Controller/CatalogSearch.php +342 -0
  68. app/code/community/Klevu/Search/Test/Controller/CatalogSearch/fixtures/search_results.yaml +133 -0
  69. app/code/community/Klevu/Search/Test/Helper/Api.php +10 -0
  70. app/code/community/Klevu/Search/Test/Helper/Compat.php +20 -0
  71. app/code/community/Klevu/Search/Test/Helper/Config.php +345 -0
  72. app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetOrderSyncEnabledFlag.yaml +2 -0
  73. app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetOrderSyncFrequency.yaml +2 -0
  74. app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetProductSyncEnabledFlag.yaml +2 -0
  75. app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetProductSyncFrequency.yaml +2 -0
  76. app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testIsExtensionEnabledDisabled.yaml +2 -0
  77. app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testIsExtensionEnabledEnabled.yaml +2 -0
  78. app/code/community/Klevu/Search/Test/Helper/Config/providers/testIsOrderSyncEnabled.yaml +24 -0
  79. app/code/community/Klevu/Search/Test/Helper/Config/providers/testIsProductSyncEnabled.yaml +24 -0
  80. app/code/community/Klevu/Search/Test/Helper/Config/providers/testIsTestModeEnabled.yaml +16 -0
  81. app/code/community/Klevu/Search/Test/Helper/Data.php +49 -0
  82. app/code/community/Klevu/Search/Test/Helper/Data/providers/testBytesToHumanReadable.yaml +18 -0
  83. app/code/community/Klevu/Search/Test/Helper/Data/providers/testGetLanguageFromLocale.yaml +12 -0
  84. app/code/community/Klevu/Search/Test/Helper/Data/providers/testHumanReadableToBytes.yaml +18 -0
  85. app/code/community/Klevu/Search/Test/Helper/Data/providers/testIsProductionDomain.yaml +15 -0
  86. app/code/community/Klevu/Search/Test/Model/Api/Action.php +55 -0
  87. app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords.php +176 -0
  88. app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFields.yaml +4 -0
  89. app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecords.yaml +14 -0
  90. app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecordsAllowedEmpty.yaml +4 -0
  91. app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecordsEmpty.yaml +10 -0
  92. app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecordsOptional.yaml +26 -0
  93. app/code/community/Klevu/Search/Test/Model/Api/Action/Adduser.php +62 -0
  94. app/code/community/Klevu/Search/Test/Model/Api/Action/Adduser/providers/testValidateRequiredFields.yaml +6 -0
  95. app/code/community/Klevu/Search/Test/Model/Api/Action/Addwebstore.php +67 -0
  96. app/code/community/Klevu/Search/Test/Model/Api/Action/Addwebstore/providers/testValidateRequiredFields.yaml +10 -0
  97. app/code/community/Klevu/Search/Test/Model/Api/Action/Getuserdetail.php +61 -0
  98. app/code/community/Klevu/Search/Test/Model/Api/Action/Getuserdetail/providers/testValidateRequiredFields.yaml +4 -0
  99. app/code/community/Klevu/Search/Test/Model/Api/Action/Idsearch.php +96 -0
  100. app/code/community/Klevu/Search/Test/Model/Api/Action/Idsearch/providers/testValidateRequiredFields.yaml +12 -0
  101. app/code/community/Klevu/Search/Test/Model/Api/Action/Producttracking.php +76 -0
  102. app/code/community/Klevu/Search/Test/Model/Api/Action/Producttracking/providers/testValidateRequiredFields.yaml +14 -0
  103. app/code/community/Klevu/Search/Test/Model/Api/Action/Startsession.php +30 -0
  104. app/code/community/Klevu/Search/Test/Model/Api/Request.php +69 -0
  105. app/code/community/Klevu/Search/Test/Model/Api/Request/Xml.php +16 -0
  106. app/code/community/Klevu/Search/Test/Model/Api/Request/Xml/providers/testGetDataAsXml.yaml +25 -0
  107. app/code/community/Klevu/Search/Test/Model/Api/Response.php +19 -0
  108. app/code/community/Klevu/Search/Test/Model/Api/Response/Data.php +31 -0
  109. app/code/community/Klevu/Search/Test/Model/Api/Response/Data/providers/testIsSuccessful.yaml +12 -0
  110. app/code/community/Klevu/Search/Test/Model/Api/Response/Empty.php +33 -0
  111. app/code/community/Klevu/Search/Test/Model/Api/Response/Invalid.php +33 -0
  112. app/code/community/Klevu/Search/Test/Model/Api/Response/Message.php +29 -0
  113. app/code/community/Klevu/Search/Test/Model/Api/Response/Message/providers/testIsSuccessful.yaml +16 -0
  114. app/code/community/Klevu/Search/Test/Model/Api/Response/Timezone.php +17 -0
  115. app/code/community/Klevu/Search/Test/Model/Api/Response/Timezone/providers/testIsSuccessful.yaml +16 -0
  116. app/code/community/Klevu/Search/Test/Model/Api/Response/providers/response_testIsSuccessful.yaml +20 -0
  117. app/code/community/Klevu/Search/Test/Model/Api/Test/Case.php +64 -0
  118. app/code/community/Klevu/Search/Test/Model/Api/data/data_response_data.xml +5 -0
  119. app/code/community/Klevu/Search/Test/Model/Api/data/data_response_failure.xml +4 -0
  120. app/code/community/Klevu/Search/Test/Model/Api/data/data_response_no_response.xml +3 -0
  121. app/code/community/Klevu/Search/Test/Model/Api/data/data_response_success.xml +18 -0
  122. app/code/community/Klevu/Search/Test/Model/Api/data/data_response_success_only.xml +3 -0
  123. app/code/community/Klevu/Search/Test/Model/Api/data/message_response_failure.xml +4 -0
  124. app/code/community/Klevu/Search/Test/Model/Api/data/message_response_missing_message.xml +3 -0
  125. app/code/community/Klevu/Search/Test/Model/Api/data/message_response_missing_status.xml +3 -0
  126. app/code/community/Klevu/Search/Test/Model/Api/data/message_response_session_id.xml +4 -0
  127. app/code/community/Klevu/Search/Test/Model/Api/data/message_response_success.xml +4 -0
  128. app/code/community/Klevu/Search/Test/Model/Api/data/response_malformed.xml +4 -0
  129. app/code/community/Klevu/Search/Test/Model/Api/data/response_noxml.xml +1 -0
  130. app/code/community/Klevu/Search/Test/Model/Api/data/response_valid.xml +4 -0
  131. app/code/community/Klevu/Search/Test/Model/Api/data/search_response_empty.xml +18 -0
  132. app/code/community/Klevu/Search/Test/Model/Api/data/search_response_paged.xml +22 -0
  133. app/code/community/Klevu/Search/Test/Model/Api/data/search_response_sorted.xml +30 -0
  134. app/code/community/Klevu/Search/Test/Model/Api/data/search_response_success.xml +30 -0
  135. app/code/community/Klevu/Search/Test/Model/Api/data/startsession_response_success.xml +5 -0
  136. app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_no_data.xml +3 -0
  137. app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_no_status.xml +4 -0
  138. app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_with_failure.xml +5 -0
  139. app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_with_success.xml +5 -0
  140. app/code/community/Klevu/Search/Test/Model/Config/Log/Level.php +19 -0
  141. app/code/community/Klevu/Search/Test/Model/Notification.php +47 -0
  142. app/code/community/Klevu/Search/Test/Model/Notification/fixtures/testLoad.yaml +7 -0
  143. app/code/community/Klevu/Search/Test/Model/Observer.php +98 -0
  144. app/code/community/Klevu/Search/Test/Model/Observer/fixtures/testScheduleOrderSync.yaml +320 -0
  145. app/code/community/Klevu/Search/Test/Model/Order/Sync.php +90 -0
  146. app/code/community/Klevu/Search/Test/Model/Order/Sync/fixtures/testAddOrderToQueue.yaml +319 -0
  147. app/code/community/Klevu/Search/Test/Model/Order/Sync/fixtures/testClearQueue.yaml +563 -0
  148. app/code/community/Klevu/Search/Test/Model/Order/Sync/fixtures/testRun.yaml +323 -0
  149. app/code/community/Klevu/Search/Test/Model/Product/Sync.php +275 -0
  150. app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testAddProducts.yaml +31 -0
  151. app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testClearAllProducts.yaml +20 -0
  152. app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testDeleteProducts.yaml +13 -0
  153. app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testRun.yaml +425 -0
  154. app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testUpdateProducts.yaml +39 -0
  155. app/code/community/Klevu/Search/Test/Model/Sync.php +130 -0
  156. app/code/community/Klevu/Search/Test/Model/Sync/providers/testSchedule.yaml +4 -0
  157. app/code/community/Klevu/Search/Test/Model/System/Config/Source/Test.php +26 -0
  158. app/code/community/Klevu/Search/Test/Model/System/Config/Source/Test/providers/testIsValidSourceModel.yaml +10 -0
  159. app/code/community/Klevu/Search/controllers/Adminhtml/Klevu/NotificationsController.php +18 -0
  160. app/code/community/Klevu/Search/controllers/Adminhtml/Klevu/Search/WizardController.php +214 -0
  161. app/code/community/Klevu/Search/controllers/Adminhtml/Klevu/SearchController.php +64 -0
  162. app/code/community/Klevu/Search/controllers/SearchController.php +139 -0
  163. app/code/community/Klevu/Search/etc/adminhtml.xml +26 -0
  164. app/code/community/Klevu/Search/etc/config.xml +285 -0
  165. app/code/community/Klevu/Search/etc/system.xml +342 -0
  166. app/code/community/Klevu/Search/sql/klevu_search_setup/mysql4-data-upgrade-1.1.1-1.1.2.php +60 -0
  167. app/code/community/Klevu/Search/sql/klevu_search_setup/mysql4-data-upgrade-1.1.7-1.1.8.php +113 -0
  168. app/code/community/Klevu/Search/sql/klevu_search_setup/mysql4-install-1.0.0.php +76 -0
  169. app/design/adminhtml/default/default/layout/klevu/search.xml +61 -0
  170. app/design/adminhtml/default/default/template/klevu/search/form/field/array_readonly.phtml +25 -0
  171. app/design/adminhtml/default/default/template/klevu/search/form/field/sync/button.phtml +9 -0
  172. app/design/adminhtml/default/default/template/klevu/search/form/field/sync/logbutton.phtml +9 -0
  173. app/design/adminhtml/default/default/template/klevu/search/form/information.phtml +11 -0
  174. app/design/adminhtml/default/default/template/klevu/search/notifications.phtml +8 -0
  175. app/design/adminhtml/default/default/template/klevu/search/wizard/complete.phtml +17 -0
  176. app/design/adminhtml/default/default/template/klevu/search/wizard/config/button.phtml +11 -0
  177. app/design/adminhtml/default/default/template/klevu/search/wizard/configure/attributes.phtml +22 -0
  178. app/design/adminhtml/default/default/template/klevu/search/wizard/configure/store.phtml +49 -0
  179. app/design/adminhtml/default/default/template/klevu/search/wizard/configure/user.phtml +106 -0
  180. app/design/adminhtml/default/default/template/klevu/search/wizard/form/field/array.phtml +139 -0
  181. app/design/frontend/base/default/layout/klevu/search.xml +28 -0
  182. app/design/frontend/base/default/template/klevu/search/form_js.phtml +55 -0
  183. app/design/frontend/base/default/template/klevu/search/klevulog.phtml +7 -0
  184. app/design/frontend/base/default/template/klevu/search/product_tracking.phtml +35 -0
  185. app/etc/modules/Klevu_Search.xml +9 -0
  186. js/klevu/search/lib/Wizard.js +179 -0
  187. package.xml +25 -0
  188. skin/adminhtml/default/default/klevu/search/notifications.css +10 -0
  189. skin/adminhtml/default/default/klevu/search/wizard.css +105 -0
  190. skin/frontend/base/default/css/klevu/klevu-landing-page-style.css +618 -0
  191. skin/frontend/base/default/css/klevu/klevu-landing-responsive.css +142 -0
  192. skin/frontend/base/default/images/klevu/btn-gridview.png +0 -0
  193. skin/frontend/base/default/images/klevu/btn-listview.png +0 -0
  194. skin/frontend/base/default/images/klevu/ku-loader.gif +0 -0
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Attribute/Mappings.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Form_Field_Attribute_Mappings extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract {
4
+
5
+ protected $klevu_attribute_renderer;
6
+
7
+ protected $magento_attribute_renderer;
8
+
9
+ protected function _prepareToRender() {
10
+ $this->addColumn("klevu_attribute", array(
11
+ 'label' => Mage::helper('klevu_search')->__("Klevu Attribute"),
12
+ 'renderer' => $this->getKlevuAttributeRenderer()
13
+ ));
14
+ $this->addColumn("magento_attribute", array(
15
+ 'label' => Mage::helper('klevu_search')->__("Magento Attribute"),
16
+ 'renderer' => $this->getMagentoAttributeRenderer()
17
+ ));
18
+
19
+ $this->_addAfter = false;
20
+ $this->_addButtonLabel = Mage::helper('klevu_search')->__("Add Mapping");
21
+ }
22
+
23
+ protected function _prepareArrayRow(Varien_Object $row) {
24
+ $row->setData(
25
+ 'option_extra_attr_' . $this->getKlevuAttributeRenderer()->calcOptionHash($row->getData('klevu_attribute')),
26
+ 'selected="selected"'
27
+ );
28
+ $row->setData(
29
+ 'option_extra_attr_' . $this->getMagentoAttributeRenderer()->calcOptionHash($row->getData('magento_attribute')),
30
+ 'selected="selected"'
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Return a block to render the select element for Klevu Attribute.
36
+ *
37
+ * @return Klevu_Search_Block_Adminhtml_Form_Field_Html_Select
38
+ */
39
+ protected function getKlevuAttributeRenderer() {
40
+ if (!$this->klevu_attribute_renderer) {
41
+ /** @var Mage_Core_Block_Html_Select $renderer */
42
+ $renderer = $this->getLayout()->createBlock('klevu_search/adminhtml_form_field_html_select', '', array(
43
+ 'is_render_to_js_template' => true
44
+ ));
45
+ $renderer->setOptions(Mage::getModel('klevu_search/system_config_source_additional_attributes')->toOptionArray());
46
+ $renderer->setExtraParams('style="width:120px"');
47
+
48
+ $this->klevu_attribute_renderer = $renderer;
49
+ }
50
+
51
+ return $this->klevu_attribute_renderer;
52
+ }
53
+
54
+ /**
55
+ * Return a block to render the select element for Magento Attribute.
56
+ *
57
+ * @return Klevu_Search_Block_Adminhtml_Form_Field_Html_Select
58
+ */
59
+ protected function getMagentoAttributeRenderer() {
60
+ if (!$this->magento_attribute_renderer) {
61
+ /** @var Mage_Core_Block_Html_Select $renderer */
62
+ $renderer = $this->getLayout()->createBlock('klevu_search/adminhtml_form_field_html_select', '', array(
63
+ 'is_render_to_js_template' => true
64
+ ));
65
+ $renderer->setOptions($this->getOptions());
66
+ $renderer->setExtraParams('style="width:120px"');
67
+
68
+ $this->magento_attribute_renderer = $renderer;
69
+ }
70
+
71
+ return $this->magento_attribute_renderer;
72
+ }
73
+
74
+ /**
75
+ * Get the options from our product attribute source model, and filter out the search attributes.
76
+ * @return array
77
+ */
78
+ protected function getOptions() {
79
+ $options_with_search_filters = Mage::getModel('klevu_search/system_config_source_product_attributes')->toOptionArray();
80
+ $search_attributes_map = Mage::helper('klevu_search/config')->getAutomaticAttributesMap(Mage::app()->getRequest()->getParam('store'));
81
+ $options = array();
82
+
83
+ // Flatten the search_attributes_map
84
+ $search_attributes = array();
85
+ foreach($search_attributes_map as $attribute) {
86
+ $search_attributes[] = $attribute['magento_attribute'];
87
+ }
88
+
89
+ // We only want options that are not in the search_attributes array.
90
+ foreach($options_with_search_filters as $option) {
91
+ if(!in_array($option['value'], $search_attributes)) {
92
+ $options[] = $option;
93
+ }
94
+ }
95
+
96
+ return $options;
97
+ }
98
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Automatic/Attribute/Mappings.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Form_Field_Automatic_Attribute_Mappings extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract {
4
+
5
+ /**
6
+ * Check if columns are defined, set template
7
+ *
8
+ */
9
+ public function __construct()
10
+ {
11
+ parent::__construct();
12
+ $this->setTemplate('klevu/search/form/field/array_readonly.phtml');
13
+ }
14
+
15
+ protected function _prepareToRender() {
16
+ $this->addColumn("klevu_attribute", array(
17
+ 'label' => Mage::helper('klevu_search')->__("Klevu Attribute")
18
+ ));
19
+ $this->addColumn("magento_attribute", array(
20
+ 'label' => Mage::helper('klevu_search')->__("Magento Attribute")
21
+ ));
22
+ }
23
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Html/Select.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Form_Field_Html_Select extends Mage_Adminhtml_Block_Html_Select {
4
+
5
+ public function setInputName($value) {
6
+ return $this->setName($value);
7
+ }
8
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Image/Button.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Block_Adminhtml_Form_Field_Image_Button
5
+ *
6
+ * @method setStoreId($id)
7
+ * @method string getStoreId()
8
+ */
9
+
10
+ class Klevu_Search_Block_Adminhtml_Form_Field_Image_Button extends Mage_Adminhtml_Block_System_Config_Form_Field {
11
+
12
+ protected function _prepareLayout() {
13
+ parent::_prepareLayout();
14
+
15
+ // Set the default template
16
+ if (!$this->getTemplate()) {
17
+ $this->setTemplate('klevu/search/form/field/sync/button.phtml');
18
+ }
19
+
20
+ return $this;
21
+ }
22
+
23
+ public function render(Varien_Data_Form_Element_Abstract $element) {
24
+ if ($element->getScope() == "stores") {
25
+ $this->setStoreId($element->getScopeId());
26
+ }
27
+
28
+ // Remove the scope information so it doesn't get printed out
29
+ $element
30
+ ->unsScope()
31
+ ->unsCanUseWebsiteValue()
32
+ ->unsCanUseDefaultValue();
33
+
34
+ return parent::render($element);
35
+ }
36
+
37
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
38
+ $url_params = ($this->getStoreId()) ? array("store" => $this->getStoreId()) : array();
39
+ $label_suffix = ($this->getStoreId()) ? " for This Store" : "";
40
+
41
+ $this->addData(array(
42
+ "html_id" => $element->getHtmlId(),
43
+ "button_label" => sprintf("Generate Thumbnails For All Products%s", $label_suffix),
44
+ "destination_url" => $this->getUrl("adminhtml/klevu_search/generate_thumbnail", $url_params)
45
+ ));
46
+
47
+ return $this->_toHtml();
48
+ }
49
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Image/Log.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Block_Adminhtml_Form_Field_Image_Button
5
+ *
6
+ * @method setStoreId($id)
7
+ * @method string getStoreId()
8
+ */
9
+
10
+ class Klevu_Search_Block_Adminhtml_Form_Field_Image_Log extends Mage_Adminhtml_Block_System_Config_Form_Field {
11
+
12
+ protected function _prepareLayout() {
13
+ parent::_prepareLayout();
14
+
15
+ // Set the default template
16
+ if (!$this->getTemplate()) {
17
+ $this->setTemplate('klevu/search/form/field/sync/logbutton.phtml');
18
+ }
19
+
20
+ return $this;
21
+ }
22
+
23
+ public function render(Varien_Data_Form_Element_Abstract $element) {
24
+ if ($element->getScope() == "stores") {
25
+ $this->setStoreId($element->getScopeId());
26
+ }
27
+
28
+ // Remove the scope information so it doesn't get printed out
29
+ $element
30
+ ->unsScope()
31
+ ->unsCanUseWebsiteValue()
32
+ ->unsCanUseDefaultValue();
33
+
34
+ return parent::render($element);
35
+ }
36
+
37
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
38
+ $url_params = array("debug" => "klevu");
39
+ $label_suffix = ($this->getStoreId()) ? " for This Store" : "";
40
+
41
+ $this->addData(array(
42
+ "html_id" => $element->getHtmlId(),
43
+ "button_label" => sprintf("Send Log"),
44
+ "destination_url" => $this->getUrl("klevu/search/runexternaly", $url_params)
45
+ ));
46
+
47
+ return $this->_toHtml();
48
+ }
49
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Store/Level/Label.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Form_Field_Store_Level_Label extends Mage_Adminhtml_Block_System_Config_Form_Field {
4
+
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
6
+ if ($element->getScope() == "stores") {
7
+ return $element->getEscapedValue();
8
+ } else {
9
+ return Mage::helper("klevu_search")->__("Switch to store scope to see");
10
+ }
11
+ }
12
+
13
+ public function render(Varien_Data_Form_Element_Abstract $element) {
14
+ $this->setData('scope', $element->getScope());
15
+
16
+ // Remove the inheritance checkbox
17
+ $element
18
+ ->unsCanUseWebsiteValue()
19
+ ->unsCanUseDefaultValue();
20
+
21
+ return parent::render($element);
22
+ }
23
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Field/Sync/Button.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Block_Adminhtml_Form_Field_Sync_Button
5
+ *
6
+ * @method setStoreId($id)
7
+ * @method string getStoreId()
8
+ */
9
+ class Klevu_Search_Block_Adminhtml_Form_Field_Sync_Button extends Mage_Adminhtml_Block_System_Config_Form_Field {
10
+
11
+ protected function _prepareLayout() {
12
+ parent::_prepareLayout();
13
+
14
+ // Set the default template
15
+ if (!$this->getTemplate()) {
16
+ $this->setTemplate('klevu/search/form/field/sync/button.phtml');
17
+ }
18
+
19
+ return $this;
20
+ }
21
+
22
+ public function render(Varien_Data_Form_Element_Abstract $element) {
23
+ if ($element->getScope() == "stores") {
24
+ $this->setStoreId($element->getScopeId());
25
+ }
26
+
27
+ // Remove the scope information so it doesn't get printed out
28
+ $element
29
+ ->unsScope()
30
+ ->unsCanUseWebsiteValue()
31
+ ->unsCanUseDefaultValue();
32
+
33
+ return parent::render($element);
34
+ }
35
+
36
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
37
+ $url_params = ($this->getStoreId()) ? array("store" => $this->getStoreId()) : array();
38
+ $label_suffix = ($this->getStoreId()) ? " for This Store" : "";
39
+
40
+ $this->addData(array(
41
+ "html_id" => $element->getHtmlId(),
42
+ "button_label" => sprintf("Sync All Products%s", $label_suffix),
43
+ "destination_url" => $this->getUrl("adminhtml/klevu_search/sync_all", $url_params)
44
+ ));
45
+
46
+ return $this->_toHtml();
47
+ }
48
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Form/Information.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Form_Information extends Mage_Adminhtml_Block_System_Config_Form_Fieldset {
4
+
5
+ protected function _construct() {
6
+ parent::_construct();
7
+
8
+ if (!$this->getTemplate()) {
9
+ // Set the default template
10
+ $this->setTemplate("klevu/search/form/information.phtml");
11
+ }
12
+ }
13
+
14
+ public function render(Varien_Data_Form_Element_Abstract $element) {
15
+ $html = $this->_getHeaderHtml($element);
16
+
17
+ $html .= $this->_toHtml();
18
+
19
+ $html .= $this->_getFooterHtml($element);
20
+
21
+ return $html;
22
+ }
23
+
24
+ public function getVersion() {
25
+ return Mage::getConfig()->getModuleConfig('Klevu_Search')->version;
26
+ }
27
+
28
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Notifications.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Notifications extends Mage_Adminhtml_Block_Template {
4
+
5
+ /**
6
+ * Return all notifications.
7
+ *
8
+ * @return Klevu_Search_Model_Resource_Notification_Collection
9
+ */
10
+ protected function getNotifications() {
11
+ return Mage::getResourceModel("klevu_search/notification_collection");
12
+ }
13
+
14
+ /**
15
+ * Return the URL to dismiss the given notification.
16
+ *
17
+ * @param $notification
18
+ *
19
+ * @return string
20
+ */
21
+ protected function getDismissUrl($notification) {
22
+ return $this->getUrl("adminhtml/klevu_notifications/dismiss", array("id" => $notification->getId()));
23
+ }
24
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Config/Button.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class Klevu_Search_Block_Adminhtml_Wizard_Config_Button
4
+ *
5
+ * @method string getHtmlId()
6
+ * @method string getWizardUrl()
7
+ */
8
+ class Klevu_Search_Block_Adminhtml_Wizard_Config_Button extends Mage_Adminhtml_Block_System_Config_Form_Field {
9
+
10
+ protected function _prepareLayout() {
11
+ parent::_prepareLayout();
12
+
13
+ // Set the default template
14
+ if (!$this->getTemplate()) {
15
+ $this->setTemplate('klevu/search/wizard/config/button.phtml');
16
+ }
17
+
18
+ return $this;
19
+ }
20
+
21
+ public function render(Varien_Data_Form_Element_Abstract $element) {
22
+ // Only show the current scope hasn't been configured yet
23
+ switch($element->getScope()) {
24
+ case "stores":
25
+ if ($this->hasApiKeys($element->getScopeId())) {
26
+ return "";
27
+ }
28
+ break;
29
+ case "websites":
30
+ $website = Mage::app()->getWebsite($element->getScopeId());
31
+ if ($this->hasApiKeys($website->getStores())) {
32
+ return "";
33
+ }
34
+ break;
35
+ default:
36
+ if ($this->hasApiKeys()) {
37
+ return "";
38
+ }
39
+ }
40
+
41
+ // Remove the scope information so it doesn't get printed out
42
+ $element
43
+ ->unsScope()
44
+ ->unsCanUseWebsiteValue()
45
+ ->unsCanUseDefaultValue();
46
+
47
+ return parent::render($element);
48
+ }
49
+
50
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
51
+ $this->addData(array(
52
+ 'html_id' => $element->getHtmlId(),
53
+ 'wizard_url' => $this->getUrl("adminhtml/klevu_search_wizard/configure_user")
54
+ ));
55
+
56
+ return $this->_toHtml();
57
+ }
58
+
59
+ /**
60
+ * Check if the given stores all have Klevu API keys. If no stores are given, checks
61
+ * all configured stores.
62
+ *
63
+ * @param null $stores
64
+ *
65
+ * @return bool true if all stores have API keys, false otherwise.
66
+ */
67
+ protected function hasApiKeys($stores = null) {
68
+ $config = Mage::helper("klevu_search/config");
69
+
70
+ if ($stores === null) {
71
+ $stores = Mage::app()->getStores(false);
72
+ }
73
+
74
+ if (!is_array($stores)) {
75
+ $stores = array($stores);
76
+ }
77
+
78
+ foreach ($stores as $store) {
79
+ if (!$config->getJsApiKey($store) || !$config->getRestApiKey($store)) {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ return true;
85
+ }
86
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Configure/Attributes.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Wizard_Configure_Attributes extends Mage_Adminhtml_Block_Template {
4
+
5
+ /**
6
+ * Return the submit URL for the store configuration form.
7
+ *
8
+ * @return string
9
+ */
10
+ protected function getFormActionUrl() {
11
+ return $this->getUrl("adminhtml/klevu_search_wizard/configure_attributes_post");
12
+ }
13
+
14
+ protected function getAttributeMappingsHtml() {
15
+ $element = new Varien_Data_Form_Element_Text(array(
16
+ "name" => "attributes",
17
+ "label" => $this->__("Additional Attributes"),
18
+ "comment" => $this->__('Here you can set optional product attributes sent to Klevu by mapping them to your Magento attributes. If you specify multiple mappings for the same Klevu attribute, only the first mapping found on the product sent will be used, except for the "Other" attribute where all existing mappings are used.'),
19
+ "tooltip" => "",
20
+ "hint" => "",
21
+ "value" => Mage::helper("klevu_search/config")->getAdditionalAttributesMap($this->getStore()),
22
+ "inherit" => false,
23
+ "class" => "",
24
+ "can_use_default_value" => false,
25
+ "can_use_website_value" => false
26
+ ));
27
+ $element->setForm(new Varien_Data_Form());
28
+
29
+ /** @var Klevu_Search_Block_Adminhtml_Form_Field_Attribute_Mappings $renderer */
30
+ $renderer = Mage::getBlockSingleton("klevu_search/adminhtml_form_field_attribute_mappings");
31
+ $renderer->setTemplate("klevu/search/wizard/form/field/array.phtml");
32
+
33
+ return $renderer->render($element);
34
+ }
35
+
36
+ /**
37
+ * Return the Store model for the currently configured store.
38
+ *
39
+ * @return Mage_Core_Model_Store|null
40
+ */
41
+ protected function getStore() {
42
+ if (!$this->hasData('store')) {
43
+ $store_code = Mage::getSingleton('klevu_search/session')->getConfiguredStoreCode();
44
+
45
+ $this->setData('store', Mage::app()->getStore($store_code));
46
+ }
47
+
48
+ return $this->getData('store');
49
+ }
50
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Configure/Store.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Wizard_Configure_Store extends Mage_Adminhtml_Block_Template {
4
+
5
+ /**
6
+ * Return the submit URL for the store configuration form.
7
+ *
8
+ * @return string
9
+ */
10
+ protected function getFormActionUrl() {
11
+ return $this->getUrl("adminhtml/klevu_search_wizard/configure_store_post");
12
+ }
13
+
14
+ /**
15
+ * Return the list of stores that can be selected to be configured (i.e. haven't
16
+ * been configured already), organised by website name and group name.
17
+ *
18
+ * @return array
19
+ */
20
+ protected function getStoreSelectData() {
21
+ $stores = Mage::app()->getStores(false);
22
+ $config = Mage::helper("klevu_search/config");
23
+
24
+ $data = array();
25
+
26
+ foreach ($stores as $store) {
27
+ /** @var Mage_Core_Model_Store $store */
28
+ if ($config->getJsApiKey($store) && $config->getRestApiKey($store)) {
29
+ // Skip already configured stores
30
+ continue;
31
+ }
32
+
33
+ $website = $store->getWebsite()->getName();
34
+ $group = $store->getGroup()->getName();
35
+
36
+ if (!isset($data[$website])) {
37
+ $data[$website] = array();
38
+ }
39
+ if (!isset($data[$website][$group])) {
40
+ $data[$website][$group] = array();
41
+ }
42
+
43
+ $data[$website][$group][] = $store;
44
+ }
45
+
46
+ return $data;
47
+ }
48
+ }
app/code/community/Klevu/Search/Block/Adminhtml/Wizard/Configure/User.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Adminhtml_Wizard_Configure_User extends Mage_Adminhtml_Block_Template {
4
+
5
+ /**
6
+ * Return the submit URL for the user configuration form.
7
+ *
8
+ * @return string
9
+ */
10
+ protected function getFormActionUrl() {
11
+ return $this->getUrl('adminhtml/klevu_search_wizard/configure_user_post');
12
+ }
13
+
14
+ /**
15
+ * Return the base URL for the store.
16
+ *
17
+ * @return string
18
+ */
19
+ protected function getStoreUrl() {
20
+ return $this->getBaseUrl();
21
+ }
22
+ }
app/code/community/Klevu/Search/Block/Catalog/Product/Tracking.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Block_Catalog_Product_Tracking extends Mage_Core_Block_Template {
4
+
5
+ /**
6
+ * JSON of required tracking parameter for Klevu Product Click Tracking, based on current product
7
+ * @return string
8
+ * @throws Exception
9
+ */
10
+ public function getJsonTrackingData() {
11
+ // Get the product
12
+ $product = Mage::registry('current_product');
13
+ $api_key = Mage::helper('klevu_search/config')->getJsApiKey();
14
+
15
+ $product = array(
16
+ 'klevu_apiKey' => $api_key,
17
+ 'klevu_term' => '',
18
+ 'klevu_type' => 'clicked',
19
+ 'klevu_productId' => $product->getId(),
20
+ 'klevu_productName' => $product->getName(),
21
+ 'klevu_productUrl' => $product->getProductUrl()
22
+ );
23
+
24
+ return json_encode($product);
25
+ }
26
+ }
app/code/community/Klevu/Search/Helper/Api.php ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Helper_Api extends Mage_Core_Helper_Abstract {
4
+
5
+ const ENDPOINT_PROTOCOL = 'https://';
6
+ const ENDPOINT_DEFAULT_HOSTNAME = 'box.klevu.com';
7
+ /**
8
+ * Create a new Klevu user using the API and return the user details.
9
+ *
10
+ * @param $email
11
+ * @param $password
12
+ * @param $url
13
+ *
14
+ * @return array An array containing the following keys:
15
+ * success: boolean value indicating whether the user was created successfully.
16
+ * customer_id: the customer ID for the newly created user (on success only).
17
+ * message: a message to be shown to the user.
18
+ */
19
+ public function createUser($email, $password, $userPlan, $url,$merchantEmail,$contactNo) {
20
+ $response = Mage::getModel("klevu_search/api_action_adduser")->execute(array(
21
+ "email" => $email,
22
+ "password" => $password,
23
+ "userPlan" => $userPlan,
24
+ "url" => $url,
25
+ "merchantEmail" => $merchantEmail,
26
+ "contactNo" => $contactNo
27
+
28
+ ));
29
+
30
+ if ($response->isSuccessful()) {
31
+ return array(
32
+ "success" => true,
33
+ "customer_id" => $response->getCustomerId(),
34
+ "message" => $response->getMessage()
35
+ );
36
+ } else {
37
+ return array(
38
+ "success" => false,
39
+ "message" => $response->getMessage()
40
+ );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Retrieve the details for the given Klevu user from the API.
46
+ *
47
+ * @param $email
48
+ * @param $password
49
+ *
50
+ * @return array An array containing the following keys:
51
+ * success: boolean value indicating whether the operation was successful.
52
+ * customer_id: (on success only) The customer ID of the requested user.
53
+ * webstores: (on success only) A list of webstores the given user has configured.
54
+ * message: (on failure only) Error message to be shown to the user.
55
+ */
56
+ public function getUser($email, $password) {
57
+ $response = Mage::getModel("klevu_search/api_action_getuserdetail")->execute(array(
58
+ "email" => $email,
59
+ "password" => $password
60
+ ));
61
+
62
+ if ($response->isSuccessful()) {
63
+ $webstores = array();
64
+
65
+ // Add each webstore as a Varien_Object
66
+ $webstores_data = $response->getWebstores();
67
+ if ($webstores_data && isset($webstores_data['webstore'])) {
68
+ $webstores_data = $webstores_data['webstore'];
69
+
70
+ if (isset($webstores_data['storeName'])) {
71
+ // Got a single webstore
72
+ $webstores_data = array($webstores_data);
73
+ }
74
+
75
+ $i = 0;
76
+ foreach ($webstores_data as $webstore_data) {
77
+ $webstore = array(
78
+ 'id' => $i++
79
+ );
80
+ foreach($webstore_data as $key => $value) {
81
+ // Convert field names from camelCase to underscore (code taken from Varien_Object)
82
+ $webstore[strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $key))] = $value;
83
+ }
84
+ $webstores[] = new Varien_Object($webstore);
85
+ }
86
+ }
87
+
88
+ return array(
89
+ "success" => true,
90
+ "customer_id" => $response->getCustomerId(),
91
+ "webstores" => $webstores
92
+ );
93
+ } else {
94
+ return array(
95
+ "success" => false,
96
+ "message" => $response->getMessage()
97
+ );
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Create a Klevu Webstore using the API for the given Magento store.
103
+ *
104
+ * @param $customer_id
105
+ * @param Mage_Core_Model_Store $store
106
+ * @param bool $test_mode
107
+ *
108
+ * @return array An array with the following keys:
109
+ * success: boolean value indicating whether the operation was successful.
110
+ * webstore: (success only) Varien_Object containing Webstore information.
111
+ * message: message to be displayed to the user.
112
+ */
113
+ public function createWebstore($customer_id, Mage_Core_Model_Store $store, $test_mode = false) {
114
+ $name = sprintf("%s - %s - %s",
115
+ $store->getWebsite()->getName(),
116
+ $store->getName(),
117
+ $store->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB)
118
+ );
119
+ $language = Mage::helper("klevu_search")->getStoreLanguage($store);
120
+ $timezone = $store->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE);
121
+ $country = $store->getConfig(Mage_Core_Helper_Data::XML_PATH_DEFAULT_COUNTRY);
122
+ $locale = $store->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE);
123
+
124
+ $version = $this->getVersion();
125
+
126
+ // Convert $test_mode to string
127
+ $test_mode = ($test_mode) ? "true" : "false";
128
+
129
+ $response = Mage::getModel("klevu_search/api_action_addwebstore")->execute(array(
130
+ "customerId" => $customer_id,
131
+ "storeName" => $name,
132
+ "language" => $language,
133
+ "timezone" => $timezone,
134
+ "version" => $version,
135
+ "country" => $country,
136
+ "locale" => $locale,
137
+ "testMode" => $test_mode,
138
+ ));
139
+
140
+ if ($response->isSuccessful()) {
141
+ $webstore = new Varien_Object(array(
142
+ "store_name" => $name,
143
+ "js_api_key" => $response->getJsApiKey(),
144
+ "rest_api_key" => $response->getRestApiKey(),
145
+ "test_account_enabled" => $test_mode,
146
+ "hosted_on" => $response->getHostedOn(),
147
+ "cloud_search_url" => $response->getCloudSearchUrl(),
148
+ "analytics_url" => $response->getAnalyticsUrl(),
149
+ "js_url" => $response->getJsUrl(),
150
+ "rest_hostname" => $response->getRestUrl(),
151
+ ));
152
+
153
+ return array(
154
+ "success" => true,
155
+ "webstore" => $webstore,
156
+ "message" => $response->getMessage()
157
+ );
158
+ } else {
159
+ return array(
160
+ "success" => false,
161
+ "message" => $response->getMessage()
162
+ );
163
+ }
164
+ }
165
+
166
+ public function getTimezoneOptions() {
167
+ $response = Mage::getModel('klevu_search/api_action_gettimezone')->execute();
168
+
169
+ if ($response->isSuccessful()) {
170
+ $options = array();
171
+
172
+ $data = $response->getTimezone();
173
+
174
+ if (!is_array($data)) {
175
+ $data = array($data);
176
+ }
177
+
178
+ foreach ($data as $timezone) {
179
+ $options[] = array(
180
+ "label" => $this->__($timezone),
181
+ "value" => $this->escapeHtml($timezone)
182
+ );
183
+ }
184
+
185
+ return $options;
186
+ } else {
187
+ return $response->getMessage();
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Build the API Endpoint URL, based on system configuration API Hostname, and endpoint provided.
193
+ * @param string $endpoint
194
+ * @param null|Mage_Core_Model_Store $store
195
+ * @param null|string $hostname
196
+ * @return string
197
+ */
198
+ public function buildEndpoint($endpoint, $store = null, $hostname = null) {
199
+
200
+ return static::ENDPOINT_PROTOCOL . (($hostname) ? $hostname : Mage::helper('klevu_search/config')->getHostname($store)) . $endpoint;
201
+ }
202
+
203
+ /**
204
+ * Get the module version number from the module config.
205
+ * @return string
206
+ */
207
+ public function getVersion() {
208
+ return Mage::getConfig()->getModuleConfig('Klevu_Search')->version;
209
+ }
210
+ }
app/code/community/Klevu/Search/Helper/Compat.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Helper_Compat extends Mage_Core_Helper_Abstract {
4
+
5
+ /**
6
+ * Return a Select statement for retrieving URL rewrites for the given list of products.
7
+ *
8
+ * Uses the Product URL Rewrite Helper if available, falling back to building the query manually.
9
+ *
10
+ * @param array $product_ids
11
+ * @param $category_id
12
+ * @param $store_id
13
+ *
14
+ * @return Varien_Db_Select
15
+ */
16
+ public function getProductUrlRewriteSelect(array $product_ids, $category_id, $store_id) {
17
+ if (version_compare(Mage::getVersion(), '1.8.0', '>=')) {
18
+ $factory_model_class = Mage::app()->getConfig()->getModelClassName("catalog/factory");
19
+ if (class_exists($factory_model_class)) {
20
+ return Mage::getModel("catalog/factory")->getProductUrlRewriteHelper()
21
+ ->getTableSelect($product_ids, $category_id, $store_id);
22
+ }
23
+ }
24
+
25
+ $resource = Mage::getModel("core/resource");
26
+
27
+ return $resource->getConnection("core_write")->select()
28
+ ->from($resource->getTableName("core/url_rewrite"), array("product_id", "request_path"))
29
+ ->where("store_id = ?", (int) $store_id)
30
+ ->where("is_system = ?", 1)
31
+ ->where("category_id = ? OR category_id IS NULL", (int) $category_id)
32
+ ->where("product_id IN (?)", $product_ids)
33
+ ->order("category_id " . Varien_Data_Collection::SORT_ORDER_DESC);
34
+ }
35
+
36
+ /**
37
+ * Return the current date in internal format.
38
+ *
39
+ * @param bool $withoutTime day only flag
40
+ *
41
+ * @return string
42
+ */
43
+ public function now($withoutTime = false) {
44
+ if (method_exists("Varien_Date", "now")) {
45
+ return Varien_Date::now($withoutTime);
46
+ } else {
47
+ $format = ($withoutTime) ? "Y-m-d" : "Y-m-d H:i:s";
48
+ return date($format);
49
+ }
50
+ }
51
+ }
app/code/community/Klevu/Search/Helper/Config.php ADDED
@@ -0,0 +1,642 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Helper_Config extends Mage_Core_Helper_Abstract {
4
+
5
+ const XML_PATH_EXTENSION_ENABLED = "klevu_search/general/enabled";
6
+ const XML_PATH_TEST_MODE = "klevu_search/general/test_mode";
7
+ const XML_PATH_TAX_ENABLED = "klevu_search/tax_setting/enabled";
8
+ const XML_PATH_LANDING_ENABLED = "klevu_search/searchlanding/landenabled";
9
+ const XML_PATH_JS_API_KEY = "klevu_search/general/js_api_key";
10
+ const XML_PATH_REST_API_KEY = "klevu_search/general/rest_api_key";
11
+ const XML_PATH_TEST_JS_API_KEY = "klevu_search/general/test_js_api_key";
12
+ const XML_PATH_TEST_REST_API_KEY = "klevu_search/general/test_rest_api_key";
13
+ const XML_PATH_PRODUCT_SYNC_ENABLED = "klevu_search/product_sync/enabled";
14
+ const XML_PATH_PRODUCT_SYNC_FREQUENCY = "klevu_search/product_sync/frequency";
15
+ const XML_PATH_PRODUCT_SYNC_LAST_RUN = "klevu_search/product_sync/last_run";
16
+ const XML_PATH_ATTRIBUTES_ADDITIONAL = "klevu_search/attributes/additional";
17
+ const XML_PATH_ATTRIBUTES_AUTOMATIC = "klevu_search/attributes/automatic";
18
+ const XML_PATH_ATTRIBUTES_OTHER = "klevu_search/attributes/other";
19
+ const XML_PATH_ATTRIBUTES_BOOSTING = "klevu_search/attributes/boosting";
20
+ const XML_PATH_ORDER_SYNC_ENABLED = "klevu_search/order_sync/enabled";
21
+ const XML_PATH_ORDER_SYNC_FREQUENCY = "klevu_search/order_sync/frequency";
22
+ const XML_PATH_ORDER_SYNC_LAST_RUN = "klevu_search/order_sync/last_run";
23
+ const XML_PATH_FORCE_LOG = "klevu_search/developer/force_log";
24
+ const XML_PATH_LOG_LEVEL = "klevu_search/developer/log_level";
25
+ const XML_PATH_STORE_ID = "stores/%s/system/store/id";
26
+ const XML_PATH_HOSTNAME = "klevu_search/general/hostname";
27
+ const XML_PATH_RESTHOSTNAME = "klevu_search/general/rest_hostname";
28
+ const XML_PATH_TEST_HOSTNAME = "klevu_search/general/test_hostname";
29
+ const XML_PATH_CLOUD_SEARCH_URL = "klevu_search/general/cloud_search_url";
30
+ const XML_PATH_TEST_CLOUD_SEARCH_URL = "klevu_search/general/test_cloud_search_url";
31
+ const XML_PATH_ANALYTICS_URL = "klevu_search/general/analytics_url";
32
+ const XML_PATH_TEST_ANALYTICS_URL = "klevu_search/general/test_analytics_url";
33
+ const XML_PATH_JS_URL = "klevu_search/general/js_url";
34
+ const XML_PATH_TEST_JS_URL = "klevu_search/general/test_js_url";
35
+
36
+ const DATETIME_FORMAT = "Y-m-d H:i:s T";
37
+
38
+ /**
39
+ * Set the Enable on Frontend flag in System Configuration for the given store.
40
+ *
41
+ * @param $flag
42
+ * @param Mage_Core_Model_Store|int|null $store Store to set the flag for. Defaults to current store.
43
+ *
44
+ * @return $this
45
+ */
46
+ public function setExtensionEnabledFlag($flag, $store = null) {
47
+ $flag = ($flag) ? 1 : 0;
48
+ $this->setStoreConfig(static::XML_PATH_EXTENSION_ENABLED, $flag, $store);
49
+ return $this;
50
+ }
51
+
52
+ /**
53
+ * Check if the Klevu_Search extension is enabled in the system configuration for the current store.
54
+ *
55
+ * @param $store_id
56
+ *
57
+ * @return bool
58
+ */
59
+ public function isExtensionEnabled($store_id = null) {
60
+ return Mage::getStoreConfigFlag(static::XML_PATH_EXTENSION_ENABLED, $store_id);
61
+ }
62
+
63
+ /**
64
+ * Check if the Tax is enabled in the system configuration for the current store.
65
+ *
66
+ * @param $store_id
67
+ *
68
+ * @return bool
69
+ */
70
+ public function isTaxEnabled($store_id = null) {
71
+ return Mage::getStoreConfigFlag(static::XML_PATH_TAX_ENABLED, $store_id);
72
+ }
73
+
74
+ /**
75
+ * Check if the Landing is enabled in the system configuration for the current store.
76
+ *
77
+ * @param $store_id
78
+ *
79
+ * @return bool
80
+ */
81
+ public function isLandingEnabled($store = null) {
82
+ return intval(Mage::getStoreConfig(static::XML_PATH_LANDING_ENABLED, $store));
83
+ }
84
+
85
+ /**
86
+ * Set the Test Mode flag in System Configuration for the given store.
87
+ *
88
+ * @param $flag
89
+ * @param null $store Store to use. If not specified, uses the current store.
90
+ *
91
+ * @return $this
92
+ */
93
+ public function setTestModeEnabledFlag($flag, $store = null) {
94
+ $flag = ($flag) ? 1 : 0;
95
+ $this->setStoreConfig(static::XML_PATH_TEST_MODE, $flag, $store);
96
+ return $this;
97
+ }
98
+
99
+ /**
100
+ * Return the configuration flag for enabling test mode.
101
+ *
102
+ * @param Mage_Core_Model_Store|int $store
103
+ *
104
+ * @return bool
105
+ */
106
+ public function getTestModeEnabledFlag($store = null) {
107
+ return Mage::getStoreConfigFlag(static::XML_PATH_TEST_MODE, $store);
108
+ }
109
+
110
+ /**
111
+ * Set the Tax mode in System Configuration for the given store.
112
+ *
113
+ * @param $flag
114
+ * @param null $store Store to use. If not specified, uses the current store.
115
+ *
116
+ * @return $this
117
+ */
118
+ public function setTaxEnabledFlag($flag, $store = null) {
119
+
120
+ $flag = ($flag) ? 1 : 0;
121
+ $this->setStoreConfig(static::XML_PATH_TAX_ENABLED, $flag, $store);
122
+ return $this;
123
+ }
124
+
125
+ /**
126
+ * Check if Test Mode is enabled for the given store.
127
+ *
128
+ * @param Mage_Core_Model_Store|int $store
129
+ *
130
+ * @return bool
131
+ */
132
+ public function isTestModeEnabled($store = null) {
133
+ return $this->getTestModeEnabledFlag($store);
134
+ }
135
+
136
+ /**
137
+ * Set the JS API key in System Configuration for the given store.
138
+ *
139
+ * @param string $key
140
+ * @param Mage_Core_Model_Store|int $store Store to use. If not specified, will use the current store.
141
+ * @param bool $test_mode Set the key to be used in Test Mode.
142
+ *
143
+ * @return $this
144
+ */
145
+ public function setJsApiKey($key, $store = null, $test_mode = false) {
146
+ $path = ($test_mode) ? static::XML_PATH_TEST_JS_API_KEY : static::XML_PATH_JS_API_KEY;
147
+ $this->setStoreConfig($path, $key, $store);
148
+ return $this;
149
+ }
150
+
151
+ /**
152
+ * Return the JS API key configured for the specified store.
153
+ *
154
+ * @param Mage_Core_Model_Store|int $store
155
+ *
156
+ * @return string
157
+ */
158
+ public function getJsApiKey($store = null) {
159
+ if ($this->isTestModeEnabled($store)) {
160
+ return Mage::getStoreConfig(static::XML_PATH_TEST_JS_API_KEY, $store);
161
+ } else {
162
+ return Mage::getStoreConfig(static::XML_PATH_JS_API_KEY, $store);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Set the REST API key in System Configuration for the given store.
168
+ *
169
+ * @param string $key
170
+ * @param Mage_Core_Model_Store|int $store Store to use. If not specified, will use the current store.
171
+ * @param bool $test_mode Set the key to be used in Test Mode.
172
+ *
173
+ * @return $this
174
+ */
175
+ public function setRestApiKey($key, $store = null, $test_mode = false) {
176
+ $path = ($test_mode) ? static::XML_PATH_TEST_REST_API_KEY : static::XML_PATH_REST_API_KEY;
177
+ $this->setStoreConfig($path, $key, $store);
178
+ return $this;
179
+ }
180
+
181
+ /**
182
+ * Return the REST API key configured for the specified store.
183
+ *
184
+ * @param Mage_Core_Model_Store|int $store
185
+ *
186
+ * @return mixed
187
+ */
188
+ public function getRestApiKey($store = null) {
189
+ if ($this->isTestModeEnabled($store)) {
190
+ return Mage::getStoreConfig(static::XML_PATH_TEST_REST_API_KEY, $store);
191
+ } else {
192
+ return Mage::getStoreConfig(static::XML_PATH_REST_API_KEY, $store);
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Set the API Hostname value in System Configuration for a given store
198
+ * @param $hostname
199
+ * @param null $store
200
+ * @param bool $test_mode
201
+ * @return $this
202
+ */
203
+ public function setHostname($hostname, $store = null, $test_mode = false) {
204
+ $path = ($test_mode) ? static::XML_PATH_TEST_HOSTNAME : static::XML_PATH_HOSTNAME;
205
+ $this->setStoreConfig($path, $hostname, $store);
206
+ return $this;
207
+ }
208
+
209
+ /**
210
+ * Return the API Hostname configured, used for API requests, for a specified store
211
+ * @param Mage_Core_Model_Store|int|null $store
212
+ * @return string
213
+ */
214
+ public function getHostname($store = null) {
215
+ // Store was provided, check for test mode before getting the configured hostname
216
+ if($this->isTestModeEnabled($store)) {
217
+ $hostname = Mage::getStoreConfig(static::XML_PATH_TEST_HOSTNAME, $store);
218
+ } else {
219
+ $hostname = Mage::getStoreConfig(static::XML_PATH_HOSTNAME, $store);
220
+ }
221
+
222
+ return ($hostname) ? $hostname : Klevu_Search_Helper_Api::ENDPOINT_DEFAULT_HOSTNAME;
223
+ }
224
+
225
+ /**
226
+ * Return the API Rest Hostname configured, used for API requests, for a specified store
227
+ * @param Mage_Core_Model_Store|int|null $store
228
+ * @return string
229
+ */
230
+ public function getRestHostname($store = null) {
231
+ // Store was provided, check for test mode before getting the configured hostname
232
+ if($this->isTestModeEnabled($store)) {
233
+ $hostname = Mage::getStoreConfig(static::XML_PATH_RESTHOSTNAME, $store);
234
+ } else {
235
+ $hostname = Mage::getStoreConfig(static::XML_PATH_RESTHOSTNAME, $store);
236
+ }
237
+
238
+ return ($hostname) ? $hostname : Klevu_Search_Helper_Api::ENDPOINT_DEFAULT_HOSTNAME;
239
+ }
240
+
241
+ /**
242
+ * Set the Rest Hostname value in System Configuration for a given store
243
+ * @param $url
244
+ * @param null $store
245
+ * @param bool $test_mode
246
+ * @return $this
247
+ */
248
+ public function setRestHostname($url, $store = null, $test_mode = false) {
249
+ $path = ($test_mode) ? static::XML_PATH_RESTHOSTNAME : static::XML_PATH_RESTHOSTNAME;
250
+ $this->setStoreConfig($path, $url, $store);
251
+ return $this;
252
+ }
253
+
254
+ /**
255
+ * @param $url
256
+ * @param null $store
257
+ * @param bool $test_mode
258
+ * @return $this
259
+ */
260
+ public function setCloudSearchUrl($url, $store = null, $test_mode = false) {
261
+ $path = ($test_mode) ? static::XML_PATH_TEST_CLOUD_SEARCH_URL : static::XML_PATH_CLOUD_SEARCH_URL;
262
+ $this->setStoreConfig($path, $url, $store);
263
+ return $this;
264
+ }
265
+
266
+ /**
267
+ * @param null $store
268
+ * @return string
269
+ */
270
+ public function getCloudSearchUrl($store = null) {
271
+ if($this->isTestModeEnabled($store)) {
272
+ $url = Mage::getStoreConfig(static::XML_PATH_TEST_CLOUD_SEARCH_URL, $store);
273
+ } else {
274
+ $url = Mage::getStoreConfig(static::XML_PATH_CLOUD_SEARCH_URL, $store);
275
+ }
276
+
277
+ return ($url) ? $url : Klevu_Search_Helper_Api::ENDPOINT_DEFAULT_HOSTNAME;
278
+ }
279
+
280
+ /**
281
+ * @param $url
282
+ * @param null $store
283
+ * @param bool $test_mode
284
+ * @return $this
285
+ */
286
+ public function setAnalyticsUrl($url, $store = null, $test_mode = false) {
287
+ $path = ($test_mode) ? static::XML_PATH_TEST_ANALYTICS_URL : static::XML_PATH_ANALYTICS_URL;
288
+ $this->setStoreConfig($path, $url, $store);
289
+ return $this;
290
+ }
291
+
292
+ /**
293
+ * @param null $store
294
+ * @return string
295
+ */
296
+ public function getAnalyticsUrl($store = null) {
297
+ if($this->isTestModeEnabled($store)) {
298
+ $url = Mage::getStoreConfig(static::XML_PATH_TEST_ANALYTICS_URL);
299
+ } else {
300
+ $url = Mage::getStoreConfig(static::XML_PATH_ANALYTICS_URL);
301
+ }
302
+
303
+ return ($url) ? $url : Klevu_Search_Helper_Api::ENDPOINT_DEFAULT_HOSTNAME;
304
+ }
305
+
306
+ /**
307
+ * @param $url
308
+ * @param null $store
309
+ * @param bool $test_mode
310
+ * @return $this
311
+ */
312
+ public function setJsUrl($url, $store = null, $test_mode = false) {
313
+ $path = ($test_mode) ? static::XML_PATH_TEST_JS_URL : static::XML_PATH_JS_URL;
314
+ $this->setStoreConfig($path, $url, $store);
315
+ return $this;
316
+ }
317
+
318
+ /**
319
+ * @param null $store
320
+ * @return string
321
+ */
322
+ public function getJsUrl($store = null) {
323
+ if($this->isTestModeEnabled($store)) {
324
+ $url = Mage::getStoreConfig(static::XML_PATH_TEST_JS_URL);
325
+ } else {
326
+ $url = Mage::getStoreConfig(static::XML_PATH_JS_URL);
327
+ }
328
+
329
+ return ($url) ? $url : Klevu_Search_Helper_Api::ENDPOINT_DEFAULT_HOSTNAME;
330
+ }
331
+
332
+ /**
333
+ * Check if the Klevu Search extension is configured for the given store.
334
+ *
335
+ * @param null $store_id
336
+ *
337
+ * @return bool
338
+ */
339
+ public function isExtensionConfigured($store_id = null) {
340
+ $js_api_key = $this->getJsApiKey($store_id);
341
+ $rest_api_key = $this->getRestApiKey($store_id);
342
+
343
+ return (
344
+ $this->isExtensionEnabled($store_id)
345
+ && !empty($js_api_key)
346
+ && !empty($rest_api_key)
347
+ );
348
+ }
349
+
350
+ /**
351
+ * Return the system configuration setting for enabling Product Sync for the specified store.
352
+ * The returned value can have one of three possible meanings: Yes, No and Forced. The
353
+ * values mapping to these meanings are available as constants on
354
+ * Klevu_Search_Model_System_Config_Source_Yesnoforced.
355
+ *
356
+ * @param $store_id
357
+ *
358
+ * @return int
359
+ */
360
+ public function getProductSyncEnabledFlag($store_id = null) {
361
+ return intval(Mage::getStoreConfig(static::XML_PATH_PRODUCT_SYNC_ENABLED, $store_id));
362
+ }
363
+
364
+ /**
365
+ * Check if Product Sync is enabled for the specified store and domain.
366
+ *
367
+ * @param $store_id
368
+ *
369
+ * @return bool
370
+ */
371
+ public function isProductSyncEnabled($store_id = null) {
372
+ $flag = $this->getProductSyncEnabledFlag($store_id);
373
+
374
+ // Require "Forced" configuration setting to enable Product Sync on non production environments
375
+ //if (Mage::helper("klevu_search")->isProductionDomain(Mage::getBaseUrl())) {
376
+ return in_array($flag, array(
377
+ Klevu_Search_Model_System_Config_Source_Yesnoforced::YES,
378
+ //Klevu_Search_Model_System_Config_Source_Yesnoforced::FORCED
379
+ ));
380
+ //} else {
381
+ // return $flag === Klevu_Search_Model_System_Config_Source_Yesnoforced::FORCED;
382
+ //}
383
+ }
384
+
385
+ /**
386
+ * Return the configured frequency expression for Product Sync.
387
+ *
388
+ * @return string
389
+ */
390
+ public function getProductSyncFrequency() {
391
+ return Mage::getStoreConfig(static::XML_PATH_PRODUCT_SYNC_FREQUENCY);
392
+ }
393
+
394
+ /**
395
+ * Set the last Product Sync run time in System Configuration for the given store.
396
+ *
397
+ * @param DateTime|string $datetime If string is passed, it will be converted to DateTime.
398
+ * @param Mage_Core_Model_Store|int|null $store
399
+ *
400
+ * @return $this
401
+ */
402
+ public function setLastProductSyncRun($datetime = "now", $store = null) {
403
+ if (!$datetime instanceof DateTime) {
404
+ $datetime = new DateTime($datetime);
405
+ }
406
+
407
+ $this->setStoreConfig(static::XML_PATH_PRODUCT_SYNC_LAST_RUN, $datetime->format(static::DATETIME_FORMAT), $store);
408
+
409
+ return $this;
410
+ }
411
+
412
+ /**
413
+ * Check if Product Sync has ever run for the given store.
414
+ *
415
+ * @param Mage_Core_Model_Store|int|null $store
416
+ *
417
+ * @return bool
418
+ */
419
+ public function hasProductSyncRun($store = null) {
420
+ $config = Mage::getConfig();
421
+
422
+ if (!$config->getNode(static::XML_PATH_PRODUCT_SYNC_LAST_RUN, "store", Mage::app()->getStore($store)->getId())) {
423
+ return false;
424
+ }
425
+
426
+ return true;
427
+ }
428
+
429
+ public function setAdditionalAttributesMap($map, $store = null) {
430
+ unset($map["__empty"]);
431
+ $this->setStoreConfig(static::XML_PATH_ATTRIBUTES_ADDITIONAL, serialize($map), $store);
432
+ return $this;
433
+ }
434
+
435
+ /**
436
+ * Return the map of additional Klevu attributes to Magento attributes.
437
+ *
438
+ * @param int|Mage_Core_Model_Store $store
439
+ *
440
+ * @return array
441
+ */
442
+ public function getAdditionalAttributesMap($store = null) {
443
+ $map = unserialize(Mage::getStoreConfig(static::XML_PATH_ATTRIBUTES_ADDITIONAL, $store));
444
+
445
+ return (is_array($map)) ? $map : array();
446
+ }
447
+
448
+ /**
449
+ * Set the automatically mapped attributes
450
+ * @param array $map
451
+ * @param int|Mage_Core_Model_Store $store
452
+ * @return $this
453
+ */
454
+ public function setAutomaticAttributesMap($map, $store = null) {
455
+ unset($map["__empty"]);
456
+ $this->setStoreConfig(static::XML_PATH_ATTRIBUTES_AUTOMATIC, serialize($map), $store);
457
+ return $this;
458
+ }
459
+
460
+ /**
461
+ * Returns the automatically mapped attributes
462
+ * @param int|Mage_Core_Model_Store $store
463
+ * @return array
464
+ */
465
+ public function getAutomaticAttributesMap($store = null) {
466
+ $map = unserialize(Mage::getStoreConfig(static::XML_PATH_ATTRIBUTES_AUTOMATIC, $store));
467
+
468
+ return (is_array($map)) ? $map : array();
469
+ }
470
+
471
+ /**
472
+ * Return the System Configuration setting for enabling Order Sync for the given store.
473
+ * The returned value can have one of three possible meanings: Yes, No and Forced. The
474
+ * values mapping to these meanings are available as constants on
475
+ * Klevu_Search_Model_System_Config_Source_Yesnoforced.
476
+ *
477
+ * @param Mage_Core_Model_Store|int $store
478
+ *
479
+ * @return int
480
+ */
481
+ public function getOrderSyncEnabledFlag($store = null) {
482
+ return intval(Mage::getStoreConfig(static::XML_PATH_ORDER_SYNC_ENABLED, $store));
483
+ }
484
+
485
+ /**
486
+ * Check if Order Sync is enabled for the given store on the current domain.
487
+ *
488
+ * @param Mage_Core_Model_Store|int $store
489
+ *
490
+ * @return bool
491
+ */
492
+ public function isOrderSyncEnabled($store = null) {
493
+ $flag = $this->getOrderSyncEnabledFlag($store);
494
+ // Require "Forced" configuration setting to enable Sync on non production environments
495
+ //if (Mage::helper("klevu_search")->isProductionDomain(Mage::getBaseUrl())) {
496
+ return in_array($flag, array(
497
+ Klevu_Search_Model_System_Config_Source_Yesnoforced::YES,
498
+ ));
499
+ //} else {
500
+ //return $flag === Klevu_Search_Model_System_Config_Source_Yesnoforced::FORCED;
501
+ //}
502
+ }
503
+
504
+ /**
505
+ * Return the configured frequency expression for Order Sync.
506
+ *
507
+ * @return string
508
+ */
509
+ public function getOrderSyncFrequency() {
510
+ return Mage::getStoreConfig(static::XML_PATH_ORDER_SYNC_FREQUENCY);
511
+ }
512
+
513
+ /**
514
+ * Set the last Order Sync run time in System Configuration.
515
+ *
516
+ * @param DateTime|string $datetime If string is passed, it will be converted to DateTime.
517
+ *
518
+ * @return $this
519
+ */
520
+ public function setLastOrderSyncRun($datetime = "now") {
521
+ if (!$datetime instanceof DateTime) {
522
+ $datetime = new DateTime($datetime);
523
+ }
524
+
525
+ $this->setGlobalConfig(static::XML_PATH_ORDER_SYNC_LAST_RUN, $datetime->format(static::DATETIME_FORMAT));
526
+
527
+ return $this;
528
+ }
529
+
530
+ /**
531
+ * Check if default Magento log settings should be overridden to force logging for this module.
532
+ *
533
+ * @return bool
534
+ */
535
+ public function isLoggingForced() {
536
+ return Mage::getStoreConfigFlag(static::XML_PATH_FORCE_LOG);
537
+ }
538
+
539
+ /**
540
+ * Return the minimum log level configured. Default to Zend_Log::WARN.
541
+ *
542
+ * @return int
543
+ */
544
+ public function getLogLevel() {
545
+ $log_level = Mage::getStoreConfig(static::XML_PATH_LOG_LEVEL);
546
+
547
+ return ($log_level !== null) ? intval($log_level) : Zend_Log::INFO;
548
+ }
549
+
550
+ /**
551
+ * Return an multi-dimensional array of magento and klevu attributes that are mapped by default.
552
+ * @return array
553
+ */
554
+ public function getDefaultMappedAttributes() {
555
+ return array(
556
+ "magento_attribute" => array(
557
+ "name",
558
+ "sku",
559
+ "image",
560
+ "description",
561
+ "short_description",
562
+ "price",
563
+ "tax_class_id",
564
+ "weight"),
565
+ "klevu_attribute" => array(
566
+ "name",
567
+ "sku",
568
+ "image",
569
+ "desc",
570
+ "shortDesc",
571
+ "salePrice",
572
+ "salePrice",
573
+ "weight"
574
+ )
575
+ );
576
+ }
577
+
578
+ /**
579
+ * Returns array of other attributes map from store configuration.
580
+ *
581
+ * @param Mage_Core_Model_Store|int|null $store
582
+ * @return array
583
+ */
584
+ public function getOtherAttributesToIndex($store = null) {
585
+ if (Mage::getStoreConfig(static::XML_PATH_ATTRIBUTES_OTHER, $store)) {
586
+ return explode(",", Mage::getStoreConfig(static::XML_PATH_ATTRIBUTES_OTHER, $store));
587
+ }
588
+
589
+ return array();
590
+ }
591
+
592
+ /**
593
+ * Return the boosting attribute defined in store configuration.
594
+ *
595
+ * @param Mage_Core_Model_Store|int|null $store
596
+ * @return array
597
+ */
598
+ public function getBoostingAttribute($store = null) {
599
+ return Mage::getStoreConfig(static::XML_PATH_ATTRIBUTES_BOOSTING, $store);
600
+ }
601
+
602
+ /**
603
+ * Set the global scope System Configuration value for the given key.
604
+ *
605
+ * @param string $key
606
+ * @param string $value
607
+ *
608
+ * @return $this
609
+ */
610
+ protected function setGlobalConfig($key, $value) {
611
+ Mage::getConfig()
612
+ ->saveConfig($key, $value, "default")
613
+ ->reinit();
614
+
615
+ return $this;
616
+ }
617
+
618
+ /**
619
+ * Set the store scope System Configuration value for the given key.
620
+ *
621
+ * @param string $key
622
+ * @param string $value
623
+ * @param Mage_Core_Model_Store|int|null $store If not given, current store will be used.
624
+ *
625
+ * @return $this
626
+ */
627
+ public function setStoreConfig($key, $value, $store = null) {
628
+ $config = Mage::getConfig();
629
+
630
+ $store_code = Mage::app()->getStore($store)->getCode();
631
+ $scope_id = $config->getNode(sprintf(static::XML_PATH_STORE_ID, $store_code));
632
+ if ($scope_id !== null) {
633
+ $scope_id = (int) $scope_id;
634
+
635
+ $config->saveConfig($key, $value, "stores", $scope_id);
636
+
637
+ $config->reinit();
638
+ }
639
+
640
+ return $this;
641
+ }
642
+ }
app/code/community/Klevu/Search/Helper/Data.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Helper_Data extends Mage_Core_Helper_Abstract {
4
+
5
+ const LOG_FILE = "Klevu_Search.log";
6
+
7
+ const ID_SEPARATOR = "-";
8
+
9
+ const SANITISE_STRING = "/:|,|;/";
10
+
11
+ /**
12
+ * Given a locale code, extract the language code from it
13
+ * e.g. en_GB => en, fr_FR => fr
14
+ *
15
+ * @param string $locale
16
+ */
17
+ function getLanguageFromLocale($locale) {
18
+ if (strlen($locale) == 5 && strpos($locale, "_") == 2) {
19
+ return substr($locale, 0, 2);
20
+ }
21
+
22
+ return $locale;
23
+ }
24
+
25
+ /**
26
+ * Return the language code for the given store.
27
+ *
28
+ * @param int|Mage_Core_Model_Store $store
29
+ *
30
+ * @return string
31
+ */
32
+ function getStoreLanguage($store = null) {
33
+ if ($store = Mage::app()->getStore($store)) {
34
+ return $this->getLanguageFromLocale($store->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE));
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Check if the given domain is considered to be a valid domain for a production environment.
40
+ *
41
+ * @param $domain
42
+ *
43
+ * @return bool
44
+ */
45
+ public function isProductionDomain($domain) {
46
+ return preg_match("/\b(staging|dev|local)\b/", $domain) == 0;
47
+ }
48
+
49
+ /**
50
+ * Generate a Klevu product ID for the given product.
51
+ *
52
+ * @param int $product_id Magento ID of the product to generate a Klevu ID for.
53
+ * @param null|int $parent_id Optional Magento ID of the parent product.
54
+ *
55
+ * @return string
56
+ */
57
+ public function getKlevuProductId($product_id, $parent_id = 0) {
58
+ if ($parent_id != 0) {
59
+ $parent_id .= static::ID_SEPARATOR;
60
+ } else {
61
+ $parent_id = "";
62
+ }
63
+
64
+ return sprintf("%s%s", $parent_id, $product_id);
65
+ }
66
+
67
+ /**
68
+ * Convert a Klevu product ID back into a Magento product ID. Returns an
69
+ * array with "product_id" element for the product ID and a "parent_id"
70
+ * element for the parent product ID or 0 if the Klevu product doesn't have
71
+ * a parent.
72
+ *
73
+ * @param $klevu_id
74
+ *
75
+ * @return array
76
+ */
77
+ public function getMagentoProductId($klevu_id) {
78
+ $parts = explode(static::ID_SEPARATOR, $klevu_id, 2);
79
+
80
+ if (count($parts) > 1) {
81
+ return array('product_id' => $parts[1], 'parent_id' => $parts[0]);
82
+ } else {
83
+ return array('product_id' => $parts[0], 'parent_id' => "0");
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Format bytes into a human readable representation, e.g.
89
+ * 6815744 => 6.5M
90
+ *
91
+ * @param $bytes
92
+ * @param int $precision
93
+ *
94
+ * @return string
95
+ */
96
+ public function bytesToHumanReadable($bytes, $precision = 2) {
97
+ $suffixes = array("", "k", "M", "G", "T", "P");
98
+ $base = log($bytes) / log(1024);
99
+ return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
100
+ }
101
+
102
+ /**
103
+ * Convert human readable formatting of bytes to bytes, e.g.
104
+ * 6.5M => 6815744
105
+ *
106
+ * @param $string
107
+ *
108
+ * @return int
109
+ */
110
+ public function humanReadableToBytes($string) {
111
+ $suffix = strtolower(substr($string, -1));
112
+ $result = substr($string, 0, -1);
113
+
114
+ switch ($suffix) {
115
+ case 'g': // G is the max unit as of PHP 5.5.12
116
+ $result *= 1024;
117
+ case 'm':
118
+ $result *= 1024;
119
+ case 'k':
120
+ $result *= 1024;
121
+ break;
122
+ default:
123
+ $result = $string;
124
+ }
125
+
126
+ return ceil($result);
127
+ }
128
+
129
+ /**
130
+ * Return the configuration data for a "Sync All Products" button displayed
131
+ * on the Manage Products page in the backend.
132
+ *
133
+ * @return array
134
+ */
135
+ public function getSyncAllButtonData() {
136
+ return array(
137
+ 'label' => $this->__("Sync All Products to Klevu"),
138
+ 'onclick' => sprintf("setLocation('%s')", Mage::getModel('adminhtml/url')->getUrl("adminhtml/klevu_search/sync_all"))
139
+ );
140
+ }
141
+
142
+ /**
143
+ * Write a log message to the Klevu_Search log file.
144
+ *
145
+ * @param int $level
146
+ * @param string $message
147
+ */
148
+ public function log($level, $message) {
149
+ $config = Mage::helper("klevu_search/config");
150
+
151
+ if ($level <= $config->getLogLevel()) {
152
+ Mage::log($message, $level, static::LOG_FILE, $config->isLoggingForced());
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Remove the characters used to organise the other attribute values from the
158
+ * passed in string.
159
+ *
160
+ * @param string $value
161
+ * @return string
162
+ */
163
+ public function santiseAttributeValue($value) {
164
+ if (is_array($value)) {
165
+ $sanitised_array = array();
166
+ foreach($value as $item) {
167
+ $sanitised_array[] = preg_replace(self::SANITISE_STRING, " ", $item);
168
+ }
169
+ return $sanitised_array;
170
+ }
171
+ return preg_replace(self::SANITISE_STRING, " ", $value);
172
+ }
173
+
174
+ /**
175
+ * Return whether or not the current page is CatalogSearch.
176
+ * @return bool
177
+ */
178
+ public function isCatalogSearch() {
179
+ return in_array('catalogsearch_result_index', Mage::app()->getLayout()->getUpdate()->getHandles());
180
+ }
181
+ /**
182
+ Generate a Klevu product sku with parent product.
183
+ *
184
+ * @param string $product_sku Magento Sku of the product to generate a Klevu sku for.
185
+ * @param null $parent_sku Optional Magento Parent Sku of the parent product.
186
+ *
187
+ * @return string
188
+ */
189
+ public function getKlevuProductSku($product_sku, $parent_sku = "") {
190
+ if (!empty($parent_sku)) {
191
+ $parent_sku .= static::ID_SEPARATOR;
192
+ } else {
193
+ $parent_sku = "";
194
+ }
195
+ return sprintf("%s%s", $parent_sku, $product_sku);
196
+ }
197
+ }
app/code/community/Klevu/Search/Model/Api/Action.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action extends Varien_Object {
4
+
5
+ const ENDPOINT = "";
6
+ const METHOD = "GET";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response";
10
+
11
+ /** @var Klevu_Search_Model_Api_Request $request */
12
+ protected $request;
13
+
14
+ /** @var Klevu_Search_Model_Api_Response $response */
15
+ protected $response;
16
+
17
+ /**
18
+ * Set the request model to use for this API action.
19
+ *
20
+ * @param Klevu_Search_Model_Api_Request $request_model
21
+ *
22
+ * @return $this
23
+ */
24
+ public function setRequest(Klevu_Search_Model_Api_Request $request_model) {
25
+ $this->request = $request_model;
26
+
27
+ return $this;
28
+ }
29
+
30
+ /**
31
+ * Return the request model used for this API action.
32
+ *
33
+ * @return Klevu_Search_Model_Api_Request
34
+ */
35
+ public function getRequest() {
36
+ if (!$this->request) {
37
+ $this->request = Mage::getModel(static::DEFAULT_REQUEST_MODEL);
38
+ }
39
+
40
+ return $this->request;
41
+ }
42
+
43
+ /**
44
+ * Set the response model to use for this API action.
45
+ *
46
+ * @param Klevu_Search_Model_Api_Response $response_model
47
+ *
48
+ * @return $this
49
+ */
50
+ public function setResponse(Klevu_Search_Model_Api_Response $response_model) {
51
+ $this->response = $response_model;
52
+
53
+ return $this;
54
+ }
55
+
56
+ /**
57
+ * Return the response model used for this API action.
58
+ *
59
+ * @return Klevu_Search_Model_Api_Response
60
+ */
61
+ public function getResponse() {
62
+ if (!$this->response) {
63
+ $this->response = Mage::getModel(static::DEFAULT_RESPONSE_MODEL);
64
+ }
65
+
66
+ return $this->response;
67
+ }
68
+
69
+ /**
70
+ * Execute the API action with the given parameters.
71
+ *
72
+ * @param array $parameters
73
+ *
74
+ * @return Klevu_Search_Model_Api_Response
75
+ */
76
+ public function execute($parameters = array()) {
77
+ $validation_result = $this->validate($parameters);
78
+ if ($validation_result !== true) {
79
+ return Mage::getModel('klevu_search/api_response_invalid')->setErrors($validation_result);
80
+ }
81
+
82
+ $request = $this->getRequest();
83
+
84
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(static::ENDPOINT, $this->getStore(),Mage::helper('klevu_search/config')->getHostname($this->getStore()));
85
+
86
+ $request
87
+ ->setResponseModel($this->getResponse())
88
+ ->setEndpoint($endpoint)
89
+ ->setMethod(static::METHOD)
90
+ ->setData($parameters);
91
+
92
+ return $request->send();
93
+ }
94
+
95
+ /**
96
+ * Get the store used for this request
97
+ * @return Mage_Core_Model_Store
98
+ */
99
+ public function getStore() {
100
+ if(!$this->hasData('store')) {
101
+ $this->setData('store', Mage::app()->getStore());
102
+ }
103
+ return $this->getData('store');
104
+ }
105
+
106
+ /**
107
+ * Validate the given parameters against the API action specification and
108
+ * return true if validation passed or an array of validation error messages
109
+ * otherwise.
110
+ *
111
+ * @param $parameters
112
+ *
113
+ * @return bool|array
114
+ */
115
+ protected function validate($parameters) {
116
+ return true;
117
+ }
118
+ }
app/code/community/Klevu/Search/Model/Api/Action/Addrecords.php ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Addrecords extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/rest/service/addRecords";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_xml";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_message";
10
+
11
+ // mandatory_field_name => allowed_empty
12
+ protected $mandatory_fields = array(
13
+ "id" => false,
14
+ "name" => false,
15
+ "url" => false,
16
+ "salePrice" => false,
17
+ "currency" => false,
18
+ "category" => true,
19
+ "listCategory" => true
20
+ );
21
+
22
+ public function execute($parameters = array()) {
23
+ $response = $this->getResponse();
24
+
25
+ $validation_result = $this->validate($parameters);
26
+ if ($validation_result !== true) {
27
+ return Mage::getModel('klevu_search/api_response_invalid')->setErrors($validation_result);
28
+ }
29
+
30
+ $skipped_records = $this->validateRecords($parameters);
31
+ if (count($parameters['records']) > 0) {
32
+ if ($skipped_records != null) {
33
+ // Validation has removed some of the records due to errors, but the rest
34
+ // can still be submitted, so just log and proceed
35
+ $response->setData("skipped_records", $skipped_records);
36
+ }
37
+ } else {
38
+ return Mage::getModel('klevu_search/api_response_invalid')->setErrors(array(
39
+ "all_records_invalid" => implode(", ", $skipped_records["messages"])
40
+ ));
41
+ }
42
+
43
+ $this->prepareParameters($parameters);
44
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(static::ENDPOINT, $this->getStore(),Mage::helper('klevu_search/config')->getRestHostname($this->getStore()));
45
+
46
+ $request = $this->getRequest();
47
+ $request
48
+ ->setResponseModel($response)
49
+ ->setEndpoint($endpoint)
50
+ ->setMethod(static::METHOD)
51
+ ->setData($parameters);
52
+
53
+ return $request->send();
54
+ }
55
+
56
+ /**
57
+ * Get the store used for this request.
58
+ * @return Mage_Core_Model_Store
59
+ */
60
+ public function getStore() {
61
+ if (!$this->hasData('store')) {
62
+ $this->setData('store', Mage::app()->getStore());
63
+ }
64
+
65
+ return $this->getData('store');
66
+ }
67
+
68
+ protected function validate($parameters) {
69
+ $errors = array();
70
+
71
+ if (!isset($parameters['sessionId']) || empty($parameters['sessionId'])) {
72
+ $errors['sessionId'] = "Missing session ID";
73
+ }
74
+
75
+ if (!isset($parameters['records']) || !is_array($parameters['records']) || count($parameters['records']) == 0) {
76
+ $errors['records'] = "No records";
77
+ }
78
+
79
+ if (count($errors) == 0) {
80
+ return true;
81
+ }
82
+
83
+ return $errors;
84
+ }
85
+
86
+ /**
87
+ * Validate the records parameter and remove records that are invalid. Modifies
88
+ * the $parameters argument in place. Return the list of skipped records and
89
+ * their error messages.
90
+ *
91
+ * @param $parameters
92
+ *
93
+ * @return array
94
+ */
95
+ protected function validateRecords(&$parameters) {
96
+ if (isset($parameters['records']) && is_array($parameters['records'])) {
97
+ $skipped_records = array(
98
+ "index" => array(),
99
+ "messages" => array()
100
+ );
101
+
102
+ foreach ($parameters['records'] as $i => $record) {
103
+ $missing_fields = array();
104
+ $empty_fields = array();
105
+
106
+ foreach ($this->mandatory_fields as $mandatory_field => $allowed_empty) {
107
+ if (!array_key_exists($mandatory_field, $record)) {
108
+ $missing_fields[] = $mandatory_field;
109
+ } else {
110
+ if (!$allowed_empty && !is_numeric($record[$mandatory_field]) && empty($record[$mandatory_field])) {
111
+ $empty_fields[] = $mandatory_field;
112
+ }
113
+ }
114
+ }
115
+
116
+ $id = (isset($record['id']) && !empty($record['id'])) ? sprintf(" (id: %d)", $record['id']) : "";
117
+
118
+ if (count($missing_fields) > 0 || count($empty_fields) > 0) {
119
+ unset($parameters["records"][$i]);
120
+ $skipped_records["index"][] = $i;
121
+ if (count($missing_fields) > 0) {
122
+ $skipped_records["messages"][] = sprintf("Record %d%s is missing mandatory fields: %s", $i, $id, implode(", ", $missing_fields));
123
+ }
124
+ if (count($empty_fields) > 0) {
125
+ $skipped_records["messages"][] = sprintf("Record %d%s has empty mandatory fields: %s", $i, $id, implode(", ", $empty_fields));
126
+ }
127
+ }
128
+ }
129
+
130
+ return $skipped_records;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Convert the given parameters to a format expected by the XML request model.
136
+ *
137
+ * @param $parameters
138
+ */
139
+ protected function prepareParameters(&$parameters) {
140
+ foreach ($parameters['records'] as &$record) {
141
+ if (isset($record['listCategory']) && is_array($record['listCategory'])) {
142
+ $record['listCategory'] = implode(";", $record['listCategory']);
143
+ }
144
+
145
+ if (isset($record['other']) && is_array($record['other'])) {
146
+ $this->prepareOtherParameters($record);
147
+ }
148
+
149
+ if (isset($record['otherAttributeToIndex']) && is_array($record['otherAttributeToIndex'])) {
150
+ $this->prepareOtherAttributeToIndexParameters($record);
151
+ }
152
+
153
+ $pairs = array();
154
+
155
+
156
+ foreach ($record as $key => $value) {
157
+ $pairs[] = array(
158
+ 'pair' => array(
159
+ 'key' => $key,
160
+ 'value' => $value
161
+ )
162
+ );
163
+ }
164
+
165
+ $record = array(
166
+ 'record' => array(
167
+ 'pairs' => $pairs
168
+ )
169
+ );
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Flattens other parameters array to a string formatted: key:value[,value]
175
+ * @param string
176
+ */
177
+ protected function prepareOtherParameters(&$record) {
178
+ foreach ($record['other'] as $key => &$value) {
179
+ $key = $this->sanitiseOtherAttribute($key);
180
+ if(is_array($value)){
181
+ $label = $this->sanitiseOtherAttribute($value['label']);
182
+ $value = $this->sanitiseOtherAttribute($value['values']);
183
+ }else {
184
+ $label = $this->sanitiseOtherAttribute($key);
185
+ $value = $this->sanitiseOtherAttribute($value);
186
+ }
187
+
188
+ if (is_array($value)) {
189
+ $value = implode(",", $value);
190
+ }
191
+
192
+ $value = sprintf("%s:%s:%s", $key, $label, $value);
193
+ }
194
+ $record['other'] = implode(";", $record['other']);
195
+ }
196
+
197
+ /**
198
+ * Flattens otherAttributeToIndex parameters array to a string formatted: key:value[,value]
199
+ * @param string
200
+ */
201
+ protected function prepareOtherAttributeToIndexParameters(&$record) {
202
+ foreach ($record['otherAttributeToIndex'] as $key => &$value) {
203
+ $key = $this->sanitiseOtherAttribute($key);
204
+ if(is_array($value)){
205
+ $label = $this->sanitiseOtherAttribute($value['label']);
206
+ $value = $this->sanitiseOtherAttribute($value['values']);
207
+ }else {
208
+ $label = $this->sanitiseOtherAttribute($key);
209
+ $value = $this->sanitiseOtherAttribute($value);
210
+ }
211
+
212
+ if (is_array($value)) {
213
+ $value = implode(",", $value);
214
+ }
215
+
216
+ $value = sprintf("%s:%s:%s", $key, $label, $value);
217
+ }
218
+ $record['otherAttributeToIndex'] = implode(";", $record['otherAttributeToIndex']);
219
+ }
220
+
221
+ /**
222
+ * Remove the characters used to organise the other attribute values from the
223
+ * passed in string.
224
+ *
225
+ * @param $value
226
+ *
227
+ * @return string
228
+ */
229
+ protected function sanitiseOtherAttribute($value) {
230
+ return Mage::helper('klevu_search')->santiseAttributeValue($value);
231
+ }
232
+ }
app/code/community/Klevu/Search/Model/Api/Action/Adduser.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Adduser extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/n-search/addUser";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_data";
10
+
11
+ protected function validate($parameters) {
12
+ $errors = array();
13
+
14
+ if (!isset($parameters['email']) || empty($parameters['email'])) {
15
+ $errors['email'] = "Missing email";
16
+ }
17
+
18
+ if (!isset($parameters['password']) || empty($parameters['password'])) {
19
+ $errors['password'] = "Missing password";
20
+ }
21
+
22
+ if (!isset($parameters['url']) || empty($parameters['password'])) {
23
+ $errors['url'] = "Missing url";
24
+ }
25
+
26
+ if (count($errors) == 0) {
27
+ return true;
28
+ }
29
+
30
+ return $errors;
31
+ }
32
+ }
app/code/community/Klevu/Search/Model/Api/Action/Addwebstore.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Addwebstore extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/n-search/addWebstore";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_data";
10
+
11
+ protected function validate($parameters) {
12
+ $errors = array();
13
+
14
+ if (!isset($parameters['customerId']) || empty($parameters['customerId'])) {
15
+ $errors['customerId'] = "Missing customer id.";
16
+ }
17
+
18
+ if (!isset($parameters['testMode']) || empty($parameters['testMode'])) {
19
+ $errors['testMode'] = "Missing test mode.";
20
+ } else {
21
+ if (!in_array($parameters['testMode'], array("true", "false"))) {
22
+ $errors['testMode'] = "Test mode must contain the text true or false.";
23
+ }
24
+ }
25
+
26
+ if (!isset($parameters['storeName']) || empty($parameters['storeName'])) {
27
+ $errors['storeName'] = "Missing store name.";
28
+ }
29
+
30
+ if (!isset($parameters['language']) || empty($parameters['language'])) {
31
+ $errors['language'] = "Missing language.";
32
+ }
33
+
34
+ if (!isset($parameters['timezone']) || empty($parameters['timezone'])) {
35
+ $errors['timezone'] = "Missing timezone.";
36
+ }
37
+
38
+ if (!isset($parameters['version']) || empty($parameters['version'])) {
39
+ $errors['version'] = "Missing module version";
40
+ }
41
+
42
+ if (!isset($parameters['country']) || empty($parameters['country'])) {
43
+ $errors['country'] = "Missing country.";
44
+ }
45
+
46
+ if (!isset($parameters['locale']) || empty($parameters['locale'])) {
47
+ $errors['locale'] = "Missing locale.";
48
+ }
49
+
50
+ if (count($errors) == 0) {
51
+ return true;
52
+ }
53
+
54
+ return $errors;
55
+ }
56
+ }
app/code/community/Klevu/Search/Model/Api/Action/Debuginfo.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Klevu_Search_Model_Api_Action_Debuginfo extends Klevu_Search_Model_Api_Action {
3
+
4
+ const ENDPOINT = "/n-search/logReceiver";
5
+ const METHOD = "POST";
6
+
7
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
8
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_data";
9
+
10
+ public function debugKlevu($parameters)
11
+ {
12
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(static::ENDPOINT);
13
+ $response = $this->getResponse();
14
+ $request = $this->getRequest();
15
+ $request
16
+ ->setResponseModel($response)
17
+ ->setEndpoint($endpoint)
18
+ ->setMethod(static::METHOD)
19
+ ->setData($parameters);
20
+ return $request->send();
21
+
22
+
23
+ }
24
+ }
app/code/community/Klevu/Search/Model/Api/Action/Deleterecords.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Deleterecords extends Klevu_Search_Model_Api_Action_Addrecords {
4
+
5
+ const ENDPOINT = "/rest/service/deleteRecords";
6
+ const METHOD = "POST";
7
+
8
+ // mandatory_field_name => allowed_empty
9
+ protected $mandatory_fields = array(
10
+ "id" => false
11
+ );
12
+ }
app/code/community/Klevu/Search/Model/Api/Action/Gettimezone.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Gettimezone extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/analytics/getTimezone";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_timezone";
10
+
11
+ protected function validate($parameters) {
12
+ return true;
13
+ }
14
+ }
app/code/community/Klevu/Search/Model/Api/Action/Getuserdetail.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Getuserdetail extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/n-search/getUserDetail";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_data";
10
+
11
+ protected function validate($parameters) {
12
+ $errors = array();
13
+
14
+ if (!isset($parameters['email']) || empty($parameters['email'])) {
15
+ $errors['email'] = "Missing email";
16
+ }
17
+
18
+ if (!isset($parameters['password']) || empty($parameters['password'])) {
19
+ $errors['password'] = "Missing password";
20
+ }
21
+
22
+ if (count($errors) == 0) {
23
+ return true;
24
+ }
25
+
26
+ return $errors;
27
+ }
28
+ }
app/code/community/Klevu/Search/Model/Api/Action/Idsearch.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Idsearch extends Klevu_Search_Model_Api_Action {
4
+ const ENDPOINT = "/cloud-search/n-search/idsearch";
5
+ const METHOD = "GET";
6
+
7
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
8
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_search";
9
+
10
+ protected function validate($parameters) {
11
+ $errors = array();
12
+
13
+ if (!isset($parameters['ticket']) || empty($parameters['ticket'])) {
14
+ $errors['ticket'] = "Missing ticket (Search API Key)";
15
+ }
16
+
17
+ if (!isset($parameters['noOfResults']) || empty($parameters['noOfResults'])) {
18
+ $errors['noOfResults'] = "Missing number of results to return";
19
+ }
20
+
21
+ if(!isset($parameters['term']) || empty($parameters['term'])) {
22
+ $errors['term'] = "Missing search term";
23
+ }
24
+
25
+ if(!isset($parameters['paginationStartsFrom'])) {
26
+ $errors['paginationStartsFrom'] = "Missing pagination start from value ";
27
+ } else if (intval($parameters['paginationStartsFrom']) < 0) {
28
+ $errors['paginationStartsFrom'] = "Pagination needs to start from 0 or higher";
29
+ }
30
+
31
+ if(!isset($parameters['klevuSort']) || empty($parameters['klevuSort'])) {
32
+ $errors['klevuSort'] = "Missing Klevu Sort order";
33
+ }
34
+
35
+ if(!isset($parameters['enableFilters']) || empty($parameters['enableFilters'])) {
36
+ $errors['enableFilters'] = "Missing Enable Filters parameter";
37
+ }
38
+
39
+ if (count($errors) == 0) {
40
+ return true;
41
+ }
42
+ return $errors;
43
+ }
44
+
45
+ /**
46
+ * Execute the API action with the given parameters.
47
+ *
48
+ * @param array $parameters
49
+ *
50
+ * @return Klevu_Search_Model_Api_Response
51
+ */
52
+ public function execute($parameters = array()) {
53
+ $validation_result = $this->validate($parameters);
54
+ if ($validation_result !== true) {
55
+ return Mage::getModel('klevu_search/api_response_invalid')->setErrors($validation_result);
56
+ }
57
+
58
+ $request = $this->getRequest();
59
+
60
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(static::ENDPOINT, $this->getStore(), Mage::helper('klevu_search/config')->getCloudSearchUrl($this->getStore()));
61
+
62
+ $request
63
+ ->setResponseModel($this->getResponse())
64
+ ->setEndpoint($endpoint)
65
+ ->setMethod(static::METHOD)
66
+ ->setData($parameters);
67
+
68
+ return $request->send();
69
+ }
70
+ }
app/code/community/Klevu/Search/Model/Api/Action/Producttracking.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Producttracking extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/analytics/productTracking";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_data";
10
+
11
+ protected function validate($parameters) {
12
+ $errors = array();
13
+
14
+ if (!isset($parameters["klevu_apiKey"]) || empty($parameters["klevu_apiKey"])) {
15
+ $errors["klevu_apiKey"] = "Missing JS API key.";
16
+ }
17
+
18
+ if (!isset($parameters["klevu_type"]) || empty($parameters["klevu_type"])) {
19
+ $errors["klevu_type"] = "Missing type.";
20
+ }
21
+
22
+ if (!isset($parameters["klevu_productId"]) || empty($parameters["klevu_productId"])) {
23
+ $errors["klevu_productId"] = "Missing product ID.";
24
+ }
25
+
26
+ if (!isset($parameters["klevu_unit"]) || empty($parameters["klevu_unit"])) {
27
+ $errors["klevu_unit"] = "Missing unit.";
28
+ }
29
+
30
+ if (!isset($parameters["klevu_salePrice"]) || empty($parameters["klevu_salePrice"])) {
31
+ $errors["klevu_salePrice"] = "Missing sale price.";
32
+ }
33
+
34
+ if (!isset($parameters["klevu_currency"]) || empty($parameters["klevu_currency"])) {
35
+ $errors["klevu_currency"] = "Missing currency.";
36
+ }
37
+
38
+
39
+ if (count($errors) == 0) {
40
+ return true;
41
+ }
42
+ return $errors;
43
+ }
44
+
45
+
46
+ /**
47
+ * Execute the API action with the given parameters.
48
+ *
49
+ * @param array $parameters
50
+ *
51
+ * @return Klevu_Search_Model_Api_Response
52
+ */
53
+ public function execute($parameters = array()) {
54
+ $validation_result = $this->validate($parameters);
55
+ if ($validation_result !== true) {
56
+ return Mage::getModel('klevu_search/api_response_invalid')->setErrors($validation_result);
57
+ }
58
+
59
+ $request = $this->getRequest();
60
+
61
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(
62
+ static::ENDPOINT,
63
+ $this->getStore(),
64
+ Mage::helper('klevu_search/config')->getAnalyticsUrl()
65
+ );
66
+
67
+ $request
68
+ ->setResponseModel($this->getResponse())
69
+ ->setEndpoint($endpoint)
70
+ ->setMethod(static::METHOD)
71
+ ->setData($parameters);
72
+
73
+ return $request->send();
74
+ }
75
+ }
app/code/community/Klevu/Search/Model/Api/Action/Removetestmode.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Klevu_Search_Model_Api_Action_Removetestmode extends Klevu_Search_Model_Api_Action {
3
+
4
+ const ENDPOINT = "/n-search/changeWebstoreMode";
5
+ const METHOD = "POST";
6
+
7
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_post";
8
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_data";
9
+
10
+ public function removeTestMode($parameters)
11
+ {
12
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(static::ENDPOINT);
13
+ $response = $this->getResponse();
14
+ $request = $this->getRequest();
15
+ $request
16
+ ->setResponseModel($response)
17
+ ->setEndpoint($endpoint)
18
+ ->setMethod(static::METHOD)
19
+ ->setData($parameters);
20
+ return $request->send();
21
+
22
+
23
+ }
24
+ }
app/code/community/Klevu/Search/Model/Api/Action/Startsession.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Startsession extends Klevu_Search_Model_Api_Action {
4
+
5
+ const ENDPOINT = "/rest/service/startSession";
6
+ const METHOD = "POST";
7
+
8
+ const DEFAULT_REQUEST_MODEL = "klevu_search/api_request_xml";
9
+ const DEFAULT_RESPONSE_MODEL = "klevu_search/api_response_message";
10
+
11
+ public function execute($parameters = array()) {
12
+ $validation_result = $this->validate($parameters);
13
+ if ($validation_result !== true) {
14
+ return Mage::getModel('klevu_search/api_response_invalid')->setErrors($validation_result);
15
+ }
16
+
17
+ $request = $this->getRequest();
18
+ $endpoint = Mage::helper('klevu_search/api')->buildEndpoint(static::ENDPOINT,$parameters['store'],Mage::helper('klevu_search/config')->getRestHostname($parameters['store']));
19
+
20
+ $request
21
+ ->setResponseModel($this->getResponse())
22
+ ->setEndpoint($endpoint)
23
+ ->setMethod(static::METHOD)
24
+ ->setHeader("Authorization", $parameters['api_key']);
25
+
26
+ return $request->send();
27
+ }
28
+
29
+ protected function validate($parameters) {
30
+ if (!isset($parameters['api_key']) || empty($parameters['api_key'])) {
31
+ return array("Missing API key.");
32
+ } else {
33
+ return true;
34
+ }
35
+ }
36
+
37
+ }
app/code/community/Klevu/Search/Model/Api/Action/Updaterecords.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Action_Updaterecords extends Klevu_Search_Model_Api_Action_Addrecords {
4
+
5
+ const ENDPOINT = "/rest/service/updateRecords";
6
+ const METHOD = "POST";
7
+
8
+ // mandatory_field_name => allowed_empty
9
+ protected $mandatory_fields = array(
10
+ "id" => false
11
+ );
12
+ }
app/code/community/Klevu/Search/Model/Api/Request.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Request extends Varien_Object {
4
+
5
+ protected $endpoint;
6
+
7
+ protected $method;
8
+
9
+ protected $headers;
10
+
11
+ protected $response_model;
12
+
13
+ public function _construct() {
14
+ parent::_construct();
15
+
16
+ $this->method = Zend_Http_Client::GET;
17
+ $this->headers = array();
18
+ $this->response_model = Mage::getModel('klevu_search/api_response');
19
+ }
20
+
21
+ /**
22
+ * Set the target endpoint URL for this API request.
23
+ *
24
+ * @param $url
25
+ *
26
+ * @return $this
27
+ */
28
+ public function setEndpoint($url) {
29
+ $this->endpoint = $url;
30
+
31
+ return $this;
32
+ }
33
+
34
+ /**
35
+ * Return the target endpoint for this API request.
36
+ *
37
+ * @return string
38
+ */
39
+ public function getEndpoint() {
40
+ return $this->endpoint;
41
+ }
42
+
43
+ /**
44
+ * Set the HTTP method to use for this API request.
45
+ *
46
+ * @param $method
47
+ *
48
+ * @return $this
49
+ */
50
+ public function setMethod($method) {
51
+ $this->method = $method;
52
+
53
+ return $this;
54
+ }
55
+
56
+ /**
57
+ * Get the HTTP method configured for this API request.
58
+ *
59
+ * @return mixed
60
+ */
61
+ public function getMethod() {
62
+ return $this->method;
63
+ }
64
+
65
+ /**
66
+ * Set a HTTP header for this API request.
67
+ *
68
+ * @param $name
69
+ * @param $value
70
+ *
71
+ * @return $this
72
+ */
73
+ public function setHeader($name, $value) {
74
+ $this->headers[$name] = $value;
75
+
76
+ return $this;
77
+ }
78
+
79
+ /**
80
+ * Get the array of HTTP headers configured for this API request.
81
+ *
82
+ * @return array
83
+ */
84
+ public function getHeaders() {
85
+ return $this->headers;
86
+ }
87
+
88
+ /**
89
+ * Set the response model to use for this API request.
90
+ *
91
+ * @param Klevu_Search_Model_Api_Response $response_model
92
+ *
93
+ * @return $this
94
+ */
95
+ public function setResponseModel(Klevu_Search_Model_Api_Response $response_model) {
96
+ $this->response_model = $response_model;
97
+
98
+ return $this;
99
+ }
100
+
101
+ /**
102
+ * Return the response model used for this API request.
103
+ *
104
+ * @return Klevu_Search_Model_Api_Response
105
+ */
106
+ public function getResponseModel() {
107
+ return $this->response_model;
108
+ }
109
+
110
+ /**
111
+ * Perform the API request and return the received response.
112
+ *
113
+ * @return Klevu_Search_Model_Api_Response
114
+ */
115
+ public function send() {
116
+ if (!$this->getEndpoint()) {
117
+ // Can't make a request without a URL
118
+ Mage::throwException("Unable to send a Klevu Search API request: No URL specified.");
119
+ }
120
+
121
+ $raw_request = $this->build();
122
+ Mage::helper('klevu_search')->log(Zend_Log::DEBUG, sprintf("API request:\n%s", $this->__toString()));
123
+
124
+ try {
125
+ $raw_response = $raw_request->request();
126
+ } catch (Zend_Http_Client_Exception $e) {
127
+ // Return an empty response
128
+ Mage::helper('klevu_search')->log(Zend_Log::ERR, sprintf("HTTP error: %s", $e->getMessage()));
129
+ return Mage::getModel('klevu_search/api_response_empty');
130
+ }
131
+
132
+ Mage::helper('klevu_search')->log(Zend_Log::DEBUG, sprintf(
133
+ "API response:\n%s\n%s",
134
+ $raw_response->getHeadersAsString(true, "\n"),
135
+ $raw_response->getBody()
136
+ ));
137
+
138
+ $response = $this->getResponseModel();
139
+ $response->setRawResponse($raw_response);
140
+
141
+ return $response;
142
+ }
143
+
144
+ /**
145
+ * Return the string representation of the API request.
146
+ *
147
+ * @return string
148
+ */
149
+ public function __toString() {
150
+ $headers = $this->getHeaders();
151
+ if (count($headers) > 0) {
152
+ array_walk($headers, function (&$value, $key) {
153
+ $value = ($value !== null && $value !== false) ? sprintf("%s: %s", $key, $value) : null;
154
+ });
155
+ }
156
+ return sprintf("%s %s\n%s\n",
157
+ $this->getMethod(),
158
+ $this->getEndpoint(),
159
+ implode("\n", array_filter($headers))
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Build the HTTP request to be sent.
165
+ *
166
+ * @return Zend_Http_Client
167
+ */
168
+ protected function build() {
169
+ $client = new Zend_Http_Client();
170
+
171
+ $client
172
+ ->setUri($this->getEndpoint())
173
+ ->setMethod($this->getMethod())
174
+ ->setHeaders($this->getHeaders());
175
+
176
+ return $client;
177
+ }
178
+ }
app/code/community/Klevu/Search/Model/Api/Request/Get.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Request_Get extends Klevu_Search_Model_Api_Request {
4
+
5
+ public function __toString() {
6
+ $string = parent::__toString();
7
+
8
+ $parameters = $this->getData();
9
+ if (count($parameters) > 0) {
10
+ array_walk($parameters, function(&$value, $key) {
11
+ $value = sprintf("%s: %s", $key, $value);
12
+ });
13
+ }
14
+
15
+ return sprintf("%s\nGET parameters:\n%s\n", $string, implode("\n", $parameters));
16
+ }
17
+
18
+ /**
19
+ * Add GET parameters to the request, force GET method.
20
+ *
21
+ * @return Zend_Http_Client
22
+ */
23
+ protected function build() {
24
+ $client = parent::build();
25
+
26
+ $client
27
+ ->setMethod("GET")
28
+ ->setParameterGet($this->getData());
29
+
30
+ return $client;
31
+ }
32
+ }
app/code/community/Klevu/Search/Model/Api/Request/Post.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Request_Post extends Klevu_Search_Model_Api_Request {
4
+
5
+ public function __toString() {
6
+ $string = parent::__toString();
7
+
8
+ $parameters = $this->getData();
9
+ if (count($parameters) > 0) {
10
+ array_walk($parameters, function(&$value, $key) {
11
+ $value = sprintf("%s: %s", $key, $value);
12
+ });
13
+ }
14
+
15
+ return sprintf("%s\nPOST parameters:\n%s\n", $string, implode("\n", $parameters));
16
+ }
17
+
18
+ /**
19
+ * Add POST parameters to the request, force POST method.
20
+ *
21
+ * @return Zend_Http_Client
22
+ */
23
+ protected function build() {
24
+ $client = parent::build();
25
+
26
+ $client
27
+ ->setMethod(Zend_Http_Client::POST)
28
+ ->setParameterPost($this->getData());
29
+
30
+ return $client;
31
+ }
32
+ }
app/code/community/Klevu/Search/Model/Api/Request/Xml.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Request_Xml extends Klevu_Search_Model_Api_Request {
4
+
5
+ public function __toString() {
6
+ $string = parent::__toString();
7
+
8
+ return sprintf("%s\n%s\n", $string, $this->getDataAsXml());
9
+ }
10
+
11
+ /**
12
+ * Convert the request data into an XML string.
13
+ *
14
+ * @return string
15
+ */
16
+ public function getDataAsXml() {
17
+ $xml = new SimpleXMLElement("<request/>");
18
+ $this->_convertArrayToXml($this->getData(), $xml);
19
+ return $xml->asXML();
20
+ }
21
+
22
+ /**
23
+ * Add data to the request as XML content and set the Content-Type to application/xml.
24
+ *
25
+ * @return Zend_Http_Client
26
+ */
27
+ protected function build() {
28
+ $client = parent::build();
29
+
30
+ $client
31
+ ->setHeaders("Content-Type", "application/xml")
32
+ ->setRawData($this->getDataAsXml());
33
+
34
+ return $client;
35
+ }
36
+
37
+ /**
38
+ * Convert the given array of data into a SimpleXMLElement. Uses array keys as XML element
39
+ * names and values as element values, except for numeric keys where the element name gets
40
+ * set to "item{numeric_key}" unless the value is an array in which case it gets added to
41
+ * the parent XML element directly. Recursively descends into array values to convert them
42
+ * into XML. For example:
43
+ *
44
+ * array(
45
+ * "sessionId" => "Klevu-ses-132123123123_123",
46
+ * "records" => array(
47
+ * 0 => array(
48
+ * "record" => array(
49
+ * "pairs" => array(
50
+ * 0 => array(
51
+ * "pair" => array(
52
+ * "key" => "id",
53
+ * "value" => "1"
54
+ * )
55
+ * ),
56
+ * 1 => array(
57
+ * "pair" => array(
58
+ * "key" => "name",
59
+ * "value" => "Test product"
60
+ * )
61
+ * )
62
+ * )
63
+ * )
64
+ * ),
65
+ * 1 => array(
66
+ * "record" => array(
67
+ * "pairs" => array(
68
+ * 0 => array(
69
+ * "pair" => array(
70
+ * "key" => "id",
71
+ * "value" => "1"
72
+ * )
73
+ * )
74
+ * )
75
+ * )
76
+ * )
77
+ * )
78
+ * );
79
+ *
80
+ * will get converted to:
81
+ *
82
+ * <?xml version="1.0"?>
83
+ * <request>
84
+ * <sessionId>Klevu-ses-132123123123_123</sessionId>
85
+ * <records>
86
+ * <record>
87
+ * <pairs>
88
+ * <pair>
89
+ * <key>id</key>
90
+ * <value>1</value>
91
+ * </pair>
92
+ * <pair>
93
+ * <key>name</key>
94
+ * <value>Test product</value>
95
+ * </pair>
96
+ * </pairs>
97
+ * </record>
98
+ * <record>
99
+ * <pairs>
100
+ * <pair>
101
+ * <key>id</key>
102
+ * <value>1</value>
103
+ * </pair>
104
+ * </pairs>
105
+ * </record>
106
+ * </records>
107
+ * </request>
108
+ *
109
+ * @param array $array The data to convert.
110
+ * @param SimpleXmlElement $parent XML element used as a parent for the data.
111
+ */
112
+ protected function _convertArrayToXml(array $array, SimpleXmlElement &$parent) {
113
+ foreach ($array as $key => $value) {
114
+ if (is_array($value)) {
115
+ if (is_numeric($key)) {
116
+ $this->_convertArrayToXml($value, $parent);
117
+ } else {
118
+ $child = $parent->addChild($key);
119
+ $this->_convertArrayToXml($value, $child);
120
+ }
121
+ } else {
122
+ $key = (is_numeric($key)) ? "item" . $key : $key;
123
+ $parent->addChild($key, htmlspecialchars($value));
124
+ }
125
+ }
126
+ }
127
+ }
app/code/community/Klevu/Search/Model/Api/Response.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Model_Api_Response
5
+ *
6
+ * @method setMessage($message)
7
+ * @method getMessage()
8
+ */
9
+ class Klevu_Search_Model_Api_Response extends Varien_Object {
10
+
11
+ protected $raw_response;
12
+
13
+ protected $successful;
14
+ protected $xml;
15
+
16
+ public function _construct() {
17
+ parent::_construct();
18
+
19
+ $this->successful = false;
20
+ }
21
+
22
+ /**
23
+ * Set the raw response object representing this API response.
24
+ *
25
+ * @param Zend_Http_Response $response
26
+ *
27
+ * @return $this
28
+ */
29
+ public function setRawResponse(Zend_Http_Response $response) {
30
+ $this->raw_response = $response;
31
+
32
+ $this->parseRawResponse($response);
33
+
34
+ return $this;
35
+ }
36
+
37
+ /**
38
+ * Check if the API response indicates success.
39
+ *
40
+ * @return boolean
41
+ */
42
+ public function isSuccessful() {
43
+ return $this->successful;
44
+ }
45
+
46
+ /**
47
+ * Return the response XML content.
48
+ *
49
+ * @return SimpleXMLElement
50
+ */
51
+ public function getXml() {
52
+ return $this->xml;
53
+ }
54
+
55
+ /**
56
+ * Extract the API response data from the given HTTP response object.
57
+ *
58
+ * @param Zend_Http_Response $response
59
+ *
60
+ * @return $this
61
+ */
62
+ protected function parseRawResponse(Zend_Http_Response $response) {
63
+ if ($response->isSuccessful()) {
64
+ $content = $response->getBody();
65
+
66
+ if (strlen($content) > 0) {
67
+ try {
68
+ $xml = simplexml_load_string($response->getBody());
69
+ } catch (Exception $e) {
70
+ // Failed to parse XML
71
+ $this->successful = false;
72
+ $this->setMessage("Failed to parse a response from Klevu.");
73
+ Mage::helper('klevu_search')->log(Zend_Log::ERR, sprintf("Failed to parse XML response: %s", $e->getMessage()));
74
+ return $this;
75
+ }
76
+
77
+ $this->xml = $xml;
78
+ $this->successful = true;
79
+ } else {
80
+ // Response contains no content
81
+ $this->successful = false;
82
+ $this->setMessage('Failed to parse a response from Klevu.');
83
+ Mage::helper('klevu_search')->log(Zend_Log::ERR, "API response content is empty.");
84
+ }
85
+ } else {
86
+ // Unsuccessful HTTP response
87
+ $this->successful = false;
88
+ switch ($response->getStatus()) {
89
+ case 403:
90
+ $message = "Incorrect API keys.";
91
+ break;
92
+ case 500:
93
+ $message = "API server error.";
94
+ break;
95
+ case 503:
96
+ $message = "API server unavailable.";
97
+ break;
98
+ default:
99
+ $message = "Unexpected error.";
100
+ }
101
+ $this->setMessage(sprintf("Failed to connect to Klevu: %s", $message));
102
+ Mage::helper('klevu_search')->log(Zend_Log::ERR, sprintf("Unsuccessful HTTP response: %s %s", $response->getStatus(), $response->responseCodeAsText($response->getStatus())));
103
+ }
104
+
105
+ return $this;
106
+ }
107
+ }
app/code/community/Klevu/Search/Model/Api/Response/Data.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Response_Data extends Klevu_Search_Model_Api_Response {
4
+
5
+ protected function parseRawResponse(Zend_Http_Response $response) {
6
+ parent::parseRawResponse($response);
7
+
8
+ if ($this->isSuccessful()) {
9
+ $data = $this->xmlToArray($this->getXml());
10
+
11
+ $this->successful = false;
12
+ if (isset($data['response'])) {
13
+ if (strtolower($data['response']) == 'success') {
14
+ $this->successful = true;
15
+ }
16
+ unset($data['response']);
17
+ }
18
+
19
+ foreach ($data as $key => $value) {
20
+ $this->setData($this->_underscore($key), $value);
21
+ }
22
+ }
23
+
24
+ return $this;
25
+ }
26
+
27
+ /**
28
+ * Convert XML to an array.
29
+ *
30
+ * @param SimpleXMLElement $xml
31
+ *
32
+ * @return array
33
+ */
34
+ protected function xmlToArray(SimpleXMLElement $xml) {
35
+ return json_decode(json_encode($xml), true);
36
+ }
37
+ }
app/code/community/Klevu/Search/Model/Api/Response/Empty.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Response_Empty extends Klevu_Search_Model_Api_Response {
4
+
5
+ public function _construct() {
6
+ $this->successful = false;
7
+ $this->addData(array(
8
+ 'message' => "No HTTP response received. if you are using PHP version 5.4, please make sure to enable the php_openssl.dll module in your php.ini file."
9
+ ));
10
+ }
11
+
12
+ /**
13
+ * Override the parse response method, this API response is static.
14
+ *
15
+ * @param Zend_Http_Response $response
16
+ *
17
+ * @return $this
18
+ */
19
+ protected function parseRawResponse(Zend_Http_Response $response) {
20
+ // Do nothing
21
+ return $this;
22
+ }
23
+
24
+ }
app/code/community/Klevu/Search/Model/Api/Response/Invalid.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Model_Api_Response_Invalid
5
+ *
6
+ * @method setErrors($errors)
7
+ */
8
+ class Klevu_Search_Model_Api_Response_Invalid extends Klevu_Search_Model_Api_Response {
9
+
10
+ public function _construct() {
11
+ $this->successful = false;
12
+ }
13
+
14
+ /**
15
+ * Return the array of errors.
16
+ *
17
+ * @return array
18
+ */
19
+ public function getErrors() {
20
+ $errors = $this->getData('errors');
21
+
22
+ if (!$errors) {
23
+ $errors = array();
24
+ }
25
+
26
+ if (!is_array($errors)) {
27
+ $errors = array($errors);
28
+ }
29
+
30
+ return $errors;
31
+ }
32
+
33
+ /**
34
+ * Return the response message.
35
+ *
36
+ * @return string
37
+ */
38
+ public function getMessage() {
39
+ $message = "Invalid request";
40
+
41
+ $errors = $this->getErrors();
42
+ if (count($errors) > 0) {
43
+ $message = sprintf("%s: %s", $message, implode(", ", $errors));
44
+ }
45
+
46
+ return $message;
47
+ }
48
+
49
+ /**
50
+ * Override the parse response method, this API response is doesn't use HTTP.
51
+ *
52
+ * @param Zend_Http_Response $response
53
+ *
54
+ * @return $this
55
+ */
56
+ protected function parseRawResponse(Zend_Http_Response $response) {
57
+ // Do nothing
58
+ return $this;
59
+ }
60
+ }
app/code/community/Klevu/Search/Model/Api/Response/Message.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Response_Message extends Klevu_Search_Model_Api_Response {
4
+
5
+ protected function parseRawResponse(Zend_Http_Response $response) {
6
+ parent::parseRawResponse($response);
7
+
8
+ if ($this->isSuccessful()) {
9
+ $xml = $this->getXml();
10
+
11
+ if (isset($xml->status) && strtolower($xml->status) === "success") {
12
+ $this->successful = true;
13
+ } else {
14
+ $this->successful = false;
15
+ }
16
+
17
+ if (isset($xml->msg)) {
18
+ $this->setMessage((string) $xml->msg);
19
+ }
20
+
21
+ if (isset($xml->sessionId)) {
22
+ $this->setSessionId((string) $xml->sessionId);
23
+ }
24
+ }
25
+
26
+ return $this;
27
+ }
28
+ }
app/code/community/Klevu/Search/Model/Api/Response/Search.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Response_Search extends Klevu_Search_Model_Api_Response {
4
+
5
+ protected function parseRawResponse(Zend_Http_Response $response) {
6
+ parent::parseRawResponse($response);
7
+
8
+ if ($this->isSuccessful()) {
9
+ $data = $this->xmlToArray($this->getXml());
10
+
11
+ $this->successful = false;
12
+ if (isset($data['response'])) {
13
+ if (strtolower($data['response']) == 'success') {
14
+ $this->successful = true;
15
+ }
16
+ unset($data['response']);
17
+ }
18
+
19
+ foreach ($data as $key => $value) {
20
+ switch($key) {
21
+ case 'result':
22
+ $prepared_value = $value;
23
+ if (isset($value['id'])) {
24
+ $prepared_value = array($value);
25
+ }
26
+
27
+ break;
28
+ case 'filters':
29
+ if (isset($value['filter'])) {
30
+ $prepared_value = $this->_prepareFilters($value['filter']);
31
+ } else {
32
+ $prepared_value ='';
33
+ }
34
+ break;
35
+ default:
36
+ $prepared_value = $value;
37
+ break;
38
+ }
39
+
40
+ $this->setData($this->_underscore($key), $prepared_value);
41
+ }
42
+ }
43
+
44
+ return $this;
45
+ }
46
+
47
+ protected function _prepareFilters($filters) {
48
+ $prepared_filters = array();
49
+ $i = 0;
50
+
51
+ foreach ($filters as $filter) {
52
+ $prepared_filters[$i] = $filter['@attributes'];
53
+ $options = isset($filter['option']) ? $filter['option'] : $filter[0];
54
+ foreach ($options as $option) {
55
+ $prepared_filters[$i]['options'][] = isset($option['@attributes']) ? $option['@attributes'] : $option;
56
+ }
57
+ $i++;
58
+ }
59
+
60
+ return $prepared_filters;
61
+ }
62
+
63
+ /**
64
+ * Convert XML to an array.
65
+ *
66
+ * @param SimpleXMLElement $xml
67
+ *
68
+ * @return array
69
+ */
70
+ protected function xmlToArray(SimpleXMLElement $xml) {
71
+ return json_decode(json_encode($xml), true);
72
+ }
73
+ }
app/code/community/Klevu/Search/Model/Api/Response/Timezone.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Api_Response_Timezone extends Klevu_Search_Model_Api_Response_Data {
4
+
5
+ protected function parseRawResponse(Zend_Http_Response $response) {
6
+ parent::parseRawResponse($response);
7
+
8
+ // Timezone responses don't have a status parameters, just data
9
+ // So the presence of the data is the status
10
+ if ($this->hasData('timezone')) {
11
+ $this->successful = true;
12
+ } else {
13
+ $this->successful = false;
14
+ }
15
+ }
16
+ }
app/code/community/Klevu/Search/Model/Catalog/Model/Config.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Klevu_Search_Model_Catalog_Model_Config extends Mage_Catalog_Model_Config
3
+ {
4
+ /**
5
+ * Retrieve Attributes Used for Sort by as array
6
+ * key = code, value = name
7
+ *
8
+ * @return array
9
+ */
10
+ public function getAttributeUsedForSortByArray()
11
+ {
12
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
13
+ $options = array(
14
+ 'position' => Mage::helper('catalog')->__('Position')
15
+ );
16
+ foreach ($this->getAttributesUsedForSortBy() as $attribute) {
17
+ /* @var $attribute Mage_Eav_Model_Entity_Attribute_Abstract */
18
+ $options[$attribute->getAttributeCode()] = $attribute->getStoreLabel();
19
+ }
20
+ }else {
21
+ $options = array(
22
+ 'position' => Mage::helper('catalog')->__('Position'),
23
+ 'name' => Mage::helper('catalog')->__('Name'),
24
+ 'price' => Mage::helper('catalog')->__('Price'),
25
+ );
26
+
27
+ }
28
+
29
+ return $options;
30
+ }
31
+ }
app/code/community/Klevu/Search/Model/CatalogSearch/Layer/Filter/Attribute.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Klevu_Search_Model_CatalogSearch_Layer_Filter_Attribute extends Mage_CatalogSearch_Model_Layer_Filter_Attribute {
5
+
6
+ /**
7
+ * Prepare and fetch an attributes options.
8
+ *
9
+ * @return array|null
10
+ */
11
+ protected function _getItemsData()
12
+ {
13
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
14
+ return parent::_getItemsData();
15
+ }
16
+
17
+ $attribute = $this->getAttributeModel();
18
+ $this->_requestVar = $attribute->getAttributeCode();
19
+
20
+ $key = $this->getLayer()->getStateKey().'_'.$this->_requestVar;
21
+ $data = $this->getLayer()->getAggregator()->getCacheData($key);
22
+
23
+
24
+ if ($data === null) {
25
+ $klevu_filters = $this->_getKlevuAttributeFilters();
26
+ if (!isset($klevu_filters[$this->_requestVar])) {
27
+ return array(); // No results found for filter in Klevu response. Return empty array.
28
+ }
29
+ if ($this->getLayer()->getProductCollection()->count() == 0) {
30
+ return array(); // No visible results found in search
31
+ }
32
+
33
+ $klevu_attribute = $klevu_filters[$this->_requestVar];
34
+ $options = $attribute->getFrontend()->getSelectOptions();
35
+ $data = array();
36
+ foreach ($options as $option) {
37
+ $klevu_option = $this->_findKlevuOption($option, $klevu_attribute);
38
+ if (!$klevu_option) {
39
+ continue; // Skip record since klevu option was not found.
40
+ }
41
+ if (is_array($option['value'])) {
42
+ continue;
43
+ }
44
+ if (Mage::helper('core/string')->strlen($option['value'])) {
45
+ // Check filter type
46
+ if ($this->_getIsFilterableAttribute($attribute) == self::OPTIONS_ONLY_WITH_RESULTS) {
47
+ if (!empty($klevu_option['count'])) {
48
+ $data[] = array(
49
+ 'label' => $option['label'],
50
+ 'value' => $option['value'],
51
+ 'count' => $klevu_option['count'],
52
+ );
53
+ }
54
+ }
55
+ else {
56
+ $data[] = array(
57
+ 'label' => $option['label'],
58
+ 'value' => $option['value'],
59
+ 'count' => $klevu_option['count'],
60
+ );
61
+ }
62
+ }
63
+ }
64
+
65
+ $tags = array(
66
+ Mage_Eav_Model_Entity_Attribute::CACHE_TAG.':'.$attribute->getId()
67
+ );
68
+
69
+ $tags = $this->getLayer()->getStateTags($tags);
70
+ $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
71
+ }
72
+ return $data;
73
+ }
74
+
75
+ /**
76
+ * Attempt to find the Klevu option from the array of magento options.
77
+ * Returns false if there were no matches, otherwise returns Klevu option.
78
+ *
79
+ * @param $option
80
+ * @param $klevu_attribute
81
+ * @return array|bool
82
+ */
83
+ protected function _findKlevuOption($option, $klevu_attribute) {
84
+
85
+ foreach ($klevu_attribute['options'] as $klevu_option) {
86
+ if(strtolower($option['label']) == strtolower($klevu_option['label'])) {
87
+ return $klevu_option;
88
+ }
89
+ }
90
+
91
+ return false;
92
+ }
93
+ /**
94
+ * Returns array of attribute filters from Klevu [ 'label' => 'T-Shirts', 'count' => 1, 'selected' => false ]
95
+ * @return array
96
+ */
97
+ protected function _getKlevuAttributeFilters() {
98
+ /** @var Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection $collection */
99
+ $collection = $this->getLayer()->getProductCollection();
100
+ return $collection->getKlevuFilters();
101
+ }
102
+
103
+
104
+ }
app/code/community/Klevu/Search/Model/CatalogSearch/Layer/Filter/Category.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_CatalogSearch_Layer_Filter_Category extends Mage_Catalog_Model_Layer_Filter_Category {
4
+
5
+ /**
6
+ * Get data array for building category filter items
7
+ *
8
+ * @return array
9
+ */
10
+ protected function _getItemsData()
11
+ {
12
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
13
+ return parent::_getItemsData();
14
+ }
15
+
16
+ $key = $this->getLayer()->getStateKey().'_SUBCATEGORIES';
17
+ $data = $this->getLayer()->getAggregator()->getCacheData($key);
18
+
19
+
20
+ if ($data === null) {
21
+ // Fetch filters from Klevu
22
+ $filters = $this->_getKlevuCategoryFilters();
23
+
24
+ if ($this->getLayer()->getProductCollection()->count() == 0) {
25
+ return array(); // No visible results found in search
26
+ }
27
+
28
+ // Prepare all the available category names
29
+ $category_names = array();
30
+ foreach($filters as $filter) {
31
+ $category_names[] = $filter['label'];
32
+ }
33
+ $category = $this->getCategory();
34
+ // Get the all categories returned from klevu, and apply the current parent category.
35
+ $categories = Mage::getModel('catalog/category')->getCollection()
36
+ ->addAttributeToSelect('is_active')
37
+ ->addFieldToFilter('name', array('in' => $category_names));
38
+
39
+ $this->getLayer()->getProductCollection()
40
+ ->addCountToCategories($categories);
41
+
42
+ $data = array();
43
+ $k_cat = Mage::app()->getRequest()->getParam('cat');
44
+ if(!isset($k_cat)) {
45
+ foreach ($categories as $category) {
46
+ // Ensure the category exists within the Klevu filters.
47
+ if (!$klevu_category = $this->_findKlevuCategory($category, $filters)) {
48
+ continue;
49
+ }
50
+
51
+ if ($category->getIsActive() && $category->getProductCount()) {
52
+ $data[] = array(
53
+ 'label' => Mage::helper('core')->escapeHtml($category->getName()),
54
+ 'value' => $category->getId(),
55
+ 'count' => $klevu_category['count'],
56
+ );
57
+ }
58
+ }
59
+ }
60
+ $tags = $this->getLayer()->getStateTags();
61
+ $this->getLayer()->getAggregator()->saveCacheData($data, $key, $tags);
62
+ }
63
+
64
+ return $data;
65
+ }
66
+
67
+ protected function _findKlevuCategory($category, $filters) {
68
+ foreach($filters as $filter) {
69
+ if(strtolower($filter['label']) == strtolower($category->getName())) {
70
+ return $filter;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * This method in Mage_Catalog_Model_Layer_Filter_Category would ensure when a category filter is remove, the parent
78
+ * category is applied as a filter. This isn't expected functionality for Klevu, and has been reset to a null value.
79
+ * @return null
80
+ */
81
+ public function getResetValue() {
82
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
83
+ return parent::getResetValue();
84
+ }
85
+
86
+ return null;
87
+ }
88
+
89
+ /**
90
+ * Returns array of category filters from Klevu [ 'label' => 'T-Shirts', 'count' => 1, 'selected' => false ]
91
+ * @return array
92
+ */
93
+ protected function _getKlevuCategoryFilters() {
94
+ /** @var Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection $collection */
95
+ $collection = $this->getLayer()->getProductCollection();
96
+ $klevu_filters = $collection->getKlevuFilters();
97
+ if (!empty($klevu_filters['category'])) {
98
+ return $klevu_filters['category']['options'];
99
+ }
100
+
101
+ return array();
102
+ }
103
+ }
app/code/community/Klevu/Search/Model/CatalogSearch/Layer/Filter/Price.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_CatalogSearch_Layer_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Price {
4
+
5
+ public function apply(Zend_Controller_Request_Abstract $request, $filterBlock) {
6
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
7
+ return parent::apply($request, $filterBlock);
8
+ }
9
+
10
+ // In Magento 1.7 the price filter parameter was changed from the "<index>,<range>"
11
+ // format to the "<from>-<to>" format. Klevu uses the latter, so in <1.7 we need
12
+ // to parse the parameter manually
13
+ if (version_compare(Mage::getVersion(), "1.7", ">=")) {
14
+ return parent::apply($request, $filterBlock);
15
+ } else {
16
+ $filter = $request->getParam($this->getRequestVar());
17
+ if (!$filter) {
18
+ return $this;
19
+ }
20
+
21
+ $filter = explode("-", $filter);
22
+ if (count($filter) != 2) {
23
+ return $this;
24
+ }
25
+
26
+ list($from, $to) = $filter;
27
+
28
+ $this->setInterval(array($from, $to));
29
+
30
+ $this->getLayer()->getState()->addFilter(
31
+ $this->_createItem($this->_renderRangeLabel(empty($from) ? 0 : $from, $to), $filter)
32
+ );
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Get data for build price filter items
38
+ *
39
+ * @return array
40
+ */
41
+ protected function _getItemsData()
42
+ {
43
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
44
+ return parent::_getItemsData();
45
+ }
46
+
47
+ $klevu_price_filters = $this->_getKlevuPriceFilters();
48
+ $data = array();
49
+ $k_price = Mage::app()->getRequest()->getParam('price');
50
+ if(!isset($k_price)) {
51
+ if (!empty($klevu_price_filters) && $this->getLayer()->getProductCollection()->count() > 0) {
52
+ foreach ($klevu_price_filters as $filter) {
53
+ $prices = explode(" - ", $filter['label']);
54
+ $fromPrice = $prices[0];
55
+ $toPrice = $prices[1];
56
+
57
+ $data[] = array(
58
+ 'label' => $this->_renderRangeLabel($fromPrice, $toPrice),
59
+ 'value' => $fromPrice . '-' . $toPrice,
60
+ 'count' => $filter['count'],
61
+ );
62
+ }
63
+ }
64
+ }
65
+
66
+ return $data;
67
+ }
68
+
69
+ protected function _renderRangeLabel($fromPrice, $toPrice) {
70
+ //if (method_exists(get_parent_class($this), "_renderRangeLabel")) {
71
+ // return parent::_renderRangeLabel($fromPrice, $toPrice);
72
+ // } else {
73
+ $range = $toPrice - $fromPrice;
74
+ $value = $toPrice / $range;
75
+ return parent::_renderItemLabel($range, $value);
76
+ //}
77
+ }
78
+
79
+ /**
80
+ * Returns array of price ranges from Klevu [ 'label' => '10 - 25', 'count' => 1, 'selected' => false ]
81
+ * @return array
82
+ */
83
+ protected function _getKlevuPriceFilters() {
84
+ /** @var Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection $collection */
85
+ $collection = $this->getLayer()->getProductCollection();
86
+ $klevu_filters = $collection->getKlevuFilters();
87
+ if (!empty($klevu_filters['Price Range'])) {
88
+ return $klevu_filters['Price Range']['options'];
89
+ }
90
+
91
+ return array();
92
+ }
93
+ }
app/code/community/Klevu/Search/Model/CatalogSearch/Resource/Fulltext/Collection.php ADDED
@@ -0,0 +1,463 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection extends Mage_CatalogSearch_Model_Mysql4_Fulltext_Collection {
4
+
5
+ /**
6
+ * Klevu Search API Parameters
7
+ * @var array
8
+ */
9
+ protected $_klevu_parameters;
10
+
11
+ /**
12
+ * Klevu Search API Product IDs
13
+ * @var array
14
+ */
15
+ protected $_klevu_product_ids = array();
16
+ protected $_klevu_parent_child_ids = array();
17
+
18
+ /**
19
+ * Klevu Search API Response
20
+ * @var Klevu_Search_Model_Api_Response
21
+ */
22
+ protected $_klevu_response;
23
+ /**
24
+ * Search query
25
+ * @var string
26
+ */
27
+ protected $_query;
28
+
29
+ /**
30
+ * Total number of results found
31
+ * @var int
32
+ */
33
+ protected $_klevu_size;
34
+ /**
35
+ * The XML Response from Klevu
36
+ * @var SimpleXMLElement
37
+ */
38
+ protected $_klevu_response_xml;
39
+
40
+ /**
41
+ * Prepare the search query for Klevu Search API Call
42
+ *
43
+ * @param string $query
44
+ * @return $this|Mage_CatalogSearch_Model_Resource_Fulltext_Collection
45
+ */
46
+ public function addSearchFilter($query) {
47
+ if (!$this->isExtensionConfigured()) {
48
+ return parent::addSearchFilter($query);
49
+ }
50
+
51
+ $this->_query = $query;
52
+ return $this;
53
+ }
54
+
55
+ /**
56
+ * Stub method to prevent sort order being changed.
57
+ * @param string $attribute
58
+ * @param string $dir
59
+ * @return $this|Mage_CatalogSearch_Model_Resource_Fulltext_Collection
60
+ */
61
+ public function setOrder($attribute, $dir = 'desc')
62
+ {
63
+ if (!$this->isExtensionConfigured()) {
64
+ return parent::setOrder($attribute, $dir);
65
+ }
66
+
67
+ return $this;
68
+ }
69
+
70
+
71
+ /**
72
+ * Return the Klevu api search filters
73
+ * @return array
74
+ */
75
+ public function getSearchFilters() {
76
+ if (empty($this->_klevu_parameters)) {
77
+
78
+ $noOfResults = $this->getPageSize();
79
+
80
+ // If getPageSize() returns false, we need to get the page size from the toolbar block.
81
+ // Rather than re-writing our own version.
82
+ if(!$noOfResults) {
83
+ /** @var Mage_Catalog_Block_Product_List $productListBlock */
84
+ $productListBlock = Mage::getBlockSingleton('catalog/product_list');
85
+ $toolbarBlock = $productListBlock->getToolbarBlock();
86
+ $noOfResults = (int) $toolbarBlock->getLimit();
87
+ }
88
+
89
+ $page = Mage::app()->getRequest()->getParam('p');
90
+ $this->_klevu_parameters = array(
91
+ 'ticket' => Mage::helper('klevu_search/config')->getJsApiKey(),
92
+ 'noOfResults' => $noOfResults,
93
+ 'term' => $this->_query,
94
+ 'paginationStartsFrom' => $this->_getStartFrom($page),
95
+ 'klevuSort' => $this->_getSortOrder(),
96
+ 'enableFilters' => 'true',
97
+ 'filterResults' => $this->_getPreparedFilters(),
98
+
99
+ );
100
+ $this->log(Zend_Log::DEBUG, sprintf("Starting search for term: %s", $this->_getQuery()->getQueryText()));
101
+ }
102
+
103
+ return $this->_klevu_parameters;
104
+ }
105
+
106
+ /**
107
+ * Send the API Request and return the API Response.
108
+ * @return Klevu_Search_Model_Api_Response
109
+ */
110
+ public function getKlevuResponse() {
111
+ if (!$this->_klevu_response) {
112
+ $this->_klevu_response = Mage::getModel('klevu_search/api_action_idsearch')->execute($this->getSearchFilters());
113
+ }
114
+ return $this->_klevu_response;
115
+ }
116
+
117
+ public function getKlevuFilters() {
118
+ $attributes = array();
119
+ $filters = $this->getKlevuResponse()->getData('filters');
120
+
121
+ // If there are no filters, return empty array.
122
+ if (empty($filters)) {
123
+ return array();
124
+ }
125
+
126
+ foreach($filters as $filter)
127
+ {
128
+ $key = (string) $filter['key'];
129
+ $attributes[$key] = array('label' => (string) $filter['label']);
130
+ $attributes[$key]['options'] = array();
131
+ if($filter['options']) {
132
+ foreach($filter['options'] as $option) {
133
+ $attributes[$key]['options'][] = array(
134
+ 'label' => trim((string) $option['name']),
135
+ 'count' => trim((string) $option['count']),
136
+ 'selected' => trim((string) $option['selected'])
137
+ );
138
+ }
139
+ }
140
+ }
141
+
142
+ return $attributes;
143
+ }
144
+
145
+ protected function _beforeLoad() {
146
+ if ($this->isExtensionConfigured()) {
147
+ $this->setVisibility(array(
148
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE,
149
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG,
150
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH,
151
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH));
152
+ $this->addAttributeToSelect('visibility');
153
+ }
154
+
155
+ return parent::_beforeLoad();
156
+ }
157
+
158
+ protected function _renderFilters()
159
+ {
160
+ if (!$this->isExtensionConfigured()) {
161
+ return parent::_renderFilters();
162
+ }
163
+
164
+ // Do nothing. The results returned by the API are already filtered
165
+ // and the collection is filtered to only include those results
166
+ // in _loadEntities()
167
+
168
+ return $this;
169
+ }
170
+
171
+ protected function _renderOrders()
172
+ {
173
+ if (!$this->isExtensionConfigured()) {
174
+ return parent::_renderOrders();
175
+ }
176
+
177
+ // Do nothing. The results returned by the API are already in order
178
+ // which is enforced in _loadEntities()
179
+
180
+ return $this;
181
+ }
182
+
183
+ protected function _afterLoad()
184
+ {
185
+ parent::_afterLoad();
186
+
187
+ if (!$this->isExtensionConfigured()) {
188
+ return $this;
189
+ }
190
+
191
+ foreach ($this->_klevu_parent_child_ids as $item) {
192
+
193
+
194
+ if ($item['parent_id'] > 0) {
195
+ /** @var Mage_Catalog_Model_Product $parent */
196
+ $parent = $this->_items[$item['parent_id']];
197
+ /** @var Mage_Catalog_Model_Product $child */
198
+ $child = $this->_items[$item['product_id']];
199
+
200
+ // Parent isn't visible. Unset both child and parent products and skip.
201
+ if (!$parent || !$this->_isProductVisible($parent)) {
202
+ unset($this->_items[$item['parent_id']], $this->_items[$item['product_id']]);
203
+ continue;
204
+ }
205
+
206
+ if ($child) {
207
+ // Set children images on parent product
208
+ if (($image = $child->getData('image')) != 'no_selection') {
209
+ $parent->setData('image', $image);
210
+ }
211
+
212
+ if (($small_image = $child->getData('small_image')) != 'no_selection') {
213
+ $parent->setData('small_image', $small_image);
214
+ }
215
+
216
+ if (($thumbnail = $child->getData('thumbnail')) != 'no_selection') {
217
+ $parent->setData('thumbnail', $thumbnail);
218
+ }
219
+ }
220
+
221
+ unset($this->_items[$item['product_id']]);
222
+ }
223
+
224
+ // If the child exists, but isn't visible unset the item from our collection.
225
+ if (isset($this->_items[$item['product_id']]) && !$this->_isProductVisible($this->_items[$item['product_id']])) {
226
+ unset($this->_items[$item['product_id']]);
227
+ }
228
+ }
229
+
230
+ return $this;
231
+ }
232
+
233
+ /**
234
+ * @param Mage_Catalog_Model_Product $product
235
+ * @return bool
236
+ */
237
+ protected function _isProductVisible($product) {
238
+ return in_array(
239
+ $product->getData('visibility'),
240
+ array(
241
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH,
242
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH
243
+ )
244
+ );
245
+ }
246
+
247
+ /**
248
+ * This method executes the the Klevu API request if it has not already been called, and takes the result
249
+ * with the result we get all the item IDs, pass into our helper which returns the child and parent id's.
250
+ * We then add all these values to our class variable $_klevu_product_ids.
251
+ *
252
+ * @return array
253
+ */
254
+ protected function _getProductIds() {
255
+ if (empty($this->_klevu_product_ids)) {
256
+
257
+ // If no results, return an empty array
258
+ if (!$this->getKlevuResponse()->hasData('result')) {
259
+ return array();
260
+ }
261
+
262
+ foreach ($this->getKlevuResponse()->getData('result') as $result) {
263
+ $item_id = Mage::helper('klevu_search')->getMagentoProductId((string) $result['id']);
264
+ $this->_klevu_parent_child_ids[] = $item_id;
265
+ if ($item_id['parent_id'] != 0) {
266
+ $this->_klevu_product_ids[$item_id['parent_id']] = $item_id['parent_id'];
267
+ }
268
+
269
+ $this->_klevu_product_ids[$item_id['product_id']] = $item_id['product_id'];
270
+ }
271
+ $this->_klevu_product_ids = array_unique($this->_klevu_product_ids);
272
+ $this->log(Zend_Log::DEBUG, sprintf("Products count returned: %s", count($this->_klevu_product_ids)));
273
+ }
274
+
275
+ return $this->_klevu_product_ids;
276
+ }
277
+
278
+ /**
279
+ * Return the current sort order, as used by Klevu.
280
+ *
281
+ * @return string
282
+ */
283
+ protected function _getSortOrder() {
284
+ $order = $this->_getToolbar()->getCurrentOrder();
285
+ $direction = $this->_getToolbar()->getCurrentDirection();
286
+
287
+ switch ($order) {
288
+ case "price":
289
+ return ($direction == "desc") ? "htl" : "lth";
290
+ case "name":
291
+ return ($direction == "desc") ? "namedesc" : "nameasc";
292
+ default:
293
+ // Default to sorting by relevance
294
+ return "rel";
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Returns where Klevu should start pagination from, e.g. 0, 30 or 60 records (page 1, 2 and 3)
300
+ *
301
+ * @param null|int $current_page
302
+ * @return int
303
+ */
304
+ protected function _getStartFrom($current_page = null) {
305
+ if ($current_page == 1 || is_null($current_page)) {
306
+ return 0;
307
+ }
308
+ return ($current_page - 1) * ($this->getPageSize());
309
+ }
310
+
311
+ /**
312
+ * Overwriting the getSize method to use Klevu's result of total records.
313
+ *
314
+ * @return int
315
+ */
316
+ public function getSize() {
317
+ if (!$this->isExtensionConfigured()) {
318
+ return parent::getSize();
319
+ }
320
+
321
+ $response = $this->getKlevuResponse()->getData('meta');
322
+ return (int) $response['totalResultsFound'];
323
+ }
324
+
325
+ /**
326
+ * Get the current page size. If _pageSize is false, get the limit from the toolbar block.
327
+ *
328
+ * @return int|string
329
+ */
330
+ public function getPageSize() {
331
+ if (!$this->isExtensionConfigured()) {
332
+ return parent::getPageSize();
333
+ }
334
+
335
+ if(!$this->_pageSize) {
336
+ return $this->_getToolbar()->getLimit();
337
+ }
338
+ return $this->_pageSize;
339
+ }
340
+
341
+ /**
342
+ * Fetch the toolbar block
343
+ *
344
+ * @return Mage_Catalog_Block_Product_List_Toolbar
345
+ */
346
+ protected function _getToolbar() {
347
+ /** @var Mage_Catalog_Block_Product_List $productListBlock */
348
+ $productListBlock = Mage::getBlockSingleton('catalog/product_list');
349
+ return $productListBlock->getToolbarBlock();
350
+ }
351
+
352
+ /**
353
+ * Load entities records into items
354
+ *
355
+ * Removed page limiting SQL from this method to prevent issues with paging and Klevu.
356
+ *
357
+ * @throws Exception
358
+ * @return Mage_Eav_Model_Entity_Collection_Abstract
359
+ */
360
+ public function _loadEntities($printQuery = false, $logQuery = false)
361
+ {
362
+ if (!$this->isExtensionConfigured()) {
363
+ return parent::_loadEntities($printQuery, $logQuery);
364
+ }
365
+
366
+ // API results are already filtered, so include only the products
367
+ // returned by the API in the collection
368
+ $this->getSelect()->reset(Zend_Db_Select::WHERE);
369
+ $this->addFieldToFilter('entity_id', array('in' => $this->_getProductIds()));
370
+
371
+ // API results are ordered using the selected sort order, so enforce
372
+ // the collection order to match the API results
373
+ $this->getSelect()->reset(Zend_Db_Select::ORDER);
374
+ $this->getSelect()->reset(Zend_Db_Select::LIMIT_OFFSET);
375
+ if (count($this->_getProductIds())) {
376
+ // Use "FIELD (column, 1[,2,3,4]) ASC" for ordering, where "1[,2,3,4]" is the list of IDs in the order required
377
+ $this->getSelect()->order(sprintf('FIELD(`e`.`entity_id`, %s) ASC', implode(',', $this->_getProductIds())));
378
+ }
379
+
380
+ $this->printLogQuery($printQuery, $logQuery);
381
+
382
+ try {
383
+ /**
384
+ * Prepare select query
385
+ * @var string $query
386
+ */
387
+ if (is_callable(array($this, "_prepareSelect"))) {
388
+ $query = $this->_prepareSelect($this->getSelect());
389
+ } else {
390
+ $query = $this->getSelect();
391
+ }
392
+ $rows = $this->_fetchAll($query);
393
+ } catch (Exception $e) {
394
+ Mage::printException($e, $query);
395
+ $this->printLogQuery(true, true, $query);
396
+ throw $e;
397
+ }
398
+
399
+ foreach ($rows as $v) {
400
+ $object = $this->getNewEmptyItem()
401
+ ->setData($v);
402
+ $this->addItem($object);
403
+ if (isset($this->_itemsById[$object->getId()])) {
404
+ $this->_itemsById[$object->getId()][] = $object;
405
+ } else {
406
+ $this->_itemsById[$object->getId()] = array($object);
407
+ }
408
+ }
409
+
410
+ return $this;
411
+ }
412
+
413
+ /**
414
+ * Get the active filters, then prepare them for Klevu.
415
+ *
416
+ * @return string
417
+ */
418
+ protected function _getPreparedFilters() {
419
+ $layer = Mage::getSingleton('catalogsearch/layer');
420
+ $filters = $layer->getState()->getFilters();
421
+ $prepared_filters = array();
422
+
423
+ /** @var Mage_Catalog_Model_Layer_Filter_Item $filter */
424
+ foreach ($filters as $filter) {
425
+ $filter_type = $filter->getFilter()->getRequestVar();
426
+ $label = Mage::helper('klevu_search')->santiseAttributeValue(strtolower($filter->getData('label')));
427
+
428
+ switch($filter_type) {
429
+ case "cat":
430
+ $prepared_filters['category'] = $label;
431
+ break;
432
+ case "price":
433
+ $prepared_filters['klevu_price'] = implode(' - ', $filter->getFilter()->getData('interval'));
434
+ break;
435
+ default:
436
+ $prepared_filters[$filter->getFilter()->getAttributeModel()->getAttributeCode()] = $label;
437
+ break;
438
+ }
439
+ }
440
+
441
+ $this->log(Zend_Log::DEBUG, sprintf('Active Filters: %s', var_export($prepared_filters, true)));
442
+
443
+ return implode(
444
+ ';;',
445
+ array_map(
446
+ function($v, $k) {
447
+ return sprintf('%s:%s', $k, $v);
448
+ },
449
+ $prepared_filters,
450
+ array_keys($prepared_filters)
451
+ )
452
+ );
453
+
454
+ }
455
+
456
+ protected function log($level, $message) {
457
+ Mage::helper('klevu_search')->log($level, $message);
458
+ }
459
+
460
+ protected function isExtensionConfigured() {
461
+ return Mage::helper('klevu_search/config')->isExtensionConfigured();
462
+ }
463
+ }
app/code/community/Klevu/Search/Model/CatalogSearch/Resource/Layer/Filter/Attribute.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_CatalogSearch_Resource_Layer_Filter_Attribute extends Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Attribute {
4
+
5
+ /**
6
+ * Stub method to prevent filters being applied. Klevu handles all filtering.
7
+ *
8
+ * @param Mage_Catalog_Model_Layer_Filter_Attribute $filter
9
+ * @param int $value
10
+ * @return $this|Mage_Catalog_Model_Resource_Layer_Filter_Attribute
11
+ */
12
+ public function applyFilterToCollection($filter, $value) {
13
+ // If the Klevu module is not configured/enabled, run the parent method.
14
+ if (!Mage::helper('klevu_search/config')->isExtensionConfigured() || !Mage::helper('klevu_search')->isCatalogSearch()) {
15
+ parent::applyFilterToCollection($filter, $value);
16
+ }
17
+
18
+ return $this;
19
+ }
20
+ }
app/code/community/Klevu/Search/Model/Config/Log/Level.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Config_Log_Level extends Mage_Core_Model_Config_Data {
4
+
5
+ /**
6
+ * Return the log level value. Return Zend_Log::WARN as default, if none set.
7
+ *
8
+ * @return int
9
+ */
10
+ public function getValue() {
11
+ $value = $this->getData('value');
12
+
13
+ return ($value != null) ? intval($value) : Zend_Log::WARN;
14
+ }
15
+ }
app/code/community/Klevu/Search/Model/Cron.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Cron extends Varien_Object {
4
+
5
+ public function clearCronCheckNotification() {
6
+ $collection = Mage::getResourceModel("klevu_search/notification_collection");
7
+ $collection->addFieldToFilter("type", array("eq" => "cron_check"));
8
+
9
+ foreach ($collection as $notification) {
10
+ $notification->delete();
11
+ }
12
+ }
13
+ }
app/code/community/Klevu/Search/Model/Notification.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Notification extends Mage_Core_Model_Abstract {
4
+
5
+ protected function _construct() {
6
+ $this->_init("klevu_search/notification");
7
+ }
8
+ }
app/code/community/Klevu/Search/Model/Observer.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Model_Observer
5
+ *
6
+ * @method setIsProductSyncScheduled($flag)
7
+ * @method bool getIsProductSyncScheduled()
8
+ */
9
+ class Klevu_Search_Model_Observer extends Varien_Object {
10
+
11
+ /**
12
+ * Schedule a Product Sync to run immediately.
13
+ *
14
+ * @param Varien_Event_Observer $observer
15
+ */
16
+ public function scheduleProductSync(Varien_Event_Observer $observer) {
17
+ if (!$this->getIsProductSyncScheduled()) {
18
+ Mage::getModel("klevu_search/product_sync")->schedule();
19
+ $this->setIsProductSyncScheduled(true);
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Schedule an Order Sync to run immediately. If the observed event
25
+ * contains an order, add it to the sync queue before scheduling.
26
+ *
27
+ * @param Varien_Event_Observer $observer
28
+ */
29
+ public function scheduleOrderSync(Varien_Event_Observer $observer) {
30
+ $model = Mage::getModel("klevu_search/order_sync");
31
+
32
+ $order = $observer->getEvent()->getOrder();
33
+ if ($order) {
34
+ $model->addOrderToQueue($order);
35
+ }
36
+
37
+ $model->schedule();
38
+ }
39
+
40
+ /**
41
+ * When products are updated in bulk, update products so that they will be synced.
42
+ * @param Varien_Event_Observer $observer
43
+ */
44
+ public function setProductsToSync(Varien_Event_Observer $observer) {
45
+ $product_ids = $observer->getData('product_ids');
46
+
47
+ if(empty($product_ids)) {
48
+ return;
49
+ }
50
+
51
+ $product_ids = implode(',', $product_ids);
52
+ $where = sprintf("product_id IN(%s) OR parent_id IN(%s)", $product_ids, $product_ids);
53
+ $resource = Mage::getSingleton('core/resource');
54
+ $resource->getConnection('core_write')
55
+ ->update(
56
+ $resource->getTableName('klevu_search/product_sync'),
57
+ array('last_synced_at' => '0'),
58
+ $where
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Mark all of the products for update and then schedule a sync
64
+ * to run immediately.
65
+ *
66
+ * @param Varien_Event_Observer $observer
67
+ */
68
+ public function syncAllProducts(Varien_Event_Observer $observer) {
69
+ $store = null;
70
+ $sync = Mage::getModel("klevu_search/product_sync");
71
+
72
+ $attribute = $observer->getEvent()->getAttribute();
73
+ if ($attribute instanceof Mage_Catalog_Model_Resource_Eav_Attribute) {
74
+ // On attribute change, sync only if the attribute was added
75
+ // or removed from layered navigation
76
+ if ($attribute->getOrigData("is_filterable_in_search") == $attribute->getData("is_filterable_in_search")) {
77
+ return;
78
+ }
79
+ }
80
+
81
+ if ($observer->getEvent()->getStore()) {
82
+ // Only sync products for a specific store if the event was fired in that store
83
+ $store = Mage::app()->getStore($observer->getEvent()->getStore());
84
+ }
85
+
86
+ $sync->markAllProductsForUpdate($store);
87
+
88
+ if (!$this->getIsProductSyncScheduled()) {
89
+ $sync->schedule();
90
+ $this->setIsProductSyncScheduled(true);
91
+ }
92
+ }
93
+ /**
94
+ * When product image updated from admin this will generate the image thumb.
95
+ * @param Varien_Event_Observer $observer
96
+ */
97
+ public function createThumb(Varien_Event_Observer $observer) {
98
+ $image = $observer->getEvent()->getProduct()->getImage();
99
+ if(($image != "no_selection") && (!empty($image))) {
100
+ Mage::getModel("klevu_search/product_sync")->thumbImage($image);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Apply model rewrites for the search landing page, if it is enabled.
106
+ *
107
+ * @param Varien_Event_Observer $observer
108
+ */
109
+ public function applyLandingPageModelRewrites(Varien_Event_Observer $observer) {
110
+ if (Mage::helper("klevu_search/config")->isLandingEnabled()) {
111
+ $rewrites = array(
112
+ "global/models/catalogsearch_resource/rewrite/fulltext_collection" => "Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection",
113
+ "global/models/catalogsearch_mysql4/rewrite/fulltext_collection" => "Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection",
114
+ "global/models/catalogsearch/rewrite/layer_filter_attribute" => "Klevu_Search_Model_CatalogSearch_Layer_Filter_Attribute",
115
+ "global/models/catalog/rewrite/config" => "Klevu_Search_Model_Catalog_Model_Config",
116
+ "global/models/catalog/rewrite/layer_filter_price" => "Klevu_Search_Model_CatalogSearch_Layer_Filter_Price",
117
+ "global/models/catalog/rewrite/layer_filter_category" => "Klevu_Search_Model_CatalogSearch_Layer_Filter_Category",
118
+ "global/models/catalog_resource/rewrite/layer_filter_attribute" => "Klevu_Search_Model_CatalogSearch_Resource_Layer_Filter_Attribute",
119
+ "global/models/catalog_resource_eav_mysql4/rewrite/layer_filter_attribute" => "Klevu_Search_Model_CatalogSearch_Resource_Layer_Filter_Attribute"
120
+ );
121
+
122
+ $config = Mage::app()->getConfig();
123
+ foreach ($rewrites as $key => $value) {
124
+ $config->setNode($key, $value);
125
+ }
126
+ }
127
+ }
128
+
129
+ public function removeTest()
130
+ {
131
+ Mage::getModel("klevu_search/product_sync")->removeTestMode();
132
+
133
+ }
134
+
135
+
136
+
137
+ }
app/code/community/Klevu/Search/Model/Order/Sync.php ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Model_Order_Sync
5
+ * @method Varien_Db_Adapter_Interface getConnection()
6
+ */
7
+ class Klevu_Search_Model_Order_Sync extends Klevu_Search_Model_Sync {
8
+
9
+ const NOTIFICATION_TYPE = "order_sync";
10
+
11
+ public function _construct() {
12
+ parent::_construct();
13
+
14
+ $this->addData(array(
15
+ "connection" => Mage::getModel("core/resource")->getConnection("core_write")
16
+ ));
17
+ }
18
+
19
+ public function getJobCode() {
20
+ return "klevu_search_order_sync";
21
+ }
22
+
23
+ /**
24
+ * Add the items from the given order to the Order Sync queue. Does nothing if
25
+ * Order Sync is disabled for the store that the order was placed in.
26
+ *
27
+ * @param Mage_Sales_Model_Order $order
28
+ * @param bool $force Skip enabled check
29
+ *
30
+ * @return $this
31
+ */
32
+ public function addOrderToQueue(Mage_Sales_Model_Order $order, $force = false) {
33
+ if (!$this->isEnabled($order->getStoreId()) && !$force) {
34
+ return $this;
35
+ }
36
+
37
+ $items = array();
38
+ foreach ($order->getAllVisibleItems() as $item) {
39
+ /** @var Mage_Sales_Model_Order_Item $item */
40
+
41
+ // For configurable products add children items only, for all other products add parents
42
+ if ($item->getProductType() == Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
43
+ foreach ($item->getChildrenItems() as $child) {
44
+ if($child->getId()!=null) {
45
+ $items[] = $child->getId();
46
+ }
47
+ }
48
+ } else {
49
+ if($item->getId()!=null) {
50
+ $items[] = $item->getId();
51
+ }
52
+
53
+ }
54
+ }
55
+
56
+ // in case of multiple addresses used for shipping
57
+ // its possible that items object here is empty
58
+ // if so, we do not add to the item.
59
+ if(!empty($items)) {
60
+ $this->addItemsToQueue($items);
61
+ }
62
+
63
+ return $this;
64
+ }
65
+
66
+ /**
67
+ * Clear the Order Sync queue for the given store. If no store is given, clears
68
+ * the queue for all stores.
69
+ *
70
+ * @param Mage_Core_Model_Store|int|null $store
71
+ *
72
+ * @return int
73
+ */
74
+ public function clearQueue($store = null) {
75
+ $select = $this->getConnection()
76
+ ->select()
77
+ ->from(array("k" => $this->getTableName("klevu_search/order_sync")));
78
+
79
+ if ($store) {
80
+ $store = Mage::app()->getStore($store);
81
+ $select
82
+ ->join(
83
+ array("i" => $this->getTableName("sales/order_item")),
84
+ "k.order_item_id = i.item_id",
85
+ ""
86
+ )
87
+ ->where("i.store_id = ?", $store->getId());
88
+ }
89
+
90
+ $result = $this->getConnection()->query($select->deleteFromSelect("k"));
91
+ return $result->rowCount();
92
+ }
93
+
94
+ public function run() {
95
+ try {
96
+ if ($this->isRunning(2)) {
97
+ // Stop if another copy is already running
98
+ $this->log(Zend_Log::INFO, "Another copy is already running. Stopped.");
99
+ return;
100
+ }
101
+
102
+ $this->log(Zend_Log::INFO, "Starting sync.");
103
+
104
+ $items_synced = 0;
105
+ $errors = 0;
106
+
107
+ $item = Mage::getModel("sales/order_item");
108
+
109
+ $stmt = $this->getConnection()->query($this->getSyncQueueSelect());
110
+ while ($item_id = $stmt->fetchColumn()) {
111
+ if ($this->rescheduleIfOutOfMemory()) {
112
+ return;
113
+ }
114
+
115
+ $item->setData(array());
116
+ $item->load($item_id);
117
+
118
+ if ($item->getId()) {
119
+ if ($this->isEnabled($item->getStoreId())) {
120
+ if ($this->getApiKey($item->getStoreId())) {
121
+ $result = $this->sync($item);
122
+ if ($result === true) {
123
+ $this->removeItemFromQueue($item_id);
124
+ $items_synced++;
125
+ } else {
126
+ $this->log(Zend_Log::INFO, sprintf("Skipped order item %d: %s", $item_id, $result));
127
+ $errors++;
128
+ }
129
+ }
130
+ } else {
131
+ $this->log(Zend_Log::ERR, sprintf("Skipped item %d: Order Sync is not enabled for this store.", $item_id));
132
+ $this->removeItemFromQueue($item_id);
133
+ }
134
+ } else {
135
+ $this->log(Zend_Log::ERR, sprintf("Order item %d does not exist: Removed from sync!", $item_id));
136
+ $this->removeItemFromQueue($item_id);
137
+ $errors++;
138
+ }
139
+ }
140
+
141
+ $this->log(Zend_Log::INFO, sprintf("Sync finished. %d items synced.", $items_synced));
142
+ Mage::helper("klevu_search/config")->setLastOrderSyncRun();
143
+
144
+ if ($errors) {
145
+ //$this->notify(Mage::helper("klevu_search")->__("Order Sync failed to sync some of the order items. Please consult the logs for more details."));
146
+ } else {
147
+ // If a sync finished without errors, existing notifications no longer apply
148
+ $this->deleteNotifications();
149
+ }
150
+ } catch(Exception $e) {
151
+ // Catch the exception that was thrown, log it, then throw a new exception to be caught the Magento cron.
152
+ Mage::helper('klevu_search')->log(Zend_Log::CRIT, sprintf("Exception thrown in %s::%s - %s", __CLASS__, __METHOD__, $e->getMessage()));
153
+ throw $e;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Sync the given order item to Klevu. Returns true on successful sync and
159
+ * the error message otherwise.
160
+ *
161
+ * @param Mage_Sales_Model_Order_Item $item
162
+ *
163
+ * @return bool|string
164
+ */
165
+ protected function sync($item) {
166
+ if (!$this->getApiKey($item->getStoreId())) {
167
+ return "Klevu Search is not configured for this store.";
168
+ }
169
+
170
+ $parent = null;
171
+ if ($item->getParentItemId()) {
172
+ $parent = Mage::getModel("sales/order_item")->load($item->getParentItemId());
173
+ }
174
+
175
+ $response = Mage::getModel("klevu_search/api_action_producttracking")
176
+ ->setStore(Mage::app()->getStore($item->getStoreId()))
177
+ ->execute(array(
178
+ "klevu_apiKey" => $this->getApiKey($item->getStoreId()),
179
+ "klevu_type" => "checkout",
180
+ "klevu_productId" => Mage::helper("klevu_search")->getKlevuProductId($item->getProductId(), ($parent) ? $parent->getProductId() : 0),
181
+ "klevu_unit" => $item->getQtyOrdered() ? $item->getQtyOrdered() : ($parent ? $parent->getQtyOrdered() : null),
182
+ "klevu_salePrice" => $item->getPriceInclTax() ? $item->getPriceInclTax() : ($parent ? $parent->getPriceInclTax() : null),
183
+ "klevu_currency" => $this->getStoreCurrency($item->getStoreId()),
184
+ "klevu_shopperIP" => $this->getOrderIP($item->getOrderId())
185
+ ));
186
+
187
+ if ($response->isSuccessful()) {
188
+ return true;
189
+ } else {
190
+ return $response->getMessage();
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Check if Order Sync is enabled for the given store.
196
+ *
197
+ * @param $store_id
198
+ *
199
+ * @return bool
200
+ */
201
+ protected function isEnabled($store_id) {
202
+ $is_enabled = $this->getData("is_enabled");
203
+ if (!is_array($is_enabled)) {
204
+ $is_enabled = array();
205
+ }
206
+
207
+ if (!isset($is_enabled[$store_id])) {
208
+ $is_enabled[$store_id] = Mage::helper("klevu_search/config")->isOrderSyncEnabled($store_id);
209
+ $this->setData("is_enabled", $is_enabled);
210
+ }
211
+
212
+ return $is_enabled[$store_id];
213
+ }
214
+
215
+ /**
216
+ * Return the JS API key for the given store.
217
+ *
218
+ * @param $store_id
219
+ *
220
+ * @return string|null
221
+ */
222
+ protected function getApiKey($store_id) {
223
+ $api_keys = $this->getData("api_keys");
224
+ if (!is_array($api_keys)) {
225
+ $api_keys = array();
226
+ }
227
+
228
+ if (!isset($api_keys[$store_id])) {
229
+ $api_keys[$store_id] = Mage::helper("klevu_search/config")->getJsApiKey($store_id);
230
+ $this->setData("api_keys", $api_keys);
231
+ }
232
+
233
+ return $api_keys[$store_id];
234
+ }
235
+
236
+ /**
237
+ * Get the currency code for the given store.
238
+ *
239
+ * @param $store_id
240
+ *
241
+ * @return string
242
+ */
243
+ protected function getStoreCurrency($store_id) {
244
+ $currencies = $this->getData("currencies");
245
+ if (!is_array($currencies)) {
246
+ $currencies = array();
247
+ }
248
+
249
+ if (!isset($currencies[$store_id])) {
250
+ $currencies[$store_id] = Mage::app()->getStore($store_id)->getDefaultCurrencyCode();
251
+ $this->setData("currencies", $currencies);
252
+ }
253
+
254
+ return $currencies[$store_id];
255
+ }
256
+
257
+ /**
258
+ * Return the customer IP for the given order.
259
+ *
260
+ * @param $order_id
261
+ *
262
+ * @return string
263
+ */
264
+ protected function getOrderIP($order_id) {
265
+ $order_ips = $this->getData("order_ips");
266
+ if (!is_array($order_ips)) {
267
+ $order_ips = array();
268
+ }
269
+
270
+ if (!isset($order_ips[$order_id])) {
271
+ $order_ips[$order_id] = $this->getConnection()->fetchOne(
272
+ $this->getConnection()
273
+ ->select()
274
+ ->from(array("order" => $this->getTableName("sales/order")), "remote_ip")
275
+ ->where("order.entity_id = ?", $order_id)
276
+ );
277
+ $this->setData("order_ips", $order_ips);
278
+ }
279
+
280
+ return $order_ips[$order_id];
281
+ }
282
+
283
+ /**
284
+ * Return a select statement for getting all items in the sync queue.
285
+ *
286
+ * @return Zend_Db_Select
287
+ */
288
+ protected function getSyncQueueSelect() {
289
+ return $this->getConnection()
290
+ ->select()
291
+ ->from($this->getTableName("klevu_search/order_sync"));
292
+ }
293
+
294
+ /**
295
+ * Add the given order item IDs to the sync queue.
296
+ *
297
+ * @param $order_item_ids
298
+ *
299
+ * @return int
300
+ */
301
+ protected function addItemsToQueue($order_item_ids) {
302
+ if (!is_array($order_item_ids)) {
303
+ $order_item_ids = array($order_item_ids);
304
+ }
305
+
306
+ return $this->getConnection()->insertArray(
307
+ $this->getTableName("klevu_search/order_sync"),
308
+ array("order_item_id"),
309
+ $order_item_ids
310
+ );
311
+ }
312
+
313
+ /**
314
+ * Remove the given item from the sync queue.
315
+ *
316
+ * @param $order_item_id
317
+ *
318
+ * @return bool
319
+ */
320
+ protected function removeItemFromQueue($order_item_id) {
321
+ return $this->getConnection()->delete(
322
+ $this->getTableName("klevu_search/order_sync"),
323
+ array("order_item_id" => $order_item_id)
324
+ ) === 1;
325
+ }
326
+
327
+ /**
328
+ * Create an Adminhtml notification for Order Sync, overwriting
329
+ * any existing ones.
330
+ *
331
+ * @param $message
332
+ *
333
+ * @return $this
334
+ */
335
+ protected function notify($message) {
336
+ $notification = Mage::getResourceModel("klevu_search/notification_collection")
337
+ ->addFieldToFilter("type", array("eq" => static::NOTIFICATION_TYPE))
338
+ ->getFirstItem();
339
+
340
+ $notification->addData(array(
341
+ "type" => static::NOTIFICATION_TYPE,
342
+ "date" => Mage::getModel("core/date")->timestamp(),
343
+ "message" => $message
344
+ ));
345
+
346
+ $notification->save();
347
+
348
+ return $this;
349
+ }
350
+
351
+ /**
352
+ * Delete Adminhtml notifications for Order Sync.
353
+ *
354
+ * @return $this
355
+ */
356
+ protected function deleteNotifications() {
357
+ $this->getConnection()->delete(
358
+ $this->getTableName('klevu_search/notification'),
359
+ array("type" => static::NOTIFICATION_TYPE)
360
+ );
361
+
362
+ return $this;
363
+ }
364
+ }
app/code/community/Klevu/Search/Model/Product/Sync.php ADDED
@@ -0,0 +1,1969 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Klevu_Search_Model_Product_Sync
5
+ * @method Varien_Db_Adapter_Interface getConnection()
6
+ * @method Mage_Core_Model_Store getStore()
7
+ * @method string getSessionId()
8
+ */
9
+ class Klevu_Search_Model_Product_Sync extends Klevu_Search_Model_Sync {
10
+
11
+ /**
12
+ * It has been determined during development that Product Sync uses around
13
+ * 120kB of memory for each product it syncs, or around 10MB of memory for
14
+ * each 100 product page.
15
+ */
16
+ const RECORDS_PER_PAGE = 100;
17
+
18
+ const NOTIFICATION_GLOBAL_TYPE = "product_sync";
19
+ const NOTIFICATION_STORE_TYPE_PREFIX = "product_sync_store_";
20
+
21
+ public function _construct() {
22
+ parent::_construct();
23
+
24
+ $this->addData(array(
25
+ 'connection' => Mage::getModel('core/resource')->getConnection("core_write")
26
+ ));
27
+ }
28
+
29
+ public function getJobCode() {
30
+ return "klevu_search_product_sync";
31
+ }
32
+
33
+ /**
34
+ * Perform Product Sync on any configured stores, adding new products, updating modified and
35
+ * deleting removed products since last sync.
36
+ */
37
+ public function run() {
38
+ try {
39
+ if ($this->isRunning(2)) {
40
+ // Stop if another copy is already running
41
+ $this->log(Zend_Log::INFO, "Stopping because another copy is already running.");
42
+ return;
43
+ }
44
+
45
+ $stores = Mage::app()->getStores();
46
+
47
+ foreach ($stores as $store) {
48
+ /** @var Mage_Core_Model_Store $store */
49
+ $this->reset();
50
+
51
+ if ($this->rescheduleIfOutOfMemory()) {
52
+ return;
53
+ }
54
+
55
+ if (!$this->setupSession($store)) {
56
+ continue;
57
+ }
58
+
59
+ $this->log(Zend_Log::INFO, sprintf("Starting sync for %s (%s).", $store->getWebsite()->getName(), $store->getName()));
60
+
61
+ $actions = array(
62
+ 'delete' => $this->getConnection()
63
+ ->select()
64
+ /*
65
+ * Select synced products in the current store/mode that are no longer enabled
66
+ * (don't exist in the products table, or have status disabled for the current
67
+ * store, or have status disabled for the default store) or are not visible
68
+ * (in the case of configurable products, check the parent visibility instead).
69
+ */
70
+ ->from(
71
+ array('k' => $this->getTableName("klevu_search/product_sync")),
72
+ array('product_id' => "k.product_id", 'parent_id' => "k.parent_id")
73
+ )
74
+ ->joinLeft(
75
+ array('v' => $this->getTableName("catalog/category_product_index")),
76
+ "v.product_id = k.product_id AND v.store_id = :store_id",
77
+ ""
78
+ )
79
+ ->joinLeft(
80
+ array('p' => $this->getTableName("catalog/product")),
81
+ "p.entity_id = k.product_id",
82
+ ""
83
+ )
84
+ ->joinLeft(
85
+ array('ss' => $this->getProductStatusAttribute()->getBackendTable()),
86
+ "ss.attribute_id = :status_attribute_id AND ss.entity_id = k.product_id AND ss.store_id = :store_id",
87
+ ""
88
+ )
89
+ ->joinLeft(
90
+ array('sd' => $this->getProductStatusAttribute()->getBackendTable()),
91
+ "sd.attribute_id = :status_attribute_id AND sd.entity_id = k.product_id AND sd.store_id = :default_store_id",
92
+ ""
93
+ )
94
+ ->where("(k.store_id = :store_id) AND (k.test_mode = :test_mode) AND ((p.entity_id IS NULL) OR (CASE WHEN ss.value_id > 0 THEN ss.value ELSE sd.value END != :status_enabled) OR (CASE WHEN k.parent_id = 0 THEN k.product_id ELSE k.parent_id END NOT IN (?)))",
95
+ $this->getConnection()
96
+ ->select()
97
+ ->from(
98
+ array('i' => $this->getTableName("catalog/category_product_index")),
99
+ array('id' => "i.product_id")
100
+ )
101
+ ->where("(i.store_id = :store_id) AND (i.visibility IN (:visible_both, :visible_search))")
102
+ )
103
+ ->group(array('k.product_id', 'k.parent_id'))
104
+ ->bind(array(
105
+ 'store_id' => $store->getId(),
106
+ 'default_store_id' => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID,
107
+ 'test_mode' => $this->isTestModeEnabled(),
108
+ 'status_attribute_id' => $this->getProductStatusAttribute()->getId(),
109
+ 'status_enabled' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED,
110
+ 'visible_both' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
111
+ 'visible_search' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH
112
+ )),
113
+
114
+ 'update' => $this->getConnection()
115
+ ->select()
116
+ ->union(array(
117
+ // Select products without parents that need to be updated
118
+ $this->getConnection()
119
+ ->select()
120
+ /*
121
+ * Select synced non-configurable products for the current store/mode
122
+ * that are visible (using the category product index) and have been
123
+ * updated since last sync.
124
+ */
125
+ ->from(
126
+ array('k' => $this->getTableName("klevu_search/product_sync")),
127
+ array('product_id' => "k.product_id", 'parent_id' => "k.parent_id")
128
+ )
129
+ ->join(
130
+ array('p' => $this->getTableName("catalog/product")),
131
+ "p.entity_id = k.product_id",
132
+ ""
133
+ )
134
+ ->join(
135
+ array('i' => $this->getTableName("catalog/category_product_index")),
136
+ "i.product_id = k.product_id AND k.store_id = i.store_id AND i.visibility IN (:visible_both, :visible_search)",
137
+ ""
138
+ )
139
+ ->where("(k.store_id = :store_id) AND (k.test_mode = :test_mode) AND (p.type_id != :configurable) AND (p.updated_at > k.last_synced_at)"),
140
+ // Select products with parents (configurable) that need to be updated
141
+ $this->getConnection()
142
+ ->select()
143
+ /*
144
+ * Select synced products for the current store/mode that are configurable
145
+ * children (have entries in the super link table), are enabled for the current
146
+ * store (or the default store), have visible parents (using the category product
147
+ * index) and, either the product or the parent, have been updated since last sync.
148
+ */
149
+ ->from(
150
+ array('k' => $this->getTableName("klevu_search/product_sync")),
151
+ array('product_id' => "k.product_id", 'parent_id' => "k.parent_id")
152
+ )
153
+ ->join(
154
+ array('s' => $this->getTableName("catalog/product_super_link")),
155
+ "k.parent_id = s.parent_id AND k.product_id = s.product_id",
156
+ ""
157
+ )
158
+ ->join(
159
+ array('i' => $this->getTableName("catalog/category_product_index")),
160
+ "k.parent_id = i.product_id AND k.store_id = i.store_id AND i.visibility IN (:visible_both, :visible_search)",
161
+ ""
162
+ )
163
+ ->join(
164
+ array('p1' => $this->getTableName("catalog/product")),
165
+ "k.product_id = p1.entity_id",
166
+ ""
167
+ )
168
+ ->join(
169
+ array('p2' => $this->getTableName("catalog/product")),
170
+ "k.parent_id = p2.entity_id",
171
+ ""
172
+ )
173
+ ->joinLeft(
174
+ array('ss' => $this->getProductStatusAttribute()->getBackendTable()),
175
+ "ss.attribute_id = :status_attribute_id AND ss.entity_id = k.product_id AND ss.store_id = :store_id",
176
+ ""
177
+ )
178
+ ->joinLeft(
179
+ array('sd' => $this->getProductStatusAttribute()->getBackendTable()),
180
+ "sd.attribute_id = :status_attribute_id AND sd.entity_id = k.product_id AND sd.store_id = :default_store_id",
181
+ ""
182
+ )
183
+ ->where("(k.store_id = :store_id) AND (k.test_mode = :test_mode) AND (CASE WHEN ss.value_id > 0 THEN ss.value ELSE sd.value END = :status_enabled) AND ((p1.updated_at > k.last_synced_at) OR (p2.updated_at > k.last_synced_at))")
184
+ ))
185
+ ->group(array('k.product_id', 'k.parent_id'))
186
+ ->bind(array(
187
+ 'store_id' => $store->getId(),
188
+ 'default_store_id' => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID,
189
+ 'test_mode' => $this->isTestModeEnabled(),
190
+ 'configurable' => Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE,
191
+ 'visible_both' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
192
+ 'visible_search' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH,
193
+ 'status_attribute_id' => $this->getProductStatusAttribute()->getId(),
194
+ 'status_enabled' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED,
195
+ )),
196
+
197
+ 'add' => $this->getConnection()
198
+ ->select()
199
+ ->union(array(
200
+ // Select non-configurable products that need to be added
201
+ $this->getConnection()
202
+ ->select()
203
+ /*
204
+ * Select non-configurable products that are visible in the current
205
+ * store (using the category product index), but have not been synced
206
+ * for this store yet.
207
+ */
208
+ ->from(
209
+ array('p' => $this->getTableName("catalog/product")),
210
+ array('product_id' => "p.entity_id", 'parent_id' => new Zend_Db_Expr("0"))
211
+ )
212
+ ->join(
213
+ array('i' => $this->getTableName("catalog/category_product_index")),
214
+ "p.entity_id = i.product_id AND i.store_id = :store_id AND i.visibility IN (:visible_both, :visible_search)",
215
+ ""
216
+ )
217
+ ->joinLeft(
218
+ array('k' => $this->getTableName("klevu_search/product_sync")),
219
+ "p.entity_id = k.product_id AND k.parent_id = 0 AND i.store_id = k.store_id AND k.test_mode = :test_mode",
220
+ ""
221
+ )
222
+ ->where("(p.type_id != :configurable) AND (k.product_id IS NULL)"),
223
+ // Select configurable parent & product pairs that need to be added
224
+ $this->getConnection()
225
+ ->select()
226
+ /*
227
+ * Select configurable product children that are enabled (for the current
228
+ * store or for the default store), have visible parents (using the category
229
+ * product index) and have not been synced yet for the current store with
230
+ * the current parent.
231
+ */
232
+ ->from(
233
+ array('s' => $this->getTableName("catalog/product_super_link")),
234
+ array('product_id' => "s.product_id", 'parent_id' => "s.parent_id")
235
+ )
236
+ ->join(
237
+ array('i' => $this->getTableName("catalog/category_product_index")),
238
+ "s.parent_id = i.product_id AND i.store_id = :store_id AND i.visibility IN (:visible_both, :visible_search)",
239
+ ""
240
+ )
241
+ ->joinLeft(
242
+ array('ss' => $this->getProductStatusAttribute()->getBackendTable()),
243
+ "ss.attribute_id = :status_attribute_id AND ss.entity_id = s.product_id AND ss.store_id = :store_id",
244
+ ""
245
+ )
246
+ ->joinLeft(
247
+ array('sd' => $this->getProductStatusAttribute()->getBackendTable()),
248
+ "sd.attribute_id = :status_attribute_id AND sd.entity_id = s.product_id AND sd.store_id = :default_store_id",
249
+ ""
250
+ )
251
+ ->joinLeft(
252
+ array('k' => $this->getTableName("klevu_search/product_sync")),
253
+ "s.parent_id = k.parent_id AND s.product_id = k.product_id AND k.store_id = :store_id AND k.test_mode = :test_mode",
254
+ ""
255
+ )
256
+ ->where("(CASE WHEN ss.value_id > 0 THEN ss.value ELSE sd.value END = :status_enabled) AND (k.product_id IS NULL)")
257
+ ))
258
+ ->group(array('k.product_id', 'k.parent_id'))
259
+ ->bind(array(
260
+ 'store_id' => $store->getId(),
261
+ 'default_store_id' => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID,
262
+ 'test_mode' => $this->isTestModeEnabled(),
263
+ 'configurable' => Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE,
264
+ 'visible_both' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
265
+ 'visible_search' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH,
266
+ 'status_attribute_id' => $this->getProductStatusAttribute()->getId(),
267
+ 'status_enabled' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED
268
+ ))
269
+ );
270
+
271
+ $errors = 0;
272
+
273
+ foreach ($actions as $action => $statement) {
274
+ if ($this->rescheduleIfOutOfMemory()) {
275
+ return;
276
+ }
277
+ $method = $action . "Products";
278
+
279
+ $products = $this->getConnection()->fetchAll($statement, $statement->getBind());
280
+
281
+ $total = count($products);
282
+ $this->log(Zend_Log::INFO, sprintf("Found %d products to %s.", $total, $action));
283
+ $pages = ceil($total / static::RECORDS_PER_PAGE);
284
+ for ($page = 1; $page <= $pages; $page++) {
285
+ if ($this->rescheduleIfOutOfMemory()) {
286
+ return;
287
+ }
288
+
289
+ $offset = ($page - 1) * static::RECORDS_PER_PAGE;
290
+ $result = $this->$method(array_slice($products, $offset, static::RECORDS_PER_PAGE));
291
+
292
+ if ($result !== true) {
293
+ $errors++;
294
+ $this->log(Zend_Log::ERR, sprintf("Errors occurred while attempting to %s products %d - %d: %s",
295
+ $action,
296
+ $offset + 1,
297
+ ($offset + static::RECORDS_PER_PAGE <= $total) ? $offset + static::RECORDS_PER_PAGE : $total,
298
+ $result
299
+ ));
300
+ /*$this->notify(
301
+ Mage::helper('klevu_search')->__("Product Sync for %s (%s) failed to %s some products. Please consult the logs for more details.",
302
+ $store->getWebsite()->getName(),
303
+ $store->getName(),
304
+ $action
305
+ ),
306
+ $store
307
+ );*/
308
+ }
309
+ }
310
+ }
311
+
312
+ $this->log(Zend_Log::INFO, sprintf("Finished sync for %s (%s).", $store->getWebsite()->getName(), $store->getName()));
313
+
314
+ $config = Mage::helper('klevu_search/config');
315
+ if (!$config->isExtensionEnabled($store) && !$config->hasProductSyncRun($store)) {
316
+ // Enable Klevu Search after the first sync
317
+ $config->setExtensionEnabledFlag(true, $store);
318
+ $this->log(Zend_Log::INFO, sprintf("Automatically enabled Klevu Search on Frontend for %s (%s).",
319
+ $store->getWebsite()->getName(),
320
+ $store->getName()
321
+ ));
322
+ }
323
+ $config->setLastProductSyncRun("now", $store);
324
+
325
+ if ($errors == 0) {
326
+ // If Product Sync finished without any errors, notifications are not relevant anymore
327
+ $this->deleteNotifications($store);
328
+ }
329
+ }
330
+ } catch (Exception $e) {
331
+ // Catch the exception that was thrown, log it, then throw a new exception to be caught the Magento cron.
332
+ Mage::helper('klevu_search')->log(Zend_Log::CRIT, sprintf("Exception thrown in %s::%s - %s", __CLASS__, __METHOD__, $e->getMessage()));
333
+ throw $e;
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Run the product sync manually, creating a cron schedule entry
339
+ * to prevent other syncs from running.
340
+ */
341
+ public function runManually() {
342
+ $time = date_create("now")->format("Y-m-d H:i:s");
343
+ $schedule = Mage::getModel("cron/schedule");
344
+ $schedule
345
+ ->setJobCode($this->getJobCode())
346
+ ->setCreatedAt($time)
347
+ ->setScheduledAt($time)
348
+ ->setExecutedAt($time)
349
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_RUNNING)
350
+ ->save();
351
+
352
+ try {
353
+ $this->run();
354
+ } catch (Exception $e) {
355
+ Mage::logException($e);
356
+
357
+ $schedule
358
+ ->setMessages($e->getMessage())
359
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_ERROR)
360
+ ->save();
361
+
362
+ return;
363
+ }
364
+
365
+ $time = date_create("now")->format("Y-m-d H:i:s");
366
+ $schedule
367
+ ->setFinishedAt($time)
368
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_SUCCESS)
369
+ ->save();
370
+
371
+ return;
372
+ }
373
+
374
+ /**
375
+ * Mark all products to be updated the next time Product Sync runs.
376
+ *
377
+ * @param Mage_Core_Model_Store|int $store If passed, will only update products for the given store.
378
+ *
379
+ * @return $this
380
+ */
381
+ public function markAllProductsForUpdate($store = null) {
382
+ $where = "";
383
+ if ($store !== null) {
384
+ $store = Mage::app()->getStore($store);
385
+
386
+ $where = $this->getConnection()->quoteInto("store_id = ?", $store->getId());
387
+ }
388
+
389
+ $this->getConnection()->update(
390
+ $this->getTableName('klevu_search/product_sync'),
391
+ array('last_synced_at' => '0'),
392
+ $where
393
+ );
394
+
395
+ return $this;
396
+ }
397
+
398
+ /**
399
+ * Forget the sync status of all the products for the given Store and test mode.
400
+ * If no store or test mode status is given, clear products for all stores and modes respectively.
401
+ *
402
+ * @param Mage_Core_Model_Store|int|null $store
403
+ * @param bool|null $test_mode
404
+ *
405
+ * @return int
406
+ */
407
+ public function clearAllProducts($store = null, $test_mode = null) {
408
+ $select = $this->getConnection()
409
+ ->select()
410
+ ->from(
411
+ array("k" => $this->getTableName("klevu_search/product_sync"))
412
+ );
413
+
414
+ if ($store) {
415
+ $store = Mage::app()->getStore($store);
416
+
417
+ $select->where("k.store_id = ?", $store->getId());
418
+ }
419
+
420
+ if ($test_mode !== null) {
421
+ $test_mode = ($test_mode) ? 1 : 0;
422
+
423
+ $select->where("k.test_mode = ?", $test_mode);
424
+ }
425
+
426
+ $result = $this->getConnection()->query($select->deleteFromSelect("k"));
427
+ return $result->rowCount();
428
+ }
429
+
430
+ /**
431
+ * Return the product status attribute model.
432
+ *
433
+ * @return Mage_Catalog_Model_Resource_Eav_Attribute
434
+ */
435
+ protected function getProductStatusAttribute() {
436
+ if (!$this->hasData("status_attribute")) {
437
+ $this->setData("status_attribute", Mage::getSingleton('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'status'));
438
+ }
439
+
440
+ return $this->getData("status_attribute");
441
+ }
442
+
443
+ /**
444
+ * Return the product visibility attribute model.
445
+ *
446
+ * @return Mage_Catalog_Model_Resource_Eav_Attribute
447
+ */
448
+ protected function getProductVisibilityAttribute() {
449
+ if (!$this->hasData("visibility_attribute")) {
450
+ $this->setData("visibility_attribute", Mage::getSingleton('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'visibility'));
451
+ }
452
+
453
+ return $this->getData("visibility_attribute");
454
+ }
455
+
456
+ /**
457
+ * Setup an API session for the given store. Sets the store and session ID on self. Returns
458
+ * true on success or false if Product Sync is disabled, store is not configured or the
459
+ * session API call fails.
460
+ *
461
+ * @param Mage_Core_Model_Store $store
462
+ *
463
+ * @return bool
464
+ */
465
+ protected function setupSession(Mage_Core_Model_Store $store) {
466
+ $config = Mage::helper('klevu_search/config');
467
+
468
+ if (!$config->isProductSyncEnabled($store->getId())) {
469
+ $this->log(Zend_Log::INFO, sprintf("Disabled for %s (%s).", $store->getWebsite()->getName(), $store->getName()));
470
+ return null;
471
+ }
472
+
473
+ $api_key = $config->getRestApiKey($store->getId());
474
+ if (!$api_key) {
475
+ $this->log(Zend_Log::INFO, sprintf("No API key found for %s (%s).", $store->getWebsite()->getName(), $store->getName()));
476
+ return null;
477
+ }
478
+
479
+ $response = Mage::getModel('klevu_search/api_action_startsession')->execute(array(
480
+ 'api_key' => $api_key,
481
+ 'store' => $store,
482
+ ));
483
+
484
+ if ($response->isSuccessful()) {
485
+ $this->addData(array(
486
+ 'store' => $store,
487
+ 'session_id' => $response->getSessionId()
488
+ ));
489
+ return true;
490
+ } else {
491
+ $this->log(Zend_Log::ERR, sprintf("Failed to start a session for %s (%s): %s",
492
+ $store->getWebsite()->getName(),
493
+ $store->getName(),
494
+ $response->getMessage()
495
+ ));
496
+
497
+ if ($response instanceof Klevu_Search_Model_Api_Response_Empty) {
498
+ /*$this->notify(
499
+ Mage::helper('klevu_search')->__(
500
+ "Product Sync failed for %s (%s): Could not contact Klevu.",
501
+ $store->getWebsite()->getName(),
502
+ $store->getName()
503
+ )
504
+ );*/
505
+ } else {
506
+ $this->notify(
507
+ Mage::helper('klevu_search')->__(
508
+ "Product Sync failed for %s (%s): %s",
509
+ $store->getWebsite()->getName(),
510
+ $store->getName(),
511
+ $response->getMessage()
512
+ )
513
+ );
514
+ }
515
+
516
+ return false;
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Delete the given products from Klevu Search. Returns true if the operation was
522
+ * successful, or the error message if the operation failed.
523
+ *
524
+ * @param array $data List of products to delete. Each element should be an array
525
+ * containing an element with "product_id" as the key and product id as
526
+ * the value and an optional "parent_id" element with the parent id.
527
+ *
528
+ * @return bool|string
529
+ */
530
+ protected function deleteProducts(array $data) {
531
+ $total = count($data);
532
+
533
+ $response = Mage::getModel('klevu_search/api_action_deleterecords')
534
+ ->setStore($this->getStore())
535
+ ->execute(array(
536
+ 'sessionId' => $this->getSessionId(),
537
+ 'records' => array_map(function ($v) {
538
+ return array('id' => Mage::helper('klevu_search')->getKlevuProductId($v['product_id'], $v['parent_id']));
539
+ }, $data)
540
+ ));
541
+
542
+ if ($response->isSuccessful()) {
543
+ $connection = $this->getConnection();
544
+
545
+ $select = $connection
546
+ ->select()
547
+ ->from(array('k' => $this->getTableName("klevu_search/product_sync")))
548
+ ->where("k.store_id = ?", $this->getStore()->getId())
549
+ ->where("k.test_mode = ?", $this->isTestModeEnabled());
550
+
551
+ $skipped_record_ids = array();
552
+ if ($skipped_records = $response->getSkippedRecords()) {
553
+ $skipped_record_ids = array_flip($skipped_records["index"]);
554
+ }
555
+
556
+ $or_where = array();
557
+ for ($i = 0; $i < count($data); $i++) {
558
+ if (isset($skipped_record_ids[$i])) {
559
+ continue;
560
+ }
561
+ $or_where[] = sprintf("(%s AND %s)",
562
+ $connection->quoteInto("k.product_id = ?", $data[$i]['product_id']),
563
+ $connection->quoteInto("k.parent_id = ?", $data[$i]['parent_id'])
564
+ );
565
+ }
566
+ $select->where(implode(" OR ", $or_where));
567
+
568
+ $connection->query($select->deleteFromSelect("k"));
569
+
570
+ $skipped_count = count($skipped_record_ids);
571
+ if ($skipped_count > 0) {
572
+ return sprintf("%d product%s failed (%s)",
573
+ $skipped_count,
574
+ ($skipped_count > 1) ? "s" : "",
575
+ implode(", ", $skipped_records["messages"])
576
+ );
577
+ } else {
578
+ return true;
579
+ }
580
+ } else {
581
+ return sprintf("%d product%s failed (%s)",
582
+ $total,
583
+ ($total > 1) ? "s" : "",
584
+ $response->getMessage()
585
+ );
586
+ }
587
+ }
588
+
589
+ /**
590
+ * Update the given products on Klevu Search. Returns true if the operation was successful,
591
+ * or the error message if it failed.
592
+ *
593
+ * @param array $data List of products to update. Each element should be an array
594
+ * containing an element with "product_id" as the key and product id as
595
+ * the value and an optional "parent_id" element with the parent id.
596
+ *
597
+ * @return bool|string
598
+ */
599
+ protected function updateProducts(array $data) {
600
+ $total = count($data);
601
+
602
+ $this->addProductSyncData($data);
603
+
604
+ $response = Mage::getModel('klevu_search/api_action_updaterecords')
605
+ ->setStore($this->getStore())
606
+ ->execute(array(
607
+ 'sessionId' => $this->getSessionId(),
608
+ 'records' => $data
609
+ ));
610
+
611
+ if ($response->isSuccessful()) {
612
+ $helper = Mage::helper('klevu_search');
613
+ $connection = $this->getConnection();
614
+
615
+ $skipped_record_ids = array();
616
+ if ($skipped_records = $response->getSkippedRecords()) {
617
+ $skipped_record_ids = array_flip($skipped_records["index"]);
618
+ }
619
+
620
+ $where = array();
621
+ for ($i = 0; $i < count($data); $i++) {
622
+ if (isset($skipped_record_ids[$i])) {
623
+ continue;
624
+ }
625
+
626
+ $ids = $helper->getMagentoProductId($data[$i]['id']);
627
+
628
+ $where[] = sprintf("(%s AND %s)",
629
+ $connection->quoteInto("product_id = ?", $ids['product_id']),
630
+ $connection->quoteInto("parent_id = ?", $ids['parent_id'])
631
+ );
632
+ }
633
+
634
+ $where = sprintf("(%s) AND (%s) AND (%s)",
635
+ $connection->quoteInto("store_id = ?", $this->getStore()->getId()),
636
+ $connection->quoteInto("test_mode = ?", $this->isTestModeEnabled()),
637
+ implode(" OR ", $where)
638
+ );
639
+
640
+ $this->getConnection()->update(
641
+ $this->getTableName('klevu_search/product_sync'),
642
+ array('last_synced_at' => Mage::helper("klevu_search/compat")->now()),
643
+ $where
644
+ );
645
+
646
+ $skipped_count = count($skipped_record_ids);
647
+ if ($skipped_count > 0) {
648
+ return sprintf("%d product%s failed (%s)",
649
+ $skipped_count,
650
+ ($skipped_count > 1) ? "s" : "",
651
+ implode(", ", $skipped_records["messages"])
652
+ );
653
+ } else {
654
+ return true;
655
+ }
656
+ } else {
657
+ return sprintf("%d product%s failed (%s)",
658
+ $total,
659
+ ($total > 1) ? "s" : "",
660
+ $response->getMessage()
661
+ );
662
+ }
663
+ }
664
+
665
+ /**
666
+ * Add the given products to Klevu Search. Returns true if the operation was successful,
667
+ * or the error message if it failed.
668
+ *
669
+ * @param array $data List of products to add. Each element should be an array
670
+ * containing an element with "product_id" as the key and product id as
671
+ * the value and an optional "parent_id" element with the parent id.
672
+ *
673
+ * @return bool|string
674
+ */
675
+ protected function addProducts(array $data) {
676
+ $total = count($data);
677
+
678
+ $this->addProductSyncData($data);
679
+
680
+ $response = Mage::getModel('klevu_search/api_action_addrecords')
681
+ ->setStore($this->getStore())
682
+ ->execute(array(
683
+ 'sessionId' => $this->getSessionId(),
684
+ 'records' => $data
685
+ ));
686
+
687
+ if ($response->isSuccessful()) {
688
+
689
+ $skipped_record_ids = array();
690
+ if ($skipped_records = $response->getSkippedRecords()) {
691
+ $skipped_record_ids = array_flip($skipped_records["index"]);
692
+ }
693
+
694
+ $sync_time = Mage::helper("klevu_search/compat")->now();
695
+
696
+ foreach($data as $i => &$record) {
697
+ if (isset($skipped_record_ids[$i])) {
698
+ unset($data[$i]);
699
+ continue;
700
+ }
701
+
702
+ $ids = Mage::helper("klevu_search")->getMagentoProductId($data[$i]['id']);
703
+
704
+ $record = array(
705
+ $ids["product_id"],
706
+ $ids["parent_id"],
707
+ $this->getStore()->getId(),
708
+ $this->isTestModeEnabled(),
709
+ $sync_time
710
+ );
711
+ }
712
+
713
+ $this->getConnection()->insertArray(
714
+ $this->getTableName('klevu_search/product_sync'),
715
+ array("product_id", "parent_id", "store_id", "test_mode", "last_synced_at"),
716
+ $data
717
+ );
718
+
719
+ $skipped_count = count($skipped_record_ids);
720
+ if ($skipped_count > 0) {
721
+ return sprintf("%d product%s failed (%s)",
722
+ $skipped_count,
723
+ ($skipped_count > 1) ? "s" : "",
724
+ implode(", ", $skipped_records["messages"])
725
+ );
726
+ } else {
727
+ return true;
728
+ }
729
+ } else {
730
+ return sprintf("%d product%s failed (%s)",
731
+ $total,
732
+ ($total > 1) ? "s" : "",
733
+ $response->getMessage()
734
+ );
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Add the Product Sync data to each product in the given list. Updates the given
740
+ * list directly to save memory.
741
+ *
742
+ * @param array $products An array of products. Each element should be an array with
743
+ * containing an element with "id" as the key and the product
744
+ * ID as the value.
745
+ *
746
+ * @return $this
747
+ */
748
+ protected function addProductSyncData(&$products) {
749
+ $product_ids = array();
750
+ $parent_ids = array();
751
+ foreach ($products as $product) {
752
+ $product_ids[] = $product['product_id'];
753
+ if ($product['parent_id'] != 0) {
754
+ $product_ids[] = $product['parent_id'];
755
+ $parent_ids[] = $product['parent_id'];
756
+ }
757
+ }
758
+ $product_ids = array_unique($product_ids);
759
+ $parent_ids = array_unique($parent_ids);
760
+
761
+ $data = Mage::getModel('catalog/product')->getCollection()
762
+ ->addIdFilter($product_ids)
763
+ ->setStore($this->getStore())
764
+ ->addStoreFilter()
765
+ ->addAttributeToSelect($this->getUsedMagentoAttributes());
766
+
767
+ $data->load()
768
+ ->addCategoryIds();
769
+
770
+ $url_rewrite_data = $this->getUrlRewriteData($product_ids);
771
+ $visibility_data = $this->getVisibilityData($product_ids);
772
+ $price_data = $this->getPriceData($product_ids);
773
+ $configurable_price_data = $this->getConfigurablePriceData($parent_ids);
774
+
775
+ $stock_data = $this->getStockData($product_ids);
776
+
777
+ $attribute_map = $this->getAttributeMap();
778
+ $base_url = $this->getStore()->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_LINK);
779
+ $currency = $this->getStore()->getDefaultCurrencyCode();
780
+ $media_url = $this->getStore()->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA);
781
+ $media_url .= Mage::getModel('catalog/product_media_config')->getBaseMediaUrlAddition();
782
+
783
+ foreach ($products as $index => &$product) {
784
+ $item = $data->getItemById($product['product_id']);
785
+ $parent = ($product['parent_id'] != 0) ? $data->getItemById($product['parent_id']) : null;
786
+
787
+ if (!$item) {
788
+ // Product data query did not return any data for this product
789
+ // Remove it from the list to skip syncing it
790
+ $this->log(Zend_Log::WARN, sprintf("Failed to retrieve data for product ID %d", $product['product_id']));
791
+ unset($products[$index]);
792
+ continue;
793
+ }
794
+
795
+ // Add data from mapped attributes
796
+ foreach ($attribute_map as $key => $attributes) {
797
+ $product[$key] = null;
798
+
799
+ switch ($key) {
800
+ case "boostingAttribute":
801
+ foreach ($attributes as $attribute) {
802
+ if ($parent && $parent->getData($attribute)) {
803
+ $product[$key] = $parent->getData($attribute);
804
+ break;
805
+ } else {
806
+ $product[$key] = $item->getData($attribute);
807
+ break;
808
+ }
809
+ }
810
+ break;
811
+ case "otherAttributeToIndex":
812
+ case "other":
813
+ $product[$key] = array();
814
+ foreach ($attributes as $attribute) {
815
+ if ($item->getData($attribute)) {
816
+ $product[$key][$attribute] = $this->getAttributeData($attribute, $item->getData($attribute));
817
+ } else if ($parent && $parent->getData($attribute)) {
818
+ $product[$key][$attribute] = $this->getAttributeData($attribute, $parent->getData($attribute));
819
+ }
820
+ }
821
+ break;
822
+ case "sku":
823
+ foreach ($attributes as $attribute) {
824
+ if ($parent && $parent->getData($attribute)) {
825
+ $product[$key] = Mage::helper('klevu_search')->getKlevuProductSku($item->getData($attribute), $parent->getData($attribute));
826
+ break;
827
+ } else {
828
+ $product[$key] = $item->getData($attribute);
829
+ break;
830
+ }
831
+ }
832
+ break;
833
+ case "name":
834
+ foreach ($attributes as $attribute) {
835
+ if ($parent && $parent->getData($attribute)) {
836
+ $product[$key] = $parent->getData($attribute);
837
+ break;
838
+ }else if ($item->getData($attribute)) {
839
+ $product[$key] = $item->getData($attribute);
840
+ break;
841
+ }
842
+ }
843
+ break;
844
+ case "image":
845
+ foreach ($attributes as $attribute) {
846
+ if ($item->getData($attribute) && $item->getData($attribute) != "no_selection") {
847
+ $product[$key] = $item->getData($attribute);
848
+ break;
849
+ } else if ($parent && $parent->getData($attribute) && $parent->getData($attribute) != "no_selection") {
850
+ $product[$key] = $parent->getData($attribute);
851
+ break;
852
+ }
853
+ }
854
+ if ($product[$key] != "" && strpos($product[$key], "http") !== 0) {
855
+ // Prepend media base url for relative image locations
856
+ $imageResized = Mage::getBaseDir('media').DS."klevu_images".$product[$key];
857
+ if (file_exists($imageResized)) {
858
+ $product[$key] = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA)."klevu_images".$product[$key];
859
+ }else{
860
+ $product[$key] = $media_url . $product[$key];
861
+ }
862
+ }
863
+ break;
864
+ case "salePrice":
865
+ // Default to 0 if price can't be determined
866
+ $product['salePrice'] = 0;
867
+ $tax_class_id = "";
868
+ if ($item->getData("tax_class_id") !== null) {
869
+ $tax_class_id = $item->getData("tax_class_id");
870
+ } else if ($parent) {
871
+ $tax_class_id = $parent->getData("tax_class_id");
872
+ }else {
873
+ $tax_class_id = "";
874
+ }
875
+
876
+ if ($parent && $parent->getData("type_id") == Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
877
+ // Calculate configurable product price based on option values
878
+ $price = (isset($price_data[$product['parent_id']])) ? $price_data[$product['parent_id']]['min_price'] : $parent->getData("price");
879
+ $markup = 0;
880
+
881
+ if (isset($configurable_price_data[$product['parent_id']])) {
882
+ foreach ($configurable_price_data[$product['parent_id']] as $attribute => $pricing_data) {
883
+ $value = $item->getData($attribute);
884
+ if ($value && isset($pricing_data[$value])) {
885
+ if ($pricing_data[$value]["is_percent"]) {
886
+ $markup += $price * ($pricing_data[$value]["value"] / 100);
887
+ } else {
888
+ $markup += $pricing_data[$value]["value"];
889
+ }
890
+ }
891
+ }
892
+ }
893
+ // show low price for config products
894
+ $product['startPrice'] = $this->processPrice($price , $tax_class_id, $parent);
895
+ } else {
896
+ // Use price index prices to set the product price and start/end prices if available
897
+ // Falling back to product price attribute if not
898
+ if (isset($price_data[$product['product_id']])) {
899
+ // Always use minimum price as the sale price as it's the most accurate
900
+ $product['salePrice'] = $this->processPrice($price_data[$product['product_id']]['min_price'], $tax_class_id, $item);
901
+ if ($price_data[$product['product_id']]['min_price'] != $price_data[$product['product_id']]['max_price']) {
902
+ $product['startPrice'] = $this->processPrice($price_data[$product['product_id']]['min_price'], $tax_class_id, $item);
903
+
904
+ // Maximum price on a grouped product is meaningless as it depends on quantity and items bought
905
+ if ($item->getData('type_id') != Mage_Catalog_Model_Product_Type::TYPE_GROUPED) {
906
+ $product['toPrice'] = $this->processPrice($price_data[$product['product_id']]['max_price'], $tax_class_id, $item);
907
+ }
908
+ }
909
+ } else {
910
+ if ($item->getData("price") !== null) {
911
+ $product["salePrice"] = $this->processPrice($item->getData("price"), $tax_class_id, $item);
912
+ } else if ($parent) {
913
+ $product["salePrice"] = $this->processPrice($parent->getData("price"), $tax_class_id, $item);
914
+ }
915
+ }
916
+ }
917
+
918
+ break;
919
+ default:
920
+ foreach ($attributes as $attribute) {
921
+ if ($item->getData($attribute)) {
922
+ $product[$key] = $this->getAttributeData($attribute, $item->getData($attribute));
923
+ break;
924
+ } else if ($parent && $parent->getData($attribute)) {
925
+ $product[$key] = $this->getAttributeData($attribute, $parent->getData($attribute));
926
+ break;
927
+ }
928
+ }
929
+ }
930
+ }
931
+
932
+ // Add non-attribute data
933
+ $product['currency'] = $currency;
934
+
935
+ if ($item->getCategoryIds()) {
936
+ $product['category'] = $this->getLongestPathCategoryName($item->getCategoryIds());
937
+ $product['listCategory'] = $this->getCategoryNames($item->getCategoryIds());
938
+ } else if ($parent) {
939
+ $product['category'] = $this->getLongestPathCategoryName($parent->getCategoryIds());
940
+ $product['listCategory'] = $this->getCategoryNames($parent->getCategoryIds());
941
+ } else {
942
+ $product['category'] = "";
943
+ $product['listCategory'] = "";
944
+ }
945
+
946
+
947
+ // Use the parent URL if the product is invisible (and has a parent) and
948
+ // use a URL rewrite if one exists, falling back to catalog/product/view
949
+ if (isset($visibility_data[$product['product_id']]) && !$visibility_data[$product['product_id']] && $parent) {
950
+ $product['url'] = $base_url . (
951
+ (isset($url_rewrite_data[$product['parent_id']])) ?
952
+ $url_rewrite_data[$product['parent_id']] :
953
+ "catalog/product/view/id/" . $product['parent_id']
954
+ );
955
+ } else {
956
+ if($parent) {
957
+ $product['url'] = $base_url . (
958
+ (isset($url_rewrite_data[$product['parent_id']])) ?
959
+ $url_rewrite_data[$product['parent_id']] :
960
+ "catalog/product/view/id/" . $product['parent_id']
961
+ );
962
+ } else {
963
+ $product['url'] = $base_url . (
964
+ (isset($url_rewrite_data[$product['product_id']])) ?
965
+ $url_rewrite_data[$product['product_id']] :
966
+ "catalog/product/view/id/" . $product['product_id']
967
+ );
968
+ }
969
+ }
970
+
971
+ // Add stock data
972
+ $product['inStock'] = ($stock_data[$product['product_id']]) ? "yes" : "no";
973
+
974
+ // Configurable product relation
975
+ if ($product['parent_id'] != 0) {
976
+ $product['itemGroupId'] = $product['parent_id'];
977
+ }
978
+
979
+ // Set ID data
980
+ $product['id'] = Mage::helper('klevu_search')->getKlevuProductId($product['product_id'], $product['parent_id']);
981
+ unset($product['product_id']);
982
+ unset($product['parent_id']);
983
+ }
984
+
985
+ return $this;
986
+ }
987
+
988
+ /**
989
+ * Return the URL rewrite data for the given products for the current store.
990
+ *
991
+ * @param array $product_ids A list of product IDs.
992
+ *
993
+ * @return array A list with product IDs as keys and request paths as values.
994
+ */
995
+ protected function getUrlRewriteData($product_ids) {
996
+ $stmt = $this->getConnection()->query(
997
+ Mage::helper('klevu_search/compat')->getProductUrlRewriteSelect($product_ids, 0, $this->getStore()->getId())
998
+ );
999
+
1000
+ $url_suffix = Mage::helper('catalog/product')->getProductUrlSuffix($this->getStore()->getId());
1001
+ if ($url_suffix && substr($url_suffix, 0, 1) !== ".") {
1002
+ $url_suffix = "." . $url_suffix;
1003
+ }
1004
+
1005
+ $data = array();
1006
+ while ($row = $stmt->fetch()) {
1007
+ if (!isset($data[$row['product_id']])) {
1008
+ $data[$row['product_id']] = $row['request_path'];
1009
+ // Append the product URL suffix if the rewrite does not have one already
1010
+ if ($url_suffix && substr($row['request_path'], -1 * strlen($url_suffix)) !== $url_suffix) {
1011
+ $data[$row['product_id']] .= $url_suffix;
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ return $data;
1017
+ }
1018
+
1019
+ /**
1020
+ * Return the visibility data for the given products for the current store.
1021
+ *
1022
+ * @param array $product_ids A list of product IDs.
1023
+ *
1024
+ * @return array A list with product IDs as keys and boolean visibility values.
1025
+ */
1026
+ protected function getVisibilityData($product_ids) {
1027
+ $stmt = $this->getConnection()->query(
1028
+ $this->getConnection()
1029
+ ->select()
1030
+ ->from(
1031
+ array('p' => $this->getTableName("catalog/product")),
1032
+ array(
1033
+ 'product_id' => "p.entity_id"
1034
+ )
1035
+ )
1036
+ ->joinLeft(
1037
+ array('vs' => $this->getProductVisibilityAttribute()->getBackendTable()),
1038
+ "vs.attribute_id = :visibility_attribute_id AND vs.entity_id = p.entity_id AND vs.store_id = :store_id",
1039
+ ""
1040
+ )
1041
+ ->joinLeft(
1042
+ array('vd' => $this->getProductVisibilityAttribute()->getBackendTable()),
1043
+ "vd.attribute_id = :visibility_attribute_id AND vd.entity_id = p.entity_id AND vd.store_id = :default_store_id",
1044
+ array(
1045
+ "visibility" => new Zend_Db_Expr("IF(vs.value IS NOT NULL, vs.value, vd.value)")
1046
+ )
1047
+ )
1048
+ ->where("p.entity_id IN (?)", $product_ids),
1049
+ array(
1050
+ "visibility_attribute_id" => $this->getProductVisibilityAttribute()->getId(),
1051
+ "store_id" => $this->getStore()->getId(),
1052
+ "default_store_id" => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID
1053
+ )
1054
+ );
1055
+
1056
+ $data = array();
1057
+ while ($row = $stmt->fetch()) {
1058
+ $data[$row['product_id']] = ($row['visibility'] != Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE) ? true : false;
1059
+ }
1060
+
1061
+ return $data;
1062
+ }
1063
+
1064
+ /**
1065
+ * Return the "Is in stock?" flags for the given products.
1066
+ * Considers if the stock is managed on the product or per store when deciding if a product
1067
+ * is in stock.
1068
+ *
1069
+ * @param array $product_ids A list of product IDs.
1070
+ *
1071
+ * @return array A list with product IDs as keys and "Is in stock?" booleans as values.
1072
+ */
1073
+ protected function getStockData($product_ids) {
1074
+ $stmt = $this->getConnection()->query(
1075
+ $this->getConnection()
1076
+ ->select()
1077
+ ->from(
1078
+ array('s' => $this->getTableName("cataloginventory/stock_item")),
1079
+ array(
1080
+ 'product_id' => "s.product_id",
1081
+ 'in_stock' => "s.is_in_stock",
1082
+ 'manage_stock' => "s.manage_stock",
1083
+ 'use_config' => "s.use_config_manage_stock"
1084
+ )
1085
+ )
1086
+ ->where("s.product_id IN (?)", $product_ids)
1087
+ );
1088
+
1089
+ $data = array();
1090
+ while ($row = $stmt->fetch()) {
1091
+ if (($row['use_config'] && $this->getStoreManageStock()) || (!$row['use_config'] && $row['manage_stock'])) {
1092
+ $data[$row['product_id']] = ($row['in_stock']) ? true : false;
1093
+ } else {
1094
+ $data[$row['product_id']] = true;
1095
+ }
1096
+ }
1097
+
1098
+ return $data;
1099
+ }
1100
+
1101
+ /**
1102
+ * Return the price information from the price index for the given products.
1103
+ *
1104
+ * @param $product_ids
1105
+ *
1106
+ * @return array
1107
+ */
1108
+ protected function getPriceData($product_ids) {
1109
+ $stmt = $this->getConnection()->query(
1110
+ $this->getConnection()
1111
+ ->select()
1112
+ ->from(
1113
+ array('p' => $this->getTableName("catalog/product_index_price")),
1114
+ array(
1115
+ 'product_id' => "p.entity_id",
1116
+ 'price' => "p.price",
1117
+ 'final_price' => "p.final_price",
1118
+ 'min_price' => "p.min_price",
1119
+ 'max_price' => "p.max_price"
1120
+ )
1121
+ )
1122
+ ->where("p.website_id = ?", $this->getStore()->getWebsiteId())
1123
+ ->where("p.customer_group_id = ?", Mage_Customer_Model_Group::NOT_LOGGED_IN_ID)
1124
+ ->where("p.entity_id IN (?)", $product_ids)
1125
+ );
1126
+
1127
+ $data = array();
1128
+ while ($row = $stmt->fetch()) {
1129
+ $data[$row['product_id']] = $row;
1130
+ }
1131
+
1132
+ return $data;
1133
+ }
1134
+
1135
+ /**
1136
+ * Return the configurable price information (price markup for each value of each configurable
1137
+ * attribute) for the given configurable product IDs.
1138
+ *
1139
+ * @param $parent_ids
1140
+ *
1141
+ * @return array
1142
+ */
1143
+ protected function getConfigurablePriceData($parent_ids) {
1144
+ $default_website_id = Mage::app()->getStore(Mage_Core_Model_Store::ADMIN_CODE)->getWebsiteId();
1145
+ $store_website_id = $this->getStore()->getWebsiteId();
1146
+ $sort_order = ($default_website_id > $store_website_id) ? Varien_Db_Select::SQL_ASC : Varien_Db_Select::SQL_DESC;
1147
+
1148
+ $stmt = $this->getConnection()->query(
1149
+ $this->getConnection()
1150
+ ->select()
1151
+ ->from(array("s" => $this->getTableName("catalog/product_super_attribute")), "")
1152
+ ->join(array("a" => $this->getTableName("eav/attribute")), "s.attribute_id = a.attribute_id", "")
1153
+ ->join(array("p" => $this->getTableName("catalog/product_super_attribute_pricing")), "s.product_super_attribute_id = p.product_super_attribute_id", "")
1154
+ ->columns(array(
1155
+ "parent_id" => "s.product_id",
1156
+ "attribute_code" => "a.attribute_code",
1157
+ "attribute_value" => "p.value_index",
1158
+ "price_is_percent" => "p.is_percent",
1159
+ "price_value" => "p.pricing_value"
1160
+ ))
1161
+ ->where("s.product_id IN (?)", $parent_ids)
1162
+ ->where("p.website_id IN (?)", array($default_website_id, $store_website_id))
1163
+ ->order(array(
1164
+ "s.product_id " . Varien_Db_Select::SQL_ASC,
1165
+ "a.attribute_code " . Varien_Db_Select::SQL_ASC,
1166
+ "p.website_id " . $sort_order
1167
+ ))
1168
+ ->group(array("s.product_id", "a.attribute_code", "p.value_index"))
1169
+ );
1170
+
1171
+ $data = array();
1172
+ while ($row = $stmt->fetch()) {
1173
+ if (!isset($data[$row["parent_id"]])) {
1174
+ $data[$row["parent_id"]] = array();
1175
+ }
1176
+ if (!isset($data[$row["parent_id"]][$row["attribute_code"]])) {
1177
+ $data[$row["parent_id"]][$row["attribute_code"]] = array();
1178
+ }
1179
+ if (!isset($data[$row["parent_id"]][$row["attribute_code"]][$row["attribute_value"]])) {
1180
+ $data[$row["parent_id"]][$row["attribute_code"]][$row["attribute_value"]] = array(
1181
+ "is_percent" => ($row["price_is_percent"]) ? true : false,
1182
+ "value" => $row["price_value"]
1183
+ );
1184
+ }
1185
+ }
1186
+
1187
+ return $data;
1188
+ }
1189
+
1190
+ /**
1191
+ * Return a map of Klevu attributes to Magento attributes.
1192
+ *
1193
+ * @return array
1194
+ */
1195
+ protected function getAttributeMap() {
1196
+ if (!$this->hasData('attribute_map')) {
1197
+ $attribute_map = array();
1198
+
1199
+ $automatic_attributes = $this->getAutomaticAttributes();
1200
+ $attribute_map = $this->prepareAttributeMap($attribute_map, $automatic_attributes);
1201
+
1202
+ $additional_attributes = Mage::helper('klevu_search/config')->getAdditionalAttributesMap($this->getStore());
1203
+ $attribute_map = $this->prepareAttributeMap($attribute_map, $additional_attributes);
1204
+
1205
+
1206
+ // Add otherAttributeToIndex to $attribute_map.
1207
+ $otherAttributeToIndex = Mage::helper('klevu_search/config')->getOtherAttributesToIndex($this->getStore());
1208
+ if(!empty($otherAttributeToIndex)) {
1209
+ $attribute_map['otherAttributeToIndex'] = $otherAttributeToIndex;
1210
+ }
1211
+ // Add boostingAttribute to $attribute_map.
1212
+ if(($boosting_attribute = Mage::helper('klevu_search/config')->getBoostingAttribute($this->getStore())) && !is_null($boosting_attribute)) {
1213
+ $attribute_map['boostingAttribute'][] = $boosting_attribute;
1214
+ }
1215
+
1216
+ $this->setData('attribute_map', $attribute_map);
1217
+ }
1218
+
1219
+ return $this->getData('attribute_map');
1220
+ }
1221
+
1222
+ /**
1223
+ * Returns an array of all automatically matched attributes. Includes defaults and filterable in search attributes.
1224
+ * @return array
1225
+ */
1226
+ public function getAutomaticAttributes() {
1227
+ if(!$this->hasData('automatic_attributes')) {
1228
+ // Default mapped attributes
1229
+ $default_attributes = Mage::helper('klevu_search/config')->getDefaultMappedAttributes();
1230
+ $attributes = array();
1231
+ for($i = 0; $i < count($default_attributes['klevu_attribute']); $i++) {
1232
+ $attributes[] = array(
1233
+ 'klevu_attribute' => $default_attributes['klevu_attribute'][$i],
1234
+ 'magento_attribute' => $default_attributes['magento_attribute'][$i]
1235
+ );
1236
+ }
1237
+
1238
+ // Get all layered navigation / filterable in search attributes
1239
+ foreach($this->getLayeredNavigationAttributes() as $layeredAttribute) {
1240
+ $attributes[] = array (
1241
+ 'klevu_attribute' => 'other',
1242
+ 'magento_attribute' => $layeredAttribute
1243
+ );
1244
+ }
1245
+
1246
+ $this->setData('automatic_attributes', $attributes);
1247
+ // Update the store system config with the updated automatic attributes map.
1248
+ Mage::helper('klevu_search/config')->setAutomaticAttributesMap($attributes, $this->getStore());
1249
+ }
1250
+
1251
+ return $this->getData('automatic_attributes');
1252
+ }
1253
+
1254
+ /**
1255
+ * Takes system configuration attribute data and adds to $attribute_map
1256
+ * @param $attribute_map
1257
+ * @param $additional_attributes
1258
+ * @return array
1259
+ */
1260
+ protected function prepareAttributeMap($attribute_map, $additional_attributes) {
1261
+
1262
+ foreach ($additional_attributes as $mapping) {
1263
+ if (!isset($attribute_map[$mapping['klevu_attribute']])) {
1264
+ $attribute_map[$mapping['klevu_attribute']] = array();
1265
+ }
1266
+ $attribute_map[$mapping['klevu_attribute']][] = $mapping['magento_attribute'];
1267
+ }
1268
+ return $attribute_map;
1269
+ }
1270
+
1271
+ /**
1272
+ * Return the attribute codes for all filterable in search attributes.
1273
+ * @return array
1274
+ */
1275
+ protected function getLayeredNavigationAttributes() {
1276
+ $attributes = Mage::helper('klevu_search/config')->getDefaultMappedAttributes();
1277
+ $select = $this->getConnection()
1278
+ ->select()
1279
+ ->from(
1280
+ array("a" => $this->getTableName("eav/attribute")),
1281
+ array("attribute" => "a.attribute_code")
1282
+ )
1283
+ ->join(
1284
+ array("ca" => $this->getTableName("catalog/eav_attribute")),
1285
+ "ca.attribute_id = a.attribute_id",
1286
+ ""
1287
+ )
1288
+ // Only if the attribute is filterable in search, i.e. attribute appears in search layered navigation.
1289
+ ->where("ca.is_filterable_in_search = ?", "1")
1290
+ // Make sure we exclude the attributes thar synced by default.
1291
+ ->where("a.attribute_code NOT IN(?)", array_unique($attributes['magento_attribute']))
1292
+ ->group(array("attribute_code"));
1293
+
1294
+ return $this->getConnection()->fetchCol($select);
1295
+ }
1296
+
1297
+ /**
1298
+ * Return the attribute codes for all attributes currently used in
1299
+ * configurable products.
1300
+ *
1301
+ * @return array
1302
+ */
1303
+ protected function getConfigurableAttributes() {
1304
+ $select = $this->getConnection()
1305
+ ->select()
1306
+ ->from(
1307
+ array("a" => $this->getTableName("eav/attribute")),
1308
+ array("attribute" => "a.attribute_code")
1309
+ )
1310
+ ->join(
1311
+ array("s" => $this->getTableName("catalog/product_super_attribute")),
1312
+ "a.attribute_id = s.attribute_id",
1313
+ ""
1314
+ )
1315
+ ->group(array("a.attribute_code"));
1316
+
1317
+ return $this->getConnection()->fetchCol($select);
1318
+ }
1319
+
1320
+ /**
1321
+ * Return a list of all Magento attributes that are used by Product Sync
1322
+ * when collecting product data.
1323
+ *
1324
+ * @return array
1325
+ */
1326
+ protected function getUsedMagentoAttributes() {
1327
+ $result = array();
1328
+
1329
+ foreach ($this->getAttributeMap() as $attributes) {
1330
+ $result = array_merge($result, $attributes);
1331
+ }
1332
+
1333
+ $result = array_merge($result, $this->getConfigurableAttributes());
1334
+
1335
+ return array_unique($result);
1336
+ }
1337
+
1338
+ /**
1339
+ * Return an array of category paths for all the categories in the
1340
+ * current store, not including the store root.
1341
+ *
1342
+ * @return array A list of category paths where each key is a category
1343
+ * ID and each value is an array of category names for
1344
+ * each category in the path, the last element being the
1345
+ * name of the category referenced by the ID.
1346
+ */
1347
+ protected function getCategoryPaths() {
1348
+ if (!$category_paths = $this->getData('category_paths')) {
1349
+ $category_paths = array();
1350
+ $rootId = $this->getStore()->getRootCategoryId();
1351
+ $collection = Mage::getResourceModel('catalog/category_collection')
1352
+ ->setStoreId($this->getStore()->getId())
1353
+ ->addFieldToFilter('level', array('gt' => 1))
1354
+ ->addFieldToFilter('path', array('like'=> "1/$rootId/%"))
1355
+ ->addNameToResult();
1356
+
1357
+ foreach ($collection as $category) {
1358
+ $category_paths[$category->getId()] = array();
1359
+
1360
+ $path_ids = $category->getPathIds();
1361
+ foreach ($path_ids as $id) {
1362
+ if ($item = $collection->getItemById($id)) {
1363
+ $category_paths[$category->getId()][] = $item->getName();
1364
+ }
1365
+ }
1366
+ }
1367
+
1368
+ $this->setData('category_paths', $category_paths);
1369
+ }
1370
+
1371
+ return $category_paths;
1372
+ }
1373
+
1374
+ /**
1375
+ * Return a list of the names of all the categories in the
1376
+ * paths of the given categories (including the given categories)
1377
+ * up to, but not including the store root.
1378
+ *
1379
+ * @param array $categories
1380
+ *
1381
+ * @return array
1382
+ */
1383
+ protected function getCategoryNames(array $categories) {
1384
+ $category_paths = $this->getCategoryPaths();
1385
+
1386
+ $result = array();
1387
+ foreach ($categories as $category) {
1388
+ if (isset($category_paths[$category])) {
1389
+ $result = array_merge($result, $category_paths[$category]);
1390
+ }
1391
+ }
1392
+
1393
+ return array_unique($result);
1394
+ }
1395
+
1396
+ /**
1397
+ * Given a list of category IDs, return the name of the category
1398
+ * in that list that has the longest path.
1399
+ *
1400
+ * @param array $categories
1401
+ *
1402
+ * @return string
1403
+ */
1404
+ protected function getLongestPathCategoryName(array $categories) {
1405
+ $category_paths = $this->getCategoryPaths();
1406
+
1407
+ $length = 0;
1408
+ $name = "";
1409
+ foreach ($categories as $id) {
1410
+ if (isset($category_paths[$id])) {
1411
+ if (count($category_paths[$id]) > $length) {
1412
+ $length = count($category_paths[$id]);
1413
+ $name = end($category_paths[$id]);
1414
+ }
1415
+ }
1416
+ }
1417
+
1418
+ return $name;
1419
+ }
1420
+
1421
+ /**
1422
+ * Returns either array containing the label and value(s) of an attribute, or just the given value
1423
+ *
1424
+ * In the case that there are multiple options selected, all values are returned
1425
+ *
1426
+ * @param string $code
1427
+ * @param null $value
1428
+ *
1429
+ * @return array|string
1430
+ */
1431
+ protected function getAttributeData($code, $value = null) {
1432
+ if (!$attribute_data = $this->getData('attribute_data')) {
1433
+ $attribute_data = array();
1434
+
1435
+ $collection = Mage::getResourceModel('catalog/product_attribute_collection')
1436
+ ->addFieldToFilter('attribute_code', array('in' => $this->getUsedMagentoAttributes()));
1437
+
1438
+ foreach ($collection as $attr) {
1439
+ $attr->setStoreId($this->getStore()->getId());
1440
+ $attribute_data[$attr->getAttributeCode()] = array(
1441
+ 'label' => $attr->getFrontendLabel(),
1442
+ 'values' => ''
1443
+ );
1444
+
1445
+ if ($attr->usesSource()) {
1446
+ // $attribute_data[$attr->getAttributeCode()] = array();
1447
+ foreach($attr->getSource()->getAllOptions(false) as $option) {
1448
+ if (is_array($option['value'])) {
1449
+ foreach ($option['value'] as $sub_option) {
1450
+ if (strlen($sub_option)) {
1451
+ $attribute_data[$attr->getAttributeCode()]['values'][$sub_option['value']] = $sub_option['label'];
1452
+ }
1453
+ }
1454
+ } else {
1455
+ $attribute_data[$attr->getAttributeCode()]['values'][$option['value']] = $option['label'];
1456
+ }
1457
+ }
1458
+ }
1459
+ }
1460
+
1461
+ $this->setData('attribute_data', $attribute_data);
1462
+ }
1463
+
1464
+ // make sure the attribute exists
1465
+ if (isset($attribute_data[$code])) {
1466
+ // was $value passed a parameter?
1467
+ if (!is_null($value)) {
1468
+ // If not values are set on attribute_data for the attribute, return just the value passed. (attributes like: name, description etc)
1469
+ if(empty($attribute_data[$code]['values'])) {
1470
+ return $value;
1471
+ }
1472
+ // break up our value into an array by a comma, this is for catching multiple select attributes.
1473
+ $values = explode(",", $value);
1474
+
1475
+ // loop over our array of attribute values
1476
+ foreach ($values as $key => $valueOption) {
1477
+ // if there is a value on the attribute_data use that value (it will be the label for a dropdown select attribute)
1478
+ if (isset($attribute_data[$code]['values'][$valueOption])) {
1479
+ $values[$key] = $attribute_data[$code]['values'][$valueOption];
1480
+ } else { // If no label was found, log an error and unset the value.
1481
+ Mage::helper('klevu_search')->log(Zend_Log::WARN, sprintf("Attribute: %s option label was not found, option ID provided: %s", $code, $valueOption));
1482
+ unset($values[$key]);
1483
+ }
1484
+ }
1485
+
1486
+ // If there was only one value in the array, return the first (select menu, single option), or if there was more, return them all (multi-select).
1487
+ if (count($values) == 1) {
1488
+ $attribute_data[$code]['values'] = $values[0];
1489
+ } else {
1490
+ $attribute_data[$code]['values'] = $values;
1491
+ }
1492
+
1493
+ }
1494
+ return $attribute_data[$code];
1495
+ }
1496
+
1497
+ $result['label'] = $code;
1498
+ $result['values'] = $value;
1499
+ return $result;
1500
+ }
1501
+
1502
+ /**
1503
+ * Apply tax to the given price, if needed, remove if not.
1504
+ *
1505
+ * @param float $price
1506
+ * @param int $tax_class_id The tax class to use.
1507
+ *
1508
+ * @return float
1509
+ */
1510
+ protected function applyTax($price, $tax_class_id) {
1511
+ if ($this->usePriceInclTax()) {
1512
+ if (!$this->priceIncludesTax()) {
1513
+ // We need to include tax in the price
1514
+ $price += $this->calcTaxAmount($price, $tax_class_id, false);
1515
+ }
1516
+ } else {
1517
+ if ($this->priceIncludesTax()) {
1518
+ // Price includes tax, but we don't need it
1519
+ $price -= $this->calcTaxAmount($price, $tax_class_id, true);
1520
+ }
1521
+ }
1522
+
1523
+ return $price;
1524
+ }
1525
+
1526
+ /**
1527
+ * Calculate the amount of tax on the given price.
1528
+ *
1529
+ * @param $price
1530
+ * @param $tax_class_id
1531
+ * @param bool $price_includes_tax
1532
+ *
1533
+ * @return float
1534
+ */
1535
+ protected function calcTaxAmount($price, $tax_class_id, $price_includes_tax = false) {
1536
+ $calc = Mage::getSingleton("tax/calculation");
1537
+
1538
+ if (!$tax_rates = $this->getData("tax_rates")) {
1539
+ // Get tax rates for the default destination
1540
+ $tax_rates = $calc->getRatesForAllProductTaxClasses($calc->getRateOriginRequest($this->getStore()));
1541
+ $this->setData("tax_rates", $tax_rates);
1542
+ }
1543
+
1544
+ if (isset($tax_rates[$tax_class_id])) {
1545
+ return $calc->calcTaxAmount($price, $tax_rates[$tax_class_id], $price_includes_tax);
1546
+ }
1547
+
1548
+ return 0.0;
1549
+ }
1550
+
1551
+ /**
1552
+ * Convert the given price into the current store currency.
1553
+ *
1554
+ * @param $price
1555
+ *
1556
+ * @return float
1557
+ */
1558
+ protected function convertPrice($price) {
1559
+ return $this->getStore()->convertPrice($price, false);
1560
+ }
1561
+
1562
+ /**
1563
+ * Process the given product price for using in Product Sync.
1564
+ * Applies tax, if needed, and converts to the currency of the current store.
1565
+ *
1566
+ * @param $price
1567
+ * @param $tax_class_id
1568
+ * @param product object
1569
+ *
1570
+ * @return float
1571
+ */
1572
+ protected function processPrice($price, $tax_class_id, $pro) {
1573
+ if($price < 0){$price = 0;}else{$price = $price;}
1574
+ $config = Mage::helper('klevu_search/config');
1575
+ if($config->isTaxEnabled($this->getStore()->getId())) {
1576
+ return $this->convertPrice(Mage::helper("tax")->getPrice($pro, $price, true, null, null, null, $this->getStore(),false));
1577
+ } else {
1578
+ return $this->convertPrice($price);
1579
+ }
1580
+ }
1581
+
1582
+ /**
1583
+ * Return the "Manage Stock" flag for the current store.
1584
+ *
1585
+ * @return int
1586
+ */
1587
+ protected function getStoreManageStock() {
1588
+ if (!$this->hasData('store_manage_stock')) {
1589
+ $this->setData('store_manage_stock', intval(Mage::getStoreConfig(Mage_CatalogInventory_Model_Stock_Item::XML_PATH_MANAGE_STOCK, $this->getStore())));
1590
+ }
1591
+
1592
+ return $this->getData('store_manage_stock');
1593
+ }
1594
+
1595
+ /**
1596
+ * Return the "Display Out of Stock Products".
1597
+ *
1598
+ * @return bool
1599
+ */
1600
+ protected function getShowOutOfStock() {
1601
+ if (!$this->hasData('show_out_of_stock')) {
1602
+ $this->setData('show_out_of_stock', Mage::helper('cataloginventory')->isShowOutOfStock());
1603
+ }
1604
+
1605
+ return $this->getData('show_out_of_stock');
1606
+ }
1607
+
1608
+ /**
1609
+ * Check if the Test Mode is enabled for the current store.
1610
+ *
1611
+ * @return int 1 if Test Mode is enabled, 0 otherwise.
1612
+ */
1613
+ protected function isTestModeEnabled() {
1614
+ if (!$this->hasData("test_mode_enabled")) {
1615
+ $test_mode = Mage::helper("klevu_search/config")->isTestModeEnabled($this->getStore());
1616
+ $test_mode = ($test_mode) ? 1 : 0;
1617
+ $this->setData("test_mode_enabled", $test_mode);
1618
+ }
1619
+
1620
+ return $this->getData("test_mode_enabled");
1621
+ }
1622
+
1623
+ /**
1624
+ * Check if product price includes tax for the current store.
1625
+ *
1626
+ * @return bool
1627
+ */
1628
+ protected function priceIncludesTax() {
1629
+ if (!$this->hasData("price_includes_tax")) {
1630
+ $this->setData("price_includes_tax", Mage::getModel("tax/config")->priceIncludesTax($this->getStore()));
1631
+ }
1632
+
1633
+ return $this->getData("price_includes_tax");
1634
+ }
1635
+
1636
+ /**
1637
+ * Check if product prices should include tax when synced for the current store.
1638
+ *
1639
+ * @return bool
1640
+ */
1641
+ protected function usePriceInclTax() {
1642
+ if (!$this->hasData("use_price_incl_tax")) {
1643
+ // Include tax in prices in all cases except when
1644
+ // catalog prices exclude tax
1645
+ $value = true;
1646
+
1647
+ if (Mage::getModel("tax/config")->getPriceDisplayType($this->getStore()) == Mage_Tax_Model_Config::DISPLAY_TYPE_EXCLUDING_TAX) {
1648
+ $value = false;
1649
+ }
1650
+
1651
+ $this->setData("use_price_incl_tax", $value);
1652
+ }
1653
+
1654
+ return $this->getData("use_price_incl_tax");
1655
+ }
1656
+
1657
+ /**
1658
+ * Remove any session specific data.
1659
+ *
1660
+ * @return $this
1661
+ */
1662
+ protected function reset() {
1663
+ $this->unsetData('session_id');
1664
+ $this->unsetData('store');
1665
+ $this->unsetData('attribute_map');
1666
+ $this->unsetData('placeholder_image');
1667
+ $this->unsetData('category_paths');
1668
+ $this->unsetData('attribute_data');
1669
+ $this->unsetData('store_manage_stock');
1670
+ $this->unsetData('test_mode_enabled');
1671
+ $this->unsetData('tax_rates');
1672
+ $this->unsetData('price_includes_tax');
1673
+ $this->unsetData('use_price_incl_tax');
1674
+
1675
+ return $this;
1676
+ }
1677
+
1678
+ /**
1679
+ * Create an Adminhtml notification for Product Sync, overwriting any
1680
+ * existing ones. If a store is specified, creates a notification specific
1681
+ * to that store, separate from the main Product Sync notification.
1682
+ *
1683
+ * Overwrites any existing notifications for product sync.
1684
+ *
1685
+ * @param $message
1686
+ * @param Mage_Core_Model_Store|null $store
1687
+ *
1688
+ * @return $this
1689
+ */
1690
+ protected function notify($message, $store = null) {
1691
+ $type = ($store === null) ? static::NOTIFICATION_GLOBAL_TYPE : static::NOTIFICATION_STORE_TYPE_PREFIX . $store->getId();
1692
+
1693
+ /** @var Klevu_Search_Model_Notification $notification */
1694
+ $notification = Mage::getResourceModel('klevu_search/notification_collection')
1695
+ ->addFieldToFilter("type", array('eq' => $type))
1696
+ ->getFirstItem();
1697
+
1698
+ $notification->addData(array(
1699
+ 'type' => $type,
1700
+ 'date' => Mage::getModel('core/date')->timestamp(),
1701
+ 'message' => $message
1702
+ ));
1703
+
1704
+ $notification->save();
1705
+
1706
+ return $this;
1707
+ }
1708
+
1709
+ /**
1710
+ * Delete Adminhtml notifications for Product Sync. If a store is specified,
1711
+ * deletes the notifications for the specific store.
1712
+ *
1713
+ * @param Mage_Core_Model_Store|null $store
1714
+ * @return $this
1715
+ */
1716
+ protected function deleteNotifications($store = null) {
1717
+ $type = ($store === null) ? static::NOTIFICATION_GLOBAL_TYPE : static::NOTIFICATION_STORE_TYPE_PREFIX . $store->getId();
1718
+
1719
+ $this->getConnection()->delete($this->getTableName('klevu_search/notification'), array("type = ?" => $type));
1720
+
1721
+ return $this;
1722
+ }
1723
+ /**
1724
+ * Generate thumbnail for search through out the cron job
1725
+ * @return $this
1726
+ */
1727
+ public function generateThumbParrallel()
1728
+ {
1729
+ try {
1730
+ Mage::helper("klevu_search")->log(Zend_Log::INFO, sprintf("Product thumbnail generation process is started."));
1731
+ $this->curlRequest();
1732
+ Mage::helper("klevu_search")->log(Zend_Log::INFO, sprintf("Product thumbnail generation process is completed successfully."));
1733
+ } catch(Exception $e) {
1734
+ Mage::log($e->getMessage(),null,'klevu_Search.log');
1735
+ }
1736
+ }
1737
+ /**
1738
+ * Generate Parrallel curl request
1739
+ * @return $this
1740
+ */
1741
+ public function curlRequest()
1742
+ {
1743
+ $query = $this->getConnection()->select()
1744
+ ->from($this->getTableName("klevu_search/image_thumb"), array('id'))
1745
+ ->where('is_processed=?',0)
1746
+ ->limit(4);
1747
+ $data = $query->query()->fetchAll();
1748
+ if(count($data) > 0) {
1749
+ $curl_multi_handle = curl_multi_init();
1750
+ foreach($data as $key => $value)
1751
+ {
1752
+ Mage::getModel('klevu_search/product_sync')->isBelowMemoryLimit();
1753
+ $url = Mage::getUrl("klevu/search/index",array("id" => $value['id']));
1754
+ $curl_array[$value['id']] = curl_init($url);
1755
+ curl_setopt($curl_array[$value['id']], CURLOPT_RETURNTRANSFER, true);
1756
+ //Add a normal cURL handle to a cURL multi handle
1757
+ curl_multi_add_handle($curl_multi_handle, $curl_array[$value['id']]);
1758
+ $running = NULL;
1759
+ do {
1760
+ //Run the sub-connections of the current cURL handle
1761
+ $mrc = curl_multi_exec($curl_multi_handle,$running);
1762
+ } while($running > 0) ;
1763
+
1764
+ }
1765
+ curl_multi_close($curl_multi_handle);
1766
+ }
1767
+ if(count($data) > 0 && $running==0) {
1768
+ $this->curlRequest();
1769
+ }
1770
+ }
1771
+ /**
1772
+ * Generate batch for thumbnail image
1773
+ * @param $image
1774
+ * @return $this
1775
+ */
1776
+
1777
+ public function thumbImage($image)
1778
+ {
1779
+ $_imageUrl = Mage::getBaseDir('media').DS."catalog".DS."product".$image;
1780
+ $imageResized = Mage::getBaseDir('media').DS."klevu_images".$image;
1781
+ if(!file_exists($imageResized)&& file_exists($_imageUrl)) :
1782
+ $imageObj = new Varien_Image($_imageUrl);
1783
+ $imageObj->constrainOnly(TRUE);
1784
+ $imageObj->keepAspectRatio(TRUE);
1785
+ $imageObj->keepFrame(FALSE);
1786
+ $imageObj->resize(140, 140);
1787
+ $imageObj->save($imageResized);
1788
+ endif;
1789
+ }
1790
+
1791
+ /**
1792
+ * Create batch for thumbnail image
1793
+ * @return $this
1794
+ */
1795
+ public function getEntityIds()
1796
+ {
1797
+ $this->getConnection()->delete($this->getTableName("klevu_search/image_thumb"));
1798
+ $select = $this->getConnection()->select()
1799
+ ->from($this->getTableName("catalog_product_entity"), array('entity_id'));
1800
+ $data = $this->getConnection()->fetchAll($select);
1801
+ foreach ($data as $key => $value) {
1802
+ $entity_ids[] = $value['entity_id'];
1803
+ }
1804
+ $thum_batch = array_chunk($entity_ids,100);
1805
+ foreach($thum_batch as $key => $value)
1806
+ {
1807
+ $this->getConnection()->insert($this->getTableName('klevu_search/image_thumb'),
1808
+ array(
1809
+ 'batchdata'=>serialize($value)
1810
+ ));
1811
+ }
1812
+ }
1813
+
1814
+ /**
1815
+ * Get the batch which are not processed
1816
+ * @param $id
1817
+ * @return $this
1818
+ */
1819
+ public function getImageProcessingIds($id)
1820
+ {
1821
+ $query = $this->getConnection()->select()
1822
+ ->from($this->getTableName("klevu_search/image_thumb"), array('batchdata'))
1823
+ ->where("id=:id AND is_processed=:is_processed")
1824
+ ->bind(array(
1825
+ 'id' => $id,
1826
+ 'is_processed' => 0,
1827
+ ));
1828
+ $data = $this->getConnection()->fetchAll($query, $query->getBind());
1829
+ return $data;
1830
+ }
1831
+ /**
1832
+ * Update the batch which are not processed
1833
+ * @param $id
1834
+ * @return $this
1835
+ */
1836
+ public function updateImageProcessingIds($id)
1837
+ {
1838
+ $where = $this->getConnection()->quoteInto("id = ?", $id);
1839
+ $this->getConnection()->update(
1840
+ $this->getTableName('klevu_search/image_thumb'),
1841
+ array('is_processed' => '1'),
1842
+ $where
1843
+ );
1844
+ }
1845
+
1846
+
1847
+ /**
1848
+ * Get ida for debugs
1849
+ * @return $this
1850
+ */
1851
+ public function debugsIds()
1852
+ {
1853
+ $select = $this->getConnection()->select()
1854
+ ->from($this->getTableName("catalog_product_entity"), array('entity_id','updated_at'))->limit(500)->order('updated_at');
1855
+ $data = $this->getConnection()->fetchAll($select);
1856
+ return $data;
1857
+ }
1858
+
1859
+ /**
1860
+ * Get api for debugs
1861
+ * @return $this
1862
+ */
1863
+ public function getApiDebug()
1864
+ {
1865
+ $configs = Mage::getModel('core/config_data')->getCollection()
1866
+ ->addFieldToFilter('path', array("like" => "%rest_api_key%"))->load();
1867
+ $data = $configs->getData();
1868
+ return $data[0]['value'];
1869
+ }
1870
+
1871
+ /**
1872
+ * Run cron externally for debug using js api
1873
+ * @param $js_api
1874
+ * @return $this
1875
+ */
1876
+ public function sheduleCronExteranally($rest_api) {
1877
+ $configs = Mage::getModel('core/config_data')->getCollection()
1878
+ ->addFieldToFilter('value', array("like" => "%$rest_api%"))->load();
1879
+ $data = $configs->getData();
1880
+ if(!empty($data[0]['scope_id'])){
1881
+ $store = Mage::app()->getStore($data[0]['scope_id']);
1882
+ Mage::getModel('klevu_search/product_sync')
1883
+ ->markAllProductsForUpdate($store)
1884
+ ->schedule();
1885
+ }
1886
+ }
1887
+ /**
1888
+ * Schedule Image Cron Using Admin action
1889
+ * @return $this
1890
+ */
1891
+ public function scheduleImageCron($time = "now") {
1892
+ if (! $time instanceof DateTime) {
1893
+ $time = new DateTime($time);
1894
+ } else {
1895
+ // Don't modify the original parameter
1896
+ $time = clone $time;
1897
+ }
1898
+ $time_str = $time->format("Y-m-d H:i:00");
1899
+ $before_str = $time->modify("15 minutes ago")->format("Y-m-d H:i:00");
1900
+ $after_str = $time->modify("30 minutes")->format("Y-m-d H:i:00"); // Modifying the same DateTime object, so it's -15 + 30 = +15 minutes
1901
+
1902
+ // Check if Product image cron is not already scheduled to run around (±15 minutes) that time
1903
+ $collection = Mage::getResourceModel('cron/schedule_collection')
1904
+ ->addFieldToFilter("job_code", 'klevu_search_product_thumb')
1905
+ ->addFieldToFilter("status", Mage_Cron_Model_Schedule::STATUS_PENDING)
1906
+ ->addFieldToFilter("scheduled_at", array(
1907
+ "from" => $before_str,
1908
+ "to" => $after_str
1909
+ ));
1910
+
1911
+ if ($collection->getSize() == 0) {
1912
+ $schedule = Mage::getModel('cron/schedule');
1913
+ $schedule
1914
+ ->setJobCode('klevu_search_product_thumb')
1915
+ ->setCreatedAt($time_str)
1916
+ ->setScheduledAt($time_str)
1917
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_PENDING)
1918
+ ->save();
1919
+ }
1920
+ return $this;
1921
+ }
1922
+
1923
+ /**
1924
+ * Delete test mode data from product sync
1925
+ * @return $this
1926
+ */
1927
+ public function deleteTestmodeData($store) {
1928
+ $condition = array("store_id"=> $store->getId());
1929
+ $this->getConnection()->delete($this->getTableName("klevu_search/product_sync"),$condition);
1930
+ }
1931
+
1932
+ /**
1933
+ * Exchange key and value for test mode
1934
+ * @return $this
1935
+ */
1936
+ public function removeTestMode() {
1937
+ $stores = Mage::app()->getStores();
1938
+ foreach ($stores as $store) {
1939
+ $test_mode = Mage::helper("klevu_search/config")->isTestModeEnabled($store);
1940
+ if(Mage::helper('klevu_search/config')->isExtensionConfigured($store)) {
1941
+ if($test_mode){
1942
+ $final_test_rest_api = Mage::getStoreConfig('klevu_search/general/rest_api_key', $store);
1943
+ $final_rest_api = Mage::getStoreConfig('klevu_search/general/test_rest_api_key', $store);
1944
+ Mage::helper('klevu_search/config')->setStoreConfig('klevu_search/general/js_api_key', Mage::getStoreConfig('klevu_search/general/test_js_api_key', $store), $store);
1945
+ Mage::helper('klevu_search/config')->setStoreConfig('klevu_search/general/rest_api_key', Mage::getStoreConfig('klevu_search/general/test_rest_api_key', $store), $store);
1946
+ $test_hostname = Mage::getStoreConfig('klevu_search/general/test_hostname', $store);
1947
+ if(!empty($test_hostname)) {
1948
+ Mage::helper('klevu_search/config')->setStoreConfig('klevu_search/general/hostname', Mage::getStoreConfig('klevu_search/general/test_hostname', $store), $store);
1949
+ Mage::helper('klevu_search/config')->setStoreConfig('klevu_search/general/cloud_search_url', Mage::getStoreConfig('klevu_search/general/test_cloud_search_url', $store), $store);
1950
+ Mage::helper('klevu_search/config')->setStoreConfig('klevu_search/general/analytics_url', Mage::getStoreConfig('klevu_search/general/test_analytics_url', $store), $store);
1951
+ Mage::helper('klevu_search/config')->setStoreConfig('klevu_search/general/js_url', Mage::getStoreConfig('klevu_search/general/test_js_url', $store), $store);
1952
+ }
1953
+ Mage::helper("klevu_search/config")->setTestModeEnabledFlag(0, $store);
1954
+ //send responsce in kmc
1955
+ $response = Mage::getModel("klevu_search/api_action_removetestmode")->removeTestMode(array('liveRestApiKey'=>$final_rest_api,'testRestApiKey'=>$final_test_rest_api));
1956
+ if($response->getMessage()=="success") {
1957
+ $this->log(Zend_Log::INFO, $response->getMessage());
1958
+ }
1959
+ // delete prodcut entry for test mode
1960
+ Mage::getModel('klevu_search/product_sync')->deleteTestmodeData($store);
1961
+ //schedual cron for all prodcuts
1962
+ Mage::getModel('klevu_search/product_sync')
1963
+ ->markAllProductsForUpdate($store)
1964
+ ->schedule();
1965
+ }
1966
+ }
1967
+ }
1968
+ }
1969
+ }
app/code/community/Klevu/Search/Model/Resource/Notification.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Resource_Notification extends Mage_Core_Model_Mysql4_Abstract {
4
+
5
+ protected function _construct() {
6
+ $this->_init("klevu_search/notification", "id");
7
+ }
8
+ }
app/code/community/Klevu/Search/Model/Resource/Notification/Collection.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Resource_Notification_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
4
+
5
+ protected function _construct() {
6
+ $this->_init("klevu_search/notification");
7
+ }
8
+ }
app/code/community/Klevu/Search/Model/Session.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_Session extends Mage_Core_Model_Session {
4
+
5
+ public function __construct() {
6
+ $this->init('klevu_search');
7
+ }
8
+ }
app/code/community/Klevu/Search/Model/Sync.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Klevu_Search_Model_Sync extends Varien_Object {
4
+
5
+ /**
6
+ * Limit the memory usage of the sync to 80% of the memory
7
+ * limit. Considering that the minimum memory requirement
8
+ * for Magento at the time of writing is 256MB, this seems
9
+ * like a sensible default.
10
+ */
11
+ const MEMORY_LIMIT = 0.8;
12
+
13
+ /**
14
+ * Return the cron job code used for the sync model.
15
+ *
16
+ * @return string
17
+ */
18
+ abstract function getJobCode();
19
+
20
+ /**
21
+ * Perform the sync.
22
+ */
23
+ abstract function run();
24
+
25
+ /**
26
+ * Run a sync from cron at the specified time. Checks that a cron is not already
27
+ * scheduled to run in the 15 minute interval before or after the given time first.
28
+ *
29
+ * @param DateTime|string $time The scheduled time as a DateTime object or a string
30
+ * that is going to be passed into DateTime. Default is "now".
31
+ *
32
+ * @return $this
33
+ */
34
+ public function schedule($time = "now") {
35
+ if (! $time instanceof DateTime) {
36
+ $time = new DateTime($time);
37
+ } else {
38
+ // Don't modify the original parameter
39
+ $time = clone $time;
40
+ }
41
+ $time_str = $time->format("Y-m-d H:i:00");
42
+ $before_str = $time->modify("15 minutes ago")->format("Y-m-d H:i:00");
43
+ $after_str = $time->modify("30 minutes")->format("Y-m-d H:i:00"); // Modifying the same DateTime object, so it's -15 + 30 = +15 minutes
44
+
45
+ // Check if Product Sync is not already scheduled to run around (±15 minutes) that time
46
+ $collection = Mage::getResourceModel('cron/schedule_collection')
47
+ ->addFieldToFilter("job_code", $this->getJobCode())
48
+ ->addFieldToFilter("status", Mage_Cron_Model_Schedule::STATUS_PENDING)
49
+ ->addFieldToFilter("scheduled_at", array(
50
+ "from" => $before_str,
51
+ "to" => $after_str
52
+ ));
53
+
54
+ if ($collection->getSize() == 0) {
55
+ $schedule = Mage::getModel('cron/schedule');
56
+ $schedule
57
+ ->setJobCode($this->getJobCode())
58
+ ->setCreatedAt($time_str)
59
+ ->setScheduledAt($time_str)
60
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_PENDING)
61
+ ->save();
62
+ }
63
+
64
+ return $this;
65
+ }
66
+
67
+ /**
68
+ * Check if a sync is currently running from cron. A number of running copies to
69
+ * check for can be specified, which is useful if checking if another copy of sync
70
+ * is running from sync itself.
71
+ *
72
+ * Ignores processes that have been running for more than an hour as they are likely
73
+ * to have crashed.
74
+ *
75
+ * @param int $copies
76
+ *
77
+ * @return bool
78
+ */
79
+ public function isRunning($copies = 1) {
80
+ $time = new Datetime("1 hour ago");
81
+ $time = $time->format("Y-m-d H:i:00");
82
+
83
+ $collection = Mage::getResourceModel('cron/schedule_collection')
84
+ ->addFieldToFilter("job_code", $this->getJobCode())
85
+ ->addFieldToFilter("status", Mage_Cron_Model_Schedule::STATUS_RUNNING)
86
+ ->addFieldToFilter("executed_at", array("gteq" => $time));
87
+
88
+ return $collection->getSize() >= $copies;
89
+ }
90
+
91
+ /**
92
+ * Check if the current memory usage is below the limit.
93
+ *
94
+ * @return bool
95
+ */
96
+ protected function isBelowMemoryLimit() {
97
+ $helper = Mage::helper('klevu_search');
98
+
99
+ $limit = $helper->humanReadableToBytes(ini_get('memory_limit'));
100
+ $usage = memory_get_usage(true);
101
+
102
+ $this->log(Zend_Log::DEBUG, sprintf(
103
+ "Memory usage: %s of %s.",
104
+ $helper->bytesToHumanReadable($usage),
105
+ $helper->bytesToHumanReadable($limit)
106
+ ));
107
+
108
+ if ($usage / $limit > static::MEMORY_LIMIT) {
109
+ return false;
110
+ } else {
111
+ return true;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Check if the memory limit has been reached and reschedule to run
117
+ * again immediately if so.
118
+ *
119
+ * @return bool true if a new process was scheduled, false otherwise.
120
+ */
121
+ protected function rescheduleIfOutOfMemory() {
122
+ if (!$this->isBelowMemoryLimit()) {
123
+ $this->log(Zend_Log::INFO, "Memory limit reached. Stopped and rescheduled.");
124
+ $this->schedule();
125
+
126
+ return true;
127
+ }
128
+
129
+ return false;
130
+ }
131
+
132
+ /**
133
+ * Return the table name for the given model entity.
134
+ *
135
+ * @param string $entity
136
+ *
137
+ * @return string
138
+ */
139
+ protected function getTableName($entity) {
140
+ return Mage::getModel('core/resource')->getTableName($entity);
141
+ }
142
+
143
+ /**
144
+ * Write a message to the log file.
145
+ *
146
+ * @param int $level
147
+ * @param string $message
148
+ *
149
+ * @return $this
150
+ */
151
+ protected function log($level, $message) {
152
+ Mage::helper('klevu_search')->log($level, sprintf("[%s] %s", $this->getJobCode(), $message));
153
+
154
+ return $this;
155
+ }
156
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Additional/Attributes.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Additional_Attributes {
4
+
5
+ public function toOptionArray() {
6
+ $helper = Mage::helper('klevu_search');
7
+
8
+ return array(
9
+ array('value' => "brand", 'label' => $helper->__("Brand")),
10
+ array('value' => "model", 'label' => $helper->__("Model")),
11
+ array('value' => "color", 'label' => $helper->__("Color")),
12
+ array('value' => "size" , 'label' => $helper->__("Size"))
13
+ );
14
+ }
15
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Boosting/Attribute.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Boosting_Attribute {
4
+
5
+ /**
6
+ * Fetch all integer and decimal attributes and return in as options array.
7
+ *
8
+ * @return array
9
+ */
10
+ public function toOptionArray() {
11
+ $attributes = $this->getAttributeCollection();
12
+ $options = array(
13
+ array(
14
+ 'value' => null,
15
+ 'label' => '--- No Attribute Selected ---'
16
+ )
17
+ );
18
+ foreach($attributes as $attribute) {
19
+ /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
20
+ $options[] = array(
21
+ 'value' => $attribute->getAttributeCode(),
22
+ 'label' => $attribute->getAttributeCode()
23
+ );
24
+ }
25
+
26
+ return $options;
27
+ }
28
+
29
+ /**
30
+ * Get only integer and decimal attributes that are not used by Klevu yet.
31
+ *
32
+ * @return Mage_Catalog_Model_Resource_Product_Attribute_Collection
33
+ */
34
+ protected function getAttributeCollection() {
35
+ /** @var Mage_Catalog_Model_Resource_Product_Attribute_Collection $collection */
36
+ $collection = Mage::getResourceModel('catalog/product_attribute_collection');
37
+
38
+ // We only want integers and decimal attributes.
39
+ $collection->addFieldToFilter(
40
+ 'backend_type',
41
+ array(
42
+ 'in' => array(
43
+ 'int',
44
+ 'decimal',
45
+ 'varchar'
46
+ )
47
+ )
48
+ );
49
+
50
+ $attributes = Mage::helper('klevu_search/config')->getDefaultMappedAttributes();
51
+
52
+ // Exclude attributes used by default (only default int/decimal attributes)
53
+ $collection->addFieldToFilter(
54
+ 'attribute_code',
55
+ array(
56
+ 'nin' => array_unique($attributes['magento_attribute'])
57
+ )
58
+ );
59
+
60
+ return $collection;
61
+ }
62
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Frequency.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Frequency {
4
+
5
+ const CRON_HOURLY = "0 * * * *";
6
+ const CRON_EVERY_3_HOURS = "0 */3 * * *";
7
+ const CRON_EVERY_6_HOURS = "0 */6 * * *";
8
+ const CRON_EVERY_12_HOURS = "0 */12 * * *";
9
+ const CRON_DAILY = "0 3 * * *";
10
+
11
+ public function toOptionArray() {
12
+ $helper = Mage::helper("klevu_search");
13
+
14
+ return array(
15
+ array(
16
+ 'label' => $helper->__("Hourly"),
17
+ 'value' => static::CRON_HOURLY
18
+ ),
19
+ array(
20
+ 'label' => $helper->__("Every %s hours", 3),
21
+ 'value' => static::CRON_EVERY_3_HOURS
22
+ ),
23
+ array(
24
+ 'label' => $helper->__("Every %s hours", 6),
25
+ 'value' => static::CRON_EVERY_6_HOURS
26
+ ),
27
+ array(
28
+ 'label' => $helper->__("Every %s hours", 12),
29
+ 'value' => static::CRON_EVERY_12_HOURS
30
+ ),
31
+ array(
32
+ 'label' => $helper->__("Daily"),
33
+ 'value' => static::CRON_DAILY
34
+ ),
35
+ );
36
+ }
37
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Landingoptions.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Landingoptions {
4
+
5
+ const YES = 1;
6
+ const NO = 0;
7
+ const KlEVULAND = 2;
8
+
9
+ public function toOptionArray() {
10
+ $helper = Mage::helper("klevu_search");
11
+
12
+ return array(
13
+ array('value' => static::NO, 'label' => $helper->__("Disable")),
14
+ array('value' => static::KlEVULAND, 'label' => $helper->__("Based on Klevu Template (Recommended)")),
15
+ array('value' => static::YES, 'label' => $helper->__("Preserves Your Theme Layout**"))
16
+
17
+ );
18
+ }
19
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Log/Level.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Log_Level {
4
+
5
+ public function toOptionArray() {
6
+ $helper = Mage::helper('klevu_search');
7
+
8
+ return array(
9
+ array('value' => Zend_Log::EMERG, 'label' => $helper->__("Emergency")),
10
+ array('value' => Zend_Log::ALERT, 'label' => $helper->__("Alert")),
11
+ array('value' => Zend_Log::CRIT, 'label' => $helper->__("Critical")),
12
+ array('value' => Zend_Log::ERR, 'label' => $helper->__("Error")),
13
+ array('value' => Zend_Log::WARN, 'label' => $helper->__("Warning")),
14
+ array('value' => Zend_Log::NOTICE, 'label' => $helper->__("Notice")),
15
+ array('value' => Zend_Log::INFO, 'label' => $helper->__("Information")),
16
+ array('value' => Zend_Log::DEBUG, 'label' => $helper->__("Debug"))
17
+ );
18
+ }
19
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Product/Attributes.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Product_Attributes {
4
+
5
+ public function toOptionArray() {
6
+ $options = array();
7
+
8
+ $attributes = $this->getAttributeCollection();
9
+ foreach ($attributes as $attribute) {
10
+ /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
11
+ $options[] = array(
12
+ 'value' => $attribute->getAttributeCode(),
13
+ 'label' => $attribute->getAttributeCode()
14
+ );
15
+ }
16
+
17
+ return $options;
18
+ }
19
+
20
+ /**
21
+ * Return a product attribute collection.
22
+ *
23
+ * @return Mage_Catalog_Model_Resource_Product_Attribute_Collection
24
+ */
25
+ protected function getAttributeCollection() {
26
+ /** @var Mage_Catalog_Model_Resource_Product_Attribute_Collection $collection */
27
+ $collection = Mage::getResourceModel('catalog/product_attribute_collection');
28
+ // Filter out attributes mapped by default
29
+ $attributes = Mage::helper('klevu_search/config')->getDefaultMappedAttributes();
30
+ $collection->addFieldToFilter('attribute_code', array(
31
+ 'nin' => array_unique($attributes['magento_attribute'])
32
+ ));
33
+
34
+ return $collection;
35
+ }
36
+ }
app/code/community/Klevu/Search/Model/System/Config/Source/Yesnoforced.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Model_System_Config_Source_Yesnoforced {
4
+
5
+ const YES = 1;
6
+ const NO = 0;
7
+ const FORCED = 2;
8
+
9
+ public function toOptionArray() {
10
+ $helper = Mage::helper("klevu_search");
11
+
12
+ return array(
13
+ array('value' => static::YES, 'label' => $helper->__("Yes")),
14
+ array('value' => static::NO, 'label' => $helper->__("No"))
15
+ // array('value' => static::FORCED, 'label' => $helper->__("Forced"))
16
+ );
17
+ }
18
+ }
app/code/community/Klevu/Search/Test/Config/Base.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Config_Base extends EcomDev_PHPUnit_Test_Case_Config {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testClassAlias() {
9
+ $this->assertBlockAlias("klevu_search/test", "Klevu_Search_Block_Test");
10
+ $this->assertHelperAlias("klevu_search/test", "Klevu_Search_Helper_Test");
11
+ $this->assertModelAlias("klevu_search/test", "Klevu_Search_Model_Test");
12
+ }
13
+ }
app/code/community/Klevu/Search/Test/Controller/CatalogSearch.php ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Controller_CatalogSearch extends EcomDev_PHPUnit_Test_Case_Controller {
4
+
5
+ /** @var Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection */
6
+ protected $collection;
7
+
8
+ protected function tearDown() {
9
+ $this->getLayout()->reset();
10
+ $this->collection = null;
11
+ Mage::unregister('_singleton/catalogsearch/layer');
12
+ Mage::unregister('current_layer');
13
+ Mage::unregister('current_category_filter');
14
+
15
+ // Delete all products
16
+ Mage::getResourceModel('catalog/product_collection')->delete();
17
+
18
+ parent::tearDown();
19
+ }
20
+
21
+ protected function setUp() {
22
+ $this->getLayout()->reset();
23
+ // Make sure the 'price' attribute is set to is_filterable_in_search
24
+ $price = $this->getPriceAttribute();
25
+ $price->setData('is_filterable_in_search', 1);
26
+ $price->save();
27
+
28
+ parent::setUp();
29
+ }
30
+
31
+ /**
32
+ * Run search and make sure the page returns HTTP Code 200
33
+ * @test
34
+ * @loadFixture search_results
35
+ */
36
+ public function testSearchResultsPageLoads() {
37
+ $this->mockAndDispatchSearchResults();
38
+ // Assert the request was successful.
39
+ $this->assertResponseHttpCode(200);
40
+ }
41
+
42
+ /**
43
+ * Run search and make sure the layered navigation and search result list block appears
44
+ * @test
45
+ * @loadFixture search_results
46
+ */
47
+ public function testExpectedLayoutBlocksAreRendered() {
48
+ $this->mockAndDispatchSearchResults();
49
+ // Assert that the left nav (layered navigation) and the search results have been rendered.
50
+ $this->assertLayoutBlockRendered('catalogsearch.leftnav');
51
+ $this->assertLayoutBlockRendered('search_result_list');
52
+
53
+ }
54
+
55
+ /**
56
+ * Run search and check that category and price filters exist
57
+ * @test
58
+ * @loadFixture search_results
59
+ */
60
+ public function testLayeredNavigationHasFilters() {
61
+ $this->mockAndDispatchSearchResults();
62
+
63
+ // Load the layered navigation block
64
+ /** @var Klevu_Search_Block_CatalogSearch_Layer $layer_nav */
65
+ $layer_nav = $this->app()->getLayout()->getBlock('catalogsearch.leftnav');// Get the category filter block and assert it isn't null.
66
+ $category_filters_block = $layer_nav->getChild('category_filter');
67
+ $this->assertNotNull($category_filters_block);
68
+
69
+ // Get the category filter items and assert that there was one filter item returned, and it's the category "Shirts"
70
+ $category_filters = $category_filters_block->getItems();
71
+ $this->assertCount(1, $category_filters);
72
+ $this->assertEquals('Test Category', $category_filters[0]->getData('label'));
73
+
74
+ $price_filters_block = $layer_nav->getChild('price_filter');
75
+ $this->assertNotFalse($price_filters_block);
76
+
77
+ // Check the price filters are as we expect
78
+ $price_filters = $price_filters_block->getItems();
79
+ $this->assertCount(2, $price_filters);
80
+ $this->assertEquals('0-49', $price_filters[0]->getData('value'));
81
+ }
82
+
83
+ /**
84
+ * Run search and check the results are correct, and in the expected order
85
+ * @test
86
+ * @loadFixture search_results
87
+ */
88
+ public function testSearchResultsAreCorrect() {
89
+ $this->mockApiAndCollection();
90
+ $collection = $this->collection;
91
+ // Assert that the number of results is correct.
92
+ $this->assertEquals(3, $collection->getSize());
93
+
94
+ // Assert that the results are in the expected order.
95
+ $expected_product_ids = array(3,2,1);
96
+ $actual_product_ids = array();
97
+ /** @var Mage_Catalog_Model_Product $product */
98
+ foreach($collection as $product) {
99
+ $actual_product_ids[] = $product->getId();
100
+ }
101
+
102
+ $this->assertEquals($expected_product_ids, $actual_product_ids);
103
+ }
104
+
105
+ /**
106
+ * Test that a category filter can be applied
107
+ * @test
108
+ * @loadFixture search_results
109
+ */
110
+ public function testSearchCategoryFilterIsApplied() {
111
+ $this->app()->getRequest()->setQuery('cat', '3');
112
+ $this->mockAndDispatchSearchResults();
113
+
114
+ $this->assertCount(1, $this->getAppliedFilters()); // We expect only 1 applied filter.
115
+ $this->assertEquals("Test Category", $this->getFirstAppliedFilter()->getData('label'));
116
+ }
117
+
118
+ /**
119
+ * Test that a price filter can be applied
120
+ * @test
121
+ * @loadFixture search_results
122
+ */
123
+ public function testSearchPriceFilterIsApplied() {
124
+ $this->app()->getRequest()->setQuery('price', '0-49');
125
+ $this->mockAndDispatchSearchResults();
126
+
127
+ $this->assertCount(1, $this->getAppliedFilters()); // We expect only 1 applied filter.
128
+ $this->assertEquals(array('0', '49'), $this->getFirstAppliedFilter()->getData('value'));
129
+ }
130
+
131
+ /**
132
+ * Test that both the price and category filter can be applied at the same time
133
+ * @test
134
+ * @loadFixture search_results
135
+ */
136
+ public function testMultipleFiltersCanBeApplied() {
137
+ $this->app()->getRequest()->setQuery(array('price' => '0-49', 'cat' => '3'));
138
+ $this->mockAndDispatchSearchResults();
139
+
140
+ $this->assertCount(2, $this->getAppliedFilters()); // We expect 2 applied filters.
141
+ }
142
+
143
+ /**
144
+ * Test that we can change the sort order so results are in descending order of Price.
145
+ * @test
146
+ * @loadFixture search_results
147
+ */
148
+ public function testChangingSortOrderToPriceDesc() {
149
+ $this->app()->getRequest()->setQuery(array('dir' => 'desc', 'order' => 'price'));
150
+ $this->mockAndDispatchSearchResults('shirt', 'sorted');
151
+ $result_block = $this->getSearchResultsBlock();
152
+ // Set the updated mock collection
153
+ $result_block->setCollection($this->collection);
154
+
155
+ /** @var Klevu_Search_Model_CatalogSearch_Resource_Fulltext_Collection $product_collection */
156
+ $product_collection = $result_block->getLoadedProductCollection();
157
+
158
+ // Expected order of results
159
+ $expected_product_ids = array(2, 1, 3);
160
+ $actual_product_ids = array();
161
+ /** @var Mage_Catalog_Model_Product $product */
162
+ foreach($product_collection as $product) {
163
+ $actual_product_ids[] = $product->getId();
164
+ }
165
+ $this->assertEquals($expected_product_ids, $actual_product_ids, 'Products are in the wrong order');
166
+ }
167
+
168
+ /**
169
+ * Test that we can access the 2nd page of results and that the expected products show.
170
+ * @test
171
+ * @loadFixture search_results
172
+ */
173
+ public function testSecondPageOfResultsExists() {
174
+ // Default: page size = 9, set the page to page 2, meaning we expect to see results from 10+
175
+ $this->getRequest()->setQuery('p', '2');
176
+ // Our response will return the 10th product, our response contains a total result size of 10.
177
+ $this->mockAndDispatchSearchResults('shirt', 'paged', 9);
178
+
179
+ $result_block = $this->getSearchResultsBlock();
180
+ $result_block->setCollection($this->collection);
181
+ $toolbar = $result_block->getToolbarBlock();
182
+
183
+ $toolbar->setCollection($this->collection);
184
+
185
+ //Assert that we can see the 10th item, out of 10 results.
186
+ $pager_text = version_compare(Mage::getVersion(), "1.9", ">=") ? '10-10 of 10' : 'Items 10 to 10 of 10 total';
187
+ $this->assertContains($pager_text, $toolbar->toHtml());
188
+
189
+ }
190
+
191
+ /**
192
+ * Test that when we perform a search which does not return results, the no results message is displayed.
193
+ * @test
194
+ * @loadFixture search_results
195
+ */
196
+ public function testNoResults() {
197
+ $this->mockAndDispatchSearchResults('returnsnothing', 'empty');
198
+
199
+ $result_block = $this->getSearchResultsBlock();
200
+ $result_block->setCollection($this->collection);
201
+ $result_html = $result_block->toHtml();
202
+
203
+ $this->assertContains('There are no products matching the selection.', $result_html);
204
+ }
205
+
206
+ /**
207
+ * Return the search result list block
208
+ * @return Mage_Catalog_Block_Product_List
209
+ */
210
+ protected function getSearchResultsBlock() {
211
+ return $this->app()->getLayout()->getBlock('search_result_list');
212
+ }
213
+
214
+ /**
215
+ * Get all applied filters
216
+ * @return array
217
+ */
218
+ protected function getAppliedFilters() {
219
+
220
+ /** @var Klevu_Search_Block_CatalogSearch_Layer $layer_nav */
221
+ $layer_nav = $this->app()->getLayout()->getBlock('catalogsearch.leftnav');
222
+ return $layer_nav->getLayer()->getState()->getFilters();
223
+ }
224
+
225
+ /**
226
+ * Fetch the first applied filter
227
+ * @return Mage_Catalog_Model_Layer_Filter_Item
228
+ */
229
+ protected function getFirstAppliedFilter() {
230
+ $filters = $this->getAppliedFilters();
231
+ return $filters[0];
232
+ }
233
+
234
+ /**
235
+ * @param string $query
236
+ * @return $this
237
+ * @throws Zend_Controller_Exception
238
+ */
239
+ protected function mockAndDispatchSearchResults($query = 'shirt', $response_type = 'successful', $pagination = 0) {
240
+ $this->mockApiAndCollection($query, $response_type, $pagination);
241
+ // Set the search query
242
+ $this->getRequest()->setQuery('q', $query);
243
+
244
+ // Load the search results page
245
+ return $this->dispatch('catalogsearch/result/index');
246
+ }
247
+
248
+ protected function mockApiAndCollection($query = 'shirt', $response_type = 'successful', $pagination = 0) {
249
+ // Mock the API Action
250
+ switch($response_type) {
251
+ default:
252
+ case 'successful':
253
+ $response = $this->getSearchResponse('search_response_success.xml');
254
+ break;
255
+ case 'empty':
256
+ $response = $this->getSearchResponse('search_response_empty.xml');
257
+ break;
258
+ case 'sorted':
259
+ $response = $this->getSearchResponse('search_response_sorted.xml');
260
+ break;
261
+ case 'paged':
262
+ $response = $this->getSearchResponse('search_response_paged.xml');
263
+ break;
264
+ }
265
+
266
+ $this->replaceApiActionByMock("klevu_search/api_action_idsearch", $response);
267
+
268
+ $return_value = array(
269
+ 'ticket' => 'some-api-key',
270
+ 'noOfResults' => 9,
271
+ 'term' => $query,
272
+ 'paginationStartsFrom' => $pagination,
273
+ 'klevuSort' => 'rel',
274
+ 'enableFilters' => 'true',
275
+ 'filterResults' => '',
276
+ );
277
+
278
+ // Mock the klevu search resource collection model, and force the search filters data
279
+ $this->collection = $this->getResourceModelMock('catalogsearch/fulltext_collection', array('getSearchFilters'));
280
+ $this->collection->expects($this->any())
281
+ ->method('getSearchFilters')
282
+ ->will($this->returnValue($return_value));
283
+ }
284
+
285
+ /**
286
+ * Create a mock class of the given API action model which will expect to be executed
287
+ * once and will return the given response. Then replace that model in Magento with
288
+ * the created mock.
289
+ *
290
+ * @param string $alias A grouped class name of the API action model to mock
291
+ * @param Klevu_Search_Model_Api_Response $response
292
+ *
293
+ * @return $this
294
+ */
295
+ protected function replaceApiActionByMock($alias, $response) {
296
+ $mock = $this->getModelMock($alias, array("execute"));
297
+ $mock
298
+ ->expects($this->any())
299
+ ->method("execute")
300
+ ->will($this->returnValue($response));
301
+
302
+ $this->replaceByMock("model", $alias, $mock);
303
+
304
+
305
+
306
+ return $this;
307
+ }
308
+
309
+ /**
310
+ * Return a klevu_search/api_response_message model with a successful response from
311
+ * a startSession API call.
312
+ *
313
+ * @return Klevu_Search_Model_Api_Response_Message
314
+ */
315
+ protected function getSearchResponse($data_file) {
316
+ $model = Mage::getModel('klevu_search/api_response_search')->setRawResponse(
317
+ new Zend_Http_Response(200, array(), $this->getDataFileContents($data_file))
318
+ );
319
+
320
+ return $model;
321
+
322
+ }
323
+
324
+ protected function getDataFileContents($file) {
325
+ $directory_tree = array(
326
+ Mage::getModuleDir('', 'Klevu_Search'),
327
+ 'Test',
328
+ 'Model',
329
+ 'Api',
330
+ 'data',
331
+ $file
332
+ );
333
+
334
+ $file_path = join(DS, $directory_tree);
335
+
336
+ return file_get_contents($file_path);
337
+ }
338
+
339
+ protected function getPriceAttribute() {
340
+ return Mage::getModel('eav/entity_attribute')->loadByCode(Mage_Catalog_Model_Product::ENTITY, 'price');
341
+ }
342
+ }
app/code/community/Klevu/Search/Test/Controller/CatalogSearch/fixtures/search_results.yaml ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_rest_api_key: test-api-key
5
+ default/klevu_search/general/test_js_api_key: test-api-key
6
+ default/klevu_search/product_sync/enabled: 2
7
+ default/catalog/frontend/grid_per_page_values: 9,15,30
8
+ default/catalog/frontend/grid_per_page: 9
9
+ eav:
10
+ catalog_category:
11
+ - entity_id: 3
12
+ parent_id: 2
13
+ path: 1/2/3
14
+ position: 2
15
+ level: 2
16
+ children_count: 0
17
+ name: Test Category
18
+ url_key: test-category
19
+ is_active: 1
20
+ is_anchor: 0
21
+ display_mode: PRODUCTS
22
+ include_in_menu: 1
23
+ catalog_product:
24
+ - entity_id: 1
25
+ attribute_set_id: 4
26
+ type_id: simple
27
+ sku: example1
28
+ name: Example 1
29
+ short_description: Example short description
30
+ description: Example description
31
+ url_key: example-1
32
+ weight: null
33
+ stock:
34
+ qty: 100.00
35
+ is_in_stock: 1
36
+ website_ids:
37
+ - base # Default website
38
+ category_ids:
39
+ - 3 # Default category
40
+ price: 34
41
+ tax_class_id: 2
42
+ status: 1
43
+ visibility: 4
44
+ created_at: 2007-08-29 19:56:27
45
+ updated_at: 2008-06-28 01:57:22
46
+ - entity_id: 2
47
+ attribute_set_id: 4
48
+ type_id: simple
49
+ sku: example2
50
+ name: Example 2
51
+ short_description: Example short description
52
+ description: Example description
53
+ url_key: example-2
54
+ weight: null
55
+ stock:
56
+ qty: 100.00
57
+ is_in_stock: 1
58
+ website_ids:
59
+ - base # Default website
60
+ category_ids:
61
+ - 3 # Default category
62
+ price: 34
63
+ tax_class_id: 2
64
+ status: 1
65
+ visibility: 4
66
+ created_at: 2007-08-29 19:56:27
67
+ updated_at: 2008-06-28 01:57:22
68
+ - entity_id: 3
69
+ attribute_set_id: 4
70
+ type_id: simple
71
+ sku: example3
72
+ name: Example 3
73
+ short_description: Example short description
74
+ description: Example description
75
+ url_key: example-3
76
+ weight: null
77
+ stock:
78
+ qty: 100.00
79
+ is_in_stock: 1
80
+ website_ids:
81
+ - base # Default website
82
+ category_ids:
83
+ - 3 # Default category
84
+ price: 34
85
+ tax_class_id: 2
86
+ status: 1
87
+ visibility: 4
88
+ created_at: 2007-08-29 19:56:27
89
+ updated_at: 2008-06-28 01:57:22
90
+ - entity_id: 10
91
+ attribute_set_id: 4
92
+ type_id: simple
93
+ sku: example10
94
+ name: Example 10
95
+ short_description: Example short description
96
+ description: Example description
97
+ url_key: example-10
98
+ weight: null
99
+ stock:
100
+ qty: 100.00
101
+ is_in_stock: 1
102
+ website_ids:
103
+ - base # Default website
104
+ category_ids:
105
+ - 3 # Default category
106
+ price: 34
107
+ tax_class_id: 2
108
+ status: 1
109
+ visibility: 4
110
+ created_at: 2007-08-29 19:56:27
111
+ updated_at: 2008-06-28 01:57:22
112
+ - entity_id: 11
113
+ attribute_set_id: 4
114
+ type_id: simple
115
+ sku: example11
116
+ name: Example 11
117
+ short_description: Example short description
118
+ description: Example description
119
+ url_key: example-11
120
+ weight: null
121
+ stock:
122
+ qty: 100.00
123
+ is_in_stock: 1
124
+ website_ids:
125
+ - base # Default website
126
+ category_ids:
127
+ - 3 # Default category
128
+ price: 34
129
+ tax_class_id: 2
130
+ status: 1
131
+ visibility: 4
132
+ created_at: 2007-08-29 19:56:27
133
+ updated_at: 2008-06-28 01:57:22
app/code/community/Klevu/Search/Test/Helper/Api.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Klevu_Search_Test_Helper_Api extends EcomDev_PHPUnit_Test_Case {
3
+
4
+ const VERSION_NUMBER = '1.1.2';
5
+
6
+ public function testGetVersion() {
7
+ $version = Mage::getConfig()->getModuleConfig('Klevu_Search')->version;
8
+ $this->assertEquals(self::VERSION_NUMBER, $version);
9
+ }
10
+ }
app/code/community/Klevu/Search/Test/Helper/Compat.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Helper_Compat extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /** @var Klevu_Search_Helper_Compat $helper */
6
+ protected $helper;
7
+
8
+ protected function setUp() {
9
+ parent::setUp();
10
+
11
+ $this->helper = Mage::helper("klevu_search/compat");
12
+ }
13
+
14
+ /**
15
+ * @test
16
+ */
17
+ public function testGetProductUrlRewriteSelect() {
18
+ $this->assertInstanceOf("Varien_Db_Select", $this->helper->getProductUrlRewriteSelect(array(1), 0, 1));
19
+ }
20
+ }
app/code/community/Klevu/Search/Test/Helper/Config.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Helper_Config extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /** @var Klevu_Search_Helper_Config $helper */
6
+ protected $helper;
7
+
8
+ protected function setUp() {
9
+ parent::setUp();
10
+
11
+ $this->helper = Mage::helper("klevu_search/config");
12
+ }
13
+
14
+ protected function tearDown() {
15
+ $this->getConfig()->deleteConfig("klevu_search/general/enabled");
16
+ $this->getConfig()->deleteConfig("klevu_search/general/test_mode");
17
+ $this->getConfig()->deleteConfig("klevu_search/general/js_api_key");
18
+ $this->getConfig()->deleteConfig("klevu_search/general/rest_api_key");
19
+ $this->getConfig()->deleteConfig("klevu_search/general/test_js_api_key");
20
+ $this->getConfig()->deleteConfig("klevu_search/general/test_rest_api_key");
21
+ $this->getConfig()->deleteConfig("klevu_search/product_sync/enabled");
22
+ $this->getConfig()->deleteConfig("klevu_search/product_sync/frequency");
23
+ $this->getConfig()->deleteConfig("klevu_search/attributes/additional");
24
+ $this->getConfig()->deleteConfig("klevu_search/order_sync/enabled");
25
+ $this->getConfig()->deleteConfig("klevu_search/order_sync/frequency");
26
+ $this->getConfig()->deleteConfig("klevu_search/developer/force_log");
27
+ $this->getConfig()->deleteConfig("klevu_search/developer/log_level");
28
+
29
+ parent::tearDown();
30
+ }
31
+
32
+ /**
33
+ * @test
34
+ * @loadFixture
35
+ */
36
+ public function testIsExtensionEnabledEnabled() {
37
+ $this->assertEquals(true, $this->helper->isExtensionEnabled());
38
+ }
39
+
40
+ /**
41
+ * @test
42
+ * @loadFixture
43
+ */
44
+ public function testIsExtensionEnabledDisabled() {
45
+ $this->assertEquals(false, $this->helper->isExtensionEnabled());
46
+ }
47
+
48
+ /**
49
+ * @test
50
+ */
51
+ public function testGetTestModeEnabledFlag() {
52
+ $this->assertEquals(false, $this->helper->getTestModeEnabledFlag(),
53
+ "Failed asserting that Test Mode flag is disabled by default."
54
+ );
55
+
56
+ $this->getConfig()
57
+ ->saveConfig("klevu_search/general/test_mode", 1)
58
+ ->cleanCache();
59
+
60
+ $this->clearConfigCache();
61
+
62
+ $this->assertEquals(true, $this->helper->getTestModeEnabledFlag(),
63
+ "Failed asserting that Test Mode flag returns true when Test Mode is enabled in the config."
64
+ );
65
+ }
66
+
67
+ /**
68
+ * @test
69
+ * @dataProvider dataProvider
70
+ */
71
+ public function testIsTestModeEnabled($test_mode, $is_production_domain, $result) {
72
+ $this->getConfig()
73
+ ->saveConfig("klevu_search/general/test_mode", $test_mode)
74
+ ->cleanCache();
75
+
76
+ $this->clearConfigCache();
77
+
78
+ $this->mockIsProductionDomain($is_production_domain);
79
+
80
+ $this->assertEquals($result, $this->helper->isTestModeEnabled());
81
+ }
82
+
83
+ /**
84
+ * @test
85
+ */
86
+ public function testGetJsApiKeyProduction() {
87
+ $api_key = "test-js-api-key";
88
+
89
+ $this->mockIsProductionDomain(true);
90
+
91
+ $this->assertEquals(null, $this->helper->getJsApiKey());
92
+
93
+ $this->getConfig()
94
+ ->saveConfig("klevu_search/general/js_api_key", $api_key)
95
+ ->cleanCache();
96
+
97
+ $this->clearConfigCache();
98
+
99
+ $this->assertEquals($api_key, $this->helper->getJsApiKey());
100
+ }
101
+
102
+ /**
103
+ * @test
104
+ */
105
+ public function testGetJsApiKeyStaging() {
106
+ $api_key = "test-js-api-key";
107
+
108
+ $this->mockIsProductionDomain(false);
109
+
110
+ $this->assertEquals(null, $this->helper->getJsApiKey());
111
+
112
+ $this->getConfig()
113
+ ->saveConfig("klevu_search/general/test_js_api_key", $api_key)
114
+ ->cleanCache();
115
+
116
+ $this->clearConfigCache();
117
+
118
+ $this->assertEquals($api_key, $this->helper->getJsApiKey());
119
+ }
120
+
121
+ /**
122
+ * @test
123
+ */
124
+ public function testGetRestApiKeyProduction() {
125
+ $api_key = "test-rest-api-key";
126
+
127
+ $this->mockIsProductionDomain(true);
128
+
129
+ $this->assertEquals(null, $this->helper->getRestApiKey());
130
+
131
+ $this->getConfig()
132
+ ->saveConfig("klevu_search/general/rest_api_key", $api_key)
133
+ ->cleanCache();
134
+
135
+ $this->clearConfigCache();
136
+
137
+ $this->assertEquals($api_key, $this->helper->getRestApiKey());
138
+ }
139
+
140
+ /**
141
+ * @test
142
+ */
143
+ public function testGetRestApiKeyStaging() {
144
+ $api_key = "test-rest-api-key";
145
+
146
+ $this->mockIsProductionDomain(false);
147
+
148
+ $this->assertEquals(null, $this->helper->getRestApiKey());
149
+
150
+ $this->getConfig()
151
+ ->saveConfig("klevu_search/general/test_rest_api_key", $api_key)
152
+ ->cleanCache();
153
+
154
+ $this->clearConfigCache();
155
+
156
+ $this->assertEquals($api_key, $this->helper->getRestApiKey());
157
+ }
158
+
159
+ /**
160
+ * @test
161
+ */
162
+ public function testGetProductSyncEnabledFlagDefault() {
163
+ $this->assertEquals(2, $this->helper->getProductSyncEnabledFlag());
164
+ }
165
+
166
+ /**
167
+ * @test
168
+ * @loadFixture
169
+ */
170
+ public function testGetProductSyncEnabledFlag() {
171
+ $this->assertEquals(2, $this->helper->getProductSyncEnabledFlag());
172
+ }
173
+
174
+ /**
175
+ * @test
176
+ * @dataProvider dataProvider
177
+ */
178
+ public function testIsProductSyncEnabled($is_production_domain, $config_flag, $result) {
179
+ $data_helper = $this->getHelperMock('klevu_search', array("isProductionDomain"));
180
+ $data_helper
181
+ ->expects($this->once())
182
+ ->method("isProductionDomain")
183
+ ->will($this->returnValue($is_production_domain));
184
+ $this->replaceByMock("helper", "klevu_search", $data_helper);
185
+
186
+ $this->clearConfigCache();
187
+
188
+ $this->getConfig()
189
+ ->saveConfig("klevu_search/product_sync/enabled", $config_flag)
190
+ ->cleanCache();
191
+
192
+ $this->assertEquals($result, $this->helper->isProductSyncEnabled());
193
+ }
194
+
195
+ /**
196
+ * @test
197
+ */
198
+ public function testGetProductSyncFrequencyDefault() {
199
+ $this->assertEquals("0 * * * *", $this->helper->getProductSyncFrequency());
200
+ }
201
+
202
+ /**
203
+ * @test
204
+ * @loadFixture
205
+ */
206
+ public function testGetProductSyncFrequency() {
207
+ $this->assertEquals("0 */5 * * *", $this->helper->getProductSyncFrequency());
208
+ }
209
+
210
+ /**
211
+ * @test
212
+ */
213
+ public function testGetAdditionalAttributesMap() {
214
+ $map = array(
215
+ "_1" => array("klevu_attribute" => "k_test", "magento_attribute" => "m_test"),
216
+ "_2" => array("klevu_attribute" => "k_other", "magento_attribute" => "m_something")
217
+ );
218
+
219
+ // Test the default value
220
+ $this->assertEquals(array(), $this->helper->getAdditionalAttributesMap(), "getAdditionalAttributesMap() did not default to an empty array.");
221
+
222
+ $this->getConfig()
223
+ ->saveConfig("klevu_search/attributes/additional", serialize($map))
224
+ ->reinit();
225
+
226
+ $this->assertEquals($map, $this->helper->getAdditionalAttributesMap(), "getAdditionalAttributesMap() failed to return the map set.");
227
+ }
228
+
229
+ /**
230
+ * @test
231
+ */
232
+ public function testGetOrderSyncEnabledFlagDefault() {
233
+ $this->assertEquals(1, $this->helper->getOrderSyncEnabledFlag());
234
+ }
235
+
236
+ /**
237
+ * @test
238
+ * @loadFixture
239
+ */
240
+ public function testGetOrderSyncEnabledFlag() {
241
+ $this->assertEquals(2, $this->helper->getOrderSyncEnabledFlag());
242
+ }
243
+
244
+ /**
245
+ * @test
246
+ * @dataProvider dataProvider
247
+ */
248
+ public function testIsOrderSyncEnabled($is_production_domain, $config_flag, $result) {
249
+ $data_helper = $this->getHelperMock('klevu_search', array("isProductionDomain"));
250
+ $data_helper
251
+ ->expects($this->once())
252
+ ->method("isProductionDomain")
253
+ ->will($this->returnValue($is_production_domain));
254
+ $this->replaceByMock("helper", "klevu_search", $data_helper);
255
+
256
+ $this->clearConfigCache();
257
+
258
+ $this->getConfig()
259
+ ->saveConfig("klevu_search/order_sync/enabled", $config_flag)
260
+ ->cleanCache();
261
+
262
+ $this->assertEquals($result, $this->helper->isOrderSyncEnabled());
263
+ }
264
+
265
+ /**
266
+ * @test
267
+ */
268
+ public function testGetOrderSyncFrequencyDefault() {
269
+ $this->assertEquals("0 * * * *", $this->helper->getOrderSyncFrequency());
270
+ }
271
+
272
+ /**
273
+ * @test
274
+ * @loadFixture
275
+ */
276
+ public function testGetOrderSyncFrequency() {
277
+ $this->assertEquals("0 */3 * * *", $this->helper->getOrderSyncFrequency());
278
+ }
279
+
280
+ /**
281
+ * @test
282
+ */
283
+ public function testIsLoggingForced() {
284
+ // Test the default value
285
+ $this->assertEquals(false, $this->helper->isLoggingForced());
286
+
287
+ $this->clearConfigCache();
288
+
289
+ // Test a set value
290
+ $this->getConfig()
291
+ ->saveConfig('klevu_search/developer/force_log', true)
292
+ ->cleanCache();
293
+
294
+ $this->assertEquals(true, $this->helper->isLoggingForced());
295
+ }
296
+
297
+ public function testGetLogLevel() {
298
+ // Test the default value
299
+ $this->assertEquals(Zend_Log::WARN, $this->helper->getLogLevel(), "getLogLevel() returned an incorrect default value.");
300
+
301
+ $this->clearConfigCache();
302
+
303
+ // Test a set value
304
+ $this->getConfig()
305
+ ->saveConfig('klevu_search/developer/log_level', Zend_Log::INFO)
306
+ ->cleanCache();
307
+
308
+ $this->assertEquals(Zend_Log::INFO, $this->helper->getLogLevel(), "getLogLevel() failed to return the value set.");
309
+ }
310
+
311
+ /**
312
+ * Mock the helper method that checks whether the current domain is a production domain.
313
+ *
314
+ * @param bool $result The result to be returned by the method
315
+ */
316
+ protected function mockIsProductionDomain($result = false) {
317
+ $data_helper = $this->getHelperMock('klevu_search', array("isProductionDomain"));
318
+ $data_helper
319
+ ->expects($this->any())
320
+ ->method("isProductionDomain")
321
+ ->will($this->returnValue($result));
322
+ $this->replaceByMock("helper", "klevu_search", $data_helper);
323
+ }
324
+
325
+ protected function getConfig() {
326
+ return Mage::app()->getConfig();
327
+ }
328
+
329
+ /**
330
+ * Get around Magento's aggressive caching strategy and actually clear the configuration cache.
331
+ */
332
+ protected function clearConfigCache() {
333
+ // Flush website and store configuration caches
334
+ foreach (Mage::app()->getWebsites(true) as $website) {
335
+ EcomDev_Utils_Reflection::setRestrictedPropertyValue(
336
+ $website, '_configCache', array()
337
+ );
338
+ }
339
+ foreach (Mage::app()->getStores(true) as $store) {
340
+ EcomDev_Utils_Reflection::setRestrictedPropertyValue(
341
+ $store, '_configCache', array()
342
+ );
343
+ }
344
+ }
345
+ }
app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetOrderSyncEnabledFlag.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ config:
2
+ default/klevu_search/order_sync/enabled: 2
app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetOrderSyncFrequency.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ config:
2
+ default/klevu_search/order_sync/frequency: 0 */3 * * *
app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetProductSyncEnabledFlag.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ config:
2
+ default/klevu_search/product_sync/enabled: 2
app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testGetProductSyncFrequency.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ config:
2
+ default/klevu_search/product_sync/frequency: 0 */5 * * *
app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testIsExtensionEnabledDisabled.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 0
app/code/community/Klevu/Search/Test/Helper/Config/fixtures/testIsExtensionEnabledEnabled.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
app/code/community/Klevu/Search/Test/Helper/Config/providers/testIsOrderSyncEnabled.yaml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ is_production_domain: true
3
+ config_flag: 0 # Disabled
4
+ order_sync_enabled: false
5
+ -
6
+ is_production_domain: true
7
+ config_flag: 1 # Enabled
8
+ order_sync_enabled: true
9
+ -
10
+ is_production_domain: true
11
+ config_flag: 2 # Forced
12
+ order_sync_enabled: true
13
+ -
14
+ is_production_domain: false
15
+ config_flag: 0 # Disabled
16
+ order_sync_enabled: false
17
+ -
18
+ is_production_domain: false
19
+ config_flag: 1 # Enabled
20
+ order_sync_enabled: false
21
+ -
22
+ is_production_domain: false
23
+ config_flag: 2 # Forced
24
+ order_sync_enabled: true
app/code/community/Klevu/Search/Test/Helper/Config/providers/testIsProductSyncEnabled.yaml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ is_production_domain: true
3
+ config_flag: 0 # Disabled
4
+ product_sync_enabled: false
5
+ -
6
+ is_production_domain: true
7
+ config_flag: 1 # Enabled
8
+ product_sync_enabled: true
9
+ -
10
+ is_production_domain: true
11
+ config_flag: 2 # Forced
12
+ product_sync_enabled: true
13
+ -
14
+ is_production_domain: false
15
+ config_flag: 0 # Disabled
16
+ product_sync_enabled: false
17
+ -
18
+ is_production_domain: false
19
+ config_flag: 1 # Enabled
20
+ product_sync_enabled: false
21
+ -
22
+ is_production_domain: false
23
+ config_flag: 2 # Forced
24
+ product_sync_enabled: true
app/code/community/Klevu/Search/Test/Helper/Config/providers/testIsTestModeEnabled.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ test_mode: 0
3
+ is_production_domain: 0
4
+ result: true
5
+ -
6
+ test_mode: 1
7
+ is_production_domain: 0
8
+ result: true
9
+ -
10
+ test_mode: 0
11
+ is_production_domain: 1
12
+ result: false
13
+ -
14
+ test_mode: 1
15
+ is_production_domain: 1
16
+ result: true
app/code/community/Klevu/Search/Test/Helper/Data.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Helper_Data extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /** @var Klevu_Search_Helper_Data $helper */
6
+ protected $helper;
7
+
8
+ protected function setUp() {
9
+ parent::setUp();
10
+
11
+ $this->helper = Mage::helper("klevu_search");
12
+ }
13
+
14
+ /**
15
+ * @test
16
+ * @dataProvider dataProvider
17
+ */
18
+ public function testGetLanguageFromLocale($input, $output) {
19
+ $this->assertEquals($output, $this->helper->getLanguageFromLocale($output));
20
+ }
21
+
22
+ /**
23
+ * @test
24
+ * @dataProvider dataProvider
25
+ */
26
+ public function testIsProductionDomain($domain, $result) {
27
+ $this->assertEquals(
28
+ $result,
29
+ $this->helper->isProductionDomain($domain),
30
+ sprintf("Domain %s should %s a production domain.", $domain, ($result ? "be" : "NOT be"))
31
+ );
32
+ }
33
+
34
+ /**
35
+ * @test
36
+ * @dataProvider dataProvider
37
+ */
38
+ public function testBytesToHumanReadable($input, $output) {
39
+ $this->assertEquals($output, $this->helper->bytesToHumanReadable($input));
40
+ }
41
+
42
+ /**
43
+ * @test
44
+ * @dataProvider dataProvider
45
+ */
46
+ public function testHumanReadableToBytes($input, $output) {
47
+ $this->assertEquals($output, $this->helper->humanReadableToBytes($input));
48
+ }
49
+ }
app/code/community/Klevu/Search/Test/Helper/Data/providers/testBytesToHumanReadable.yaml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ input: 100
3
+ output: 100
4
+ -
5
+ input: 1024
6
+ output: 1k
7
+ -
8
+ input: 1048576
9
+ output: 1M
10
+ -
11
+ input: 1073741824
12
+ output: 1G
13
+ -
14
+ input: 6815744
15
+ output: 6.5M
16
+ -
17
+ input: 24962496
18
+ output: 23.81M
app/code/community/Klevu/Search/Test/Helper/Data/providers/testGetLanguageFromLocale.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ input: en_GB
3
+ output: en
4
+ -
5
+ input: fr_FR
6
+ output: fr
7
+ -
8
+ input: test
9
+ output: test
10
+ -
11
+ input: en
12
+ output: en
app/code/community/Klevu/Search/Test/Helper/Data/providers/testHumanReadableToBytes.yaml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ input: 100
3
+ output: 100
4
+ -
5
+ input: 1k
6
+ output: 1024
7
+ -
8
+ input: 1M
9
+ output: 1048576
10
+ -
11
+ input: 1G
12
+ output: 1073741824
13
+ -
14
+ input: 6.5M
15
+ output: 6815744
16
+ -
17
+ input: 23.81M
18
+ output: 24966595
app/code/community/Klevu/Search/Test/Helper/Data/providers/testIsProductionDomain.yaml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ - www.klevu.com
3
+ - true
4
+ -
5
+ - klevu.co.uk
6
+ - true
7
+ -
8
+ - staging.klevu.com
9
+ - false
10
+ -
11
+ - klevu.dev
12
+ - false
13
+ -
14
+ - klevu.local
15
+ - false
app/code/community/Klevu/Search/Test/Model/Api/Action.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testExecute() {
9
+ $response_model = Mage::getModel('klevu_search/api_response');
10
+ $response_model
11
+ ->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
12
+
13
+ $request_model = $this->getModelMock('klevu_search/api_request', array("send"));
14
+ $request_model
15
+ ->expects($this->once())
16
+ ->method("send")
17
+ ->will($this->returnValue($response_model));
18
+
19
+ $action = Mage::getModel('klevu_search/api_action');
20
+ $action
21
+ ->setRequest($request_model)
22
+ ->setResponse($response_model);
23
+
24
+ $this->assertEquals($response_model, $action->execute());
25
+ }
26
+
27
+ /**
28
+ * @test
29
+ */
30
+ public function testValidate() {
31
+ $errors = array("Test error", "Another test error");
32
+
33
+ $action = $this->getModelMock('klevu_search/api_action', array("validate"));
34
+ $action
35
+ ->expects($this->once())
36
+ ->method("validate")
37
+ ->will($this->returnValue($errors));
38
+
39
+ $request_model = $this->getModelMock('klevu_search/api_request', array("send"));
40
+ $request_model
41
+ ->expects($this->never())
42
+ ->method("send");
43
+
44
+ $action->setRequest($request_model);
45
+
46
+ $response = $action->execute();
47
+
48
+ $this->assertInstanceOf($this->getGroupedClassName("model", "klevu_search/api_response_invalid"), $response);
49
+ $this->assertEquals(
50
+ $errors,
51
+ $response->getErrors(),
52
+ "Returned response model does not contain the validation errors expected."
53
+ );
54
+ }
55
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Addrecords extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ */
9
+ public function testValidateRequiredFields($field) {
10
+ $parameters = $this->getTestParameters();
11
+ unset($parameters[$field]);
12
+
13
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
14
+ $request
15
+ ->expects($this->never())
16
+ ->method("send");
17
+
18
+ $action = Mage::getModel('klevu_search/api_action_addrecords');
19
+ $action
20
+ ->setRequest($request);
21
+
22
+ $response = $action->execute($parameters);
23
+
24
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
25
+
26
+ $this->assertArrayHasKey(
27
+ $field,
28
+ $response->getErrors(),
29
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
30
+ );
31
+ }
32
+
33
+ /**
34
+ * @test
35
+ * @dataProvider dataProvider
36
+ */
37
+ public function testValidateRequiredFieldsRecords($field) {
38
+ $parameters = $this->getTestParameters();
39
+ unset($parameters['records'][0][$field]);
40
+
41
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
42
+ $request
43
+ ->expects($this->never())
44
+ ->method("send");
45
+
46
+ $action = Mage::getModel('klevu_search/api_action_addrecords');
47
+ $action
48
+ ->setRequest($request);
49
+
50
+ $response = $action->execute($parameters);
51
+
52
+ $this->assertInstanceOf(
53
+ "Klevu_Search_Model_Api_Response_Invalid",
54
+ $response,
55
+ sprintf("Failed to assert that validation fails when one of the records is missing a %s field.", $field)
56
+ );
57
+ }
58
+
59
+ /**
60
+ * @test
61
+ * @dataProvider dataProvider
62
+ */
63
+ public function testValidateRequiredFieldsRecordsOptional($field) {
64
+ $parameters = $this->getTestParameters();
65
+ $parameters['records'][0][$field] = "";
66
+
67
+ $response = Mage::getModel('klevu_search/api_response');
68
+ $response
69
+ ->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
70
+
71
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
72
+ $request
73
+ ->expects($this->once())
74
+ ->method("send")
75
+ ->will($this->returnValue($response));
76
+
77
+ $action = Mage::getModel('klevu_search/api_action_addrecords');
78
+ $action
79
+ ->setRequest($request);
80
+
81
+ $this->assertEquals(
82
+ $response,
83
+ $action->execute($parameters),
84
+ sprintf("Failed to assert that validation passes when one of the records is missing the optional %s field.", $field)
85
+ );
86
+ }
87
+
88
+ /**
89
+ * @test
90
+ * @dataProvider dataProvider
91
+ */
92
+ public function testValidateRequiredFieldsRecordsEmpty($field) {
93
+ $parameters = $this->getTestParameters();
94
+ $parameters['records'][0][$field] = "";
95
+
96
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
97
+ $request
98
+ ->expects($this->never())
99
+ ->method("send");
100
+
101
+ $action = Mage::getModel('klevu_search/api_action_addrecords');
102
+ $action
103
+ ->setRequest($request);
104
+
105
+ $response = $action->execute($parameters);
106
+
107
+ $this->assertInstanceOf(
108
+ "Klevu_Search_Model_Api_Response_Invalid",
109
+ $response,
110
+ sprintf("Failed to assert that validation fails when one of the records as an empty %s field.", $field)
111
+ );
112
+ }
113
+
114
+ /**
115
+ * @test
116
+ * @dataProvider dataProvider
117
+ */
118
+ public function testValidateRequiredFieldsRecordsAllowedEmpty($field) {
119
+ $parameters = $this->getTestParameters();
120
+ $parameters['records'][0][$field] = "";
121
+
122
+ $response = Mage::getModel('klevu_search/api_response');
123
+ $response
124
+ ->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
125
+
126
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
127
+ $request
128
+ ->expects($this->once())
129
+ ->method("send")
130
+ ->will($this->returnValue($response));
131
+
132
+ $action = Mage::getModel('klevu_search/api_action_addrecords');
133
+ $action
134
+ ->setRequest($request);
135
+
136
+ $this->assertEquals(
137
+ $response,
138
+ $action->execute($parameters),
139
+ sprintf("Failed to assert that validation passes when one of the records has an empty %s field.", $field)
140
+ );
141
+ }
142
+
143
+ protected function getTestParameters() {
144
+ return array(
145
+ 'sessionId' => "Klevu-session-1234567890",
146
+ 'records' => array(
147
+ array(
148
+ 'id' => "1",
149
+ 'name' => "Test Product",
150
+ 'url' => "http://box.klevu.com/",
151
+ 'image' => "http://box.klevu.com/image.jpg",
152
+ 'salePrice' => "19.99",
153
+ 'startPrice' => "15.99",
154
+ 'toPrice' => "29.99",
155
+ 'currency' => "GBP",
156
+ 'shortDesc' => "A test product",
157
+ 'desc' => "A longer description of the test product",
158
+ 'category' => "Tablets",
159
+ 'listCategory' => array("Electronics", "Tablets", "Sale"),
160
+ 'inStock' => "yes",
161
+ 'brand' => "Klevu",
162
+ 'model' => "X-300",
163
+ 'color' => "blue",
164
+ 'size' => "N/A",
165
+ 'weight' => "100",
166
+ 'other' => array(
167
+ 'modes' => array(
168
+ 'label' => 'Modes',
169
+ 'values' => array("fast", "eco", "precise")
170
+ )
171
+ )
172
+ )
173
+ )
174
+ );
175
+ }
176
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFields.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ -
2
+ field: sessionId
3
+ -
4
+ field: records
app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecords.yaml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ field: id
3
+ -
4
+ field: name
5
+ -
6
+ field: url
7
+ -
8
+ field: salePrice
9
+ -
10
+ field: currency
11
+ -
12
+ field: category
13
+ -
14
+ field: listCategory
app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecordsAllowedEmpty.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ -
2
+ field: category
3
+ -
4
+ field: listCategory
app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecordsEmpty.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ field: id
3
+ -
4
+ field: name
5
+ -
6
+ field: url
7
+ -
8
+ field: salePrice
9
+ -
10
+ field: currency
app/code/community/Klevu/Search/Test/Model/Api/Action/Addrecords/providers/testValidateRequiredFieldsRecordsOptional.yaml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ field: itemGroupId
3
+ -
4
+ field: inStock
5
+ -
6
+ field: startPrice
7
+ -
8
+ field: toPrice
9
+ -
10
+ field: brand
11
+ -
12
+ field: model
13
+ -
14
+ field: color
15
+ -
16
+ field: size
17
+ -
18
+ field: weight
19
+ -
20
+ field: other
21
+ -
22
+ field: shortDesc
23
+ -
24
+ field: desc
25
+ -
26
+ field: image
app/code/community/Klevu/Search/Test/Model/Api/Action/Adduser.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Adduser extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testValidate() {
9
+ $parameters = $this->getTestParameters();
10
+
11
+ $response = Mage::getModel('klevu_search/api_response');
12
+ $response->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
13
+
14
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
15
+ $request
16
+ ->expects($this->once())
17
+ ->method("send")
18
+ ->will($this->returnValue($response));
19
+
20
+ $action = Mage::getModel('klevu_search/api_action_adduser');
21
+ $action
22
+ ->setRequest($request);
23
+
24
+ $this->assertEquals($response, $action->execute($parameters));
25
+ }
26
+
27
+ /**
28
+ * @test
29
+ * @dataProvider dataProvider
30
+ */
31
+ public function testValidateRequiredFields($field) {
32
+ $parameters = $this->getTestParameters();
33
+ unset($parameters[$field]);
34
+
35
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
36
+ $request
37
+ ->expects($this->never())
38
+ ->method("send");
39
+
40
+ $action = Mage::getModel('klevu_search/api_action_adduser');
41
+ $action
42
+ ->setRequest($request);
43
+
44
+ $response = $action->execute($parameters);
45
+
46
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
47
+
48
+ $this->assertArrayHasKey(
49
+ $field,
50
+ $response->getErrors(),
51
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
52
+ );
53
+ }
54
+
55
+ protected function getTestParameters() {
56
+ return array(
57
+ "email" => "test@klevu.com",
58
+ "password" => "password1",
59
+ "url" => "http://www.klevu.com/"
60
+ );
61
+ }
62
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Adduser/providers/testValidateRequiredFields.yaml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ -
2
+ field: email
3
+ -
4
+ field: password
5
+ -
6
+ field: url
app/code/community/Klevu/Search/Test/Model/Api/Action/Addwebstore.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Addwebstore extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testValidate() {
9
+ $parameters = $this->getTestParameters();
10
+
11
+ $response = Mage::getModel('klevu_search/api_response');
12
+ $response->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
13
+
14
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
15
+ $request
16
+ ->expects($this->once())
17
+ ->method("send")
18
+ ->will($this->returnValue($response));
19
+
20
+ $action = Mage::getModel('klevu_search/api_action_addwebstore');
21
+ $action
22
+ ->setRequest($request);
23
+
24
+ $this->assertEquals($response, $action->execute($parameters));
25
+ }
26
+
27
+ /**
28
+ * @test
29
+ * @dataProvider dataProvider
30
+ */
31
+ public function testValidateRequiredFields($field) {
32
+ $parameters = $this->getTestParameters();
33
+ unset($parameters[$field]);
34
+
35
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
36
+ $request
37
+ ->expects($this->never())
38
+ ->method("send");
39
+
40
+ $action = Mage::getModel('klevu_search/api_action_addwebstore');
41
+ $action
42
+ ->setRequest($request);
43
+
44
+ $response = $action->execute($parameters);
45
+
46
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
47
+
48
+ $this->assertArrayHasKey(
49
+ $field,
50
+ $response->getErrors(),
51
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
52
+ );
53
+ }
54
+
55
+ protected function getTestParameters() {
56
+ return array(
57
+ "customerId" => "42",
58
+ "testMode" => "true",
59
+ "storeName" => "Test Store",
60
+ "language" => "en",
61
+ "timezone" => "Europe/London",
62
+ "version" => "1.0.0",
63
+ "locale" => "en_GB",
64
+ "country" => "GB"
65
+ );
66
+ }
67
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Addwebstore/providers/testValidateRequiredFields.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ field: customerId
3
+ -
4
+ field: testMode
5
+ -
6
+ field: storeName
7
+ -
8
+ field: language
9
+ -
10
+ field: timezone
app/code/community/Klevu/Search/Test/Model/Api/Action/Getuserdetail.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Getuserdetail extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testValidate() {
9
+ $parameters = $this->getTestParameters();
10
+
11
+ $response = Mage::getModel('klevu_search/api_response');
12
+ $response->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
13
+
14
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
15
+ $request
16
+ ->expects($this->once())
17
+ ->method("send")
18
+ ->will($this->returnValue($response));
19
+
20
+ $action = Mage::getModel('klevu_search/api_action_getuserdetail');
21
+ $action
22
+ ->setRequest($request);
23
+
24
+ $this->assertEquals($response, $action->execute($parameters));
25
+ }
26
+
27
+ /**
28
+ * @test
29
+ * @dataProvider dataProvider
30
+ */
31
+ public function testValidateRequiredFields($field) {
32
+ $parameters = $this->getTestParameters();
33
+ unset($parameters[$field]);
34
+
35
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
36
+ $request
37
+ ->expects($this->never())
38
+ ->method("send");
39
+
40
+ $action = Mage::getModel('klevu_search/api_action_getuserdetail');
41
+ $action
42
+ ->setRequest($request);
43
+
44
+ $response = $action->execute($parameters);
45
+
46
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
47
+
48
+ $this->assertArrayHasKey(
49
+ $field,
50
+ $response->getErrors(),
51
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
52
+ );
53
+ }
54
+
55
+ protected function getTestParameters() {
56
+ return array(
57
+ 'email' => "test@klevu.com",
58
+ 'password' => "password1"
59
+ );
60
+ }
61
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Getuserdetail/providers/testValidateRequiredFields.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ -
2
+ field: email
3
+ -
4
+ field: password
app/code/community/Klevu/Search/Test/Model/Api/Action/Idsearch.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Idsearch extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * Test that validation passes and successful response is received.
7
+ * @test
8
+ */
9
+ public function testValidate() {
10
+ $parameters = $this->getTestParameters();
11
+
12
+ $response = Mage::getModel('klevu_search/api_response');
13
+ $response->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
14
+
15
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
16
+ $request
17
+ ->expects($this->once())
18
+ ->method("send")
19
+ ->will($this->returnValue($response));
20
+
21
+ $action = Mage::getModel('klevu_search/api_action_idsearch');
22
+ $action
23
+ ->setRequest($request);
24
+
25
+ $this->assertEquals($response, $action->execute($parameters));
26
+ }
27
+
28
+ /**
29
+ * @test
30
+ * @dataProvider dataProvider
31
+ */
32
+ public function testValidateRequiredFields($field) {
33
+ $parameters = $this->getTestParameters();
34
+ unset($parameters[$field]);
35
+
36
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
37
+ $request
38
+ ->expects($this->never())
39
+ ->method("send");
40
+
41
+ $action = Mage::getModel('klevu_search/api_action_idsearch');
42
+ $action
43
+ ->setRequest($request);
44
+
45
+ $response = $action->execute($parameters);
46
+
47
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
48
+
49
+ $this->assertArrayHasKey(
50
+ $field,
51
+ $response->getErrors(),
52
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
53
+ );
54
+ }
55
+
56
+ /**
57
+ * @test
58
+ */
59
+ public function testValidationPaginationStartsFromLessThanZero() {
60
+ $field = 'paginationStartsFrom';
61
+ $parameters = $this->getTestParameters();
62
+ $parameters[$field] = -1;
63
+
64
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
65
+ $request
66
+ ->expects($this->never())
67
+ ->method("send");
68
+
69
+ $action = Mage::getModel('klevu_search/api_action_idsearch');
70
+ $action
71
+ ->setRequest($request);
72
+
73
+ $response = $action->execute($parameters);
74
+
75
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
76
+
77
+ $this->assertArrayHasKey(
78
+ $field,
79
+ $response->getErrors(),
80
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
81
+ );
82
+ }
83
+
84
+ protected function getTestParameters() {
85
+ return array(
86
+ 'ticket' => 'test-search-api-key',
87
+ 'noOfResults' => 30,
88
+ 'term' => 'computers',
89
+ 'paginationStartsFrom' => 0,
90
+ 'ipAddress' => '127.0.0.1',
91
+ 'klevuSort' => 'rel',
92
+ 'enableFilters' => 1,
93
+ 'filterResults' => ''
94
+ );
95
+ }
96
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Idsearch/providers/testValidateRequiredFields.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ field: ticket
3
+ -
4
+ field: noOfResults
5
+ -
6
+ field: term
7
+ -
8
+ field: paginationStartsFrom
9
+ -
10
+ field: klevuSort
11
+ -
12
+ field: enableFilters
app/code/community/Klevu/Search/Test/Model/Api/Action/Producttracking.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Producttracking extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testValidate() {
9
+ $parameters = $this->getTestParameters();
10
+
11
+ $response = Mage::getModel('klevu_search/api_response');
12
+ $response->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
13
+
14
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
15
+ $request
16
+ ->expects($this->once())
17
+ ->method("send")
18
+ ->will($this->returnValue($response));
19
+
20
+ $action = $this->getModel();
21
+ $action
22
+ ->setRequest($request);
23
+
24
+ $this->assertEquals($response, $action->execute($parameters));
25
+ }
26
+
27
+ /**
28
+ * @test
29
+ * @dataProvider dataProvider
30
+ */
31
+ public function testValidateRequiredFields($field) {
32
+ $parameters = $this->getTestParameters();
33
+ unset($parameters[$field]);
34
+
35
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
36
+ $request
37
+ ->expects($this->never())
38
+ ->method("send");
39
+
40
+ $action = $this->getModel();
41
+ $action
42
+ ->setRequest($request);
43
+
44
+ $response = $action->execute($parameters);
45
+
46
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $response);
47
+
48
+ $this->assertArrayHasKey(
49
+ $field,
50
+ $response->getErrors(),
51
+ sprintf("Failed to assert that an error is returned for %s parameter.", $field)
52
+ );
53
+ }
54
+
55
+ /**
56
+ * Return the model being tested.
57
+ *
58
+ * @return Klevu_Search_Model_Api_Action_Producttracking
59
+ */
60
+ protected function getModel() {
61
+ return Mage::getModel('klevu_search/api_action_producttracking');
62
+ }
63
+
64
+ protected function getTestParameters() {
65
+ return array(
66
+ 'klevu_apiKey' => "test-api-key",
67
+ 'klevu_type' => "checkout",
68
+ 'klevu_productId' => 1,
69
+ 'klevu_unit' => 1,
70
+ 'klevu_salePrice' => 100.00,
71
+ 'klevu_currency' => "GBP",
72
+ 'klevu_shopperIP' => "127.0.0.1"
73
+ );
74
+ }
75
+
76
+ }
app/code/community/Klevu/Search/Test/Model/Api/Action/Producttracking/providers/testValidateRequiredFields.yaml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ field: klevu_apiKey
3
+ -
4
+ field: klevu_type
5
+ -
6
+ field: klevu_productId
7
+ -
8
+ field: klevu_unit
9
+ -
10
+ field: klevu_salePrice
11
+ -
12
+ field: klevu_currency
13
+ -
14
+ field: klevu_shopperIP
app/code/community/Klevu/Search/Test/Model/Api/Action/Startsession.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Action_Startsession extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ public function testValidate() {
6
+ $parameters = array(
7
+ 'api_key' => "dGVzdC1hcGkta2V5"
8
+ );
9
+
10
+ $response = Mage::getModel('klevu_search/api_response');
11
+ $response->setRawResponse(new Zend_Http_Response(200, array(), "Test response"));
12
+
13
+ $request = $this->getModelMock('klevu_search/api_request', array("send"));
14
+ $request
15
+ ->expects($this->once())
16
+ ->method("send")
17
+ ->will($this->returnValue($response));
18
+
19
+ $action = Mage::getModel('klevu_search/api_action_startsession');
20
+ $action
21
+ ->setRequest($request);
22
+
23
+ $this->assertEquals($response, $action->execute($parameters));
24
+
25
+ $returned_response = $action->execute(array());
26
+
27
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Invalid", $returned_response);
28
+ $this->assertEquals(array("Missing API key."), $returned_response->getErrors());
29
+ }
30
+ }
app/code/community/Klevu/Search/Test/Model/Api/Request.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Request extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @expectedException Mage_Core_Exception
8
+ */
9
+ public function testSendNoEndpoint() {
10
+ $model = $this->getModelMock('klevu_search/api_request', array("build"));
11
+ $model
12
+ ->expects($this->never())
13
+ ->method("build");
14
+
15
+ $model->send(); // Should throw an exception
16
+ }
17
+
18
+ /**
19
+ * @test
20
+ */
21
+ public function testSendNoResponse() {
22
+ $http_client = $this->getMock("Zend_Http_Client", array("request"));
23
+ $http_client
24
+ ->expects($this->once())
25
+ ->method("request")
26
+ ->will($this->throwException(new Zend_Http_Client_Exception("Test exception")));
27
+
28
+ $model = $this->getModelMock('klevu_search/api_request', array("build"));
29
+ $model
30
+ ->expects($this->once())
31
+ ->method("build")
32
+ ->will($this->returnValue($http_client));
33
+
34
+ $model->setEndpoint("http://test.klevu.com/");
35
+
36
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response_Empty", $model->send());
37
+ }
38
+
39
+ /**
40
+ * @test
41
+ */
42
+ public function testSendValidResponse() {
43
+ $test_raw_response = new Zend_Http_Response(200, array());
44
+
45
+ $http_client = $this->getMock("Zend_Http_Client", array("request"));
46
+ $http_client
47
+ ->expects($this->once())
48
+ ->method("request")
49
+ ->will($this->returnValue($test_raw_response));
50
+
51
+ $response_model = $this->getModelMock('klevu_search/api_response', array("setRawResponse"));
52
+ $response_model
53
+ ->expects($this->once())
54
+ ->method("setRawResponse")
55
+ ->with($this->equalTo($test_raw_response));
56
+
57
+ $request_model = $this->getModelMock('klevu_search/api_request', array("build"));
58
+ $request_model
59
+ ->expects($this->once())
60
+ ->method("build")
61
+ ->will($this->returnValue($http_client));
62
+
63
+ $request_model
64
+ ->setEndpoint("http://test.klevu.com/")
65
+ ->setResponseModel($response_model);
66
+
67
+ $this->assertInstanceOf("Klevu_Search_Model_Api_Response", $request_model->send());
68
+ }
69
+ }
app/code/community/Klevu/Search/Test/Model/Api/Request/Xml.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Request_Xml extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ */
9
+ public function testGetDataAsXml($data, $xml) {
10
+ $request = Mage::getModel('klevu_search/api_request_xml');
11
+
12
+ $request->setData($data);
13
+
14
+ $this->assertEquals($xml, preg_replace("/\n/", "", $request->getDataAsXml()));
15
+ }
16
+ }
app/code/community/Klevu/Search/Test/Model/Api/Request/Xml/providers/testGetDataAsXml.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ data:
3
+ sessionId: Klevu-session-1234567890
4
+ xml: <?xml version="1.0"?><request><sessionId>Klevu-session-1234567890</sessionId></request>
5
+ -
6
+ data:
7
+ sessionId: Klevu-session-1234567890
8
+ records:
9
+ - record:
10
+ pairs:
11
+ - pair:
12
+ key: id
13
+ value: 1
14
+ - pair:
15
+ key: name
16
+ value: Test Product
17
+ - record:
18
+ pairs:
19
+ - pair:
20
+ key: id
21
+ value: 3
22
+ xml: <?xml version="1.0"?><request><sessionId>Klevu-session-1234567890</sessionId><records><record><pairs><pair><key>id</key><value>1</value></pair><pair><key>name</key><value>Test Product</value></pair></pairs></record><record><pairs><pair><key>id</key><value>3</value></pair></pairs></record></records></request>
23
+ -
24
+ data: []
25
+ xml: <?xml version="1.0"?><request/>
app/code/community/Klevu/Search/Test/Model/Api/Response.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Response extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ * @dataProviderFile response_testIsSuccessful.yaml
9
+ */
10
+ public function testIsSuccessful($response_code, $response_data_file, $is_successful) {
11
+ $response_body = ($response_data_file) ? $this->getDataFileContents($response_data_file) : "";
12
+ $http_response = new Zend_Http_Response($response_code, array(), $response_body);
13
+
14
+ $model = Mage::getModel('klevu_search/api_response');
15
+ $model->setRawResponse($http_response);
16
+
17
+ $this->assertEquals($is_successful, $model->isSuccessful());
18
+ }
19
+ }
app/code/community/Klevu/Search/Test/Model/Api/Response/Data.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Response_Data extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ */
9
+ public function testIsSuccessful($response_code, $response_data_file, $is_successful) {
10
+ $http_response = new Zend_Http_Response($response_code, array(), $this->getDataFileContents($response_data_file));
11
+
12
+ $model = Mage::getModel('klevu_search/api_response_data');
13
+ $model->setRawResponse($http_response);
14
+
15
+ $this->assertEquals($is_successful, $model->isSuccessful());
16
+ }
17
+
18
+ /**
19
+ * @test
20
+ */
21
+ public function testData() {
22
+ $http_response = new Zend_Http_Response(200, array(), $this->getDataFileContents("data_response_data.xml"));
23
+
24
+ $model = Mage::getModel('klevu_search/api_response_data');
25
+ $model->setRawResponse($http_response);
26
+
27
+ $this->assertEquals("test", $model->getTest(), "Failed asserting that data gets set on the response.");
28
+ $this->assertNull($model->getResponse(), "Failed asserting that 'response' element gets removed from data.");
29
+ $this->assertEquals("test", $model->getCamelCase(), "Failed asserting that data keys get converted from camel case.");
30
+ }
31
+ }
app/code/community/Klevu/Search/Test/Model/Api/Response/Data/providers/testIsSuccessful.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ response_code: 200
3
+ response_data_file: data_response_success.xml
4
+ is_successful: true
5
+ -
6
+ response_code: 200
7
+ response_data_file: data_response_failure.xml
8
+ is_successful: false
9
+ -
10
+ response_code: 200
11
+ response_data_file: data_response_no_response.xml
12
+ is_successful: false
app/code/community/Klevu/Search/Test/Model/Api/Response/Empty.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Response_Empty extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testIsSuccessful() {
9
+ $model = Mage::getModel('klevu_search/api_response_empty');
10
+
11
+ $this->assertEquals(
12
+ false,
13
+ $model->isSuccessful(),
14
+ "Failed asserting that isSuccessful() returns false when no HTTP response is provided."
15
+ );
16
+
17
+ $model->setRawResponse(new Zend_Http_Response(200, array()));
18
+
19
+ $this->assertEquals(
20
+ false,
21
+ $model->isSuccessful(),
22
+ "Failed asserting that isSuccessful() returns false when given a successful HTTP response."
23
+ );
24
+
25
+ $model->setRawResponse(new Zend_Http_Response(500, array()));
26
+
27
+ $this->assertEquals(
28
+ false,
29
+ $model->isSuccessful(),
30
+ "Failed asserting that isSuccessful() returns false when given an unsuccessful HTTP response."
31
+ );
32
+ }
33
+ }
app/code/community/Klevu/Search/Test/Model/Api/Response/Invalid.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Response_Invalid extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testIsSuccessful() {
9
+ $model = Mage::getModel('klevu_search/api_response_invalid');
10
+
11
+ $this->assertEquals(
12
+ false,
13
+ $model->isSuccessful(),
14
+ "Failed asserting that isSuccessful() returns false when no HTTP response is provided."
15
+ );
16
+
17
+ $model->setRawResponse(new Zend_Http_Response(200, array()));
18
+
19
+ $this->assertEquals(
20
+ false,
21
+ $model->isSuccessful(),
22
+ "Failed asserting that isSuccessful() returns false when given a successful HTTP response."
23
+ );
24
+
25
+ $model->setRawResponse(new Zend_Http_Response(500, array()));
26
+
27
+ $this->assertEquals(
28
+ false,
29
+ $model->isSuccessful(),
30
+ "Failed asserting that isSuccessful() returns false when given an unsuccessful HTTP response."
31
+ );
32
+ }
33
+ }
app/code/community/Klevu/Search/Test/Model/Api/Response/Message.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Response_Message extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ */
9
+ public function testIsSuccessful($response_code, $response_data_file, $is_successful) {
10
+ $http_response = new Zend_Http_Response($response_code, array(), $this->getDataFileContents($response_data_file));
11
+
12
+ $model = Mage::getModel('klevu_search/api_response_message');
13
+ $model->setRawResponse($http_response);
14
+
15
+ $this->assertEquals($is_successful, $model->isSuccessful());
16
+ }
17
+
18
+ /**
19
+ * @test
20
+ */
21
+ public function testGetSessionId() {
22
+ $http_response = new Zend_Http_Response(200, array(), $this->getDataFileContents("message_response_session_id.xml"));
23
+
24
+ $model = Mage::getModel('klevu_search/api_response_message');
25
+ $model->setRawResponse($http_response);
26
+
27
+ $this->assertEquals("Klevu-session-1234567890", $model->getSessionId());
28
+ }
29
+ }
app/code/community/Klevu/Search/Test/Model/Api/Response/Message/providers/testIsSuccessful.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ response_code: 200
3
+ response_data_file: message_response_success.xml
4
+ is_successful: true
5
+ -
6
+ response_code: 200
7
+ response_data_file: message_response_failure.xml
8
+ is_successful: false
9
+ -
10
+ response_code: 200
11
+ response_data_file: message_response_missing_status.xml
12
+ is_successful: false
13
+ -
14
+ response_code: 200
15
+ response_data_file: message_response_missing_message.xml
16
+ is_successful: true
app/code/community/Klevu/Search/Test/Model/Api/Response/Timezone.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Api_Response_Timezone extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ */
9
+ public function testIsSuccessful($response_code, $response_data_file, $is_successful) {
10
+ $http_response = new Zend_Http_Response($response_code, array(), $this->getDataFileContents($response_data_file));
11
+
12
+ $model = Mage::getModel('klevu_search/api_response_timezone');
13
+ $model->setRawResponse($http_response);
14
+
15
+ $this->assertEquals($is_successful, $model->isSuccessful());
16
+ }
17
+ }
app/code/community/Klevu/Search/Test/Model/Api/Response/Timezone/providers/testIsSuccessful.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ response_code: 200
3
+ response_data_file: timezone_response_no_status.xml
4
+ is_successful: true
5
+ -
6
+ response_code: 200
7
+ response_data_file: timezone_response_with_success.xml
8
+ is_successful: true
9
+ -
10
+ response_code: 200
11
+ response_data_file: timezone_response_with_failure.xml
12
+ is_successful: true
13
+ -
14
+ response_code: 200
15
+ response_data_file: timezone_response_no_data.xml
16
+ is_successful: false
app/code/community/Klevu/Search/Test/Model/Api/Response/providers/response_testIsSuccessful.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ response_code: 200
3
+ response_data_file: ""
4
+ is_successful: false
5
+ -
6
+ response_code: 500
7
+ response_data_file: ""
8
+ is_successful: false
9
+ -
10
+ response_code: 200
11
+ response_data_file: "response_valid.xml"
12
+ is_successful: true
13
+ -
14
+ response_code: 200
15
+ response_data_file: "response_malformed.xml"
16
+ is_successful: false
17
+ -
18
+ response_code: 200
19
+ response_data_file: "response_noxml.xml"
20
+ is_successful: false
app/code/community/Klevu/Search/Test/Model/Api/Test/Case.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Klevu_Search_Test_Model_Api_Test_Case extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ protected function getDataFileContents($file) {
6
+ $directory_tree = array(
7
+ Mage::getModuleDir('', 'Klevu_Search'),
8
+ 'Test',
9
+ 'Model',
10
+ 'Api',
11
+ 'data',
12
+ $file
13
+ );
14
+
15
+ $file_path = join(DS, $directory_tree);
16
+
17
+ return file_get_contents($file_path);
18
+ }
19
+
20
+ /**
21
+ * Create a mock class of the given API action model which will expect to be executed
22
+ * once and will return the given response. Then replace that model in Magento with
23
+ * the created mock.
24
+ *
25
+ * @param string $alias A grouped class name of the API action model to mock
26
+ * @param Klevu_Search_Model_Api_Response $response
27
+ *
28
+ * @return $this
29
+ */
30
+ protected function replaceApiActionByMock($alias, $response) {
31
+ $mock = $this->getModelMock($alias, array("execute"));
32
+ $mock
33
+ ->expects($this->once())
34
+ ->method("execute")
35
+ ->will($this->returnValue($response));
36
+
37
+ $this->replaceByMock("model", $alias, $mock);
38
+
39
+ return $this;
40
+ }
41
+
42
+ /**
43
+ * Create a mock class of the given session model, disabling session initialisation, and replace
44
+ * that model in Magento with the created mock.
45
+ *
46
+ * @param string $alias A grouped class name of the session model to mock.
47
+ *
48
+ * @return $this
49
+ */
50
+ protected function replaceSessionByMock($alias) {
51
+ $session_mock = $this->getModelMockBuilder($alias)
52
+ ->disableOriginalConstructor()
53
+ ->setMethods(array("init"))
54
+ ->getMock();
55
+
56
+ $session_mock
57
+ ->expects($this->any())
58
+ ->method("init")
59
+ ->will($this->returnSelf());
60
+
61
+ $this->replaceByMock("model", $alias, $session_mock);
62
+ $this->replaceByMock("singleton", $alias, $session_mock);
63
+ }
64
+ }
app/code/community/Klevu/Search/Test/Model/Api/data/data_response_data.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ <test>test</test>
4
+ <camelCase>test</camelCase>
5
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/data_response_failure.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <data>
2
+ <response>failure</response>
3
+ <message>Test failure message</message>
4
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/data_response_no_response.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <data>
2
+ <message>Test message</message>
3
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/data_response_success.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ <customerId>116</customerId>
4
+ <webstores>
5
+ <webstore>
6
+ <jsApiKey>klevu-1396353366396116</jsApiKey>
7
+ <restApiKey>M5NjM1MzM2NjM5NjExNjpLbGV2dS1nbWFpMjZ0djE2</restApiKey>
8
+ <storeName>storeName</storeName>
9
+ <testAccountEnabled>true</testAccountEnabled>
10
+ </webstore>
11
+ <webstore>
12
+ <jsApiKey>klevu-1396353407457116</jsApiKey>
13
+ <restApiKey>M5NjM1MzQwNzQ1NzExNjpLbGV2dS1ka2hzMjZqYnNx</restApiKey>
14
+ <storeName>storeName</storeName>
15
+ <testAccountEnabled>true</testAccountEnabled>
16
+ </webstore>
17
+ </webstores>
18
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/data_response_success_only.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/message_response_failure.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <message>
2
+ <status>FAILURE</status>
3
+ <msg>Failed test status</msg>
4
+ </message>
app/code/community/Klevu/Search/Test/Model/Api/data/message_response_missing_message.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <message>
2
+ <status>SUCCESS</status>
3
+ </message>
app/code/community/Klevu/Search/Test/Model/Api/data/message_response_missing_status.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <message>
2
+ <msg>Test response with no status</msg>
3
+ </message>
app/code/community/Klevu/Search/Test/Model/Api/data/message_response_session_id.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <message>
2
+ <status>SUCCESS</status>
3
+ <sessionId>Klevu-session-1234567890</sessionId>
4
+ </message>
app/code/community/Klevu/Search/Test/Model/Api/data/message_response_success.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <message>
2
+ <status>SUCCESS</status>
3
+ <msg>Successful test status</msg>
4
+ </message>
app/code/community/Klevu/Search/Test/Model/Api/data/response_malformed.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ </message>
4
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/response_noxml.xml ADDED
@@ -0,0 +1 @@
 
1
+ TEST
app/code/community/Klevu/Search/Test/Model/Api/data/response_valid.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ <message>Successful test response</message>
4
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/search_response_empty.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <data>
3
+ <meta>
4
+ <totalResultsFound>0</totalResultsFound>
5
+ <typeOfQuery>WILDCARD_AND</typeOfQuery>
6
+ <paginationStartFrom>0</paginationStartFrom>
7
+ <noOfResults>9</noOfResults>
8
+ </meta>
9
+ <filters>
10
+ <filter type="other" label="CATEGORY" key="category">
11
+ <option name="Default Category " value="category:shirts" count="3" selected="false"/>
12
+ </filter>
13
+ <filter type="other" label="PRICE RANGE" key="Price Range">
14
+ <option name="0 - 49" value="klevu_price:0 - 49" count="3" selected="false" />
15
+ <option name="0 - 49" value="klevu_price:50 - 99" count="4" selected="false" />
16
+ </filter>
17
+ </filters>
18
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/search_response_paged.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <data>
3
+ <result>
4
+ <id>3</id>
5
+ <itemGroupId>3</itemGroupId>
6
+ </result>
7
+ <meta>
8
+ <totalResultsFound>10</totalResultsFound>
9
+ <typeOfQuery>WILDCARD_AND</typeOfQuery>
10
+ <paginationStartFrom>9</paginationStartFrom>
11
+ <noOfResults>9</noOfResults>
12
+ </meta>
13
+ <filters>
14
+ <filter type="other" label="CATEGORY" key="category">
15
+ <option name="Default Category " value="category:shirts" count="3" selected="false"/>
16
+ </filter>
17
+ <filter type="other" label="PRICE RANGE" key="Price Range">
18
+ <option name="0 - 49" value="klevu_price:0 - 49" count="3" selected="false" />
19
+ <option name="0 - 49" value="klevu_price:50 - 99" count="4" selected="false" />
20
+ </filter>
21
+ </filters>
22
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/search_response_sorted.xml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <data>
3
+ <result>
4
+ <id>2</id>
5
+ <itemGroupId>2</itemGroupId>
6
+ </result>
7
+ <result>
8
+ <id>1</id>
9
+ <itemGroupId>1</itemGroupId>
10
+ </result>
11
+ <result>
12
+ <id>3</id>
13
+ <itemGroupId>3</itemGroupId>
14
+ </result>
15
+ <meta>
16
+ <totalResultsFound>3</totalResultsFound>
17
+ <typeOfQuery>WILDCARD_AND</typeOfQuery>
18
+ <paginationStartFrom>0</paginationStartFrom>
19
+ <noOfResults>9</noOfResults>
20
+ </meta>
21
+ <filters>
22
+ <filter type="other" label="CATEGORY" key="category">
23
+ <option name="Default Category " value="category:shirts" count="3" selected="false"/>
24
+ </filter>
25
+ <filter type="other" label="PRICE RANGE" key="Price Range">
26
+ <option name="0 - 49" value="klevu_price:0 - 49" count="3" selected="false" />
27
+ <option name="0 - 49" value="klevu_price:50 - 99" count="4" selected="false" />
28
+ </filter>
29
+ </filters>
30
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/search_response_success.xml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <data>
3
+ <result>
4
+ <id>3</id>
5
+ <itemGroupId>3</itemGroupId>
6
+ </result>
7
+ <result>
8
+ <id>2</id>
9
+ <itemGroupId>2</itemGroupId>
10
+ </result>
11
+ <result>
12
+ <id>1</id>
13
+ <itemGroupId>1</itemGroupId>
14
+ </result>
15
+ <meta>
16
+ <totalResultsFound>3</totalResultsFound>
17
+ <typeOfQuery>WILDCARD_AND</typeOfQuery>
18
+ <paginationStartFrom>0</paginationStartFrom>
19
+ <noOfResults>9</noOfResults>
20
+ </meta>
21
+ <filters>
22
+ <filter type="other" label="CATEGORY" key="category">
23
+ <option name="Test Category " value="category:shirts" count="3" selected="false"/>
24
+ </filter>
25
+ <filter type="other" label="PRICE RANGE" key="Price Range">
26
+ <option name="0 - 49" value="klevu_price:0 - 49" count="3" selected="false" />
27
+ <option name="0 - 49" value="klevu_price:50 - 99" count="4" selected="false" />
28
+ </filter>
29
+ </filters>
30
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/startsession_response_success.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <message>
3
+ <status>SUCCESS</status>
4
+ <sessionId>Klevu_251-klevu-139811752368742_1399297005734</sessionId>
5
+ </message>
app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_no_data.xml ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_no_status.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <data>
2
+ <timezone>Europe/London</timezone>
3
+ <timezone>GMT</timezone>
4
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_with_failure.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <data>
2
+ <response>failure</response>
3
+ <timezone>Europe/London</timezone>
4
+ <timezone>GMT</timezone>
5
+ </data>
app/code/community/Klevu/Search/Test/Model/Api/data/timezone_response_with_success.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <data>
2
+ <response>success</response>
3
+ <timezone>Europe/London</timezone>
4
+ <timezone>GMT</timezone>
5
+ </data>
app/code/community/Klevu/Search/Test/Model/Config/Log/Level.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Config_Log_Level extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ */
8
+ public function testGetValue() {
9
+ $model = Mage::getModel('klevu_search/config_log_level');
10
+
11
+ // Test the default value
12
+ $this->assertEquals(Zend_Log::WARN, $model->getValue(), "getValue() returned an incorrect default value.");
13
+
14
+ // Test a set value
15
+ $model->setValue(Zend_Log::INFO);
16
+
17
+ $this->assertEquals(Zend_Log::INFO, $model->getValue(), "getValue() didn't return the value set.");
18
+ }
19
+ }
app/code/community/Klevu/Search/Test/Model/Notification.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Notification extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ protected function tearDown() {
6
+ $resource = Mage::getModel("core/resource");
7
+ $resource->getConnection("core_write")->delete($resource->getTableName("klevu_search/notification"));
8
+
9
+ parent::tearDown();
10
+ }
11
+
12
+ /**
13
+ * @test
14
+ * @loadFixture
15
+ */
16
+ public function testLoad() {
17
+ $notification = Mage::getModel("klevu_search/notification")->load(1);
18
+
19
+ $this->assertEquals(1, $notification->getId());
20
+ $this->assertEquals("2014-05-13 11:08:00", $notification->getDate());
21
+ $this->assertEquals("test", $notification->getType());
22
+ $this->assertEquals("Testing", $notification->getMessage());
23
+ }
24
+
25
+ /**
26
+ * @test
27
+ */
28
+ public function testSave() {
29
+ $notification = Mage::getModel("klevu_search/notification");
30
+
31
+ $notification->setData(array(
32
+ "type" => "test",
33
+ "message" => "Testing"
34
+ ));
35
+
36
+ $notification->save();
37
+
38
+ $this->assertNotNull($notification->getId());
39
+
40
+ $result = Mage::getModel("klevu_search/notification")->load($notification->getId());
41
+
42
+ $this->assertEquals($result->getId(), $result->getId());
43
+ $this->assertNotNull($result->getDate());
44
+ $this->assertEquals("test", $result->getType());
45
+ $this->assertEquals("Testing", $result->getMessage());
46
+ }
47
+ }
app/code/community/Klevu/Search/Test/Model/Notification/fixtures/testLoad.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ tables:
2
+ klevu_search/notification:
3
+ -
4
+ id: 1
5
+ date: 2014-05-13 11:08:00
6
+ type: test
7
+ message: Testing
app/code/community/Klevu/Search/Test/Model/Observer.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Observer extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ public function setUp() {
6
+ parent::setUp();
7
+
8
+ $collection = $this->getProductSyncCronScheduleCollection();
9
+ foreach ($collection as $item) {
10
+ $item->delete();
11
+ }
12
+
13
+ $collection = $this->getOrderSyncCronScheduleCollection();
14
+ foreach ($collection as $item) {
15
+ $item->delete();
16
+ }
17
+ }
18
+
19
+ public function tearDown() {
20
+ $collection = $this->getProductSyncCronScheduleCollection();
21
+ foreach ($collection as $item) {
22
+ $item->delete();
23
+ }
24
+
25
+ $resource = Mage::getModel("core/resource");
26
+ $resource->getConnection("core_write")->delete($resource->getTableName("klevu_search/order_sync"));
27
+
28
+ parent::tearDown();
29
+ }
30
+
31
+ public function testScheduleProductSync() {
32
+ $observer = Mage::getModel("klevu_search/observer");
33
+
34
+ $observer->scheduleProductSync(new Varien_Event_Observer());
35
+
36
+ $this->assertEquals(1, $this->getProductSyncCronScheduleCollection()->getSize(),
37
+ "Failed to assert that scheduleProductSync() schedules the Product Sync cron when called.");
38
+ }
39
+
40
+ /**
41
+ * @test
42
+ * @loadFixture
43
+ */
44
+ public function testScheduleOrderSync() {
45
+ $model = Mage::getModel("klevu_search/observer");
46
+
47
+ $order = Mage::getModel("sales/order")->load(1);
48
+ $event = new Varien_Event();
49
+ $event->addData(array(
50
+ "event_name" => "sales_order_place_after",
51
+ "order" => $order
52
+ ));
53
+ $observer = new Varien_Event_Observer();
54
+ $observer->addData(array("event" => $event));
55
+
56
+ $model->scheduleOrderSync($observer);
57
+
58
+ $this->assertEquals(array(array("order_item_id" => "2")), $this->getOrderSyncQueue());
59
+
60
+ $this->assertEquals(1, $this->getOrderSyncCronScheduleCollection()->getSize(),
61
+ "Failed to assert that scheduleOrderSync() schedules the Order Sync cron when called."
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Return a cron/schedule collection filtered for Product Sync jobs only.
67
+ *
68
+ * @return Mage_Cron_Model_Mysql4_Schedule_Collection
69
+ */
70
+ protected function getProductSyncCronScheduleCollection() {
71
+ return Mage::getResourceModel("cron/schedule_collection")
72
+ ->addFieldToFilter("job_code", Mage::getModel("klevu_search/product_sync")->getJobCode());
73
+ }
74
+
75
+ /**
76
+ * Return a cron/schedule collection filtered for Order Sync jobs only.
77
+ *
78
+ * @return Mage_Cron_Model_Mysql4_Schedule_Collection
79
+ */
80
+ protected function getOrderSyncCronScheduleCollection() {
81
+ return Mage::getResourceModel("cron/schedule_collection")
82
+ ->addFieldToFilter("job_code", Mage::getModel("klevu_search/order_sync")->getJobCode());
83
+ }
84
+
85
+ /**
86
+ * Return all items in the Order Sync queue.
87
+ *
88
+ * @return array
89
+ */
90
+ protected function getOrderSyncQueue() {
91
+ $resource = Mage::getModel("core/resource");
92
+ $connection = $resource->getConnection("core_write");
93
+ return $connection->fetchAll($connection
94
+ ->select()
95
+ ->from($resource->getTableName("klevu_search/order_sync"))
96
+ );
97
+ }
98
+ }
app/code/community/Klevu/Search/Test/Model/Observer/fixtures/testScheduleOrderSync.yaml ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_js_api_key: test-api-key
5
+ default/klevu_search/order_sync/enabled: 2
6
+ tables:
7
+ sales_flat_order:
8
+ -
9
+ entity_id: 1
10
+ state: new
11
+ status: pending
12
+ coupon_code: null
13
+ protect_code: bbc820
14
+ shipping_description: Flat Rate - Fixed
15
+ is_virtual: 0
16
+ store_id: 1
17
+ customer_id: null
18
+ base_discount_amount: 0
19
+ base_discount_canceled: null
20
+ base_discount_invoiced: null
21
+ base_discount_refunded: null
22
+ base_grand_total: 139.99
23
+ base_shipping_amount: 5
24
+ base_shipping_canceled: null
25
+ base_shipping_invoiced: null
26
+ base_shipping_refunded: null
27
+ base_shipping_tax_amount: 0
28
+ base_shipping_tax_refunded: null
29
+ base_subtotal: 134.99
30
+ base_subtotal_canceled: null
31
+ base_subtotal_invoiced: null
32
+ base_subtotal_refunded: null
33
+ base_tax_amount: 0
34
+ base_tax_canceled: null
35
+ base_tax_invoiced: null
36
+ base_tax_refunded: null
37
+ base_to_global_rate: 1
38
+ base_to_order_rate: 1
39
+ base_total_canceled: null
40
+ base_total_invoiced: null
41
+ base_total_invoiced_cost: null
42
+ base_total_offline_refunded: null
43
+ base_total_online_refunded: null
44
+ base_total_paid: null
45
+ base_total_qty_ordered: null
46
+ base_total_refunded: null
47
+ discount_amount: 0
48
+ discount_canceled: null
49
+ discount_invoiced: null
50
+ discount_refunded: null
51
+ grand_total: 139.99
52
+ shipping_amount: 5
53
+ shipping_canceled: null
54
+ shipping_invoiced: null
55
+ shipping_refunded: null
56
+ shipping_tax_amount: 0
57
+ shipping_tax_refunded: null
58
+ store_to_base_rate: 1
59
+ store_to_order_rate: 1
60
+ subtotal: 134.99
61
+ subtotal_canceled: null
62
+ subtotal_invoiced: null
63
+ subtotal_refunded: null
64
+ tax_amount: 0
65
+ tax_canceled: null
66
+ tax_invoiced: null
67
+ tax_refunded: null
68
+ total_canceled: null
69
+ total_invoiced: null
70
+ total_offline_refunded: null
71
+ total_online_refunded: null
72
+ total_paid: null
73
+ total_qty_ordered: 1
74
+ total_refunded: null
75
+ can_ship_partially: null
76
+ can_ship_partially_item: null
77
+ customer_is_guest: 1
78
+ customer_note_notify: 1
79
+ billing_address_id: 1
80
+ customer_group_id: 0
81
+ edit_increment: null
82
+ email_sent: 1
83
+ forced_shipment_with_invoice: null
84
+ gift_message_id: null
85
+ payment_auth_expiration: null
86
+ paypal_ipn_customer_notified: null
87
+ quote_address_id: null
88
+ quote_id: 3
89
+ shipping_address_id: 2
90
+ adjustment_negative: null
91
+ adjustment_positive: null
92
+ base_adjustment_negative: null
93
+ base_adjustment_positive: null
94
+ base_shipping_discount_amount: 0
95
+ base_subtotal_incl_tax: 134.99
96
+ base_total_due: null
97
+ payment_authorization_amount: null
98
+ shipping_discount_amount: 0
99
+ subtotal_incl_tax: 134.99
100
+ total_due: null
101
+ weight: 3
102
+ customer_dob: null
103
+ increment_id: 100000001
104
+ applied_rule_ids: null
105
+ base_currency_code: GBP
106
+ customer_email: tomas.gerulaitis@meanbee.com
107
+ customer_firstname: Tomas
108
+ customer_lastname: Gerulaitis
109
+ customer_middlename: null
110
+ customer_prefix: null
111
+ customer_suffix: null
112
+ customer_taxvat: null
113
+ discount_description: null
114
+ ext_customer_id: null
115
+ ext_order_id: null
116
+ global_currency_code: GBP
117
+ hold_before_state: null
118
+ hold_before_status: null
119
+ order_currency_code: GBP
120
+ original_increment_id: null
121
+ relation_child_id: null
122
+ relation_child_real_id: null
123
+ relation_parent_id: null
124
+ relation_parent_real_id: null
125
+ remote_ip: 127.0.0.1
126
+ shipping_method: flatrate_flatrate
127
+ store_currency_code: GBP
128
+ store_name: Main Website, Main Store, English
129
+ x_forwarded_for: null
130
+ customer_note: null
131
+ created_at: 2014-05-15 16:05:21
132
+ updated_at: 2014-05-15 16:05:21
133
+ total_item_count: 1
134
+ customer_gender: null
135
+ base_custbalance_amount: null
136
+ currency_base_id: null
137
+ currency_code: null
138
+ currency_rate: null
139
+ custbalance_amount: null
140
+ is_hold: null
141
+ is_multi_payment: null
142
+ real_order_id: null
143
+ tax_percent: null
144
+ tracking_numbers: null
145
+ hidden_tax_amount: 0
146
+ base_hidden_tax_amount: 0
147
+ shipping_hidden_tax_amount: 0
148
+ base_shipping_hidden_tax_amnt: 0
149
+ hidden_tax_invoiced: null
150
+ base_hidden_tax_invoiced: null
151
+ hidden_tax_refunded: null
152
+ base_hidden_tax_refunded: null
153
+ shipping_incl_tax: 5
154
+ base_shipping_incl_tax: 5
155
+ coupon_rule_name: null
156
+ sales_flat_order_item:
157
+ -
158
+ item_id: 1
159
+ order_id: 1
160
+ parent_item_id: null
161
+ quote_item_id: 1
162
+ store_id: 1
163
+ created_at: 2014-05-15 16:05:21
164
+ updated_at: 2014-05-15 16:05:21
165
+ product_id: 93
166
+ product_type: configurable
167
+ product_options: a:6:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}s:15:"attributes_info";a:1:{i:0;a:2:{s:5:"label";s:9:"Shoe Size";s:5:"value";s:1:"8";}}s:11:"simple_name";s:30:"ASICS® Men's GEL-Kayano® XII";s:10:"simple_sku";s:5:"asc_8";s:20:"product_calculations";i:1;s:13:"shipment_type";i:0;}
168
+ weight: 3
169
+ is_virtual: 0
170
+ sku: asc_8
171
+ name: ASICS® Men's GEL-Kayano® XII
172
+ description: null
173
+ applied_rule_ids: null
174
+ additional_data: null
175
+ free_shipping: 0
176
+ is_qty_decimal: 0
177
+ no_discount: 0
178
+ qty_backordered: null
179
+ qty_canceled: 0
180
+ qty_invoiced: 0
181
+ qty_ordered: 1
182
+ qty_refunded: 0
183
+ qty_shipped: 0
184
+ base_cost: 29.99
185
+ price: 134.99
186
+ base_price: 134.99
187
+ original_price: 134.99
188
+ base_original_price: 134.99
189
+ tax_percent: 0
190
+ tax_amount: 0
191
+ base_tax_amount: 0
192
+ tax_invoiced: 0
193
+ base_tax_invoiced: 0
194
+ discount_percent: 0
195
+ discount_amount: 0
196
+ base_discount_amount: 0
197
+ discount_invoiced: 0
198
+ base_discount_invoiced: 0
199
+ amount_refunded: 0
200
+ base_amount_refunded: 0
201
+ row_total: 134.99
202
+ base_row_total: 134.99
203
+ row_invoiced: 0
204
+ base_row_invoiced: 0
205
+ row_weight: 3
206
+ gift_message_id: null
207
+ gift_message_available: null
208
+ base_tax_before_discount: null
209
+ tax_before_discount: null
210
+ weee_tax_applied: a:0:{}
211
+ weee_tax_applied_amount: 0
212
+ weee_tax_applied_row_amount: 0
213
+ base_weee_tax_applied_amount: 0
214
+ base_weee_tax_applied_row_amnt: 0
215
+ weee_tax_disposition: 0
216
+ weee_tax_row_disposition: 0
217
+ base_weee_tax_disposition: 0
218
+ base_weee_tax_row_disposition: 0
219
+ ext_order_item_id: null
220
+ locked_do_invoice: null
221
+ locked_do_ship: null
222
+ price_incl_tax: 134.99
223
+ base_price_incl_tax: 134.99
224
+ row_total_incl_tax: 134.99
225
+ base_row_total_incl_tax: 134.99
226
+ hidden_tax_amount: 0
227
+ base_hidden_tax_amount: 0
228
+ hidden_tax_invoiced: null
229
+ base_hidden_tax_invoiced: null
230
+ hidden_tax_refunded: null
231
+ base_hidden_tax_refunded: null
232
+ is_nominal: 0
233
+ tax_canceled: null
234
+ hidden_tax_canceled: null
235
+ tax_refunded: null
236
+ base_tax_refunded: null
237
+ discount_refunded: null
238
+ base_discount_refunded: null
239
+ -
240
+ item_id: 2
241
+ order_id: 1
242
+ parent_item_id: 1
243
+ quote_item_id: 2
244
+ store_id: 1
245
+ created_at: 2014-05-15 16:05:21
246
+ updated_at: 2014-05-15 16:05:21
247
+ product_id: 30
248
+ product_type: simple
249
+ product_options: a:1:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}}
250
+ weight: 3
251
+ is_virtual: 0
252
+ sku: asc_8
253
+ name: ASICS® Men's GEL-Kayano® XII
254
+ description: null
255
+ applied_rule_ids: null
256
+ additional_data: null
257
+ free_shipping: 0
258
+ is_qty_decimal: 0
259
+ no_discount: 0
260
+ qty_backordered: null
261
+ qty_canceled: 0
262
+ qty_invoiced: 0
263
+ qty_ordered: 1
264
+ qty_refunded: 0
265
+ qty_shipped: 0
266
+ base_cost: 29.99
267
+ price: 0
268
+ base_price: 0
269
+ original_price: 0
270
+ base_original_price: null
271
+ tax_percent: 0
272
+ tax_amount: 0
273
+ base_tax_amount: 0
274
+ tax_invoiced: 0
275
+ base_tax_invoiced: 0
276
+ discount_percent: 0
277
+ discount_amount: 0
278
+ base_discount_amount: 0
279
+ discount_invoiced: 0
280
+ base_discount_invoiced: 0
281
+ amount_refunded: 0
282
+ base_amount_refunded: 0
283
+ row_total: 0
284
+ base_row_total: 0
285
+ row_invoiced: 0
286
+ base_row_invoiced: 0
287
+ row_weight: 0
288
+ gift_message_id: null
289
+ gift_message_available: null
290
+ base_tax_before_discount: null
291
+ tax_before_discount: null
292
+ weee_tax_applied: a:0:{}
293
+ weee_tax_applied_amount: 0
294
+ weee_tax_applied_row_amount: 0
295
+ base_weee_tax_applied_amount: 0
296
+ base_weee_tax_applied_row_amnt: null
297
+ weee_tax_disposition: 0
298
+ weee_tax_row_disposition: 0
299
+ base_weee_tax_disposition: 0
300
+ base_weee_tax_row_disposition: 0
301
+ ext_order_item_id: null
302
+ locked_do_invoice: null
303
+ locked_do_ship: null
304
+ price_incl_tax: null
305
+ base_price_incl_tax: null
306
+ row_total_incl_tax: null
307
+ base_row_total_incl_tax: null
308
+ hidden_tax_amount: null
309
+ base_hidden_tax_amount: null
310
+ hidden_tax_invoiced: null
311
+ base_hidden_tax_invoiced: null
312
+ hidden_tax_refunded: null
313
+ base_hidden_tax_refunded: null
314
+ is_nominal: 0
315
+ tax_canceled: null
316
+ hidden_tax_canceled: null
317
+ tax_refunded: null
318
+ base_tax_refunded: null
319
+ discount_refunded: null
320
+ base_discount_refunded: null
app/code/community/Klevu/Search/Test/Model/Order/Sync.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Order_Sync extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ protected function tearDown() {
6
+ $resource = Mage::getModel("core/resource");
7
+ $connection = $resource->getConnection("core_write");
8
+
9
+ $connection->delete($resource->getTableName("klevu_search/order_sync"));
10
+
11
+ parent::tearDown();
12
+ }
13
+
14
+ /**
15
+ * @test
16
+ * @loadFixture
17
+ */
18
+ public function testAddOrderToQueue() {
19
+ $order = Mage::getModel("sales/order");
20
+ $order->load(1);
21
+
22
+ $model = Mage::getModel("klevu_search/order_sync");
23
+ $model->addOrderToQueue($order);
24
+
25
+ $this->assertEquals(array(array("order_item_id" => "2")), $this->getOrderSyncTableContents(),
26
+ "Failed asserting that addOrderToQueue() adds the child configurable item to Order Sync queue."
27
+ );
28
+ }
29
+
30
+ /**
31
+ * @test
32
+ * @loadFixture
33
+ */
34
+ public function testClearQueue() {
35
+ $model = Mage::getModel("klevu_search/order_sync");
36
+
37
+ $model->clearQueue(1);
38
+
39
+ $this->assertEquals(array(array("order_item_id" => "3")), $this->getOrderSyncTableContents(),
40
+ "Failed asserting that clearQueue() only removes order items for the store given."
41
+ );
42
+
43
+ $model->clearQueue();
44
+
45
+ $this->assertEmpty($this->getOrderSyncTableContents(),
46
+ "Failed asserting that clearQueue() removes all items if no store is given."
47
+ );
48
+ }
49
+
50
+ /**
51
+ * @test
52
+ * @loadFixture
53
+ */
54
+ public function testRun() {
55
+ $this->replaceApiActionByMock(
56
+ "klevu_search/api_action_producttracking",
57
+ Mage::getModel("klevu_search/api_response_data")->setRawResponse(
58
+ new Zend_Http_Response(200, array(), $this->getDataFileContents("data_response_success_only.xml"))
59
+ )
60
+ );
61
+
62
+ $model = $this->getModelMock("klevu_search/order_sync", array("isRunning", "isBelowMemoryLimit"));
63
+ $model
64
+ ->expects($this->any())
65
+ ->method("isRunning")
66
+ ->will($this->returnValue(false));
67
+ $model
68
+ ->expects($this->any())
69
+ ->method("isBelowMemoryLimit")
70
+ ->will($this->returnValue(true));
71
+
72
+ $model->run();
73
+
74
+ $this->assertEmpty($this->getOrderSyncTableContents(),
75
+ "Failed asserting that order item gets removed from the sync queue."
76
+ );
77
+ }
78
+
79
+ protected function getOrderSyncTableContents($where = null) {
80
+ $resource = Mage::getModel("core/resource");
81
+ $connection = $resource->getConnection("core_write");
82
+
83
+ $select = $connection->select()->from($resource->getTableName("klevu_search/order_sync"));
84
+ if ($where) {
85
+ $select->where($where);
86
+ }
87
+
88
+ return $connection->fetchAll($select);
89
+ }
90
+ }
app/code/community/Klevu/Search/Test/Model/Order/Sync/fixtures/testAddOrderToQueue.yaml ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/js_api_key: test-api-key
4
+ default/klevu_search/order_sync/enabled: 2
5
+ tables:
6
+ sales_flat_order:
7
+ -
8
+ entity_id: 1
9
+ state: new
10
+ status: pending
11
+ coupon_code: null
12
+ protect_code: bbc820
13
+ shipping_description: Flat Rate - Fixed
14
+ is_virtual: 0
15
+ store_id: 1
16
+ customer_id: null
17
+ base_discount_amount: 0
18
+ base_discount_canceled: null
19
+ base_discount_invoiced: null
20
+ base_discount_refunded: null
21
+ base_grand_total: 139.99
22
+ base_shipping_amount: 5
23
+ base_shipping_canceled: null
24
+ base_shipping_invoiced: null
25
+ base_shipping_refunded: null
26
+ base_shipping_tax_amount: 0
27
+ base_shipping_tax_refunded: null
28
+ base_subtotal: 134.99
29
+ base_subtotal_canceled: null
30
+ base_subtotal_invoiced: null
31
+ base_subtotal_refunded: null
32
+ base_tax_amount: 0
33
+ base_tax_canceled: null
34
+ base_tax_invoiced: null
35
+ base_tax_refunded: null
36
+ base_to_global_rate: 1
37
+ base_to_order_rate: 1
38
+ base_total_canceled: null
39
+ base_total_invoiced: null
40
+ base_total_invoiced_cost: null
41
+ base_total_offline_refunded: null
42
+ base_total_online_refunded: null
43
+ base_total_paid: null
44
+ base_total_qty_ordered: null
45
+ base_total_refunded: null
46
+ discount_amount: 0
47
+ discount_canceled: null
48
+ discount_invoiced: null
49
+ discount_refunded: null
50
+ grand_total: 139.99
51
+ shipping_amount: 5
52
+ shipping_canceled: null
53
+ shipping_invoiced: null
54
+ shipping_refunded: null
55
+ shipping_tax_amount: 0
56
+ shipping_tax_refunded: null
57
+ store_to_base_rate: 1
58
+ store_to_order_rate: 1
59
+ subtotal: 134.99
60
+ subtotal_canceled: null
61
+ subtotal_invoiced: null
62
+ subtotal_refunded: null
63
+ tax_amount: 0
64
+ tax_canceled: null
65
+ tax_invoiced: null
66
+ tax_refunded: null
67
+ total_canceled: null
68
+ total_invoiced: null
69
+ total_offline_refunded: null
70
+ total_online_refunded: null
71
+ total_paid: null
72
+ total_qty_ordered: 1
73
+ total_refunded: null
74
+ can_ship_partially: null
75
+ can_ship_partially_item: null
76
+ customer_is_guest: 1
77
+ customer_note_notify: 1
78
+ billing_address_id: 1
79
+ customer_group_id: 0
80
+ edit_increment: null
81
+ email_sent: 1
82
+ forced_shipment_with_invoice: null
83
+ gift_message_id: null
84
+ payment_auth_expiration: null
85
+ paypal_ipn_customer_notified: null
86
+ quote_address_id: null
87
+ quote_id: 3
88
+ shipping_address_id: 2
89
+ adjustment_negative: null
90
+ adjustment_positive: null
91
+ base_adjustment_negative: null
92
+ base_adjustment_positive: null
93
+ base_shipping_discount_amount: 0
94
+ base_subtotal_incl_tax: 134.99
95
+ base_total_due: null
96
+ payment_authorization_amount: null
97
+ shipping_discount_amount: 0
98
+ subtotal_incl_tax: 134.99
99
+ total_due: null
100
+ weight: 3
101
+ customer_dob: null
102
+ increment_id: 100000001
103
+ applied_rule_ids: null
104
+ base_currency_code: GBP
105
+ customer_email: tomas.gerulaitis@meanbee.com
106
+ customer_firstname: Tomas
107
+ customer_lastname: Gerulaitis
108
+ customer_middlename: null
109
+ customer_prefix: null
110
+ customer_suffix: null
111
+ customer_taxvat: null
112
+ discount_description: null
113
+ ext_customer_id: null
114
+ ext_order_id: null
115
+ global_currency_code: GBP
116
+ hold_before_state: null
117
+ hold_before_status: null
118
+ order_currency_code: GBP
119
+ original_increment_id: null
120
+ relation_child_id: null
121
+ relation_child_real_id: null
122
+ relation_parent_id: null
123
+ relation_parent_real_id: null
124
+ remote_ip: 127.0.0.1
125
+ shipping_method: flatrate_flatrate
126
+ store_currency_code: GBP
127
+ store_name: Main Website, Main Store, English
128
+ x_forwarded_for: null
129
+ customer_note: null
130
+ created_at: 2014-05-15 16:05:21
131
+ updated_at: 2014-05-15 16:05:21
132
+ total_item_count: 1
133
+ customer_gender: null
134
+ base_custbalance_amount: null
135
+ currency_base_id: null
136
+ currency_code: null
137
+ currency_rate: null
138
+ custbalance_amount: null
139
+ is_hold: null
140
+ is_multi_payment: null
141
+ real_order_id: null
142
+ tax_percent: null
143
+ tracking_numbers: null
144
+ hidden_tax_amount: 0
145
+ base_hidden_tax_amount: 0
146
+ shipping_hidden_tax_amount: 0
147
+ base_shipping_hidden_tax_amnt: 0
148
+ hidden_tax_invoiced: null
149
+ base_hidden_tax_invoiced: null
150
+ hidden_tax_refunded: null
151
+ base_hidden_tax_refunded: null
152
+ shipping_incl_tax: 5
153
+ base_shipping_incl_tax: 5
154
+ coupon_rule_name: null
155
+ sales_flat_order_item:
156
+ -
157
+ item_id: 1
158
+ order_id: 1
159
+ parent_item_id: null
160
+ quote_item_id: 1
161
+ store_id: 1
162
+ created_at: 2014-05-15 16:05:21
163
+ updated_at: 2014-05-15 16:05:21
164
+ product_id: 93
165
+ product_type: configurable
166
+ product_options: a:6:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}s:15:"attributes_info";a:1:{i:0;a:2:{s:5:"label";s:9:"Shoe Size";s:5:"value";s:1:"8";}}s:11:"simple_name";s:30:"ASICS® Men's GEL-Kayano® XII";s:10:"simple_sku";s:5:"asc_8";s:20:"product_calculations";i:1;s:13:"shipment_type";i:0;}
167
+ weight: 3
168
+ is_virtual: 0
169
+ sku: asc_8
170
+ name: ASICS® Men's GEL-Kayano® XII
171
+ description: null
172
+ applied_rule_ids: null
173
+ additional_data: null
174
+ free_shipping: 0
175
+ is_qty_decimal: 0
176
+ no_discount: 0
177
+ qty_backordered: null
178
+ qty_canceled: 0
179
+ qty_invoiced: 0
180
+ qty_ordered: 1
181
+ qty_refunded: 0
182
+ qty_shipped: 0
183
+ base_cost: 29.99
184
+ price: 134.99
185
+ base_price: 134.99
186
+ original_price: 134.99
187
+ base_original_price: 134.99
188
+ tax_percent: 0
189
+ tax_amount: 0
190
+ base_tax_amount: 0
191
+ tax_invoiced: 0
192
+ base_tax_invoiced: 0
193
+ discount_percent: 0
194
+ discount_amount: 0
195
+ base_discount_amount: 0
196
+ discount_invoiced: 0
197
+ base_discount_invoiced: 0
198
+ amount_refunded: 0
199
+ base_amount_refunded: 0
200
+ row_total: 134.99
201
+ base_row_total: 134.99
202
+ row_invoiced: 0
203
+ base_row_invoiced: 0
204
+ row_weight: 3
205
+ gift_message_id: null
206
+ gift_message_available: null
207
+ base_tax_before_discount: null
208
+ tax_before_discount: null
209
+ weee_tax_applied: a:0:{}
210
+ weee_tax_applied_amount: 0
211
+ weee_tax_applied_row_amount: 0
212
+ base_weee_tax_applied_amount: 0
213
+ base_weee_tax_applied_row_amnt: 0
214
+ weee_tax_disposition: 0
215
+ weee_tax_row_disposition: 0
216
+ base_weee_tax_disposition: 0
217
+ base_weee_tax_row_disposition: 0
218
+ ext_order_item_id: null
219
+ locked_do_invoice: null
220
+ locked_do_ship: null
221
+ price_incl_tax: 134.99
222
+ base_price_incl_tax: 134.99
223
+ row_total_incl_tax: 134.99
224
+ base_row_total_incl_tax: 134.99
225
+ hidden_tax_amount: 0
226
+ base_hidden_tax_amount: 0
227
+ hidden_tax_invoiced: null
228
+ base_hidden_tax_invoiced: null
229
+ hidden_tax_refunded: null
230
+ base_hidden_tax_refunded: null
231
+ is_nominal: 0
232
+ tax_canceled: null
233
+ hidden_tax_canceled: null
234
+ tax_refunded: null
235
+ base_tax_refunded: null
236
+ discount_refunded: null
237
+ base_discount_refunded: null
238
+ -
239
+ item_id: 2
240
+ order_id: 1
241
+ parent_item_id: 1
242
+ quote_item_id: 2
243
+ store_id: 1
244
+ created_at: 2014-05-15 16:05:21
245
+ updated_at: 2014-05-15 16:05:21
246
+ product_id: 30
247
+ product_type: simple
248
+ product_options: a:1:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}}
249
+ weight: 3
250
+ is_virtual: 0
251
+ sku: asc_8
252
+ name: ASICS® Men's GEL-Kayano® XII
253
+ description: null
254
+ applied_rule_ids: null
255
+ additional_data: null
256
+ free_shipping: 0
257
+ is_qty_decimal: 0
258
+ no_discount: 0
259
+ qty_backordered: null
260
+ qty_canceled: 0
261
+ qty_invoiced: 0
262
+ qty_ordered: 1
263
+ qty_refunded: 0
264
+ qty_shipped: 0
265
+ base_cost: 29.99
266
+ price: 0
267
+ base_price: 0
268
+ original_price: 0
269
+ base_original_price: null
270
+ tax_percent: 0
271
+ tax_amount: 0
272
+ base_tax_amount: 0
273
+ tax_invoiced: 0
274
+ base_tax_invoiced: 0
275
+ discount_percent: 0
276
+ discount_amount: 0
277
+ base_discount_amount: 0
278
+ discount_invoiced: 0
279
+ base_discount_invoiced: 0
280
+ amount_refunded: 0
281
+ base_amount_refunded: 0
282
+ row_total: 0
283
+ base_row_total: 0
284
+ row_invoiced: 0
285
+ base_row_invoiced: 0
286
+ row_weight: 0
287
+ gift_message_id: null
288
+ gift_message_available: null
289
+ base_tax_before_discount: null
290
+ tax_before_discount: null
291
+ weee_tax_applied: a:0:{}
292
+ weee_tax_applied_amount: 0
293
+ weee_tax_applied_row_amount: 0
294
+ base_weee_tax_applied_amount: 0
295
+ base_weee_tax_applied_row_amnt: null
296
+ weee_tax_disposition: 0
297
+ weee_tax_row_disposition: 0
298
+ base_weee_tax_disposition: 0
299
+ base_weee_tax_row_disposition: 0
300
+ ext_order_item_id: null
301
+ locked_do_invoice: null
302
+ locked_do_ship: null
303
+ price_incl_tax: null
304
+ base_price_incl_tax: null
305
+ row_total_incl_tax: null
306
+ base_row_total_incl_tax: null
307
+ hidden_tax_amount: null
308
+ base_hidden_tax_amount: null
309
+ hidden_tax_invoiced: null
310
+ base_hidden_tax_invoiced: null
311
+ hidden_tax_refunded: null
312
+ base_hidden_tax_refunded: null
313
+ is_nominal: 0
314
+ tax_canceled: null
315
+ hidden_tax_canceled: null
316
+ tax_refunded: null
317
+ base_tax_refunded: null
318
+ discount_refunded: null
319
+ base_discount_refunded: null
app/code/community/Klevu/Search/Test/Model/Order/Sync/fixtures/testClearQueue.yaml ADDED
@@ -0,0 +1,563 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ scope:
2
+ store:
3
+ -
4
+ store_id: 2
5
+ website_id: 1
6
+ group_id: 1
7
+ code: test
8
+ name: Test Store
9
+ is_active: 1
10
+ config:
11
+ default/klevu_search/general/enabled: 1
12
+ default/klevu_search/general/js_api_key: test-api-key
13
+ default/klevu_search/order_sync/enabled: 2
14
+ tables:
15
+ sales_flat_order:
16
+ -
17
+ entity_id: 1
18
+ state: new
19
+ status: pending
20
+ coupon_code: null
21
+ protect_code: bbc820
22
+ shipping_description: Flat Rate - Fixed
23
+ is_virtual: 0
24
+ store_id: 1
25
+ customer_id: null
26
+ base_discount_amount: 0
27
+ base_discount_canceled: null
28
+ base_discount_invoiced: null
29
+ base_discount_refunded: null
30
+ base_grand_total: 139.99
31
+ base_shipping_amount: 5
32
+ base_shipping_canceled: null
33
+ base_shipping_invoiced: null
34
+ base_shipping_refunded: null
35
+ base_shipping_tax_amount: 0
36
+ base_shipping_tax_refunded: null
37
+ base_subtotal: 134.99
38
+ base_subtotal_canceled: null
39
+ base_subtotal_invoiced: null
40
+ base_subtotal_refunded: null
41
+ base_tax_amount: 0
42
+ base_tax_canceled: null
43
+ base_tax_invoiced: null
44
+ base_tax_refunded: null
45
+ base_to_global_rate: 1
46
+ base_to_order_rate: 1
47
+ base_total_canceled: null
48
+ base_total_invoiced: null
49
+ base_total_invoiced_cost: null
50
+ base_total_offline_refunded: null
51
+ base_total_online_refunded: null
52
+ base_total_paid: null
53
+ base_total_qty_ordered: null
54
+ base_total_refunded: null
55
+ discount_amount: 0
56
+ discount_canceled: null
57
+ discount_invoiced: null
58
+ discount_refunded: null
59
+ grand_total: 139.99
60
+ shipping_amount: 5
61
+ shipping_canceled: null
62
+ shipping_invoiced: null
63
+ shipping_refunded: null
64
+ shipping_tax_amount: 0
65
+ shipping_tax_refunded: null
66
+ store_to_base_rate: 1
67
+ store_to_order_rate: 1
68
+ subtotal: 134.99
69
+ subtotal_canceled: null
70
+ subtotal_invoiced: null
71
+ subtotal_refunded: null
72
+ tax_amount: 0
73
+ tax_canceled: null
74
+ tax_invoiced: null
75
+ tax_refunded: null
76
+ total_canceled: null
77
+ total_invoiced: null
78
+ total_offline_refunded: null
79
+ total_online_refunded: null
80
+ total_paid: null
81
+ total_qty_ordered: 1
82
+ total_refunded: null
83
+ can_ship_partially: null
84
+ can_ship_partially_item: null
85
+ customer_is_guest: 1
86
+ customer_note_notify: 1
87
+ billing_address_id: 1
88
+ customer_group_id: 0
89
+ edit_increment: null
90
+ email_sent: 1
91
+ forced_shipment_with_invoice: null
92
+ gift_message_id: null
93
+ payment_auth_expiration: null
94
+ paypal_ipn_customer_notified: null
95
+ quote_address_id: null
96
+ quote_id: 3
97
+ shipping_address_id: 2
98
+ adjustment_negative: null
99
+ adjustment_positive: null
100
+ base_adjustment_negative: null
101
+ base_adjustment_positive: null
102
+ base_shipping_discount_amount: 0
103
+ base_subtotal_incl_tax: 134.99
104
+ base_total_due: null
105
+ payment_authorization_amount: null
106
+ shipping_discount_amount: 0
107
+ subtotal_incl_tax: 134.99
108
+ total_due: null
109
+ weight: 3
110
+ customer_dob: null
111
+ increment_id: 100000001
112
+ applied_rule_ids: null
113
+ base_currency_code: GBP
114
+ customer_email: tomas.gerulaitis@meanbee.com
115
+ customer_firstname: Tomas
116
+ customer_lastname: Gerulaitis
117
+ customer_middlename: null
118
+ customer_prefix: null
119
+ customer_suffix: null
120
+ customer_taxvat: null
121
+ discount_description: null
122
+ ext_customer_id: null
123
+ ext_order_id: null
124
+ global_currency_code: GBP
125
+ hold_before_state: null
126
+ hold_before_status: null
127
+ order_currency_code: GBP
128
+ original_increment_id: null
129
+ relation_child_id: null
130
+ relation_child_real_id: null
131
+ relation_parent_id: null
132
+ relation_parent_real_id: null
133
+ remote_ip: 127.0.0.1
134
+ shipping_method: flatrate_flatrate
135
+ store_currency_code: GBP
136
+ store_name: Main Website, Main Store, English
137
+ x_forwarded_for: null
138
+ customer_note: null
139
+ created_at: 2014-05-15 16:05:21
140
+ updated_at: 2014-05-15 16:05:21
141
+ total_item_count: 1
142
+ customer_gender: null
143
+ base_custbalance_amount: null
144
+ currency_base_id: null
145
+ currency_code: null
146
+ currency_rate: null
147
+ custbalance_amount: null
148
+ is_hold: null
149
+ is_multi_payment: null
150
+ real_order_id: null
151
+ tax_percent: null
152
+ tracking_numbers: null
153
+ hidden_tax_amount: 0
154
+ base_hidden_tax_amount: 0
155
+ shipping_hidden_tax_amount: 0
156
+ base_shipping_hidden_tax_amnt: 0
157
+ hidden_tax_invoiced: null
158
+ base_hidden_tax_invoiced: null
159
+ hidden_tax_refunded: null
160
+ base_hidden_tax_refunded: null
161
+ shipping_incl_tax: 5
162
+ base_shipping_incl_tax: 5
163
+ coupon_rule_name: null
164
+ -
165
+ entity_id: 2
166
+ state: new
167
+ status: pending
168
+ coupon_code: null
169
+ protect_code: 58acbb
170
+ shipping_description: Flat Rate - Fixed
171
+ is_virtual: 0
172
+ store_id: 2
173
+ customer_id: null
174
+ base_discount_amount: 0
175
+ base_discount_canceled: null
176
+ base_discount_invoiced: null
177
+ base_discount_refunded: null
178
+ base_grand_total: 166.94
179
+ base_shipping_amount: 5
180
+ base_shipping_canceled: null
181
+ base_shipping_invoiced: null
182
+ base_shipping_refunded: null
183
+ base_shipping_tax_amount: 0
184
+ base_shipping_tax_refunded: null
185
+ base_subtotal: 161.94
186
+ base_subtotal_canceled: null
187
+ base_subtotal_invoiced: null
188
+ base_subtotal_refunded: null
189
+ base_tax_amount: 0
190
+ base_tax_canceled: null
191
+ base_tax_invoiced: null
192
+ base_tax_refunded: null
193
+ base_to_global_rate: 1
194
+ base_to_order_rate: 1
195
+ base_total_canceled: null
196
+ base_total_invoiced: null
197
+ base_total_invoiced_cost: null
198
+ base_total_offline_refunded: null
199
+ base_total_online_refunded: null
200
+ base_total_paid: null
201
+ base_total_qty_ordered: null
202
+ base_total_refunded: null
203
+ discount_amount: 0
204
+ discount_canceled: null
205
+ discount_invoiced: null
206
+ discount_refunded: null
207
+ grand_total: 166.94
208
+ shipping_amount: 5
209
+ shipping_canceled: null
210
+ shipping_invoiced: null
211
+ shipping_refunded: null
212
+ shipping_tax_amount: 0
213
+ shipping_tax_refunded: null
214
+ store_to_base_rate: 1
215
+ store_to_order_rate: 1
216
+ subtotal: 161.94
217
+ subtotal_canceled: null
218
+ subtotal_invoiced: null
219
+ subtotal_refunded: null
220
+ tax_amount: 0
221
+ tax_canceled: null
222
+ tax_invoiced: null
223
+ tax_refunded: null
224
+ total_canceled: null
225
+ total_invoiced: null
226
+ total_offline_refunded: null
227
+ total_online_refunded: null
228
+ total_paid: null
229
+ total_qty_ordered: 1
230
+ total_refunded: null
231
+ can_ship_partially: null
232
+ can_ship_partially_item: null
233
+ customer_is_guest: 1
234
+ customer_note_notify: 1
235
+ billing_address_id: 3
236
+ customer_group_id: 0
237
+ edit_increment: null
238
+ email_sent: 1
239
+ forced_shipment_with_invoice: null
240
+ gift_message_id: null
241
+ payment_auth_expiration: null
242
+ paypal_ipn_customer_notified: null
243
+ quote_address_id: null
244
+ quote_id: 4
245
+ shipping_address_id: 4
246
+ adjustment_negative: null
247
+ adjustment_positive: null
248
+ base_adjustment_negative: null
249
+ base_adjustment_positive: null
250
+ base_shipping_discount_amount: 0
251
+ base_subtotal_incl_tax: 161.94
252
+ base_total_due: null
253
+ payment_authorization_amount: null
254
+ shipping_discount_amount: 0
255
+ subtotal_incl_tax: 161.94
256
+ total_due: null
257
+ weight: 2
258
+ customer_dob: null
259
+ increment_id: 300000001
260
+ applied_rule_ids: null
261
+ base_currency_code: GBP
262
+ customer_email: tomas.gerulaitis@meanbee.com
263
+ customer_firstname: Tomas
264
+ customer_lastname: Gerulaitis
265
+ customer_middlename: null
266
+ customer_prefix: null
267
+ customer_suffix: null
268
+ customer_taxvat: null
269
+ discount_description: null
270
+ ext_customer_id: null
271
+ ext_order_id: null
272
+ global_currency_code: GBP
273
+ hold_before_state: null
274
+ hold_before_status: null
275
+ order_currency_code: GBP
276
+ original_increment_id: null
277
+ relation_child_id: null
278
+ relation_child_real_id: null
279
+ relation_parent_id: null
280
+ relation_parent_real_id: null
281
+ remote_ip: 127.0.0.1
282
+ shipping_method: flatrate_flatrate
283
+ store_currency_code: GBP
284
+ store_name: Main Website, Main Store, French
285
+ x_forwarded_for: null
286
+ customer_note: null
287
+ created_at: 2014-05-26 12:14:20
288
+ updated_at: 2014-05-26 12:14:20
289
+ total_item_count: 1
290
+ customer_gender: null
291
+ base_custbalance_amount: null
292
+ currency_base_id: null
293
+ currency_code: null
294
+ currency_rate: null
295
+ custbalance_amount: null
296
+ is_hold: null
297
+ is_multi_payment: null
298
+ real_order_id: null
299
+ tax_percent: null
300
+ tracking_numbers: null
301
+ hidden_tax_amount: 0
302
+ base_hidden_tax_amount: 0
303
+ shipping_hidden_tax_amount: 0
304
+ base_shipping_hidden_tax_amnt: 0
305
+ hidden_tax_invoiced: null
306
+ base_hidden_tax_invoiced: null
307
+ hidden_tax_refunded: null
308
+ base_hidden_tax_refunded: null
309
+ shipping_incl_tax: 5
310
+ base_shipping_incl_tax: 5
311
+ coupon_rule_name: null
312
+ sales_flat_order_item:
313
+ -
314
+ item_id: 1
315
+ order_id: 1
316
+ parent_item_id: null
317
+ quote_item_id: 1
318
+ store_id: 1
319
+ created_at: 2014-05-15 16:05:21
320
+ updated_at: 2014-05-15 16:05:21
321
+ product_id: 93
322
+ product_type: configurable
323
+ product_options: a:6:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}s:15:"attributes_info";a:1:{i:0;a:2:{s:5:"label";s:9:"Shoe Size";s:5:"value";s:1:"8";}}s:11:"simple_name";s:30:"ASICS® Men's GEL-Kayano® XII";s:10:"simple_sku";s:5:"asc_8";s:20:"product_calculations";i:1;s:13:"shipment_type";i:0;}
324
+ weight: 3
325
+ is_virtual: 0
326
+ sku: asc_8
327
+ name: ASICS® Men's GEL-Kayano® XII
328
+ description: null
329
+ applied_rule_ids: null
330
+ additional_data: null
331
+ free_shipping: 0
332
+ is_qty_decimal: 0
333
+ no_discount: 0
334
+ qty_backordered: null
335
+ qty_canceled: 0
336
+ qty_invoiced: 0
337
+ qty_ordered: 1
338
+ qty_refunded: 0
339
+ qty_shipped: 0
340
+ base_cost: 29.99
341
+ price: 134.99
342
+ base_price: 134.99
343
+ original_price: 134.99
344
+ base_original_price: 134.99
345
+ tax_percent: 0
346
+ tax_amount: 0
347
+ base_tax_amount: 0
348
+ tax_invoiced: 0
349
+ base_tax_invoiced: 0
350
+ discount_percent: 0
351
+ discount_amount: 0
352
+ base_discount_amount: 0
353
+ discount_invoiced: 0
354
+ base_discount_invoiced: 0
355
+ amount_refunded: 0
356
+ base_amount_refunded: 0
357
+ row_total: 134.99
358
+ base_row_total: 134.99
359
+ row_invoiced: 0
360
+ base_row_invoiced: 0
361
+ row_weight: 3
362
+ gift_message_id: null
363
+ gift_message_available: null
364
+ base_tax_before_discount: null
365
+ tax_before_discount: null
366
+ weee_tax_applied: a:0:{}
367
+ weee_tax_applied_amount: 0
368
+ weee_tax_applied_row_amount: 0
369
+ base_weee_tax_applied_amount: 0
370
+ base_weee_tax_applied_row_amnt: 0
371
+ weee_tax_disposition: 0
372
+ weee_tax_row_disposition: 0
373
+ base_weee_tax_disposition: 0
374
+ base_weee_tax_row_disposition: 0
375
+ ext_order_item_id: null
376
+ locked_do_invoice: null
377
+ locked_do_ship: null
378
+ price_incl_tax: 134.99
379
+ base_price_incl_tax: 134.99
380
+ row_total_incl_tax: 134.99
381
+ base_row_total_incl_tax: 134.99
382
+ hidden_tax_amount: 0
383
+ base_hidden_tax_amount: 0
384
+ hidden_tax_invoiced: null
385
+ base_hidden_tax_invoiced: null
386
+ hidden_tax_refunded: null
387
+ base_hidden_tax_refunded: null
388
+ is_nominal: 0
389
+ tax_canceled: null
390
+ hidden_tax_canceled: null
391
+ tax_refunded: null
392
+ base_tax_refunded: null
393
+ discount_refunded: null
394
+ base_discount_refunded: null
395
+ -
396
+ item_id: 2
397
+ order_id: 1
398
+ parent_item_id: 1
399
+ quote_item_id: 2
400
+ store_id: 1
401
+ created_at: 2014-05-15 16:05:21
402
+ updated_at: 2014-05-15 16:05:21
403
+ product_id: 30
404
+ product_type: simple
405
+ product_options: a:1:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}}
406
+ weight: 3
407
+ is_virtual: 0
408
+ sku: asc_8
409
+ name: ASICS® Men's GEL-Kayano® XII
410
+ description: null
411
+ applied_rule_ids: null
412
+ additional_data: null
413
+ free_shipping: 0
414
+ is_qty_decimal: 0
415
+ no_discount: 0
416
+ qty_backordered: null
417
+ qty_canceled: 0
418
+ qty_invoiced: 0
419
+ qty_ordered: 1
420
+ qty_refunded: 0
421
+ qty_shipped: 0
422
+ base_cost: 29.99
423
+ price: 0
424
+ base_price: 0
425
+ original_price: 0
426
+ base_original_price: null
427
+ tax_percent: 0
428
+ tax_amount: 0
429
+ base_tax_amount: 0
430
+ tax_invoiced: 0
431
+ base_tax_invoiced: 0
432
+ discount_percent: 0
433
+ discount_amount: 0
434
+ base_discount_amount: 0
435
+ discount_invoiced: 0
436
+ base_discount_invoiced: 0
437
+ amount_refunded: 0
438
+ base_amount_refunded: 0
439
+ row_total: 0
440
+ base_row_total: 0
441
+ row_invoiced: 0
442
+ base_row_invoiced: 0
443
+ row_weight: 0
444
+ gift_message_id: null
445
+ gift_message_available: null
446
+ base_tax_before_discount: null
447
+ tax_before_discount: null
448
+ weee_tax_applied: a:0:{}
449
+ weee_tax_applied_amount: 0
450
+ weee_tax_applied_row_amount: 0
451
+ base_weee_tax_applied_amount: 0
452
+ base_weee_tax_applied_row_amnt: null
453
+ weee_tax_disposition: 0
454
+ weee_tax_row_disposition: 0
455
+ base_weee_tax_disposition: 0
456
+ base_weee_tax_row_disposition: 0
457
+ ext_order_item_id: null
458
+ locked_do_invoice: null
459
+ locked_do_ship: null
460
+ price_incl_tax: null
461
+ base_price_incl_tax: null
462
+ row_total_incl_tax: null
463
+ base_row_total_incl_tax: null
464
+ hidden_tax_amount: null
465
+ base_hidden_tax_amount: null
466
+ hidden_tax_invoiced: null
467
+ base_hidden_tax_invoiced: null
468
+ hidden_tax_refunded: null
469
+ base_hidden_tax_refunded: null
470
+ is_nominal: 0
471
+ tax_canceled: null
472
+ hidden_tax_canceled: null
473
+ tax_refunded: null
474
+ base_tax_refunded: null
475
+ discount_refunded: null
476
+ base_discount_refunded: null
477
+ -
478
+ item_id: 3
479
+ order_id: 2
480
+ parent_item_id: null
481
+ quote_item_id: 3
482
+ store_id: 2
483
+ created_at: 2014-05-26 12:14:20
484
+ updated_at: 2014-05-26 12:14:20
485
+ product_id: 46
486
+ product_type: simple
487
+ product_options: a:1:{s:15:"info_buyRequest";a:5:{s:4:"uenc";s:120:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9vbHltcHVzLXN0eWx1cy03NTAtNy0xbXAtZGlnaXRhbC1jYW1lcmEuaHRtbD9fX19TSUQ9VQ,,";s:7:"product";s:2:"46";s:8:"form_key";s:16:"zmyiLczPG2zL8kLs";s:15:"related_product";s:0:"";s:3:"qty";s:1:"0";}}
488
+ weight: 2
489
+ is_virtual: 0
490
+ sku: 750
491
+ name: Olympus Stylus 750 7.1MP Digital Camera
492
+ description: null
493
+ applied_rule_ids: null
494
+ additional_data: null
495
+ free_shipping: 0
496
+ is_qty_decimal: 0
497
+ no_discount: 0
498
+ qty_backordered: null
499
+ qty_canceled: 0
500
+ qty_invoiced: 0
501
+ qty_ordered: 1
502
+ qty_refunded: 0
503
+ qty_shipped: 0
504
+ base_cost: 29.99
505
+ price: 161.94
506
+ base_price: 161.94
507
+ original_price: 161.94
508
+ base_original_price: 161.94
509
+ tax_percent: 0
510
+ tax_amount: 0
511
+ base_tax_amount: 0
512
+ tax_invoiced: 0
513
+ base_tax_invoiced: 0
514
+ discount_percent: 0
515
+ discount_amount: 0
516
+ base_discount_amount: 0
517
+ discount_invoiced: 0
518
+ base_discount_invoiced: 0
519
+ amount_refunded: 0
520
+ base_amount_refunded: 0
521
+ row_total: 161.94
522
+ base_row_total: 161.94
523
+ row_invoiced: 0
524
+ base_row_invoiced: 0
525
+ row_weight: 2
526
+ gift_message_id: null
527
+ gift_message_available: null
528
+ base_tax_before_discount: null
529
+ tax_before_discount: null
530
+ weee_tax_applied: a:0:{}
531
+ weee_tax_applied_amount: 0
532
+ weee_tax_applied_row_amount: 0
533
+ base_weee_tax_applied_amount: 0
534
+ base_weee_tax_applied_row_amnt: 0
535
+ weee_tax_disposition: 0
536
+ weee_tax_row_disposition: 0
537
+ base_weee_tax_disposition: 0
538
+ base_weee_tax_row_disposition: 0
539
+ ext_order_item_id: null
540
+ locked_do_invoice: null
541
+ locked_do_ship: null
542
+ price_incl_tax: 161.94
543
+ base_price_incl_tax: 161.94
544
+ row_total_incl_tax: 161.94
545
+ base_row_total_incl_tax: 161.94
546
+ hidden_tax_amount: 0
547
+ base_hidden_tax_amount: 0
548
+ hidden_tax_invoiced: null
549
+ base_hidden_tax_invoiced: null
550
+ hidden_tax_refunded: null
551
+ base_hidden_tax_refunded: null
552
+ is_nominal: 0
553
+ tax_canceled: null
554
+ hidden_tax_canceled: null
555
+ tax_refunded: null
556
+ base_tax_refunded: null
557
+ discount_refunded: null
558
+ base_discount_refunded: null
559
+ klevu_search/order_sync:
560
+ -
561
+ order_item_id: 2
562
+ -
563
+ order_item_id: 3
app/code/community/Klevu/Search/Test/Model/Order/Sync/fixtures/testRun.yaml ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_js_api_key: test-api-key
5
+ default/klevu_search/order_sync/enabled: 2
6
+ tables:
7
+ sales_flat_order:
8
+ -
9
+ entity_id: 1
10
+ state: new
11
+ status: pending
12
+ coupon_code: null
13
+ protect_code: bbc820
14
+ shipping_description: Flat Rate - Fixed
15
+ is_virtual: 0
16
+ store_id: 1
17
+ customer_id: null
18
+ base_discount_amount: 0
19
+ base_discount_canceled: null
20
+ base_discount_invoiced: null
21
+ base_discount_refunded: null
22
+ base_grand_total: 139.99
23
+ base_shipping_amount: 5
24
+ base_shipping_canceled: null
25
+ base_shipping_invoiced: null
26
+ base_shipping_refunded: null
27
+ base_shipping_tax_amount: 0
28
+ base_shipping_tax_refunded: null
29
+ base_subtotal: 134.99
30
+ base_subtotal_canceled: null
31
+ base_subtotal_invoiced: null
32
+ base_subtotal_refunded: null
33
+ base_tax_amount: 0
34
+ base_tax_canceled: null
35
+ base_tax_invoiced: null
36
+ base_tax_refunded: null
37
+ base_to_global_rate: 1
38
+ base_to_order_rate: 1
39
+ base_total_canceled: null
40
+ base_total_invoiced: null
41
+ base_total_invoiced_cost: null
42
+ base_total_offline_refunded: null
43
+ base_total_online_refunded: null
44
+ base_total_paid: null
45
+ base_total_qty_ordered: null
46
+ base_total_refunded: null
47
+ discount_amount: 0
48
+ discount_canceled: null
49
+ discount_invoiced: null
50
+ discount_refunded: null
51
+ grand_total: 139.99
52
+ shipping_amount: 5
53
+ shipping_canceled: null
54
+ shipping_invoiced: null
55
+ shipping_refunded: null
56
+ shipping_tax_amount: 0
57
+ shipping_tax_refunded: null
58
+ store_to_base_rate: 1
59
+ store_to_order_rate: 1
60
+ subtotal: 134.99
61
+ subtotal_canceled: null
62
+ subtotal_invoiced: null
63
+ subtotal_refunded: null
64
+ tax_amount: 0
65
+ tax_canceled: null
66
+ tax_invoiced: null
67
+ tax_refunded: null
68
+ total_canceled: null
69
+ total_invoiced: null
70
+ total_offline_refunded: null
71
+ total_online_refunded: null
72
+ total_paid: null
73
+ total_qty_ordered: 1
74
+ total_refunded: null
75
+ can_ship_partially: null
76
+ can_ship_partially_item: null
77
+ customer_is_guest: 1
78
+ customer_note_notify: 1
79
+ billing_address_id: 1
80
+ customer_group_id: 0
81
+ edit_increment: null
82
+ email_sent: 1
83
+ forced_shipment_with_invoice: null
84
+ gift_message_id: null
85
+ payment_auth_expiration: null
86
+ paypal_ipn_customer_notified: null
87
+ quote_address_id: null
88
+ quote_id: 3
89
+ shipping_address_id: 2
90
+ adjustment_negative: null
91
+ adjustment_positive: null
92
+ base_adjustment_negative: null
93
+ base_adjustment_positive: null
94
+ base_shipping_discount_amount: 0
95
+ base_subtotal_incl_tax: 134.99
96
+ base_total_due: null
97
+ payment_authorization_amount: null
98
+ shipping_discount_amount: 0
99
+ subtotal_incl_tax: 134.99
100
+ total_due: null
101
+ weight: 3
102
+ customer_dob: null
103
+ increment_id: 100000001
104
+ applied_rule_ids: null
105
+ base_currency_code: GBP
106
+ customer_email: tomas.gerulaitis@meanbee.com
107
+ customer_firstname: Tomas
108
+ customer_lastname: Gerulaitis
109
+ customer_middlename: null
110
+ customer_prefix: null
111
+ customer_suffix: null
112
+ customer_taxvat: null
113
+ discount_description: null
114
+ ext_customer_id: null
115
+ ext_order_id: null
116
+ global_currency_code: GBP
117
+ hold_before_state: null
118
+ hold_before_status: null
119
+ order_currency_code: GBP
120
+ original_increment_id: null
121
+ relation_child_id: null
122
+ relation_child_real_id: null
123
+ relation_parent_id: null
124
+ relation_parent_real_id: null
125
+ remote_ip: 127.0.0.1
126
+ shipping_method: flatrate_flatrate
127
+ store_currency_code: GBP
128
+ store_name: Main Website, Main Store, English
129
+ x_forwarded_for: null
130
+ customer_note: null
131
+ created_at: 2014-05-15 16:05:21
132
+ updated_at: 2014-05-15 16:05:21
133
+ total_item_count: 1
134
+ customer_gender: null
135
+ base_custbalance_amount: null
136
+ currency_base_id: null
137
+ currency_code: null
138
+ currency_rate: null
139
+ custbalance_amount: null
140
+ is_hold: null
141
+ is_multi_payment: null
142
+ real_order_id: null
143
+ tax_percent: null
144
+ tracking_numbers: null
145
+ hidden_tax_amount: 0
146
+ base_hidden_tax_amount: 0
147
+ shipping_hidden_tax_amount: 0
148
+ base_shipping_hidden_tax_amnt: 0
149
+ hidden_tax_invoiced: null
150
+ base_hidden_tax_invoiced: null
151
+ hidden_tax_refunded: null
152
+ base_hidden_tax_refunded: null
153
+ shipping_incl_tax: 5
154
+ base_shipping_incl_tax: 5
155
+ coupon_rule_name: null
156
+ sales_flat_order_item:
157
+ -
158
+ item_id: 1
159
+ order_id: 1
160
+ parent_item_id: null
161
+ quote_item_id: 1
162
+ store_id: 1
163
+ created_at: 2014-05-15 16:05:21
164
+ updated_at: 2014-05-15 16:05:21
165
+ product_id: 93
166
+ product_type: configurable
167
+ product_options: a:6:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}s:15:"attributes_info";a:1:{i:0;a:2:{s:5:"label";s:9:"Shoe Size";s:5:"value";s:1:"8";}}s:11:"simple_name";s:30:"ASICS® Men's GEL-Kayano® XII";s:10:"simple_sku";s:5:"asc_8";s:20:"product_calculations";i:1;s:13:"shipment_type";i:0;}
168
+ weight: 3
169
+ is_virtual: 0
170
+ sku: asc_8
171
+ name: ASICS® Men's GEL-Kayano® XII
172
+ description: null
173
+ applied_rule_ids: null
174
+ additional_data: null
175
+ free_shipping: 0
176
+ is_qty_decimal: 0
177
+ no_discount: 0
178
+ qty_backordered: null
179
+ qty_canceled: 0
180
+ qty_invoiced: 0
181
+ qty_ordered: 1
182
+ qty_refunded: 0
183
+ qty_shipped: 0
184
+ base_cost: 29.99
185
+ price: 134.99
186
+ base_price: 134.99
187
+ original_price: 134.99
188
+ base_original_price: 134.99
189
+ tax_percent: 0
190
+ tax_amount: 0
191
+ base_tax_amount: 0
192
+ tax_invoiced: 0
193
+ base_tax_invoiced: 0
194
+ discount_percent: 0
195
+ discount_amount: 0
196
+ base_discount_amount: 0
197
+ discount_invoiced: 0
198
+ base_discount_invoiced: 0
199
+ amount_refunded: 0
200
+ base_amount_refunded: 0
201
+ row_total: 134.99
202
+ base_row_total: 134.99
203
+ row_invoiced: 0
204
+ base_row_invoiced: 0
205
+ row_weight: 3
206
+ gift_message_id: null
207
+ gift_message_available: null
208
+ base_tax_before_discount: null
209
+ tax_before_discount: null
210
+ weee_tax_applied: a:0:{}
211
+ weee_tax_applied_amount: 0
212
+ weee_tax_applied_row_amount: 0
213
+ base_weee_tax_applied_amount: 0
214
+ base_weee_tax_applied_row_amnt: 0
215
+ weee_tax_disposition: 0
216
+ weee_tax_row_disposition: 0
217
+ base_weee_tax_disposition: 0
218
+ base_weee_tax_row_disposition: 0
219
+ ext_order_item_id: null
220
+ locked_do_invoice: null
221
+ locked_do_ship: null
222
+ price_incl_tax: 134.99
223
+ base_price_incl_tax: 134.99
224
+ row_total_incl_tax: 134.99
225
+ base_row_total_incl_tax: 134.99
226
+ hidden_tax_amount: 0
227
+ base_hidden_tax_amount: 0
228
+ hidden_tax_invoiced: null
229
+ base_hidden_tax_invoiced: null
230
+ hidden_tax_refunded: null
231
+ base_hidden_tax_refunded: null
232
+ is_nominal: 0
233
+ tax_canceled: null
234
+ hidden_tax_canceled: null
235
+ tax_refunded: null
236
+ base_tax_refunded: null
237
+ discount_refunded: null
238
+ base_discount_refunded: null
239
+ -
240
+ item_id: 2
241
+ order_id: 1
242
+ parent_item_id: 1
243
+ quote_item_id: 2
244
+ store_id: 1
245
+ created_at: 2014-05-15 16:05:21
246
+ updated_at: 2014-05-15 16:05:21
247
+ product_id: 30
248
+ product_type: simple
249
+ product_options: a:1:{s:15:"info_buyRequest";a:6:{s:4:"uenc";s:100:"aHR0cDovL3NreXdhcnAubG9jYWwvbWVhbmJlZS9rbGV2dS9hc2ljcy1tZW4tcy1nZWwta2F5YW5vLXhpaS5odG1sP19fX1NJRD1V";s:7:"product";s:2:"93";s:8:"form_key";s:16:"L9pfTdY6fCBMRa3z";s:15:"related_product";s:0:"";s:15:"super_attribute";a:1:{i:502;s:2:"41";}s:3:"qty";s:1:"1";}}
250
+ weight: 3
251
+ is_virtual: 0
252
+ sku: asc_8
253
+ name: ASICS® Men's GEL-Kayano® XII
254
+ description: null
255
+ applied_rule_ids: null
256
+ additional_data: null
257
+ free_shipping: 0
258
+ is_qty_decimal: 0
259
+ no_discount: 0
260
+ qty_backordered: null
261
+ qty_canceled: 0
262
+ qty_invoiced: 0
263
+ qty_ordered: 1
264
+ qty_refunded: 0
265
+ qty_shipped: 0
266
+ base_cost: 29.99
267
+ price: 0
268
+ base_price: 0
269
+ original_price: 0
270
+ base_original_price: null
271
+ tax_percent: 0
272
+ tax_amount: 0
273
+ base_tax_amount: 0
274
+ tax_invoiced: 0
275
+ base_tax_invoiced: 0
276
+ discount_percent: 0
277
+ discount_amount: 0
278
+ base_discount_amount: 0
279
+ discount_invoiced: 0
280
+ base_discount_invoiced: 0
281
+ amount_refunded: 0
282
+ base_amount_refunded: 0
283
+ row_total: 0
284
+ base_row_total: 0
285
+ row_invoiced: 0
286
+ base_row_invoiced: 0
287
+ row_weight: 0
288
+ gift_message_id: null
289
+ gift_message_available: null
290
+ base_tax_before_discount: null
291
+ tax_before_discount: null
292
+ weee_tax_applied: a:0:{}
293
+ weee_tax_applied_amount: 0
294
+ weee_tax_applied_row_amount: 0
295
+ base_weee_tax_applied_amount: 0
296
+ base_weee_tax_applied_row_amnt: null
297
+ weee_tax_disposition: 0
298
+ weee_tax_row_disposition: 0
299
+ base_weee_tax_disposition: 0
300
+ base_weee_tax_row_disposition: 0
301
+ ext_order_item_id: null
302
+ locked_do_invoice: null
303
+ locked_do_ship: null
304
+ price_incl_tax: null
305
+ base_price_incl_tax: null
306
+ row_total_incl_tax: null
307
+ base_row_total_incl_tax: null
308
+ hidden_tax_amount: null
309
+ base_hidden_tax_amount: null
310
+ hidden_tax_invoiced: null
311
+ base_hidden_tax_invoiced: null
312
+ hidden_tax_refunded: null
313
+ base_hidden_tax_refunded: null
314
+ is_nominal: 0
315
+ tax_canceled: null
316
+ hidden_tax_canceled: null
317
+ tax_refunded: null
318
+ base_tax_refunded: null
319
+ discount_refunded: null
320
+ base_discount_refunded: null
321
+ klevu_order_sync:
322
+ -
323
+ order_item_id: 2
app/code/community/Klevu/Search/Test/Model/Product/Sync.php ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Product_Sync extends Klevu_Search_Test_Model_Api_Test_Case {
4
+
5
+ protected function tearDown() {
6
+ $resource = Mage::getModel('core/resource');
7
+
8
+ $resource->getConnection("core_write")->delete($resource->getTableName("klevu_search/product_sync"));
9
+
10
+ Mage::getResourceModel('catalog/product_collection')->delete();
11
+
12
+ parent::tearDown();
13
+ }
14
+
15
+ /**
16
+ * @test
17
+ * @loadFixture
18
+ * @doNotIndexAll
19
+ */
20
+ public function testRun() {
21
+ $this->reindexAll();
22
+
23
+ $this->replaceApiActionByMock("klevu_search/api_action_startsession", $this->getSuccessfulSessionResponse());
24
+
25
+ $model = $this->getModelMock("klevu_search/product_sync", array(
26
+ "isBelowMemoryLimit", "deleteProducts", "updateProducts", "addProducts"
27
+ ));
28
+ $model
29
+ ->expects($this->any())
30
+ ->method("isBelowMemoryLimit")
31
+ ->will($this->returnValue(true));
32
+ $model
33
+ ->expects($this->once())
34
+ ->method("deleteProducts")
35
+ ->with(array(
36
+ array("product_id" => "130", "parent_id" => "0"),
37
+ array("product_id" => "201", "parent_id" => "202"),
38
+ array("product_id" => "211", "parent_id" => "212")
39
+ ))
40
+ ->will($this->returnValue(true));
41
+ $model
42
+ ->expects($this->once())
43
+ ->method("updateProducts")
44
+ ->with(array(
45
+ array("product_id" => "133", "parent_id" => "0"),
46
+ array("product_id" => "134", "parent_id" => "0"),
47
+ array("product_id" => "203", "parent_id" => "204"),
48
+ array("product_id" => "205", "parent_id" => "206")
49
+ ))
50
+ ->will($this->returnValue(true));
51
+ $model
52
+ ->expects($this->once())
53
+ ->method("addProducts")
54
+ ->with(array(
55
+ array("product_id" => "132", "parent_id" => "0"),
56
+ array("product_id" => "207", "parent_id" => "209"),
57
+ array("product_id" => "208", "parent_id" => "209")
58
+ ))
59
+ ->will($this->returnValue(true));
60
+
61
+ $model->run();
62
+ }
63
+
64
+ /**
65
+ * @test
66
+ * @loadFixture
67
+ * @doNotIndexAll
68
+ */
69
+ public function testDeleteProducts() {
70
+ $this->reindexAll();
71
+
72
+ $this->replaceApiActionByMock("klevu_search/api_action_startsession", $this->getSuccessfulSessionResponse());
73
+ $this->replaceApiActionByMock("klevu_search/api_action_deleterecords", $this->getSuccessfulMessageResponse());
74
+
75
+ Mage::getModel('klevu_search/product_sync')->run();
76
+
77
+ $this->assertEquals(array(), $this->getProductSyncTableContents());
78
+ }
79
+
80
+ /**
81
+ * @test
82
+ * @loadFixture
83
+ * @doNotIndexAll
84
+ */
85
+ public function testUpdateProducts() {
86
+ $this->reindexAll();
87
+
88
+ $this->replaceApiActionByMock("klevu_search/api_action_startsession", $this->getSuccessfulSessionResponse());
89
+ $this->replaceApiActionByMock("klevu_search/api_action_updaterecords", $this->getSuccessfulMessageResponse());
90
+
91
+ $this->replaceSessionByMock("core/session");
92
+ $this->replaceSessionByMock("customer/session");
93
+
94
+ Mage::getModel('klevu_search/product_sync')->run();
95
+
96
+ $contents = $this->getProductSyncTableContents('last_synced_at > "2008-06-27 01:57:22"');
97
+
98
+ $this->assertTrue((is_array($contents) && count($contents) == 1));
99
+ $this->assertEquals("133", $contents[0]['product_id']);
100
+ }
101
+
102
+ /**
103
+ * @test
104
+ * @loadFixture
105
+ * @doNotIndexAll
106
+ */
107
+ public function testAddProducts() {
108
+ $this->reindexAll();
109
+
110
+ $this->replaceApiActionByMock("klevu_search/api_action_startsession", $this->getSuccessfulSessionResponse());
111
+ $this->replaceApiActionByMock("klevu_search/api_action_addrecords", $this->getSuccessfulMessageResponse());
112
+
113
+ $this->replaceSessionByMock("core/session");
114
+ $this->replaceSessionByMock("customer/session");
115
+
116
+ Mage::getModel('klevu_search/product_sync')->run();
117
+
118
+ $contents = $this->getProductSyncTableContents();
119
+
120
+ $this->assertTrue((is_array($contents) && count($contents) == 1));
121
+ $this->assertEquals("133", $contents[0]['product_id']);
122
+ }
123
+
124
+ /**
125
+ * @test
126
+ * @loadFixture
127
+ */
128
+ public function testClearAllProducts() {
129
+ $model = Mage::getModel("klevu_search/product_sync");
130
+
131
+ $model->clearAllProducts(1);
132
+
133
+ $contents = $this->getProductSyncTableContents();
134
+
135
+ $this->assertTrue(
136
+ is_array($contents) && count($contents) == 1 && $contents[0]['product_id'] == 2,
137
+ "Failed asserting that clearAllProducts() only removes products for the given store."
138
+ );
139
+
140
+ $model->clearAllProducts();
141
+
142
+ $contents = $this->getProductSyncTableContents();
143
+
144
+ $this->assertTrue(
145
+ empty($contents),
146
+ "Failed asserting that clearAllProducts() removes products for all stores."
147
+ );
148
+ }
149
+
150
+ /**
151
+ * @test
152
+ */
153
+ public function testAutomaticAttributes() {
154
+ $model = Mage::getModel("klevu_search/product_sync");
155
+
156
+ $automatic_attributes = $model->getAutomaticAttributes();
157
+
158
+ $expected_attributes = $this->getExpectedAutomaticAttributes();
159
+
160
+ $this->assertEquals($expected_attributes, $automatic_attributes);
161
+ }
162
+
163
+ /**
164
+ * Return a klevu_search/api_response_message model with a successful response from
165
+ * a startSession API call.
166
+ *
167
+ * @return Klevu_Search_Model_Api_Response_Message
168
+ */
169
+ protected function getSuccessfulSessionResponse() {
170
+ $model = Mage::getModel('klevu_search/api_response_message')->setRawResponse(
171
+ new Zend_Http_Response(200, array(), $this->getDataFileContents("startsession_response_success.xml"))
172
+ );
173
+
174
+ return $model;
175
+ }
176
+
177
+ /**
178
+ * Return a klevu_search/api_response_message model with a successful response.
179
+ *
180
+ * @return Klevu_Search_Model_Api_Response_Message
181
+ */
182
+ protected function getSuccessfulMessageResponse() {
183
+ $model = Mage::getModel('klevu_search/api_response_message')->setRawResponse(
184
+ new Zend_Http_Response(200, array(), $this->getDataFileContents("message_response_success.xml"))
185
+ );
186
+
187
+ return $model;
188
+ }
189
+
190
+ /**
191
+ * Return the contents of the Product Sync table.
192
+ *
193
+ * @param string $where The where clause to use in the database query
194
+ *
195
+ * @return array
196
+ */
197
+ protected function getProductSyncTableContents($where = null) {
198
+ $resource = Mage::getModel('core/resource');
199
+ $connection = $resource->getConnection("core_write");
200
+
201
+ $select = $connection->select()->from($resource->getTableName('klevu_search/product_sync'));
202
+ if ($where) {
203
+ $select->where($where);
204
+ }
205
+
206
+ return $connection->fetchAll($select);
207
+ }
208
+
209
+ /**
210
+ * Run all of the indexers.
211
+ *
212
+ * @return $this
213
+ */
214
+ protected function reindexAll() {
215
+ $indexer = Mage::getSingleton('index/indexer');
216
+
217
+ // Delete all index events
218
+ $index_events = Mage::getResourceModel("index/event_collection");
219
+ foreach ($index_events as $event) {
220
+ /** @var Mage_Index_Model_Event $event */
221
+ $event->delete();
222
+ }
223
+
224
+ // Remove the stores cache from the category product index
225
+ if ($process = $indexer->getProcessByCode("catalog_category_product")) {
226
+ EcomDev_Utils_Reflection::setRestrictedPropertyValue(
227
+ $process->getIndexer()->getResource(), "_storesInfo", null
228
+ );
229
+ }
230
+
231
+ $processes = $indexer->getProcessesCollection();
232
+
233
+ // Reset all the indexers
234
+ foreach ($processes as $process) {
235
+ /** @var Mage_Index_Model_Process $process */
236
+ if ($process->hasData('runed_reindexall')) {
237
+ $process->setData('runed_reindexall', false);
238
+ }
239
+ }
240
+
241
+ // Run all indexers
242
+ foreach ($processes as $process) {
243
+ /** @var Mage_Index_Model_Process $process */
244
+ $process->reindexEverything();
245
+ }
246
+
247
+ return $this;
248
+ }
249
+
250
+ protected function getExpectedAutomaticAttributes() {
251
+ return array(
252
+ array(
253
+ 'klevu_attribute' => 'name',
254
+ 'magento_attribute' => 'name'),
255
+ array(
256
+ 'klevu_attribute' => 'image',
257
+ 'magento_attribute' => 'image'),
258
+ array(
259
+ 'klevu_attribute' => 'desc',
260
+ 'magento_attribute' => 'description'),
261
+ array(
262
+ 'klevu_attribute' => 'shortDesc',
263
+ 'magento_attribute' => 'short_description'),
264
+ array(
265
+ 'klevu_attribute' => 'salePrice',
266
+ 'magento_attribute' => 'price'),
267
+ array(
268
+ 'klevu_attribute' => 'salePrice',
269
+ 'magento_attribute' => 'tax_class_id'),
270
+ array(
271
+ 'klevu_attribute' => 'weight',
272
+ 'magento_attribute' => 'weight'),
273
+ );
274
+ }
275
+ }
app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testAddProducts.yaml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_rest_api_key: test-api-key
5
+ default/klevu_search/product_sync/enabled: 2
6
+ eav:
7
+ catalog_product:
8
+ -
9
+ entity_id: 133
10
+ attribute_set_id: 4
11
+ type_id: simple
12
+ sku: ac9003
13
+ name: Universal Camera Case
14
+ short_description: A stylish digital camera demands stylish protection. This leather carrying case will defend your camera from the dings and scratches of travel and everyday use while looking smart all the time.
15
+ description: A stylish digital camera demands stylish protection. This leather carrying case will defend your camera from the dings and scratches of travel and everyday use while looking smart all the time.
16
+ url_key: universal-camera-case
17
+ image: /u/n/universal-camera-case.jpg
18
+ weight: null
19
+ stock:
20
+ qty: 100.00
21
+ is_in_stock: 1
22
+ website_ids:
23
+ - 1 # Default website
24
+ category_ids:
25
+ - 2 # Default category
26
+ price: 34
27
+ tax_class_id: 2
28
+ status: 1
29
+ visibility: 4
30
+ created_at: 2007-08-29 19:56:27
31
+ updated_at: 2008-06-28 01:57:22
app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testClearAllProducts.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_rest_api_key: test-api-key
5
+ default/klevu_search/product_sync/enabled: 2
6
+ tables:
7
+ klevu_search/product_sync:
8
+ -
9
+ product_id: 1
10
+ parent_id: 0
11
+ store_id: 1
12
+ test_mode: 1
13
+ last_synced_at: 2008-08-10 13:36:29
14
+ -
15
+ product_id: 2
16
+ parent_id: 0
17
+ store_id: 2
18
+ test_mode: 1
19
+ last_synced_at: 2008-08-10 13:36:29
20
+
app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testDeleteProducts.yaml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_rest_api_key: test-api-key
5
+ default/klevu_search/product_sync/enabled: 2
6
+ tables:
7
+ klevu_search/product_sync:
8
+ -
9
+ product_id: 130
10
+ parent_id: 0
11
+ store_id: 1
12
+ test_mode: 1
13
+ last_synced_at: 2008-08-10 13:36:29
app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testRun.yaml ADDED
@@ -0,0 +1,425 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_rest_api_key: test-api-key
5
+ default/klevu_search/product_sync/enabled: 2
6
+ eav:
7
+ catalog_product:
8
+ -
9
+ entity_id: 132
10
+ attribute_set_id: 4
11
+ type_id: simple
12
+ sku: ac674
13
+ name: SLR Camera Tripod
14
+ short_description: Sturdy, lightweight tripods are designed to meet the needs of amateur and professional photographers and videographers.
15
+ description: Sturdy, lightweight tripods are designed to meet the needs of amateur and professional photographers and videographers.
16
+ url_key: slr-camera-tripod
17
+ image: /s/l/slr-camera-tripod.jpg
18
+ weight: 42
19
+ stock:
20
+ qty: 100.00
21
+ is_in_stock: 1
22
+ website_ids:
23
+ - 1 # Default website
24
+ category_ids:
25
+ - 2 # Default category
26
+ price: 99
27
+ tax_class_id: 2
28
+ status: 1
29
+ visibility: 4
30
+ created_at: 2007-08-29 19:53:55
31
+ updated_at: 2008-08-08 15:05:45
32
+ -
33
+ entity_id: 133
34
+ attribute_set_id: 4
35
+ type_id: simple
36
+ sku: ac9003
37
+ name: Universal Camera Case
38
+ short_description: A stylish digital camera demands stylish protection. This leather carrying case will defend your camera from the dings and scratches of travel and everyday use while looking smart all the time.
39
+ description: A stylish digital camera demands stylish protection. This leather carrying case will defend your camera from the dings and scratches of travel and everyday use while looking smart all the time.
40
+ url_key: universal-camera-case
41
+ image: /u/n/universal-camera-case.jpg
42
+ weight: null
43
+ stock:
44
+ qty: 100.00
45
+ is_in_stock: 1
46
+ website_ids:
47
+ - 1 # Default website
48
+ category_ids:
49
+ - 2 # Default category
50
+ price: 34
51
+ tax_class_id: 2
52
+ status: 1
53
+ visibility: 4
54
+ created_at: 2007-08-29 19:56:27
55
+ updated_at: 2008-06-28 01:57:22
56
+ -
57
+ entity_id: 134
58
+ attribute_set_id: 4
59
+ type_id: simple
60
+ sku: ac-66332
61
+ name: Universal Camera Charger
62
+ short_description: Features foldable Flat Pin for Easy Storage/ Slim/ Lightweight Design and Smart Charging LED Indicator.
63
+ description: Features foldable Flat Pin for Easy Storage/ Slim/ Lightweight Design and Smart Charging LED Indicator.
64
+ url_key: universal-camera-charger
65
+ image: /u/n/universal-camera-charger.jpg
66
+ weight: 5
67
+ stock:
68
+ qty: 100.00
69
+ is_in_stock: 1
70
+ website_ids:
71
+ - 1 # Default website
72
+ category_ids:
73
+ - 2 # Default category
74
+ price: 19
75
+ tax_class_id: 2
76
+ status: 1
77
+ visibility: 4
78
+ created_at: 2007-08-29 19:59:29
79
+ updated_at: 2008-06-28 01:57:16
80
+ -
81
+ entity_id: 138
82
+ attribute_set_id: 4
83
+ type_id: simple
84
+ sku: apevia-black
85
+ name: Apevia Black X-Cruiser Case ATX Mid-Tower Case (Default)
86
+ short_description: This magnificent new case features 2 x 80mm built-in fans with space for 2 optional fans. The Aspire X-Cruiser features front temperature gauge, front fan speed controller and gauge, and front volume controller and gauge. It also features USB2.0, Firewire and audio ports. The superior cooling solution that X-Cruiser delivers can only be found in very few of the expensive chassis in the market.
87
+ description: This superb, multi-functional Aspire X-Cruiser mid tower case is designed to follow Intel's recommendations to achieve the highest level of thermal performance. This gorgeous case outperforms most of the expensive chassis in the market, boasts an abundance of innovative features, yet carries a very affordable price tag. The Aspire X-Cruiser is fully loaded with everything you could possibly imagine that a great gaming case could have. With a built-in air duct on the side panel to funnel cool air towards the CPU, the fan-less X-Cruiser is specially designed to bring out the best performance in systems based on high-end processors.
88
+ url_key: apevia-black-x-cruiser-case-atx-mid-tower-case
89
+ image: /a/p/apevia-black-x-cruiser-case-atx-mid-tower-case-default.jpg
90
+ weight: 10
91
+ stock:
92
+ qty: 100.00
93
+ is_in_stock: 1
94
+ website_ids:
95
+ - 1 # Default website
96
+ category_ids:
97
+ - 2 # Default category
98
+ price: 150
99
+ tax_class_id: 1
100
+ status: 1
101
+ visibility: 4
102
+ created_at: 2008-07-25 00:52:03
103
+ updated_at: 2008-08-08 13:36:29
104
+ -
105
+ entity_id: 139
106
+ attribute_set_id: 4
107
+ type_id: simple
108
+ sku: nzxtlexa
109
+ name: NZXT Lexa Silver Aluminum ATX Mid-Tower Case (Default)
110
+ short_description: The Lexa boasts a ridgid, but light weight aluminum chassis. The Lexa's high-air-flow design, while quiet, ensures cooling options for the more ambitious computing enthusiasts. Three thermal probes provide temperature readings to the illuminated LCD display for monitoring your systems core components. The Lexa ships with a rugged carrying strap with protective window cover for easy single-handed transportation.
111
+ description: Introducing the next advancement in case design and technology, the Lexa. Designed from ground up to be symmetrical, the Lexa is representative of the modern lifestyle design cues of today. The Lexa's minimalist and classy design fits perfectly under the NZXT Classic Series line.
112
+ url_key: nzxt-lexa-silver-aluminum-atx-mid-tower-case
113
+ image: /n/z/nzxt-lexa-silver-aluminum-atx-mid-tower-case-default.jpg
114
+ weight: 10
115
+ stock:
116
+ qty: 100.00
117
+ is_in_stock: 1
118
+ website_ids:
119
+ - 1 # Default website
120
+ category_ids:
121
+ - 2 # Default category
122
+ price: 199.99
123
+ tax_class_id: 1
124
+ status: 1
125
+ visibility: 4
126
+ created_at: 2008-07-25 00:54:41
127
+ updated_at: 2008-07-25 00:54:41
128
+ -
129
+ entity_id: 201
130
+ attribute_set_id: 4
131
+ type_id: simple
132
+ sku: delete-simple
133
+ name: Delete Simple Product
134
+ short_description: Delete Simple Product
135
+ description: delete Simple Product
136
+ url_key: delete-simple
137
+ website_ids:
138
+ - 1 # Default website
139
+ category_ids:
140
+ - 2 # Default category
141
+ price: 19.99
142
+ tax_class_id: 2
143
+ status: 1
144
+ visibility: 1
145
+ color: 4
146
+ created_at: 2014-05-21 12:00:00
147
+ updated_at: 2014-05-21 12:00:00
148
+ -
149
+ entity_id: 202
150
+ attribute_set_id: 4
151
+ type_id: configurable
152
+ sku: delete
153
+ name: Delete Configurable Product
154
+ short_description: Delete Configurable Product
155
+ description: Delete Configurable Product
156
+ url_key: delete
157
+ super_attributes:
158
+ - color
159
+ configurable_children:
160
+ - 201
161
+ website_ids:
162
+ - 1 # Default website
163
+ category_ids:
164
+ - 2 # Default category
165
+ price: 19.99
166
+ tax_class_id: 2
167
+ status: 1
168
+ visibility: 1
169
+ created_at: 2014-05-21 12:00:00
170
+ updated_at: 2014-05-21 12:00:00
171
+ -
172
+ entity_id: 203
173
+ attribute_set_id: 4
174
+ type_id: simple
175
+ sku: update-parent-simple
176
+ name: Update Parent Simple Product
177
+ short_description: Update Parent Simple Product
178
+ description: Update Parent Simple Product
179
+ url_key: update-parent-simple
180
+ website_ids:
181
+ - 1 # Default website
182
+ category_ids:
183
+ - 2 # Default category
184
+ price: 19.99
185
+ tax_class_id: 2
186
+ status: 1
187
+ visibility: 1
188
+ color: 4
189
+ created_at: 2014-05-20 09:00:00
190
+ updated_at: 2014-05-20 09:00:00
191
+ -
192
+ entity_id: 204
193
+ attribute_set_id: 4
194
+ type_id: configurable
195
+ sku: update-parent
196
+ name: Update Parent Configurable Product
197
+ short_description: Update Parent Configurable Product
198
+ description: Update Parent Configurable Product
199
+ url_key: update-parent
200
+ super_attributes:
201
+ - color
202
+ configurable_children:
203
+ - 203
204
+ website_ids:
205
+ - 1 # Default website
206
+ category_ids:
207
+ - 2 # Default category
208
+ price: 19.99
209
+ tax_class_id: 2
210
+ status: 1
211
+ visibility: 4
212
+ created_at: 2014-05-20 09:00:00
213
+ updated_at: 2014-05-21 09:00:00
214
+ -
215
+ entity_id: 205
216
+ attribute_set_id: 4
217
+ type_id: simple
218
+ sku: update-child-simple
219
+ name: Update Child Simple Product
220
+ short_description: Update Child Simple Product
221
+ description: Update Child Simple Product
222
+ url_key: update-child-simple
223
+ website_ids:
224
+ - 1 # Default website
225
+ category_ids:
226
+ - 2 # Default category
227
+ price: 19.99
228
+ tax_class_id: 2
229
+ status: 1
230
+ visibility: 1
231
+ color: 4
232
+ created_at: 2014-05-20 09:00:00
233
+ updated_at: 2014-05-21 09:00:00
234
+ -
235
+ entity_id: 206
236
+ attribute_set_id: 4
237
+ type_id: configurable
238
+ sku: update-child
239
+ name: Update Child Configurable Product
240
+ short_description: Update Child Configurable Product
241
+ description: Update Child Configurable Product
242
+ url_key: update-child
243
+ super_attributes:
244
+ - color
245
+ configurable_children:
246
+ - 205
247
+ website_ids:
248
+ - 1 # Default website
249
+ category_ids:
250
+ - 2 # Default category
251
+ price: 19.99
252
+ tax_class_id: 2
253
+ status: 1
254
+ visibility: 4
255
+ created_at: 2014-05-20 09:00:00
256
+ updated_at: 2014-05-20 09:00:00
257
+ -
258
+ entity_id: 207
259
+ attribute_set_id: 4
260
+ type_id: simple
261
+ sku: add-simple-1
262
+ name: Add Simple Product 1
263
+ short_description: Add Simple Product 1
264
+ description: Add Simple Product 1
265
+ url_key: add-simple-1
266
+ website_ids:
267
+ - 1 # Default website
268
+ category_ids:
269
+ - 2 # Default category
270
+ price: 19.99
271
+ tax_class_id: 2
272
+ status: 1
273
+ visibility: 1
274
+ color: 4
275
+ created_at: 2014-05-20 09:00:00
276
+ updated_at: 2014-05-21 09:00:00
277
+ -
278
+ entity_id: 208
279
+ attribute_set_id: 4
280
+ type_id: simple
281
+ sku: add-simple-2
282
+ name: Add Simple Product 2
283
+ short_description: Add Simple Product 2
284
+ description: Add Simple Product 2
285
+ url_key: add-simple-2
286
+ website_ids:
287
+ - 1 # Default website
288
+ category_ids:
289
+ - 2 # Default category
290
+ price: 29.99
291
+ tax_class_id: 2
292
+ status: 1
293
+ visibility: 1
294
+ color: 3
295
+ created_at: 2014-05-20 09:00:00
296
+ updated_at: 2014-05-21 09:00:00
297
+ -
298
+ entity_id: 209
299
+ attribute_set_id: 4
300
+ type_id: configurable
301
+ sku: add
302
+ name: Add Configurable Product
303
+ short_description: Add Configurable Product
304
+ description: Add Configurable Product
305
+ url_key: add
306
+ super_attributes:
307
+ - color
308
+ configurable_children:
309
+ - 207
310
+ - 208
311
+ website_ids:
312
+ - 1 # Default website
313
+ category_ids:
314
+ - 2 # Default category
315
+ price: 19.99
316
+ tax_class_id: 2
317
+ status: 1
318
+ visibility: 4
319
+ created_at: 2014-05-20 09:00:00
320
+ updated_at: 2014-05-20 09:00:00
321
+ -
322
+ entity_id: 210
323
+ attribute_set_id: 4
324
+ type_id: simple
325
+ sku: delete-deleted-child-simple
326
+ name: Delete Deleted Child Simple Product
327
+ short_description: Delete Deleted Child Simple Product
328
+ description: Delete Deleted Child Simple Product
329
+ url_key: delete-deleted-child-simple
330
+ website_ids:
331
+ - 1 # Default website
332
+ category_ids:
333
+ - 2 # Default category
334
+ price: 19.99
335
+ tax_class_id: 2
336
+ status: 1
337
+ visibility: 1
338
+ color: 4
339
+ created_at: 2014-05-21 12:00:00
340
+ updated_at: 2014-05-21 12:00:00
341
+ -
342
+ entity_id: 212
343
+ attribute_set_id: 4
344
+ type_id: configurable
345
+ sku: delete-deleted-child
346
+ name: Delete Deleted Child Configurable Product
347
+ short_description: Delete Deleted Child Configurable Product
348
+ description: Delete Deleted Child Configurable Product
349
+ url_key: delete-deleted-child
350
+ super_attributes:
351
+ - color
352
+ configurable_children:
353
+ - 210
354
+ website_ids:
355
+ - 1 # Default website
356
+ category_ids:
357
+ - 2 # Default category
358
+ price: 19.99
359
+ tax_class_id: 2
360
+ status: 1
361
+ visibility: 4
362
+ created_at: 2014-05-21 12:00:00
363
+ updated_at: 2014-05-21 12:00:00
364
+ tables:
365
+ klevu_search/product_sync:
366
+ -
367
+ product_id: 130
368
+ parent_id: 0
369
+ store_id: 1
370
+ test_mode: 1
371
+ last_synced_at: 2008-08-10 13:36:29
372
+ -
373
+ product_id: 133
374
+ parent_id: 0
375
+ store_id: 1
376
+ test_mode: 1
377
+ last_synced_at: 2008-06-27 01:57:22
378
+ -
379
+ product_id: 134
380
+ parent_id: 0
381
+ store_id: 1
382
+ test_mode: 1
383
+ last_synced_at: 2008-06-27 01:57:16
384
+ -
385
+ product_id: 138
386
+ parent_id: 0
387
+ store_id: 1
388
+ test_mode: 1
389
+ last_synced_at: 2008-08-10 13:36:29
390
+ -
391
+ product_id: 139
392
+ parent_id: 0
393
+ store_id: 1
394
+ test_mode: 1
395
+ last_synced_at: 2008-08-10 13:36:29
396
+ -
397
+ product_id: 201
398
+ parent_id: 202
399
+ store_id: 1
400
+ test_mode: 1
401
+ last_synced_at: 2014-05-21 12:00:00
402
+ -
403
+ product_id: 203
404
+ parent_id: 204
405
+ store_id: 1
406
+ test_mode: 1
407
+ last_synced_at: 2014-05-20 12:00:00
408
+ -
409
+ product_id: 205
410
+ parent_id: 206
411
+ store_id: 1
412
+ test_mode: 1
413
+ last_synced_at: 2014-05-20 12:00:00
414
+ -
415
+ product_id: 210
416
+ parent_id: 212
417
+ store_id: 1
418
+ test_mode: 1
419
+ last_synced_at: 2014-05-21 12:00:00
420
+ -
421
+ product_id: 211
422
+ parent_id: 212
423
+ store_id: 1
424
+ test_mode: 1
425
+ last_synced_at: 2014-05-21 12:00:00
app/code/community/Klevu/Search/Test/Model/Product/Sync/fixtures/testUpdateProducts.yaml ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ config:
2
+ default/klevu_search/general/enabled: 1
3
+ default/klevu_search/general/test_mode: 1
4
+ default/klevu_search/general/test_rest_api_key: test-api-key
5
+ default/klevu_search/product_sync/enabled: 2
6
+ eav:
7
+ catalog_product:
8
+ -
9
+ entity_id: 133
10
+ attribute_set_id: 4
11
+ type_id: simple
12
+ sku: ac9003
13
+ name: Universal Camera Case
14
+ short_description: A stylish digital camera demands stylish protection. This leather carrying case will defend your camera from the dings and scratches of travel and everyday use while looking smart all the time.
15
+ description: A stylish digital camera demands stylish protection. This leather carrying case will defend your camera from the dings and scratches of travel and everyday use while looking smart all the time.
16
+ url_key: universal-camera-case
17
+ image: /u/n/universal-camera-case.jpg
18
+ weight: null
19
+ stock:
20
+ qty: 100.00
21
+ is_in_stock: 1
22
+ website_ids:
23
+ - 1 # Default website
24
+ category_ids:
25
+ - 2 # Default category
26
+ price: 34
27
+ tax_class_id: 2
28
+ status: 1
29
+ visibility: 4
30
+ created_at: 2007-08-29 19:56:27
31
+ updated_at: 2008-06-28 01:57:22
32
+ tables:
33
+ klevu_search/product_sync:
34
+ -
35
+ product_id: 133
36
+ parent_id: 0
37
+ store_id: 1
38
+ test_mode: 1
39
+ last_synced_at: 2008-06-27 01:57:22
app/code/community/Klevu/Search/Test/Model/Sync.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_Sync extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ const TEST_JOB_CODE = "klevu_search_test_job";
6
+
7
+ protected function tearDown() {
8
+ $collection = $this->getTestCronScheduleCollection();
9
+ foreach ($collection as $item) {
10
+ $item->delete();
11
+ }
12
+
13
+ parent::tearDown();
14
+ }
15
+
16
+ /**
17
+ * @test
18
+ * @dataProvider dataProvider
19
+ */
20
+ public function testSchedule($time) {
21
+ $model = $this->getTestModel();
22
+
23
+ $time = new DateTime($time);
24
+
25
+ $model->schedule($time);
26
+
27
+ $collection = $this->getTestCronScheduleCollection();
28
+
29
+ $this->assertEquals(1, $collection->getSize());
30
+
31
+ $schedule = $collection->getFirstItem();
32
+
33
+ $this->assertEquals($time->format("Y-m-d H:i:00"), $schedule->getScheduledAt());
34
+ }
35
+
36
+ /**
37
+ * @test
38
+ */
39
+ public function testAlreadyScheduled() {
40
+ $model = $this->getTestModel();
41
+ $connection = Mage::getModel('core/resource')->getConnection("core_write");
42
+ $count_sql = $this->getTestCronScheduleCollection()->getSelectCountSql();
43
+
44
+ $time = new DateTime("now");
45
+
46
+ $model->schedule($time);
47
+ $this->assertEquals(1, $connection->fetchOne($count_sql),
48
+ "Failed to assert that schedule() adds a cron entry to the schedule.");
49
+
50
+ $before = clone $time;
51
+ $before->modify("15 minutes ago");
52
+ $model->schedule($before);
53
+ $this->assertEquals(1, $connection->fetchOne($count_sql),
54
+ "Failed to assert that schedule() does not add a new cron entry if one is already scheduled for up to 15 minutes later.");
55
+
56
+ $after = clone $time;
57
+ $after->modify("15 minutes");
58
+ $model->schedule($after);
59
+ $this->assertEquals(1, $connection->fetchOne($count_sql),
60
+ "Failed to assert that schedule() does not add a new cron entry if one is already scheduled for up to 15 minutes earlier.");
61
+
62
+ $before->modify("5 minutes ago");
63
+ $model->schedule($before);
64
+ $this->assertEquals(2, $connection->fetchOne($count_sql),
65
+ "Failed to assert that schedule() can add a new cron entry 20 minutes before an existing one.");
66
+
67
+ $after->modify("5 minutes");
68
+ $model->schedule($after);
69
+ $this->assertEquals(3, $connection->fetchOne($count_sql),
70
+ "Failed to assert that schedule() can add a new cron entry 20 minutes after an existing one.");
71
+ }
72
+
73
+ /**
74
+ * @test
75
+ */
76
+ public function testIsRunning() {
77
+ $model = $this->getTestModel();
78
+
79
+ $now = new DateTime();
80
+ $now = $now->format("Y-m-d H:i:00");
81
+
82
+ $schedule = Mage::getModel('cron/schedule');
83
+ $schedule
84
+ ->setJobCode(static::TEST_JOB_CODE)
85
+ ->setCreatedAt($now)
86
+ ->setScheduledAt($now)
87
+ ->setExecutedAt($now)
88
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_RUNNING)
89
+ ->save();
90
+
91
+ $this->assertTrue($model->isRunning(), "Failed to assert that isRunning() returns true when there's a sync cron running.");
92
+ $this->assertFalse($model->isRunning(2), "Failed to assert that isRunning(2) returns false when there's only one cron running.");
93
+
94
+
95
+ $schedule = Mage::getModel('cron/schedule');
96
+ $schedule
97
+ ->setJobCode(static::TEST_JOB_CODE)
98
+ ->setCreatedAt($now)
99
+ ->setScheduledAt($now)
100
+ ->setExecutedAt($now)
101
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_RUNNING)
102
+ ->save();
103
+
104
+ $this->assertTrue($model->isRunning(2), "Failed to assert that isRunning(2) returns true when there's 2 sync crons running.");
105
+ }
106
+
107
+ /**
108
+ * Return a Mock fo the klevu_search/sync model for testing.
109
+ *
110
+ * @return PHPUnit_Framework_MockObject_MockObject
111
+ */
112
+ protected function getTestModel() {
113
+ $mock = $this->getMockForAbstractClass(Mage::app()->getConfig()->getModelClassName("klevu_search/sync"));
114
+ $mock
115
+ ->expects($this->any())
116
+ ->method("getJobCode")
117
+ ->will($this->returnValue(static::TEST_JOB_CODE));
118
+ return $mock;
119
+ }
120
+
121
+ /**
122
+ * Return a cron/schedule collection filtered for test jobs only.
123
+ *
124
+ * @return Mage_Cron_Model_Mysql4_Schedule_Collection
125
+ */
126
+ protected function getTestCronScheduleCollection() {
127
+ return Mage::getResourceModel('cron/schedule_collection')
128
+ ->addFieldToFilter("job_code", static::TEST_JOB_CODE);
129
+ }
130
+ }
app/code/community/Klevu/Search/Test/Model/Sync/providers/testSchedule.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ -
2
+ time: now
3
+ -
4
+ time: 2014-05-14 12:00:00
app/code/community/Klevu/Search/Test/Model/System/Config/Source/Test.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Test_Model_System_Config_Source_Test extends EcomDev_PHPUnit_Test_Case {
4
+
5
+ /**
6
+ * @test
7
+ * @dataProvider dataProvider
8
+ */
9
+ public function testIsValidSourceModel($alias) {
10
+ $model = Mage::getModel($alias);
11
+
12
+ $this->assertTrue($model !== false, sprintf("Model with alias %s not found.", $alias));
13
+ $this->assertTrue(method_exists($model, "toOptionArray"), "toOptionArray() method doesn't exist.");
14
+
15
+ $options = $model->toOptionArray();
16
+
17
+ $this->assertTrue(is_array($options), "toOptionArray() did not return an array.");
18
+
19
+ foreach ($options as $option) {
20
+ $this->assertTrue(is_array($option), sprintf("Each option must be an array, instead got: %s", print_r($option, true)));
21
+
22
+ $this->assertArrayHasKey("label", $option, sprintf("Each option must have a label, instead got: %s", print_r($option, true)));
23
+ $this->assertArrayHasKey("value", $option, sprintf("Each option must have a value, instead got: %s", print_r($option, true)));
24
+ }
25
+ }
26
+ }
app/code/community/Klevu/Search/Test/Model/System/Config/Source/Test/providers/testIsValidSourceModel.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ -
2
+ - klevu_search/system_config_source_yesnoforced
3
+ -
4
+ - klevu_search/system_config_source_frequency
5
+ -
6
+ - klevu_search/system_config_source_log_level
7
+ -
8
+ - klevu_search/system_config_source_additional_attributes
9
+ -
10
+ - klevu_search/system_config_source_product_attributes
app/code/community/Klevu/Search/controllers/Adminhtml/Klevu/NotificationsController.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Adminhtml_Klevu_NotificationsController extends Mage_Adminhtml_Controller_Action {
4
+
5
+ public function dismissAction() {
6
+ $id = intval($this->getRequest()->getParam("id"));
7
+
8
+ $notification = Mage::getModel('klevu_search/notification')->load($id);
9
+
10
+ if ($notification->getId()) {
11
+ $notification->delete();
12
+ } else {
13
+ Mage::getSingleton("adminhtml/session")->addError("Unable to dismiss Klevu notification as it does not exist.");
14
+ }
15
+
16
+ return $this->_redirectReferer($this->getUrl("adminhtml/dashboard"));
17
+ }
18
+ }
app/code/community/Klevu/Search/controllers/Adminhtml/Klevu/Search/WizardController.php ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Adminhtml_Klevu_Search_WizardController extends Mage_Adminhtml_Controller_Action {
4
+
5
+ public function configure_userAction() {
6
+ $this->loadLayout();
7
+ $this->initLayoutMessages('klevu_search/session');
8
+ $this->renderLayout();
9
+ }
10
+
11
+ public function configure_user_postAction() {
12
+ $request = $this->getRequest();
13
+
14
+ if (!$request->isPost() || !$request->isAjax()) {
15
+ return $this->_redirect('adminhtml/dashboard');
16
+ }
17
+
18
+ $api = Mage::helper("klevu_search/api");
19
+ $session = Mage::getSingleton('klevu_search/session');
20
+
21
+ if ($request->getPost("klevu_existing_email")) {
22
+ $result = $api->getUser(
23
+ $request->getPost("klevu_existing_email"),
24
+ $request->getPost("klevu_existing_password")
25
+ );
26
+ } else {
27
+ $klevu_new_email = $request->getPost("klevu_new_email");
28
+ $klevu_new_password = $request->getPost("klevu_new_password");
29
+ $userPlan = $request->getPost("userPlan");
30
+ $klevu_new_url = $request->getPost("klevu_new_url");
31
+ $merchantEmail = $request->getPost("merchantEmail");
32
+ $contactNo = $request->getPost("countyCode")."-".$request->getPost("contactNo");
33
+ if(!empty($klevu_new_email) && !empty($klevu_new_password) && !empty($userPlan) && !empty($klevu_new_url)
34
+ && !empty($merchantEmail) ) {
35
+ $result = $api->createUser(
36
+ $request->getPost("klevu_new_email"),
37
+ $request->getPost("klevu_new_password"),
38
+ $request->getPost("userPlan"),
39
+ $request->getPost("klevu_new_url"),
40
+ $request->getPost("merchantEmail"),
41
+ $contactNo
42
+ );
43
+ } else {
44
+ $session->addError(Mage::helper("klevu_search")->__("Missing details in the form. Please check."));
45
+ return $this->_forward("configure_user");
46
+ }
47
+ }
48
+
49
+ if ($result["success"]) {
50
+ $session->setConfiguredCustomerId($result["customer_id"]);
51
+ if (isset($result["message"])) {
52
+ $session->addSuccess(Mage::helper("klevu_search")->__($result["message"]));
53
+ }
54
+ return $this->_forward("configure_store");
55
+ } else {
56
+ $session->addError(Mage::helper("klevu_search")->__($result["message"]));
57
+ return $this->_forward("configure_user");
58
+ }
59
+ }
60
+
61
+ public function configure_storeAction() {
62
+ $request = $this->getRequest();
63
+
64
+ if (!$request->isAjax()) {
65
+ return $this->_redirect("adminhtml/dashboard");
66
+ }
67
+
68
+ $session = Mage::getSingleton("klevu_search/session");
69
+
70
+ if (!$session->getConfiguredCustomerId()) {
71
+ $session->addError(Mage::helper("klevu_search")->__("You must configure a user first."));
72
+ return $this->_redirect("*/*/configure_user");
73
+ }
74
+
75
+ $this->loadLayout();
76
+ $this->initLayoutMessages('klevu_search/session');
77
+ $this->renderLayout();
78
+ }
79
+
80
+ public function configure_store_postAction() {
81
+ $request = $this->getRequest();
82
+
83
+ if (!$request->isPost() || !$request->isAjax()) {
84
+ return $this->_redirect("adminhtml/dashboard");
85
+ }
86
+
87
+ $config = Mage::helper("klevu_search/config");
88
+ $api = Mage::helper("klevu_search/api");
89
+ $session = Mage::getSingleton('klevu_search/session');
90
+ $customer_id = $session->getConfiguredCustomerId();
91
+
92
+ if (!$customer_id) {
93
+ $session->addError(Mage::helper("klevu_search")->__("You must configure a user first."));
94
+ return $this->_redirect("*/*/configure_user");
95
+ }
96
+
97
+ $store_code = $request->getPost("store");
98
+ if (strlen($store_code) == 0) {
99
+ $session->addError(Mage::helper("klevu_search")->__("Must select a store"));
100
+ return $this->_forward("configure_store");
101
+ }
102
+
103
+ try {
104
+ $store = Mage::app()->getStore($store_code);
105
+ } catch (Mage_Core_Model_Store_Exception $e) {
106
+ $session->addError(Mage::helper("klevu_search")->__("Selected store does not exist."));
107
+ return $this->_forward("configure_store");
108
+ }
109
+
110
+ // Setup the live and test Webstores
111
+ foreach (array(false) as $test_mode) {
112
+ $result = $api->createWebstore($customer_id, $store, $test_mode);
113
+ if ($result["success"]) {
114
+ $config->setJsApiKey($result["webstore"]->getJsApiKey(), $store, $test_mode);
115
+ $config->setRestApiKey($result["webstore"]->getRestApiKey(), $store, $test_mode);
116
+ $config->setHostname($result["webstore"]->getHostedOn(), $store, $test_mode);
117
+ $config->setCloudSearchUrl($result['webstore']->getCloudSearchUrl(), $store, $test_mode);
118
+ $config->setAnalyticsUrl($result['webstore']->getAnalyticsUrl(), $store, $test_mode);
119
+ $config->setJsUrl($result['webstore']->getJsUrl(), $store, $test_mode);
120
+ $config->setRestHostname($result['webstore']->getRestHostname(), $store, $test_mode);
121
+ if (isset($result["message"])) {
122
+ $session->addSuccess(Mage::helper("klevu_search")->__($result["message"]));
123
+ }
124
+ } else {
125
+ $session->addError(Mage::helper("klevu_search")->__($result["message"]));
126
+ return $this->_forward("configure_store");
127
+ }
128
+ }
129
+
130
+
131
+ $config->setTaxEnabledFlag($request->getPost("tax_enable"), $store);
132
+
133
+ // Clear Product Sync and Order Sync data for the newly configured store
134
+ Mage::getModel("klevu_search/product_sync")->clearAllProducts($store);
135
+ Mage::getModel("klevu_search/order_sync")->clearQueue($store);
136
+
137
+ $session->setConfiguredStoreCode($store_code);
138
+
139
+ $session->addSuccess("Store configured successfully. Saved API credentials.");
140
+
141
+ // Schedule a Product Sync
142
+ Mage::getModel("klevu_search/product_sync")->schedule();
143
+
144
+ $this->loadLayout();
145
+ $this->initLayoutMessages("klevu_search/session");
146
+ $this->renderLayout();
147
+ return;
148
+ }
149
+
150
+ public function configure_attributesAction() {
151
+
152
+ $request = $this->getRequest();
153
+
154
+ if (!$request->isAjax()) {
155
+ return $this->_redirect("adminhtml/dashboard");
156
+ }
157
+
158
+ $session = Mage::getSingleton("klevu_search/session");
159
+
160
+ if (!$session->getConfiguredCustomerId()) {
161
+ $session->addError(Mage::helper("klevu_search")->__("You must configure a user first."));
162
+ return $this->_redirect("*/*/configure_user");
163
+ }
164
+
165
+ if (!$session->getConfiguredStoreCode()) {
166
+ $session->addError(Mage::helper("klevu_search")->__("Must select a store"));
167
+ return $this->_redirect("*/*/configure_store");
168
+ }
169
+
170
+ $this->loadLayout();
171
+ $this->initLayoutMessages('klevu_search/session');
172
+ $this->renderLayout();
173
+ }
174
+
175
+ public function configure_attributes_postAction() {
176
+
177
+ $request = $this->getRequest();
178
+
179
+ if (!$request->isPost() || !$request->isAjax()) {
180
+ return $this->_redirect("adminhtml/dashboard");
181
+ }
182
+
183
+ $session = Mage::getSingleton("klevu_search/session");
184
+
185
+ if (!$session->getConfiguredCustomerId()) {
186
+ $session->addError(Mage::helper("klevu_search")->__("You must configure a user first."));
187
+ return $this->_redirect("*/*/configure_user");
188
+ }
189
+
190
+ if (!$session->getConfiguredStoreCode()) {
191
+ $session->addError(Mage::helper("klevu_search")->__("Must select a store"));
192
+ return $this->_redirect("*/*/configure_store");
193
+ }
194
+
195
+ if ($attributes = $request->getPost("attributes")) {
196
+ $store = Mage::app()->getStore($session->getConfiguredStoreCode());
197
+
198
+ Mage::helper("klevu_search/config")->setAdditionalAttributesMap($attributes, $store);
199
+
200
+ $session->addSuccess(Mage::helper("klevu_search")->__("Attributes configured successfully. Attribute mappings saved to System Configuration."));
201
+
202
+ // Schedule a Product Sync
203
+ Mage::getModel("klevu_search/product_sync")->schedule();
204
+
205
+ $this->loadLayout();
206
+ $this->initLayoutMessages("klevu_search/session");
207
+ $this->renderLayout();
208
+ return;
209
+ } else {
210
+ $session->addError(Mage::helper("klevu_search")->__("Missing attributes!"));
211
+ //return $this->_forward("configure_attributes");
212
+ }
213
+ }
214
+ }
app/code/community/Klevu/Search/controllers/Adminhtml/Klevu/SearchController.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Klevu_Search_Adminhtml_Klevu_SearchController extends Mage_Adminhtml_Controller_Action {
4
+
5
+ public function sync_allAction() {
6
+ $store = $this->getRequest()->getParam("store");
7
+
8
+ if ($store !== null) {
9
+ try {
10
+ $store = Mage::app()->getStore($store);
11
+ } catch (Mage_Core_Model_Store_Exception $e) {
12
+ Mage::getSingleton("adminhtml/session")->addError($this->__("Selected store could not be found!"));
13
+ return $this->_redirectReferer("adminhtml/dashboard");
14
+ }
15
+ }
16
+
17
+ if (Mage::helper('klevu_search/config')->isProductSyncEnabled()) {
18
+ Mage::getModel('klevu_search/product_sync')
19
+ ->markAllProductsForUpdate($store)
20
+ ->schedule();
21
+
22
+ if ($store) {
23
+ Mage::helper("klevu_search")->log(Zend_Log::INFO, sprintf("Product Sync scheduled to re-sync ALL products in %s (%s).",
24
+ $store->getWebsite()->getName(),
25
+ $store->getName()
26
+ ));
27
+
28
+ Mage::getSingleton("adminhtml/session")->addSuccess($this->__("Klevu Search Product Sync scheduled to be run on the next cron run for ALL products in %s (%s).",
29
+ $store->getWebsite()->getName(),
30
+ $store->getName()
31
+ ));
32
+ } else {
33
+ Mage::helper("klevu_search")->log(Zend_Log::INFO, "Product Sync scheduled to re-sync ALL products.");
34
+
35
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__("Klevu Search Product Sync scheduled to be run on the next cron run for ALL products."));
36
+ }
37
+ } else {
38
+ Mage::getSingleton('adminhtml/session')->addError($this->__("Klevu Search Product Sync is disabled."));
39
+ }
40
+
41
+ return $this->_redirectReferer("adminhtml/dashboard");
42
+ }
43
+
44
+ public function manual_syncAction() {
45
+ Mage::getModel("klevu_search/product_sync")->runManually();
46
+
47
+ return $this->_redirectReferer("adminhtml/dashboard");
48
+ }
49
+ /**
50
+ * Get the product ids for thumbnails and schedual the cron.
51
+ */
52
+ public function generate_thumbnailAction() {
53
+ try {
54
+ Mage::getModel('klevu_search/product_sync')->getEntityIds();
55
+ Mage::getModel('klevu_search/product_sync')->scheduleImageCron();
56
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__("Klevu thumbnails generation task scheduled to be run on the next cron run."));
57
+ }
58
+ catch (Exception $e) {
59
+ Mage::logException($e);
60
+ Mage::getSingleton('adminhtml/session')->addError($this->__("Unable to generate product images. Please try later."));
61
+ }
62
+ return $this->_redirectReferer("adminhtml/dashboard");
63
+ }
64
+ }
app/code/community/Klevu/Search/controllers/SearchController.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Klevu FrontEnd Controller
4
+ */
5
+ class Klevu_Search_SearchController extends Mage_Core_Controller_Front_Action {
6
+ /**
7
+ * Genarate thumbnail using multiple curl request action
8
+ */
9
+ public function indexAction() {
10
+ try {
11
+ $id = Mage::app()->getRequest()->getParam('id');
12
+ $data = Mage::getModel('klevu_search/product_sync')->getImageProcessingIds($id);
13
+ foreach(unserialize($data[0]['batchdata']) as $key => $value) {
14
+ $product = Mage::getModel('catalog/product')->load($value);
15
+ $image = $product->getImage();
16
+ if((!empty($image)) && ($image!= "no_selection")) {
17
+ Mage::getModel('klevu_search/product_sync')->thumbImage($product->getImage());
18
+ }
19
+ }
20
+ Mage::getModel('klevu_search/product_sync')->updateImageProcessingIds($id);
21
+ }
22
+ catch(Exception $e) {
23
+ Mage::helper('klevu_search')->log(Zend_Log::DEBUG, sprintf("Image Error:\n%s", $e->getMessage()));
24
+ }
25
+
26
+ }
27
+
28
+ public function runexternalylogAction()
29
+ {
30
+ $this->loadLayout();
31
+ $this->renderLayout();
32
+ }
33
+ /**
34
+ * Send different logs to klevu server to debug the data
35
+ */
36
+ public function runexternalyAction(){
37
+ try {
38
+ $debugapi = Mage::getModel('klevu_search/product_sync')->getApiDebug();
39
+ $content="";
40
+ if($this->getRequest()->getParam('debug') == "klevu") {
41
+ // get last 500 lines from klevu log
42
+ $path = Mage::getBaseDir("log")."/Klevu_Search.log";
43
+ if($this->getRequest()->getParam('lines')) {
44
+ $line = $this->getRequest()->getParam('lines');
45
+ }else {
46
+ $line = 100;
47
+ }
48
+ $content.= $this->getLastlines($path,$line,true);
49
+
50
+ //send php and magento version
51
+ $content.= "</br>".'****Current Magento version on store:'.Mage::getVersion()."</br>";
52
+ $content.= "</br>".'****Current PHP version on store:'. phpversion()."</br>";
53
+
54
+ //send cron and logfile data
55
+ $cron = Mage::getBaseDir()."/cron.php";
56
+ $cronfile = file_get_contents($cron);
57
+ $content.= nl2br(htmlspecialchars($content)).nl2br(htmlspecialchars($cronfile));
58
+ $response = Mage::getModel("klevu_search/api_action_debuginfo")->debugKlevu(array('apiKey'=>$debugapi,'klevuLog'=>$content,'type'=>'log_file'));
59
+ if($response->getMessage()=="success") {
60
+ Mage::getSingleton('core/session')->addSuccess("Klevu search log sent.");
61
+ }
62
+
63
+ $content = serialize(Mage::getModel('klevu_search/product_sync')->debugsIds());
64
+ $response = Mage::getModel("klevu_search/api_action_debuginfo")->debugKlevu(array('apiKey'=>$debugapi,'klevuLog'=>$content,'type'=>'product_table'));
65
+
66
+ if($response->getMessage()=="success") {
67
+ Mage::getSingleton('core/session')->addSuccess("Status of indexing queue sent.");
68
+ }else {
69
+ Mage::getSingleton('core/session')->addSuccess($response->getMessage());
70
+ }
71
+ //send index status data
72
+ $content ="";
73
+ $allIndex= Mage::getSingleton('index/indexer')->getProcessesCollection();
74
+ foreach ($allIndex as $index) {
75
+ $content .= $index->getIndexerCode().":".$index->getStatus().'<br>';
76
+ }
77
+ $response = Mage::getModel("klevu_search/api_action_debuginfo")->debugKlevu(array('apiKey'=>$debugapi,'klevuLog'=>$content,'type'=>'index'));
78
+ if($response->getMessage()=="success") {
79
+ Mage::getSingleton('core/session')->addSuccess("Status of magento indices sent.");
80
+ }else {
81
+ Mage::getSingleton('core/session')->addSuccess($response->getMessage());
82
+ }
83
+ Mage::helper('klevu_search')->log(Zend_Log::DEBUG, sprintf("klevu debug data was sent to klevu server successfully."));
84
+ }
85
+ $rest_api = $this->getRequest()->getParam('api');
86
+ if(!empty($rest_api)) {
87
+ Mage::getModel('klevu_search/product_sync')->sheduleCronExteranally($rest_api);
88
+ Mage::getSingleton('core/session')->addSuccess("Cron scheduled externally.");
89
+ }
90
+ $this->_redirect('klevu/search/runexternalylog');
91
+
92
+ }
93
+ catch(Exception $e) {
94
+ Mage::helper('klevu_search')->log(Zend_Log::DEBUG, sprintf("Product Synchronization was Run externally:\n%s", $e->getMessage()));
95
+ }
96
+ }
97
+
98
+ function getLastlines($filepath, $lines, $adaptive = true) {
99
+ // Open file
100
+ $f = @fopen($filepath, "rb");
101
+ if ($f === false) return false;
102
+ // Sets buffer size
103
+ if (!$adaptive) $buffer = 4096;
104
+ else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
105
+ // Jump to last character
106
+ fseek($f, -1, SEEK_END);
107
+ // Read it and adjust line number if necessary
108
+ // (Otherwise the result would be wrong if file doesn't end with a blank line)
109
+ if (fread($f, 1) != "\n") $lines -= 1;
110
+ // Start reading
111
+ $output = '';
112
+ $chunk = '';
113
+ // While we would like more
114
+ while (ftell($f) > 0 && $lines >= 0) {
115
+ // Figure out how far back we should jump
116
+ $seek = min(ftell($f), $buffer);
117
+ // Do the jump (backwards, relative to where we are)
118
+ fseek($f, -$seek, SEEK_CUR);
119
+ // Read a chunk and prepend it to our output
120
+ $output = ($chunk = fread($f, $seek)) . $output;
121
+ // Jump back to where we started reading
122
+ fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
123
+ // Decrease our line counter
124
+ $lines -= substr_count($chunk, "\n");
125
+ }
126
+ // While we have too many lines
127
+ // (Because of buffer size we might have read too many)
128
+ while ($lines++ < 0) {
129
+ // Find first newline and remove all text before that
130
+ $output = substr($output, strpos($output, "\n") + 1);
131
+ }
132
+ // Close file and return
133
+ fclose($f);
134
+ return trim($output);
135
+ }
136
+
137
+
138
+
139
+ }
app/code/community/Klevu/Search/etc/adminhtml.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <admin>
6
+ <children>
7
+ <klevu_search translate="title" module="klevu_search">
8
+ <title>Klevu Search</title>
9
+ <sort_order>100</sort_order>
10
+ </klevu_search>
11
+ <system>
12
+ <children>
13
+ <config>
14
+ <children>
15
+ <klevu_search translate="title" module="klevu_search">
16
+ <title>Klevu Search Section</title>
17
+ </klevu_search>
18
+ </children>
19
+ </config>
20
+ </children>
21
+ </system>
22
+ </children>
23
+ </admin>
24
+ </resources>
25
+ </acl>
26
+ </config>
app/code/community/Klevu/Search/etc/config.xml ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Klevu_Search>
5
+ <version>1.1.13</version>
6
+ </Klevu_Search>
7
+ </modules>
8
+ <global>
9
+ <blocks>
10
+ <klevu_search>
11
+ <class>Klevu_Search_Block</class>
12
+ </klevu_search>
13
+ </blocks>
14
+ <helpers>
15
+ <klevu_search>
16
+ <class>Klevu_Search_Helper</class>
17
+ </klevu_search>
18
+ </helpers>
19
+ <models>
20
+ <klevu_search>
21
+ <class>Klevu_Search_Model</class>
22
+ <resourceModel>klevu_search_resource</resourceModel>
23
+ </klevu_search>
24
+ <klevu_search_resource>
25
+ <class>Klevu_Search_Model_Resource</class>
26
+ <entities>
27
+ <notification>
28
+ <table>klevu_notification</table>
29
+ </notification>
30
+ <product_sync>
31
+ <table>klevu_product_sync</table>
32
+ </product_sync>
33
+ <order_sync>
34
+ <table>klevu_order_sync</table>
35
+ </order_sync>
36
+ <image_thumb>
37
+ <table>klevu_image_thumb</table>
38
+ </image_thumb>
39
+ </entities>
40
+ </klevu_search_resource>
41
+ </models>
42
+ <resources>
43
+ <klevu_search_setup>
44
+ <setup>
45
+ <module>Klevu_Search</module>
46
+ </setup>
47
+ </klevu_search_setup>
48
+ </resources>
49
+ <events>
50
+ <admin_system_config_changed_section_klevu_search>
51
+ <observers>
52
+ <klevu_search_removetest>
53
+ <type>singleton</type>
54
+ <class>klevu_search/observer</class>
55
+ <method>removeTest</method>
56
+ </klevu_search_removetest>
57
+ </observers>
58
+ </admin_system_config_changed_section_klevu_search>
59
+ <controller_action_predispatch_catalogsearch_result_index>
60
+ <observers>
61
+ <klevu_search_landing_page>
62
+ <type>singleton</type>
63
+ <class>klevu_search/observer</class>
64
+ <method>applyLandingPageModelRewrites</method>
65
+ </klevu_search_landing_page>
66
+ </observers>
67
+ </controller_action_predispatch_catalogsearch_result_index>
68
+ <after_reindex_process_catalog_category_product>
69
+ <observers>
70
+ <klevu_search_product_sync>
71
+ <type>singleton</type>
72
+ <class>klevu_search/observer</class>
73
+ <method>scheduleProductSync</method>
74
+ </klevu_search_product_sync>
75
+ </observers>
76
+ </after_reindex_process_catalog_category_product>
77
+ <after_reindex_process_catalog_product_price>
78
+ <observers>
79
+ <klevu_search_product_sync>
80
+ <type>singleton</type>
81
+ <class>klevu_search/observer</class>
82
+ <method>scheduleProductSync</method>
83
+ </klevu_search_product_sync>
84
+ </observers>
85
+ </after_reindex_process_catalog_product_price>
86
+ <after_reindex_process_catalog_url>
87
+ <observers>
88
+ <klevu_search_product_sync>
89
+ <type>singleton</type>
90
+ <class>klevu_search/observer</class>
91
+ <method>scheduleProductSync</method>
92
+ </klevu_search_product_sync>
93
+ </observers>
94
+ </after_reindex_process_catalog_url>
95
+ <catalog_inventory_stock_item_save_commit_after>
96
+ <observers>
97
+ <klevu_search_product_sync>
98
+ <type>singleton</type>
99
+ <class>klevu_search/observer</class>
100
+ <method>scheduleProductSync</method>
101
+ </klevu_search_product_sync>
102
+ </observers>
103
+ </catalog_inventory_stock_item_save_commit_after>
104
+ <catalog_product_save_commit_after>
105
+ <observers>
106
+ <klevu_search_product_sync>
107
+ <type>singleton</type>
108
+ <class>klevu_search/observer</class>
109
+ <method>scheduleProductSync</method>
110
+ </klevu_search_product_sync>
111
+ <catalog_product_media_save_before_event>
112
+ <type>singleton</type>
113
+ <class>klevu_search/observer</class>
114
+ <method>createThumb</method>
115
+ </catalog_product_media_save_before_event>
116
+ </observers>
117
+ </catalog_product_save_commit_after>
118
+ <catalog_product_delete_commit_after>
119
+ <observers>
120
+ <klevu_search_product_sync>
121
+ <type>singleton</type>
122
+ <class>klevu_search/observer</class>
123
+ <method>scheduleProductSync</method>
124
+ </klevu_search_product_sync>
125
+ </observers>
126
+ </catalog_product_delete_commit_after>
127
+ <catalog_entity_attribute_save_commit_after>
128
+ <observers>
129
+ <klevu_search_product_sync>
130
+ <type>singleton</type>
131
+ <class>klevu_search/observer</class>
132
+ <method>syncAllProducts</method>
133
+ </klevu_search_product_sync>
134
+ </observers>
135
+ </catalog_entity_attribute_save_commit_after>
136
+ <category_move>
137
+ <observers>
138
+ <klevu_search_product_sync>
139
+ <type>singleton</type>
140
+ <class>klevu_search/observer</class>
141
+ <method>scheduleProductSync</method>
142
+ </klevu_search_product_sync>
143
+ </observers>
144
+ </category_move>
145
+ <sales_order_place_after>
146
+ <observers>
147
+ <klevu_search_order_sync>
148
+ <type>singleton</type>
149
+ <class>klevu_search/observer</class>
150
+ <method>scheduleOrderSync</method>
151
+ </klevu_search_order_sync>
152
+ <klevu_search_product_sync>
153
+ <type>singleton</type>
154
+ <class>klevu_search/observer</class>
155
+ <method>scheduleProductSync</method>
156
+ </klevu_search_product_sync>
157
+ </observers>
158
+ </sales_order_place_after>
159
+ <catalog_product_attribute_update_before>
160
+ <observers>
161
+ <klevu_search_product_sync>
162
+ <type>singleton</type>
163
+ <class>klevu_search/observer</class>
164
+ <method>setProductsToSync</method>
165
+ </klevu_search_product_sync>
166
+ </observers>
167
+ </catalog_product_attribute_update_before>
168
+ </events>
169
+ </global>
170
+ <admin>
171
+ <routers>
172
+ <adminhtml>
173
+ <args>
174
+ <modules>
175
+ <klevu_search before="Mage_Adminhtml">Klevu_Search_Adminhtml</klevu_search>
176
+ </modules>
177
+ </args>
178
+ </adminhtml>
179
+ </routers>
180
+ </admin>
181
+ <adminhtml>
182
+ <layout>
183
+ <updates>
184
+ <klevu_search>
185
+ <file>klevu/search.xml</file>
186
+ </klevu_search>
187
+ </updates>
188
+ </layout>
189
+ </adminhtml>
190
+ <frontend>
191
+ <layout>
192
+ <updates>
193
+ <klevu_search>
194
+ <file>klevu/search.xml</file>
195
+ </klevu_search>
196
+ </updates>
197
+ </layout>
198
+ <routers>
199
+ <klevu_search>
200
+ <use>standard</use>
201
+ <args>
202
+ <module>Klevu_Search</module>
203
+ <frontName>klevu</frontName>
204
+ </args>
205
+ </klevu_search>
206
+ </routers>
207
+ </frontend>
208
+ <crontab>
209
+ <jobs>
210
+ <klevu_search_clear_cron_check>
211
+ <run>
212
+ <model>klevu_search/cron::clearCronCheckNotification</model>
213
+ </run>
214
+ </klevu_search_clear_cron_check>
215
+ <klevu_search_product_sync>
216
+ <schedule>
217
+ <config_path>klevu_search/product_sync/frequency</config_path>
218
+ </schedule>
219
+ <run>
220
+ <model>klevu_search/product_sync::run</model>
221
+ </run>
222
+ </klevu_search_product_sync>
223
+ <klevu_search_order_sync>
224
+ <schedule>
225
+ <config_path>klevu_search/order_sync/frequency</config_path>
226
+ </schedule>
227
+ <run>
228
+ <model>klevu_search/order_sync::run</model>
229
+ </run>
230
+ </klevu_search_order_sync>
231
+ <klevu_search_product_thumb>
232
+ <schedule>
233
+ <config_path>klevu_search/product_sync/frequency</config_path>
234
+ </schedule>
235
+ <run>
236
+ <model>klevu_search/product_sync::generateThumbParrallel</model>
237
+ </run>
238
+ </klevu_search_product_thumb>
239
+ </jobs>
240
+ </crontab>
241
+ <default>
242
+ <klevu_search>
243
+ <general>
244
+ <enabled>0</enabled>
245
+ <test_mode>0</test_mode>
246
+ <hostname>box.klevu.com</hostname>
247
+ <rest_hostname>rest.klevu.com</rest_hostname>
248
+ <test_hostname>box.klevu.com</test_hostname>
249
+ <test_cloud_search_url>eucs.klevu.com</test_cloud_search_url>
250
+ <cloud_search_url>eucs.klevu.com</cloud_search_url>
251
+ <analytics_url>stats.klevu.com</analytics_url>
252
+ <test_analytics_url>stats.klevu.com</test_analytics_url>
253
+ <js_url>js.klevu.com</js_url>
254
+ <test_js_url>js.klevu.com</test_js_url>
255
+ </general>
256
+ <product_sync>
257
+ <enabled>1</enabled>
258
+ <frequency>0 * * * *</frequency>
259
+ <last_run>Never</last_run>
260
+ </product_sync>
261
+ <searchlanding>
262
+ <landenabled>2</landenabled>
263
+ </searchlanding>
264
+ <order_sync>
265
+ <enabled>1</enabled>
266
+ <frequency>0 * * * *</frequency>
267
+ <last_run>Never</last_run>
268
+ </order_sync>
269
+ <developer>
270
+ <force_log>1</force_log>
271
+ <!-- Log level default is set in the backend model and getter as it uses Zend_Log constants -->
272
+ </developer>
273
+ <order_sync>
274
+ <enabled>1</enabled>
275
+ </order_sync>
276
+ </klevu_search>
277
+ </default>
278
+ <phpunit>
279
+ <suite>
280
+ <modules>
281
+ <Klevu_Search/>
282
+ </modules>
283
+ </suite>
284
+ </phpunit>
285
+ </config>
app/code/community/Klevu/Search/etc/system.xml ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <klevu_search translate="label" module="klevu_search">
5
+ <label>Klevu Search</label>
6
+ <tab>catalog</tab>
7
+ <sort_order>50</sort_order>
8
+ <show_in_default>1</show_in_default>
9
+ <show_in_website>1</show_in_website>
10
+ <show_in_store>1</show_in_store>
11
+ <groups>
12
+ <information translate="label">
13
+ <label>Information</label>
14
+ <sort_order>1</sort_order>
15
+ <expanded>1</expanded>
16
+ <frontend_model>klevu_search/adminhtml_form_information</frontend_model>
17
+ <show_in_default>1</show_in_default>
18
+ <show_in_website>1</show_in_website>
19
+ <show_in_store>1</show_in_store>
20
+ </information>
21
+ <general translate="label">
22
+ <label>General Settings</label>
23
+ <sort_order>100</sort_order>
24
+ <expanded>1</expanded>
25
+ <show_in_default>1</show_in_default>
26
+ <show_in_website>1</show_in_website>
27
+ <show_in_store>1</show_in_store>
28
+ <fields>
29
+ <enabled translate="label">
30
+ <label>Enable on Frontend</label>
31
+ <frontend_type>select</frontend_type>
32
+ <source_model>adminhtml/system_config_source_yesno</source_model>
33
+ <sort_order>100</sort_order>
34
+ <show_in_default>1</show_in_default>
35
+ <show_in_website>1</show_in_website>
36
+ <show_in_store>1</show_in_store>
37
+ </enabled>
38
+ <js_api_key translate="label comment">
39
+ <label>JS API Key</label>
40
+ <comment><![CDATA[This API key is used for searches in the frontend.]]></comment>
41
+ <frontend_type>text</frontend_type>
42
+ <sort_order>121</sort_order>
43
+ <show_in_default>0</show_in_default>
44
+ <show_in_website>0</show_in_website>
45
+ <show_in_store>1</show_in_store>
46
+ </js_api_key>
47
+ <rest_api_key translate="label comment">
48
+ <label>REST API Key</label>
49
+ <comment><![CDATA[This API key is used for syncing product information in the backend.]]></comment>
50
+ <sort_order>122</sort_order>
51
+ <show_in_default>0</show_in_default>
52
+ <show_in_website>0</show_in_website>
53
+ <show_in_store>1</show_in_store>
54
+ </rest_api_key>
55
+ <wizard translate="button_label">
56
+ <label></label>
57
+ <frontend_model>klevu_search/adminhtml_wizard_config_button</frontend_model>
58
+ <sort_order>130</sort_order>
59
+ <show_in_default>1</show_in_default>
60
+ <show_in_website>1</show_in_website>
61
+ <show_in_store>1</show_in_store>
62
+ </wizard>
63
+ <hostname translate="label comment">
64
+ <label>Hostname</label>
65
+ <comment><![CDATA[This Hostname is used for sending API requests to Klevu]]></comment>
66
+ <sort_order>132</sort_order>
67
+ <show_in_default>0</show_in_default>
68
+ <show_in_website>0</show_in_website>
69
+ <show_in_store>1</show_in_store>
70
+ </hostname>
71
+ <rest_hostname translate="label comment">
72
+ <label>Product Synchronization URL</label>
73
+ <comment><![CDATA[This Hostname is used for sending product synchronization API requests to Klevu]]></comment>
74
+ <sort_order>132</sort_order>
75
+ <show_in_default>0</show_in_default>
76
+ <show_in_website>0</show_in_website>
77
+ <show_in_store>1</show_in_store>
78
+ </rest_hostname>
79
+ <cloud_search_url translate="label comment">
80
+ <label>Cloud Search URL</label>
81
+ <comment><![CDATA[The Cloud Search URL is used for sending search API requests to Klevu]]></comment>
82
+ <sort_order>132</sort_order>
83
+ <show_in_default>0</show_in_default>
84
+ <show_in_website>0</show_in_website>
85
+ <show_in_store>1</show_in_store>
86
+ </cloud_search_url>
87
+ <analytics_url translate="label comment">
88
+ <label>Analytics URL</label>
89
+ <comment><![CDATA[The Analytics URL is used when reporting search terms and click to Klevu]]></comment>
90
+ <sort_order>132</sort_order>
91
+ <show_in_default>0</show_in_default>
92
+ <show_in_website>0</show_in_website>
93
+ <show_in_store>1</show_in_store>
94
+ </analytics_url>
95
+ <js_url translate="label comment">
96
+ <label>JS URL</label>
97
+ <comment><![CDATA[The JS URL is used for getting Klevu's Javascript file]]></comment>
98
+ <sort_order>132</sort_order>
99
+ <show_in_default>0</show_in_default>
100
+ <show_in_website>0</show_in_website>
101
+ <show_in_store>1</show_in_store>
102
+ </js_url>
103
+
104
+ </fields>
105
+ </general>
106
+ <searchlanding translate="label">
107
+ <label>Search Result Page Settings</label>
108
+ <sort_order>101</sort_order>
109
+ <expanded>1</expanded>
110
+ <show_in_default>1</show_in_default>
111
+ <show_in_website>1</show_in_website>
112
+ <show_in_store>1</show_in_store>
113
+ <fields>
114
+ <landenabled translate="label">
115
+ <label>Search Result Page</label>
116
+ <comment><![CDATA[** "Preserves Your Theme Layout" option preserves layout of your theme.
117
+ ]]></comment>
118
+ <frontend_type>select</frontend_type>
119
+ <source_model>klevu_search/system_config_source_landingoptions</source_model>
120
+ <sort_order>100</sort_order>
121
+ <show_in_default>1</show_in_default>
122
+ <show_in_website>1</show_in_website>
123
+ <show_in_store>1</show_in_store>
124
+ </landenabled>
125
+ </fields>
126
+ </searchlanding>
127
+ <product_sync translate="label">
128
+ <label>Product Sync Settings</label>
129
+ <sort_order>200</sort_order>
130
+ <show_in_default>1</show_in_default>
131
+ <show_in_website>1</show_in_website>
132
+ <show_in_store>1</show_in_store>
133
+ <fields>
134
+ <enabled translate="label comment">
135
+ <label>Enable Product Sync</label>
136
+ <comment><![CDATA[Enable Product Sync]]></comment>
137
+ <frontend_type>select</frontend_type>
138
+ <source_model>klevu_search/system_config_source_yesnoforced</source_model>
139
+ <sort_order>100</sort_order>
140
+ <show_in_default>1</show_in_default>
141
+ <show_in_website>1</show_in_website>
142
+ <show_in_store>1</show_in_store>
143
+ </enabled>
144
+ <frequency translate="label">
145
+ <label>Frequency</label>
146
+ <frontend_type>select</frontend_type>
147
+ <source_model>klevu_search/system_config_source_frequency</source_model>
148
+ <sort_order>200</sort_order>
149
+ <show_in_default>1</show_in_default>
150
+ <show_in_website>0</show_in_website>
151
+ <show_in_store>0</show_in_store>
152
+ </frequency>
153
+ <last_run translate="label">
154
+ <label>Last Run</label>
155
+ <frontend_model>klevu_search/adminhtml_form_field_store_level_label</frontend_model>
156
+ <sort_order>300</sort_order>
157
+ <show_in_default>1</show_in_default>
158
+ <show_in_website>1</show_in_website>
159
+ <show_in_store>1</show_in_store>
160
+ </last_run>
161
+ <sync_button>
162
+ <label></label>
163
+ <frontend_model>klevu_search/adminhtml_form_field_sync_button</frontend_model>
164
+ <sort_order>400</sort_order>
165
+ <show_in_default>1</show_in_default>
166
+ <show_in_website>1</show_in_website>
167
+ <show_in_store>1</show_in_store>
168
+ </sync_button>
169
+ </fields>
170
+ </product_sync>
171
+ <attributes translate="label">
172
+ <label>Product Attribute Mappings</label>
173
+ <sort_order>300</sort_order>
174
+ <show_in_default>1</show_in_default>
175
+ <show_in_website>1</show_in_website>
176
+ <show_in_store>1</show_in_store>
177
+ <fields>
178
+ <additional translate="label comment">
179
+ <label>Additional Attributes</label>
180
+ <comment><![CDATA[Here you can set optional product attributes sent to Klevu by mapping them to your Magento attributes. If you specify multiple mappings for the same Klevu attribute, only the first mapping found on the product sent will be used.]]></comment>
181
+ <frontend_model>klevu_search/adminhtml_form_field_attribute_mappings</frontend_model>
182
+ <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
183
+ <sort_order>300</sort_order>
184
+ <show_in_default>0</show_in_default>
185
+ <show_in_website>0</show_in_website>
186
+ <show_in_store>0</show_in_store>
187
+ </additional>
188
+ <automatic translate="label comment">
189
+ <label>Automatic Attribute Mappings</label>
190
+ <comment><![CDATA[These product attributes are automatically mapped and synced to Klevu. The attributes configured as to be used in the search results layered navigation are mapped with the "Other" attribute above.]]></comment>
191
+ <frontend_model>klevu_search/adminhtml_form_field_automatic_attribute_mappings</frontend_model>
192
+ <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
193
+ <sort_order>400</sort_order>
194
+ <show_in_default>0</show_in_default>
195
+ <show_in_website>0</show_in_website>
196
+ <show_in_store>0</show_in_store>
197
+ </automatic>
198
+ <boosting translate="label comment">
199
+ <label>Boosting Attribute</label>
200
+ <comment><![CDATA[Select the attribute you wish to use as the Boosting attribute in Klevu. The Boosting attribute is a decimal or integer product attribute which allows you to manually boost certain products. All products have a default score of 1, to promote a product simply give the product a score higher than 1, to demote give a score lower than 1 (i.e. 0.1)]]></comment>
201
+ <frontend_type>select</frontend_type>
202
+ <source_model>klevu_search/system_config_source_boosting_attribute</source_model>
203
+ <sort_order>500</sort_order>
204
+ <show_in_default>0</show_in_default>
205
+ <show_in_website>0</show_in_website>
206
+ <show_in_store>1</show_in_store>
207
+ </boosting>
208
+ <other translate="label comment">
209
+ <label>Other Attributes To Index</label>
210
+ <comment><![CDATA[Select all other attributes you would like Klevu to index and use to improve search results.</br>Please make sure to resynchronize products by clicking on the "Sync All Products for This Store" under the "Product Sync Settings".]]></comment>
211
+ <frontend_type>multiselect</frontend_type>
212
+ <source_model>klevu_search/system_config_source_product_attributes</source_model>
213
+ <can_be_empty>1</can_be_empty>
214
+ <sort_order>600</sort_order>
215
+ <show_in_default>0</show_in_default>
216
+ <show_in_website>0</show_in_website>
217
+ <show_in_store>1</show_in_store>
218
+ </other>
219
+ </fields>
220
+ </attributes>
221
+ <order_sync translate="label">
222
+ <label>Order Sync Settings</label>
223
+ <sort_order>400</sort_order>
224
+ <show_in_default>1</show_in_default>
225
+ <show_in_default>1</show_in_default>
226
+ <show_in_store>1</show_in_store>
227
+ <fields>
228
+ <enabled translate="label comment">
229
+ <label>Enable Order Sync</label>
230
+ <comment><![CDATA[Enable Order Sync]]></comment>
231
+ <frontend_type>select</frontend_type>
232
+ <source_model>klevu_search/system_config_source_yesnoforced</source_model>
233
+ <sort_order>100</sort_order>
234
+ <show_in_default>1</show_in_default>
235
+ <show_in_website>1</show_in_website>
236
+ <show_in_store>1</show_in_store>
237
+ </enabled>
238
+ <frequency translate="label">
239
+ <label>Frequency</label>
240
+ <frontend_type>select</frontend_type>
241
+ <source_model>klevu_search/system_config_source_frequency</source_model>
242
+ <sort_order>200</sort_order>
243
+ <show_in_default>1</show_in_default>
244
+ <show_in_website>0</show_in_website>
245
+ <show_in_store>0</show_in_store>
246
+ </frequency>
247
+ <last_run translate="label">
248
+ <label>Last Run</label>
249
+ <frontend_type>label</frontend_type>
250
+ <sort_order>300</sort_order>
251
+ <show_in_default>1</show_in_default>
252
+ <show_in_website>0</show_in_website>
253
+ <show_in_store>0</show_in_store>
254
+ </last_run>
255
+ </fields>
256
+ </order_sync>
257
+ <tax_setting translate="label">
258
+ <label>Tax Settings</label>
259
+ <sort_order>400</sort_order>
260
+ <show_in_default>0</show_in_default>
261
+ <show_in_website>0</show_in_website>
262
+ <show_in_store>1</show_in_store>
263
+ <fields>
264
+ <enabled translate="label comment">
265
+ <label>Include Tax in Price</label>
266
+ <comment><![CDATA[Please make sure to resynchronize products by clicking on the "Sync All Products for This Store" under the "Product Sync Settings".]]></comment>
267
+ <frontend_type>select</frontend_type>
268
+ <source_model>adminhtml/system_config_source_yesno</source_model>
269
+ <sort_order>100</sort_order>
270
+ <show_in_default>0</show_in_default>
271
+ <show_in_website>0</show_in_website>
272
+ <show_in_store>1</show_in_store>
273
+ </enabled>
274
+
275
+ </fields>
276
+ </tax_setting>
277
+ <developer translate="label">
278
+ <label>Developer Settings</label>
279
+ <sort_order>900</sort_order>
280
+ <show_in_default>1</show_in_default>
281
+ <show_in_website>0</show_in_website>
282
+ <show_in_store>0</show_in_store>
283
+ <fields>
284
+ <force_log translate="label comment">
285
+ <label>Force Logging</label>
286
+ <comment><![CDATA[Use this setting to override default Magento log settings and enable logging. Log messages are saved in Klevu_Search.log]]></comment>
287
+ <frontend_type>select</frontend_type>
288
+ <source_model>adminhtml/system_config_source_yesno</source_model>
289
+ <sort_order>300</sort_order>
290
+ <show_in_default>1</show_in_default>
291
+ <show_in_website>0</show_in_website>
292
+ <show_in_store>0</show_in_store>
293
+ </force_log>
294
+ <log_level translate="label">
295
+ <label>Log level</label>
296
+ <frontend_type>select</frontend_type>
297
+ <source_model>klevu_search/system_config_source_log_level</source_model>
298
+ <backend_model>klevu_search/config_log_level</backend_model>
299
+ <sort_order>310</sort_order>
300
+ <show_in_default>1</show_in_default>
301
+ <show_in_website>0</show_in_website>
302
+ <show_in_store>0</show_in_store>
303
+ </log_level>
304
+ <sendlog translate="label">
305
+ <label>Send Log</label>
306
+ <comment><![CDATA[When you click on the "Send Log" Button above, the following information is submitted to the Klevu team: your magento and php version numbers; status of your product indices and the last few lines of Klevu_Search.log file.]]></comment>
307
+ <frontend_type>button</frontend_type>
308
+ <frontend_model>klevu_search/adminhtml_form_field_image_log</frontend_model>
309
+ <sort_order>311</sort_order>
310
+ <show_in_default>1</show_in_default>
311
+ <show_in_website>0</show_in_website>
312
+ <show_in_store>0</show_in_store>
313
+ </sendlog>
314
+ </fields>
315
+ </developer>
316
+ <image translate="label">
317
+ <label>Generate Thumbnail Image</label>
318
+ <sort_order>901</sort_order>
319
+ <expanded>1</expanded>
320
+ <show_in_default>1</show_in_default>
321
+ <show_in_website>0</show_in_website>
322
+ <show_in_store>0</show_in_store>
323
+ <fields>
324
+ <image_thumb translate="label">
325
+ <label>Product Thumbnails</label>
326
+ <comment><![CDATA[Click here to create thumbnails for all your product images. Once the process
327
+ has finnished, please synchronize all your products again (see Product
328
+ Synchronization Settings). This is to make sure Klevu Search uses the newly
329
+ created thumbnails instead of the original product images.]]></comment>
330
+ <frontend_type>button</frontend_type>
331
+ <frontend_model>klevu_search/adminhtml_form_field_image_button</frontend_model>
332
+ <sort_order>310</sort_order>
333
+ <show_in_default>1</show_in_default>
334
+ <show_in_website>0</show_in_website>
335
+ <show_in_store>0</show_in_store>
336
+ </image_thumb>
337
+ </fields>
338
+ </image>
339
+ </groups>
340
+ </klevu_search>
341
+ </sections>
342
+ </config>
app/code/community/Klevu/Search/sql/klevu_search_setup/mysql4-data-upgrade-1.1.1-1.1.2.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Add all of the attributes mapped in "Additional Attributes" section to the
4
+ * "Other Attributes to Index" section
5
+ */
6
+
7
+ /** @var Mage_Core_Model_Resource_Setup $installer */
8
+ $installer = $this;
9
+
10
+ $installer->startSetup();
11
+
12
+ $mage_config = Mage::getConfig();
13
+ $klevu_config = Mage::helper("klevu_search/config");
14
+
15
+ foreach (Mage::app()->getStores() as $store) {
16
+ /** @var Mage_Core_Model_Store $store */
17
+ $additional_attributes = $klevu_config->getAdditionalAttributesMap($store);
18
+
19
+ if (count($additional_attributes) == 0) {
20
+ continue;
21
+ }
22
+
23
+ $other_attributes = $klevu_config->getOtherAttributesToIndex($store);
24
+
25
+ foreach ($additional_attributes as $mapping) {
26
+ $other_attributes[] = $mapping["magento_attribute"];
27
+ }
28
+
29
+ $other_attributes = array_unique($other_attributes);
30
+
31
+ if (count($other_attributes) == 0) {
32
+ continue;
33
+ }
34
+
35
+ $other_attributes = implode(",", $other_attributes);
36
+
37
+ $scope_id = $mage_config->getNode(sprintf(Klevu_Search_Helper_Config::XML_PATH_STORE_ID, $store->getCode()));
38
+ if ($scope_id !== null) {
39
+ $scope_id = (int) $scope_id;
40
+
41
+ $mage_config->saveConfig(Klevu_Search_Helper_Config::XML_PATH_ATTRIBUTES_OTHER, $other_attributes, "stores", $scope_id);
42
+ }
43
+ }
44
+
45
+ $mage_config->reinit();
46
+
47
+ $image_thumb_table = $installer->getTable('klevu_search/image_thumb');
48
+ $installer->run("DROP TABLE IF EXISTS `{$image_thumb_table}`");
49
+ $installer->run("
50
+ CREATE TABLE `{$image_thumb_table}` (
51
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
52
+ `batchdata` text NOT NULL,
53
+ `date` timestamp NOT NULL default CURRENT_TIMESTAMP,
54
+ `is_processed` int(10) NOT NULL,
55
+ PRIMARY KEY (`id`)
56
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
57
+ ");
58
+
59
+
60
+ $installer->endSetup();
app/code/community/Klevu/Search/sql/klevu_search_setup/mysql4-data-upgrade-1.1.7-1.1.8.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Add all of the attributes mapped in "Additional Attributes" section to the
5
+ * "Other Attributes to Index" section
6
+ */
7
+
8
+ /** @var Mage_Core_Model_Resource_Setup $installer */
9
+ $installer = $this;
10
+ $installer->startSetup();
11
+ $content = '<div class="kuContainer">
12
+ <div id="loader" style="text-align: center;"><img src="{{skin url=images/klevu/ku-loader.gif}}" alt="search" /></div>
13
+ <div class="kuProListing" id="kuProListing" style="display:none;">
14
+ <div class="kuNoRecordFound" id="kuNoRecordFound" style="display:none;">
15
+ <p>No matching products found for "red awef"</p>
16
+ </div>
17
+ <div class="kuFilters" id="kuFilters">
18
+ </div><!-- End of kuFilters -->
19
+
20
+ <div class="kuResultList" id="kuResultListBlock">
21
+ <div class="kuListHeader">
22
+ <div class="kuTotResults" id="kuTotResults"></div>
23
+ <div class="kuSortingOpt">
24
+ <div class="kuSortby">
25
+ <label id="klevuSortLbl">Sort by:</label>
26
+ <select name="kuSortby" id="kuSortby" onchange="klevu_changeSortingOptionsForLandigPage(this.value);">
27
+ <option value="rel" id="klevuRelSort" >Relevance</option>
28
+ <option value="lth" id="klevuLthSort">Price: Low to high</option>
29
+ <option value="htl" id="klevuHtlSort">Price: High to low</option>
30
+ </select>
31
+ </div>
32
+
33
+ <div class="kuView">
34
+ <a class="kuGridviewBtn" id="gridViewBtn" onclick="setKuView('."'grid'".');">
35
+ <span class="icon-gridview">&nbsp;</span>
36
+ </a>
37
+ <a class="kuListviewBtn kuCurrent" id="listViewBtn" onclick="setKuView('."'list'".');">
38
+ <span class="icon-listview">&nbsp;</span>
39
+ </a>
40
+ </div>
41
+
42
+ <div class="kuPerPage">
43
+ <label id="klevuItemsPerPage">Items per page:</label>
44
+ <select onchange="klevu_changeItemsPerPage(this.value);" id="noOfRecords1">
45
+ <option>12</option>
46
+ <option>24</option>
47
+ <option>36</option>
48
+ </select>
49
+ </div>
50
+
51
+ <div class="kuPagination" id="kuPagination1">
52
+ </div>
53
+
54
+ <div class="kuClearLeft"></div>
55
+ </div>
56
+
57
+ </div>
58
+ <div class="kuListView" id="kuResultsView">
59
+
60
+ </div>
61
+ <div class="kuBottomPagi">
62
+ <div class="kuPerPage">
63
+ <label id="klevuItemsPerPageFooter">Items per page:</label>
64
+ <select onchange="klevu_changeItemsPerPage(this.value);" id="noOfRecords2">
65
+ <option>12</option>
66
+ <option>24</option>
67
+ <option>36</option>
68
+ </select>
69
+ </div>
70
+ <div class="kuPagination" id="kuPagination2">
71
+
72
+ </div>
73
+ <div class="kuClearLeft"></div>
74
+ </div>
75
+
76
+
77
+ </div>
78
+ <div class="klevu-clear-both"></div>
79
+ </div><!-- End of kuProListing -->
80
+ </div><!-- End of klevu-container -->
81
+ <input type="hidden" name="noOfRecords" id="noOfRecords" value="12"/>
82
+ <input type="hidden" name="startPos" id="startPos" value="0"/>
83
+ <input type="hidden" name="totalResultsFound" id="totalResultsFound" value="0"/>
84
+ <input type="hidden" name="searchedKeyword" id="searchedKeyword" value=""/>
85
+ <input type="hidden" name="totalPages" id="totalPages" value="0"/>
86
+ <script type="text/javascript" src="https://box.klevu.com/klevu-js-v1/js-1-1/klevu-landing.js">
87
+ </script>
88
+ <script type="text/javascript">// <![CDATA[
89
+ document.getElementById("searchedKeyword").value= klevu_getParamValue("q");
90
+ // ]]></script>';
91
+ //if you want one block for each store view, get the store collection
92
+ $stores = Mage::getModel('core/store')->getCollection()->addFieldToFilter('store_id', array('gt'=>0))->getAllIds();
93
+ // add static block
94
+ $block = Mage::getModel('cms/block');
95
+ $block->setTitle('Klevu Search Landing Page Block');
96
+ $block->setIdentifier('klevu-search-landing-page-block-html');
97
+ $block->setStores(array(0));
98
+ $block->setIsActive(1);
99
+ $block->setContent($content);
100
+ $block->save();
101
+
102
+ // add cms page
103
+ $cmsPage = array(
104
+ 'title' => 'Search results',
105
+ 'root_template' => 'one_column',
106
+ 'identifier' => 'search-result',
107
+ 'is_active' => 1,
108
+ 'stores' => array(0),
109
+ 'content' => '{{block type="cms/block" block_id="klevu-search-landing-page-block-html"}}',
110
+ );
111
+
112
+ Mage::getModel('cms/page')->setData($cmsPage)->save();
113
+ $installer->endSetup();
app/code/community/Klevu/Search/sql/klevu_search_setup/mysql4-install-1.0.0.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /** @var Mage_Core_Model_Resource_Setup $installer */
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $connection = $installer->getConnection();
8
+
9
+
10
+
11
+ $notifications_table = $installer->getTable('klevu_search/notification');
12
+
13
+ $installer->run("DROP TABLE IF EXISTS `{$notifications_table}`");
14
+
15
+ $installer->run("
16
+ CREATE TABLE `{$notifications_table}` (
17
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
18
+ `date` timestamp NOT NULL default CURRENT_TIMESTAMP,
19
+ `type` varchar(32) NOT NULL,
20
+ `message` text NOT NULL,
21
+ PRIMARY KEY (`id`)
22
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
23
+ ");
24
+
25
+ // Add a notification to setup cron and a cron job to clear
26
+ // the notification the next time cron runs
27
+ $installer->getConnection()->insert($notifications_table, array(
28
+ "type" => "cron_check",
29
+ "message" => Mage::helper("klevu_search")->__('Klevu Search relies on cron for normal operations. Please check that you have Magento cron set up correctly. You can find instructions on how to set up Magento Cron <a target="_blank" href="https://klevusearch.freshdesk.com/support/solutions/articles/5000518493-how-do-i-setup-a-cron-">here</a>.')
30
+ ));
31
+
32
+ $now = date_create("now")->format("Y-m-d H:i:00");
33
+
34
+ $schedule = Mage::getModel("cron/schedule");
35
+ $schedule
36
+ ->setJobCode("klevu_search_clear_cron_check")
37
+ ->setCreatedAt($now)
38
+ ->setScheduledAt($now)
39
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_PENDING)
40
+ ->save();
41
+
42
+
43
+
44
+ $product_sync_table = $installer->getTable('klevu_search/product_sync');
45
+
46
+ // Pre-existing sync data is of no use, so drop the existing
47
+ // table before recreating it
48
+ $installer->run("DROP TABLE IF EXISTS `{$product_sync_table}`");
49
+
50
+ $installer->run("
51
+ CREATE TABLE `{$product_sync_table}` (
52
+ `product_id` int(10) unsigned NOT NULL,
53
+ `parent_id` int(10) unsigned NOT NULL DEFAULT 0,
54
+ `store_id` smallint(5) unsigned NOT NULL,
55
+ `test_mode` int(1) NOT NULL DEFAULT 0,
56
+ `last_synced_at` timestamp NOT NULL default CURRENT_TIMESTAMP,
57
+ PRIMARY KEY (`product_id`, `parent_id`, `store_id`, `test_mode`)
58
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
59
+ ");
60
+
61
+
62
+
63
+ $order_sync_table = $installer->getTable('klevu_search/order_sync');
64
+
65
+ $installer->run("DROP TABLE IF EXISTS `{$order_sync_table}`");
66
+
67
+ $installer->run("
68
+ CREATE TABLE `{$order_sync_table}` (
69
+ `order_item_id` int(10) unsigned NOT NULL,
70
+ PRIMARY KEY (`order_item_id`)
71
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
72
+ ");
73
+
74
+
75
+
76
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/klevu/search.xml ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+
4
+ <default>
5
+ <reference name="head">
6
+ <action method="addItem"><type>skin_css</type><name>klevu/search/notifications.css</name></action>
7
+ </reference>
8
+ <reference name="notifications">
9
+ <block type="klevu_search/adminhtml_notifications" name="klevu_notifications"
10
+ template="klevu/search/notifications.phtml"/>
11
+ </reference>
12
+ </default>
13
+
14
+ <adminhtml_catalog_product_index>
15
+ <reference name="products_list">
16
+ <action method="addButton" ifconfig="klevu_search/product_sync/enabled">
17
+ <name>klevu_search_sync_all</name>
18
+ <data helper="klevu_search/getSyncAllButtonData"/>
19
+ </action>
20
+ </reference>
21
+ </adminhtml_catalog_product_index>
22
+
23
+ <adminhtml_system_config_edit>
24
+ <update handle="editor" />
25
+ <reference name="head">
26
+ <action method="addItem"><type>skin_css</type><name>klevu/search/wizard.css</name></action>
27
+ <action method="addItem"><type>js</type><name>klevu/search/lib/Wizard.js</name></action>
28
+ </reference>
29
+ </adminhtml_system_config_edit>
30
+
31
+ <adminhtml_klevu_search_wizard_configure_user>
32
+ <remove name="root"/>
33
+ <block type="klevu_search/adminhtml_wizard_configure_user" name="configure_user"
34
+ template="klevu/search/wizard/configure/user.phtml" output="toHtml">
35
+ <block type="core/messages" name="messages" as="messages" />
36
+ </block>
37
+ </adminhtml_klevu_search_wizard_configure_user>
38
+
39
+ <adminhtml_klevu_search_wizard_configure_store>
40
+ <remove name="root" />
41
+ <block type="klevu_search/adminhtml_wizard_configure_store" name="configure_store"
42
+ template="klevu/search/wizard/configure/store.phtml" output="toHtml">
43
+ <block type="core/messages" name="messages" as="messages" />
44
+ </block>
45
+ </adminhtml_klevu_search_wizard_configure_store>
46
+
47
+ <adminhtml_klevu_search_wizard_configure_attributes>
48
+ <remove name="root" />
49
+ <block type="klevu_search/adminhtml_wizard_configure_attributes" name="configure_attributes"
50
+ template="klevu/search/wizard/configure/attributes.phtml" output="toHtml">
51
+ <block type="core/messages" name="messages" as="messages"/>
52
+ </block>
53
+ </adminhtml_klevu_search_wizard_configure_attributes>
54
+
55
+ <adminhtml_klevu_search_wizard_configure_store_post>
56
+ <remove name="root"/>
57
+ <block type="adminhtml/template" name="configure_attributes_post" template="klevu/search/wizard/complete.phtml" output="toHtml">
58
+ <block type="core/messages" name="messages" as="messages" />
59
+ </block>
60
+ </adminhtml_klevu_search_wizard_configure_store_post>
61
+ </layout>
app/design/adminhtml/default/default/template/klevu/search/form/field/array_readonly.phtml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** Template copied from: app/design/adminhtml/default/default/template/system/config/form/field/array.phtml */ ?>
2
+
3
+ <?php
4
+ $_htmlId = $this->getHtmlId() ? $this->getHtmlId() : '_' . uniqid();
5
+ ?>
6
+
7
+ <div class="grid" id="grid<?php echo $_htmlId ?>">
8
+ <table cellpadding="0" cellspacing="0" class="border">
9
+ <tbody>
10
+
11
+ <tr class="headings" id="headings<?php echo $_htmlId ?>">
12
+ <?php foreach ($this->_columns as $columnName => $column):?>
13
+ <th><?php echo $column['label'] ?></th>
14
+ <?php endforeach;?>
15
+ </tr>
16
+ <?php foreach ($this->getArrayRows() as $_row): ?>
17
+ <tr>
18
+ <?php foreach ($this->_columns as $columnName => $column): ?>
19
+ <td><?php echo $_row->getData($columnName) ?></td>
20
+ <?php endforeach ?>
21
+ </tr>
22
+ <?php endforeach ?>
23
+ </tbody>
24
+ </table>
25
+ </div>
app/design/adminhtml/default/default/template/klevu/search/form/field/sync/button.phtml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Form_Field_Sync_Button $this */ ?>
2
+ <button id="<?php echo $this->getHtmlId() ?>" class="scalable" type="button">
3
+ <span><?php echo $this->__($this->getButtonLabel()) ?></span>
4
+ </button>
5
+ <script type="text/javascript">
6
+ $("<?php echo $this->getHtmlId() ?>").observe("click", function () {
7
+ setLocation("<?php echo $this->getDestinationUrl() ?>");
8
+ });
9
+ </script>
app/design/adminhtml/default/default/template/klevu/search/form/field/sync/logbutton.phtml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Form_Field_Sync_Button $this */ ?>
2
+ <button id="<?php echo $this->getHtmlId() ?>" class="scalable" type="button">
3
+ <span><?php echo $this->__($this->getButtonLabel()) ?></span>
4
+ </button>
5
+ <script type="text/javascript">
6
+ $("<?php echo $this->getHtmlId() ?>").observe("click", function () {
7
+ window.open('<?php echo $this->getDestinationUrl() ?>','_blank');
8
+ });
9
+ </script>
app/design/adminhtml/default/default/template/klevu/search/form/information.phtml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Form_Information $this */ ?>
2
+ <p><?php echo $this->__("Installed Version: {$this->getVersion()}") ?></br></p>
3
+ <p><b><?php echo $this->__("Prerequisites:") ?></b></br>
4
+ <?php echo $this->__("1. Ensure cron is running > (<a href='http://www.klevu.com/support/setup-cron.html' target='_blank'>click here</a> for more information on setting up a cron)") ?></br>
5
+ <?php echo $this->__("2. Indices are uptodate (System > Index Management)") ?></br>
6
+ <?php echo $this->__("3. Products should be enabled and have the visibility set to catalog and search.") ?></p>
7
+ <p><b><?php echo $this->__(" Klevu Merchant Centre: <a href='https://box.klevu.com' target='_blank'>https://box.klevu.com</a>");?></b></p>
8
+ <p><?php echo $this->__("Login into Klevu Merchant Centre account to customize Klevu Search. You can
9
+ change look and feel of your search results, setup product promotions, monitor
10
+ search activity, upgrade your account and many more.");?></p>
11
+ <p><b><?php echo $this->__("FAQs and Troubleshooting: <a href='http://www.klevu.com/support/support-home.html' target='_blank'>click here</a>");?></b></p>
app/design/adminhtml/default/default/template/klevu/search/notifications.phtml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Notifications $this */ ?>
2
+ <?php $notifications = $this->getNotifications(); ?>
3
+ <?php foreach ($notifications as $notification): ?>
4
+ <div class="klevu-notification notification-global">
5
+ <strong><?php echo $this->__("Klevu Search") ?>:</strong> [<?php echo $notification->getDate() ?>] <?php echo $notification->getMessage() ?>
6
+ <a class="dismiss" href="<?php echo $this->getDismissUrl($notification) ?>"><?php echo $this->__("Dismiss") ?></a>
7
+ </div>
8
+ <?php endforeach; ?>
app/design/adminhtml/default/default/template/klevu/search/wizard/complete.phtml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Mage_Core_Block_Template $this */ ?>
2
+ <div class="content-header">
3
+ <h3>Complete</h3>
4
+ </div>
5
+ <?php echo $this->getChildHtml('messages') ?>
6
+ <p><?php echo $this->__("You have completed Klevu Search Configuration Wizard.") ?></p>
7
+ <p><?php echo $this->__("Please click the \"Finish and Sync\" button below to synchronise your products to Klevu. Afterwards, the products will be automatically resynchronised periodically as well as when any information changes.") ?></p>
8
+ <p><?php echo $this->__("Klevu Search will be automatically enabled on frontend after the first synchronisation completes. Afterwards, you can manually control that using the \"Enable on Frontend\" flag on the System Configuration page.") ?></p>
9
+ <p class="warning"><b><?php echo $this->__("Warning") ?>:</b> <?php echo $this->__("Product synchronisation takes time. Please do not close or refresh the page after clicking the button until the page finishes loading.") ?></p>
10
+ <div class="button-set">
11
+ <button id="sync-button"><span><?php echo $this->__("Finish and Sync") ?></span></button>
12
+ </div>
13
+ <script type="text/javascript">
14
+ $("sync-button").observe("click", function (e) {
15
+ window.location = "<?php echo $this->getUrl("adminhtml/klevu_search/manual_sync") ?>";
16
+ });
17
+ </script>
app/design/adminhtml/default/default/template/klevu/search/wizard/config/button.phtml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Wizard_Config_Button $this */ ?>
2
+ <button id="<?php echo $this->getHtmlId() ?>" class="scalable" type="button">
3
+ <span><?php echo $this->__("Start Configuration Wizard") ?></span>
4
+ </button>
5
+ <script type="text/javascript">
6
+ $("<?php echo $this->getHtmlId() ?>").observe("click", function () {
7
+ new Klevu.Search.Wizard({
8
+ url: "<?php echo $this->getWizardUrl() ?>"
9
+ });
10
+ });
11
+ </script>
app/design/adminhtml/default/default/template/klevu/search/wizard/configure/attributes.phtml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Wizard_Configure_Attributes $this */ ?>
2
+ <div class="content-header">
3
+ <h3>Configure Attributes</h3>
4
+ </div>
5
+ <?php echo $this->getChildHtml('messages') ?>
6
+ <form id="klevu_search_wizard_configure_attributes_form" action="<?php echo $this->getFormActionUrl() ?>" method="POST">
7
+ <input type="hidden" name="form_key" value="<?php echo $this->getFormKey() ?>" />
8
+ <p><?php echo $this->__("Klevu Search uses most of the standard Magento product attributes for search. However, if you have any custom attributes that you want to be used in search or want to use any additional filters, such as color or size, you can map them below by selecting the Klevu attribute you want to define and the Magento attribute that it should map to.") ?></p>
9
+ <fieldset class="fieldset">
10
+ <table cellspacing="0" class="form-list">
11
+ <tbody>
12
+ <tr>
13
+ <td class="label"></td>
14
+ <td class="value"><?php echo $this->getAttributeMappingsHtml() ?></td>
15
+ </tr>
16
+ </tbody>
17
+ </table>
18
+ </fieldset>
19
+ <div class="button-set">
20
+ <button type="submit"><span><?php echo $this->__("Continue") ?></span></button>
21
+ </div>
22
+ </form>
app/design/adminhtml/default/default/template/klevu/search/wizard/configure/store.phtml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Wizard_Configure_Store $this */ ?>
2
+ <?php $store = $this->getStore() ?>
3
+ <div class="content-header">
4
+ <h3>Configure Store</h3>
5
+ </div>
6
+ <?php echo $this->getChildHtml('messages') ?>
7
+ <form id="klevu_search_wizard_configure_store_form" action="<?php echo $this->getFormActionUrl() ?>" method="POST">
8
+ <input type="hidden" name="form_key" value="<?php echo $this->getFormKey() ?>" />
9
+ <p><?php echo $this->__("Select a Magento store you want to configure Klevu search for.") ?></p>
10
+ <fieldset class="fieldset">
11
+ <table cellspacing="0" class="form-list">
12
+ <tbody>
13
+ <tr>
14
+ <td class="label"><label for="store"><?php echo $this->__("Magento store:") ?></label></td>
15
+ <td class="value">
16
+ <select name="store">
17
+ <option value=""></option>
18
+ <?php $data = $this->getStoreSelectData() ?>
19
+ <?php foreach ($data as $website => $groups): ?>
20
+ <optgroup label="<?php echo $this->escapeHtml($website) ?>"></optgroup>
21
+ <?php foreach ($groups as $group => $stores): ?>
22
+ <optgroup label="&nbsp;&nbsp;<?php echo $this->escapeHtml($group) ?>">
23
+ <?php foreach ($stores as $store): ?>
24
+ <?php /** @var Mage_Core_Model_Store $store */ ?>
25
+ <option value="<?php echo $store->getCode() ?>">&nbsp;&nbsp;&nbsp;&nbsp;<?php echo $this->escapeHtml($store->getName()) ?></option>
26
+ <?php endforeach; ?>
27
+ </optgroup>
28
+ <?php endforeach; ?>
29
+ <?php endforeach; ?>
30
+ </select>
31
+ </td>
32
+ </tr>
33
+
34
+ <tr>
35
+ <td class="label"><label for="tax_enable"><?php echo $this->__("Want to include tax in price:") ?></label></td>
36
+ <td class="value">
37
+ <select name="tax_enable" id="tax_enable">
38
+ <option value="0" selected="selected"><?php echo $this->__("No") ?></option>
39
+ <option value="1"><?php echo $this->__("Yes") ?></option>
40
+ </select>
41
+ </td>
42
+ </tr>
43
+ </tbody>
44
+ </table>
45
+ </fieldset>
46
+ <div class="button-set">
47
+ <button type="submit"><span><?php echo $this->__("Continue") ?></span></button>
48
+ </div>
49
+ </form>
app/design/adminhtml/default/default/template/klevu/search/wizard/configure/user.phtml ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Adminhtml_Wizard_Configure_User $this */ ?>
2
+ <?php echo $this->getChildHtml('messages') ?>
3
+
4
+ <form id="klevu_search_wizard_configure_user_form" action="<?php echo $this->getFormActionUrl() ?>" method="POST">
5
+ <input type="hidden" name="form_key" value="<?php echo $this->getFormKey() ?>" />
6
+
7
+ <fieldset class="fieldset">
8
+ <table cellspacing="0" class="form-list" style="width:100%;">
9
+ <tbody>
10
+ <tr>
11
+ <td>
12
+ <table class="klevu_signup_form">
13
+ <tr>
14
+ <td colspan="2">
15
+ <div class="content-header">
16
+ <h3>Register New User</h3>
17
+ </div>
18
+ <p><?php echo $this->__("Fill out the form below to create a new Klevu user to use with this Magento installation:") ?></p>
19
+ </td>
20
+ </tr>
21
+ <tr>
22
+ <td class="label"><label for="klevu_new_email"><?php echo $this->__("Primary Email:") ?><span class="required">&nbsp;<em>*</em></span></label>
23
+ </td>
24
+ <td class="value"><input type="text" name="klevu_new_email" id="klevu_new_email" class="input-text " />
25
+ <p class="note">For login and technical support</p></td>
26
+ </tr>
27
+ <tr>
28
+ <td class="label"><label for="merchantEmail"><?php echo $this->__("Retailer Email:") ?><span class="required">&nbsp;<em>*</em></span></label></td>
29
+ <td class="value"><input type="text" name="merchantEmail" id="merchantEmail" class="input-text" /><p class="note">
30
+ For search insights and billing</p></td>
31
+ </tr>
32
+ <tr>
33
+ <td class="label"><label for="klevu_new_password"><?php echo $this->__("Password:") ?><span class="required">&nbsp;<em>*</em></span></label></td>
34
+ <td class="value"><input type="password" name="klevu_new_password" id="klevu_new_password" class="input-text" />
35
+ </td>
36
+ </tr>
37
+ <tr>
38
+ <td class="label"><label for="userPlan"><?php echo $this->__("Choose Your Plan:") ?><span class="required">&nbsp;<em>*</em></span></label></td>
39
+ <td class="value">
40
+ <input type="radio" name="userPlan" value="free">&nbsp;Free&nbsp;&nbsp;
41
+ <input type="radio" name="userPlan" checked="checked" value="trial">&nbsp;PRO trial
42
+ <a href="#" onClick="checkplan();">what's included?</a>
43
+ <p class="note">If your store exceeds the guidelines of FREE edition, then PRO trial will be enabled.</p>
44
+ </td>
45
+ </tr>
46
+
47
+ <tr>
48
+ <td class="label"><label for="contactNo"> <?php echo $this->__("Phone Number:") ?><span class="required">&nbsp;</span></label></td>
49
+ <td class="value"><input type="text" placeholder="Country code" name="countyCode" id="countyCode" class="input-text" value="<?php echo $this->getCountyCode() ?>" /> <input type="text" name="contactNo" id="contactNo" class="input-text" value="<?php echo $this->getContactNo() ?>" /></td>
50
+ </tr>
51
+
52
+ <tr>
53
+ <td class="label"><label for="klevu_new_url"> <?php echo $this->__("Store URL:") ?><span class="required">&nbsp;<em>*</em></span></label></td>
54
+ <td class="value"><input type="text" name="klevu_new_url" id="klevu_new_url" class="input-text" value="<?php echo $this->getStoreUrl() ?>" /></td>
55
+ </tr>
56
+
57
+ <tr>
58
+ <td class="label"></td>
59
+ <td class="value" style="padding-right: 26px;">
60
+ <div class="button-set">
61
+ <button type="submit"><span><?php echo $this->__("Register") ?></span></button>
62
+ </div>
63
+ </td>
64
+ </tr>
65
+ </table>
66
+ </td>
67
+ <td>
68
+ <table class="klevu_signup_form">
69
+ <tr>
70
+ <td colspan="2">
71
+ <div class="content-header">
72
+ <h3>Already Registered?</h3>
73
+ </div>
74
+ <p><?php echo $this->__("If you have an account with Klevu, please log in:") ?></p>
75
+ </td>
76
+ </tr>
77
+ <tr>
78
+ <td class="label"><label for="klevu_existing_email"><?php echo $this->__("Primary Email:") ?><span class="required">&nbsp;<em>*</em></span></label></td>
79
+ <td class="value"><input type="text" name="klevu_existing_email" id="klevu_existing_email" class="input-text" /></td>
80
+ </tr>
81
+ <tr>
82
+ <td class="label"><label for="klevu_existing_password"><?php echo $this->__("Password:") ?><span class="required">&nbsp;<em>*</em></span></label></td>
83
+ <td class="value"><input type="password" name="klevu_existing_password" id="klevu_existing_password" class="input-text" /></td>
84
+ </tr>
85
+ <tr>
86
+ <td class="label"></td>
87
+ <td class="value">
88
+ <div class="button-set">
89
+ <button type="submit"><span><?php echo $this->__("Login") ?></span></button>
90
+ </div>
91
+ </td>
92
+ </tr>
93
+
94
+ </table>
95
+
96
+ </td>
97
+ </tr>
98
+
99
+
100
+
101
+
102
+ </tbody>
103
+ </table>
104
+ </fieldset>
105
+ </form>
106
+
app/design/adminhtml/default/default/template/klevu/search/wizard/form/field/array.phtml ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** Template copied from: app/design/adminhtml/default/default/template/system/config/form/field/array.phtml */ ?>
2
+ <?php
3
+ $_htmlId = $this->getHtmlId() ? $this->getHtmlId() : '_' . uniqid();
4
+
5
+ $_colspan = 2;
6
+ if (!$this->_addAfter) {
7
+ $_colspan -= 1;
8
+ }
9
+ $_colspan = $_colspan > 1 ? 'colspan="' . $_colspan . '"' : '';
10
+ ?>
11
+
12
+ <div class="grid" id="grid<?php echo $_htmlId ?>">
13
+ <table cellpadding="0" cellspacing="0" class="border">
14
+ <tbody>
15
+
16
+ <tr class="headings" id="headings<?php echo $_htmlId ?>">
17
+ <?php foreach ($this->_columns as $columnName => $column):?>
18
+ <th><?php echo $column['label'] ?></th>
19
+ <?php endforeach;?>
20
+ <th <?php echo $_colspan?>></th>
21
+ </tr>
22
+
23
+ <tr id="addRow<?php echo $_htmlId ?>">
24
+ <td colspan="<?php echo count($this->_columns) ?>"></td>
25
+ <td <?php echo $_colspan?>>
26
+ <button style="" onclick="" class="scalable add" type="button" id="addToEndBtn<?php echo $_htmlId ?>">
27
+ <span><span><span><?php echo $this->_addButtonLabel ?></span></span></span>
28
+ </button>
29
+ </td>
30
+ </tr>
31
+
32
+ </tbody>
33
+ </table>
34
+ <input type="hidden" name="<?php echo $this->getElement()->getName() ?>[__empty]" value="" />
35
+ </div>
36
+ <div id="empty<?php echo $_htmlId ?>">
37
+ <button style="" onclick="" class="scalable add" type="button" id="emptyAddBtn<?php echo $_htmlId ?>">
38
+ <span><span><span><?php echo $this->_addButtonLabel ?></span></span></span>
39
+ </button>
40
+ </div>
41
+
42
+ <script type="text/javascript">
43
+ //<![CDATA[
44
+ // create row creator
45
+ // Make sure it's in the global scope
46
+ window.arrayRow<?php echo $_htmlId ?> = {
47
+ // define row prototypeJS template
48
+ template : new Template(
49
+ '<tr id="#{_id}">'
50
+ <?php foreach ($this->_columns as $columnName => $column):?>
51
+ +'<td>'
52
+ +'<?php echo $this->_renderCellTemplate($columnName)?>'
53
+ +'<\/td>'
54
+ <?php endforeach;?>
55
+ <?php if ($this->_addAfter):?>
56
+ +'<td><button onclick="" class="scalable add" type="button" id="addAfterBtn#{_id}"><span><span><span><?php echo Mage::helper('adminhtml')->__('Add after') ?><\/span><\/span><\/span><\/button><\/td>'
57
+ <?php endif;?>
58
+ +'<td><button onclick="arrayRow<?php echo $_htmlId ?>.del(\'#{_id}\')" class="scalable delete" type="button"><span><span><span><?php echo Mage::helper('adminhtml')->__('Delete') ?><\/span><\/span><\/span><\/button><\/td>'
59
+ +'<\/tr>'
60
+ ),
61
+
62
+ rowsCount : 0,
63
+
64
+ add : function(templateData, insertAfterId)
65
+ {
66
+ // generate default template data
67
+ if ('' == templateData) {
68
+ var d = new Date();
69
+ var templateData = {
70
+ <?php foreach ($this->_columns as $columnName => $column):?>
71
+ <?php echo $columnName ?> : '',
72
+ <?php endforeach;?>
73
+ _id : '_' + d.getTime() + '_' + d.getMilliseconds()
74
+ };
75
+ }
76
+
77
+ // insert before last row
78
+ if ('' == insertAfterId) {
79
+ Element.insert($('addRow<?php echo $_htmlId ?>'), {before: this.template.evaluate(templateData)});
80
+ }
81
+ // insert after specified row
82
+ else {
83
+ Element.insert($(insertAfterId), {after: this.template.evaluate(templateData)});
84
+ }
85
+
86
+ <?php if ($this->_addAfter):?>
87
+ Event.observe('addAfterBtn' + templateData._id, 'click', this.add.bind(this, '', templateData._id));
88
+ <?php endif;?>
89
+
90
+ this.rowsCount += 1;
91
+ },
92
+
93
+ del : function(rowId)
94
+ {
95
+ $(rowId).remove();
96
+ this.rowsCount -= 1;
97
+ if (0 == this.rowsCount) {
98
+ this.showButtonOnly();
99
+ }
100
+ },
101
+
102
+ showButtonOnly : function()
103
+ {
104
+ $('grid<?php echo $_htmlId ?>').hide();
105
+ $('empty<?php echo $_htmlId ?>').show();
106
+ }
107
+ }
108
+
109
+ // bind add action to "Add" button in last row
110
+ Event.observe('addToEndBtn<?php echo $_htmlId ?>', 'click', arrayRow<?php echo $_htmlId ?>.add.bind(arrayRow<?php echo $_htmlId ?>, '', ''));
111
+
112
+ // add existing rows
113
+ <?php
114
+ $_addAfterId = "headings{$_htmlId}";
115
+ foreach ($this->getArrayRows() as $_rowId => $_row) {
116
+ echo "arrayRow{$_htmlId}.add(" . $_row->toJson() . ", '{$_addAfterId}');\n";
117
+ $_addAfterId = $_rowId;
118
+ }
119
+ ?>
120
+
121
+ // initialize standalone button
122
+ $('empty<?php echo $_htmlId ?>').hide();
123
+ Event.observe('emptyAddBtn<?php echo $_htmlId ?>', 'click', function () {
124
+ $('grid<?php echo $_htmlId ?>').show();
125
+ $('empty<?php echo $_htmlId ?>').hide();
126
+ arrayRow<?php echo $_htmlId ?>.add('', '');
127
+ });
128
+
129
+ // if no rows, hide grid and show button only
130
+ <?php if (!$this->getArrayRows()):?>
131
+ arrayRow<?php echo $_htmlId ?>.showButtonOnly();
132
+ <?php endif;?>
133
+
134
+ // toggle the grid, if element is disabled (depending on scope)
135
+ <?php if ($this->getElement()->getDisabled()):?>
136
+ toggleValueElements({checked:true}, $('grid<?php echo $_htmlId ?>').parentNode);
137
+ <?php endif;?>
138
+ //]]>
139
+ </script>
app/design/frontend/base/default/layout/klevu/search.xml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <default>
4
+ <reference name="head">
5
+ <action method="addCss"><stylesheet>css/klevu/klevu-landing-page-style.css</stylesheet></action>
6
+ <action method="addCss"><stylesheet>css/klevu/klevu-landing-responsive.css</stylesheet></action>
7
+ </reference>
8
+ <reference name="before_body_end">
9
+ <block type="core/template" name="klevu.search.form_js" template="klevu/search/form_js.phtml"/>
10
+ </reference>
11
+ </default>
12
+ <catalog_product_view>
13
+ <reference name="before_body_end">
14
+ <block type="klevu_search/catalog_product_tracking" name="klevu_search.catalog.product.tracking" template="klevu/search/product_tracking.phtml" />
15
+ </reference>
16
+ </catalog_product_view>
17
+ <klevu_search_runexternalylog>
18
+ <label>Klevu Log</label>
19
+ <remove name="right"/>
20
+ <remove name="left"/>
21
+ <reference name="root">
22
+ <action method="setTemplate"><template>page/1column.phtml</template></action>
23
+ </reference>
24
+ <reference name="content">
25
+ <block type="core/template" name="klevulog" template="klevu/klevulog.phtml"/>
26
+ </reference>
27
+ </klevu_search_runexternalylog>
28
+ </layout>
app/design/frontend/base/default/template/klevu/search/form_js.phtml ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Mage_Core_Block_Template $this */ ?>
2
+ <?php $config = Mage::helper('klevu_search/config'); ?>
3
+ <?php $helper = Mage::helper('klevu_search'); ?>
4
+ <?php if ($config->isExtensionConfigured()): ?>
5
+ <script type="text/javascript">
6
+ <?php
7
+ if ($config->isLandingEnabled()==0){ ?>
8
+ (function () {
9
+ // Remove Magento event observers from the search box
10
+ // No Redirection of landing page
11
+ var search_input = $('search');
12
+ search_input.stopObserving('click');
13
+ search_input.stopObserving('keydown');
14
+ search_input.form.stopObserving('submit');
15
+ search_input.form.observe('submit', function (e) {
16
+ Event.stop(e);
17
+ klevu_autoSuggest($('search').value, e);
18
+ return false;
19
+ });
20
+ })();
21
+ <?php } else if($config->isLandingEnabled()==2) { ?>
22
+ (function () {
23
+ // Remove Magento event observers from the search box
24
+ // Redirect to klevu js page
25
+ var search_input = $('search');
26
+ search_input.stopObserving('click');
27
+ search_input.stopObserving('keydown');
28
+ search_input.form.observe('submit', function (e) {
29
+ Event.stop(e);
30
+ document.location='<?php echo Mage::getBaseUrl(); ?>' + "search-result?q="+$('search').value;
31
+ return false;
32
+ });
33
+
34
+ })();
35
+ var klevu_showQuickSearchOnEnter=false;
36
+ <?php } else if($config->isLandingEnabled()==1) { ?>
37
+ (function () {
38
+ // Remove Magento event observers from the search box
39
+ // default magetno layout landing page
40
+ var search_input = $('search');
41
+ search_input.stopObserving('click');
42
+ search_input.stopObserving('keydown');
43
+ })();
44
+ var klevu_showQuickSearchOnEnter=false;
45
+ <?php } ?>
46
+ // call store js
47
+ var klevu_apiKey = '<?php echo $config->getJsApiKey() ?>',
48
+ searchTextBoxName = 'search',
49
+ klevu_lang = '<?php echo $helper->getStoreLanguage() ?>',
50
+ klevu_result_top_margin = '',
51
+ klevu_result_left_margin = '';
52
+ (function () { var ws = document.createElement('script'),kl_protocol =("https:"===document.location.protocol?"https://":"http://"); ws.type = 'text/javascript'; ws.async = true; ws.src = kl_protocol+'<?php echo $config->getJsUrl() ?>/klevu-js-v1/js/klevu-webstore.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ws, s); })();
53
+ </script>
54
+
55
+ <?php endif; ?>
app/design/frontend/base/default/template/klevu/search/klevulog.phtml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php /** @var Mage_Core_Block_Template $this */
2
+ echo "hi";
3
+ exit;
4
+ ?>
5
+ <div id="messages_product_view">
6
+ <?php echo $this->getMessagesBlock()->getGroupedHtml(); ?>
7
+ </div>
app/design/frontend/base/default/template/klevu/search/product_tracking.phtml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /** @var Klevu_Search_Block_Catalog_Product_Tracking $this */ ?>
2
+ <?php $config = Mage::helper('klevu_search/config'); ?>
3
+ <?php if ($config->isExtensionConfigured()): ?>
4
+ <script type="text/javascript">
5
+ (function() {
6
+ var referrer, search_term, klevu_search_product_tracking;
7
+ referrer = document.referrer;
8
+ search_term = referrer.toQueryParams().q;
9
+
10
+ if (referrer.indexOf('catalogsearch/result') > -1 && search_term) {
11
+ klevu_search_product_tracking = <?php echo $this->getJsonTrackingData() ?>;
12
+ klevu_search_product_tracking.klevu_term = search_term;
13
+
14
+ // Send the ajax request
15
+ new Ajax.Request('//<?php echo $config->getAnalyticsUrl() ?>/analytics/productTracking', {
16
+ method: "GET",
17
+ parameters: klevu_search_product_tracking,
18
+
19
+ // We need to remove the AJAX headers so the request does not get preflighted and break cross-origin request policy
20
+ onCreate: function(response) {
21
+ var t = response.transport;
22
+ t.setRequestHeader = t.setRequestHeader.wrap(function(original, k, v) {
23
+ if (/^(accept|accept-language|content-language)$/i.test(k))
24
+ return original(k, v);
25
+ if (/^content-type$/i.test(k) &&
26
+ /^(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)(;.+)?$/i.test(v))
27
+ return original(k, v);
28
+ return;
29
+ });
30
+ }
31
+ });
32
+ }
33
+ })();
34
+ </script>
35
+ <?php endif; ?>
app/etc/modules/Klevu_Search.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Klevu_Search>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Klevu_Search>
8
+ </modules>
9
+ </config>
js/klevu/search/lib/Wizard.js ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ;'use strict';
2
+
3
+ if (!window.Klevu) window.Klevu = {};
4
+ if (!window.Klevu.Search) window.Klevu.Search = {};
5
+
6
+ (function () {
7
+ /**
8
+ * This class controls a popup Klevu Search Configuration Wizard window
9
+ * in System Configuration.
10
+ */
11
+ var Wizard = Class.create({
12
+
13
+ /**
14
+ * Create a new Wizard.
15
+ *
16
+ * @constructor
17
+ * @param options An Object containing any of the following configuration options:
18
+ * id - the DOM id to use for the Wizard window (default: klevu_search_wizard)
19
+ * title - the Wizard window title (default: Klevu Search Configuration Wizard)
20
+ * url - the initial Wizard URL to load
21
+ * onChange - a callback function to be called after every time Wizard window
22
+ * content is updated with the Window content DOM as a parameter.
23
+ */
24
+ initialize: function (options) {
25
+ var self = this;
26
+
27
+ self.config = Object.extend({
28
+ id: "klevu_search_wizard",
29
+ title: "Klevu Search Configuration Wizard",
30
+ url: null,
31
+ onChange: null
32
+ }, options || {});
33
+
34
+ self.load();
35
+ },
36
+
37
+ /**
38
+ * Request the given URL through AJAX and display the response in the Wizard window.
39
+ *
40
+ * @param url
41
+ */
42
+ load: function (url) {
43
+ var self = this;
44
+
45
+ url = url || this.config.url;
46
+
47
+ if (url) {
48
+ self.display();
49
+ new Ajax.Updater(self.dialog.content, url, {
50
+ evalScripts: true,
51
+ onComplete: self.onChangeCallback.bind(self)
52
+ });
53
+ }
54
+ },
55
+
56
+ /**
57
+ * Submit the given form through AJAX and display the response in the Wizard window.
58
+ *
59
+ * @param form
60
+ */
61
+ submit: function (form) {
62
+ var self = this;
63
+
64
+ self.display();
65
+ new Ajax.Updater(self.dialog.content, form.action, {
66
+ method: form.method,
67
+ parameters: form.serialize(),
68
+ evalScripts: true,
69
+ onComplete: self.onChangeCallback.bind(self)
70
+ });
71
+ },
72
+
73
+ /**
74
+ * A callback function executed after content is updated in the Wizard window.
75
+ * Calls the user-defined callback at the end.
76
+ */
77
+ onChangeCallback: function () {
78
+ var self = this;
79
+
80
+ var content = self.dialog.getContent();
81
+
82
+ // Overwrite submit on any forms in the content
83
+ content.select('form').each(function (form) {
84
+ form.observe('submit', function (event) {
85
+ self.submit(this);
86
+ Event.stop(event);
87
+ });
88
+ });
89
+
90
+ // Call the user-defined callback
91
+ if (self.config.onChange) {
92
+ self.config.onChange(content);
93
+ }
94
+ },
95
+
96
+ /**
97
+ * Display the Wizard window.
98
+ */
99
+ display: function () {
100
+ var self = this;
101
+
102
+ if (self.dialog && $(self.config.id) && typeof(Windows) != 'undefined') {
103
+ Windows.focus(self.config.id);
104
+ } else {
105
+ self.dialog = Dialog.info('<div class="loading">Loading...</div>', {
106
+ id: self.config.id,
107
+ className: "magento",
108
+ windowClassName: "popup-window",
109
+ title: self.config.title,
110
+ width: 1000,
111
+ // height: 450, Height is set to auto in css, with min-height: 450px
112
+ top: 50,
113
+ zIndex: 300, // Above content, but below the AJAX spinner, which is at 500 by default
114
+ recenterAuto: true,
115
+ draggable: true,
116
+ resizable: false,
117
+ closable: true,
118
+ showEffect: Element.show,
119
+ hideEffect: Element.hide,
120
+ onClose: self.close,
121
+ options: {}
122
+ });
123
+ }
124
+ },
125
+
126
+ /**
127
+ * Close the Wizard window.
128
+ *
129
+ * @param dialog The dialog window to close
130
+ */
131
+ close: function(dialog) {
132
+ var self = this;
133
+
134
+ if (!dialog) {
135
+ dialog = self.dialog;
136
+ }
137
+
138
+ if (dialog) {
139
+ dialog.close();
140
+ }
141
+
142
+ // Reload the page after close as it may have changed some information on the page
143
+ window.location.reload();
144
+ }
145
+ });
146
+
147
+ window.Klevu.Search.Wizard = Wizard;
148
+ })();
149
+
150
+ function showPopup(sUrl) {
151
+ oPopup = new Window({
152
+ id:'popup_window',
153
+ className: 'magento',
154
+ url: sUrl,
155
+ title: "Klevu Search Pro Features",
156
+ width: 980,
157
+ height: 600,
158
+ minimizable: false,
159
+ maximizable: false,
160
+ showEffectOptions: {
161
+ duration: 0.4
162
+ },
163
+ hideEffectOptions:{
164
+ duration: 0.4
165
+ },
166
+ destroyOnClose: true
167
+ });
168
+ oPopup.setZIndex(100);
169
+ oPopup.showCenter(true);
170
+ }
171
+
172
+ function closePopup() {
173
+ Windows.close('popup_window');
174
+ }
175
+
176
+ function checkplan()
177
+ {
178
+ showPopup("http://www.klevu.com/magento-free-vs-pro.html");
179
+ }
package.xml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Klevu_SmartSearch</name>
4
+ <version>1.1.13</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://klevu.com">Commercial</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Klevu Search replaces the default Magento search functiontality with a intelligent, dynamic, lightning fast search provided by Klevu.</summary>
10
+ <description>Klevu Search is a search extension which takes over the Magento search box and seamlessly integrates it with the Klevu Search engine. Klevu Search offers several advantages, including:&#xD;
11
+ Lightning fast search - Shoppers see instantly what they want.&#xD;
12
+ Trend mining - Shoppers trends are always up in the results.&#xD;
13
+ Intelligent boosting - Just select products you want to promote, no need to manually enter keywords.&#xD;
14
+ Dynamic filters - Shoppers get excellent shopping experience.&#xD;
15
+ Search as you type - Shoppers see results even with long keywords.&#xD;
16
+ Actionable insights - Drive traffic by learning from shoppers' search patterns.</description>
17
+ <notes>- Attributes change&#xD;
18
+ </notes>
19
+ <authors><author><name>Klevu</name><user>Klevu</user><email>niraj.aswani@klevu.com</email></author></authors>
20
+ <date>2015-03-11</date>
21
+ <time>12:36:01</time>
22
+ <contents><target name="mageetc"><dir name="modules"><file name="Klevu_Search.xml" hash="49674c121481f67bcfc2f31bb21e5aaf"/></dir></target><target name="magecommunity"><dir name="Klevu"><dir name="Search"><dir name="Block"><dir name="Adminhtml"><dir name="Form"><dir name="Field"><dir name="Attribute"><file name="Mappings.php" hash="293c4d19663fa3aad76c17287fec2114"/></dir><dir name="Automatic"><dir name="Attribute"><file name="Mappings.php" hash="b0d192bd03319957d09f34d510914a2f"/></dir></dir><dir name="Html"><file name="Select.php" hash="adc22272b93deb46b524f46f7000972f"/></dir><dir name="Image"><file name="Button.php" hash="bebde12b67a3fd9717e0fb3a1b489bfb"/><file name="Log.php" hash="b74aaef7b0f94f0e775d0da6491316af"/></dir><dir name="Store"><dir name="Level"><file name="Label.php" hash="c2a77e64a2decb8abd101f15b4ea3e3f"/></dir></dir><dir name="Sync"><file name="Button.php" hash="a6f8f69290f84f0ecd731585b3cca0e7"/></dir></dir><file name="Information.php" hash="b422d8aa8524b4277f2b4013e90379a0"/></dir><file name="Notifications.php" hash="9fc2511a89e04cb2c9ce86fc7c3684cd"/><dir name="Wizard"><dir name="Config"><file name="Button.php" hash="d17ba7640d5777612ab9fb27f85aa5c9"/></dir><dir name="Configure"><file name="Attributes.php" hash="3801cd4ebb45b3d29cfbacf830af2874"/><file name="Store.php" hash="a5e900bf91db47115ce54497669fd384"/><file name="User.php" hash="f353da92a383854f419333e842380273"/></dir></dir></dir><dir name="Catalog"><dir name="Product"><file name="Tracking.php" hash="a472d4caf068d12f9151d0ed282293e4"/></dir></dir></dir><dir name="Helper"><file name="Api.php" hash="72588aad499bee2c3a543a8a558a8a90"/><file name="Compat.php" hash="3726862a4576a53c5a44dda59a1e9a86"/><file name="Config.php" hash="04f3028dd50c1e6e16df2d5c2f588f47"/><file name="Data.php" hash="22e73727ba0b0f2575da81b04eb2e4c4"/></dir><dir name="Model"><dir name="Api"><dir name="Action"><file name="Addrecords.php" hash="36ee2cb10481d4b223c02e7a339482bd"/><file name="Adduser.php" hash="d8b6e6d37fe6ce522747fd6951c8e9df"/><file name="Addwebstore.php" hash="ea33f2c7032682a6ec2bdb8324cdaf0e"/><file name="Debuginfo.php" hash="dd92df091719aa73eca07a08d9681d42"/><file name="Deleterecords.php" hash="0e404db0aab2c58ff90a4e7e4fe73818"/><file name="Gettimezone.php" hash="4bb572b68f42236d765f8194e413b1ca"/><file name="Getuserdetail.php" hash="849a0f04bfdbf39ae75a2627d26717ca"/><file name="Idsearch.php" hash="9244fbdf1e67beea19c99ad0a014d0fc"/><file name="Producttracking.php" hash="50dacac910b7a68b035078771e9f8ff6"/><file name="Removetestmode.php" hash="bd2e7ecf6c233b1430e2989b2b62000f"/><file name="Startsession.php" hash="14edcef879dce92c76a9c78d4106fc6c"/><file name="Updaterecords.php" hash="9e99ce8da72a030b10e9af8353233427"/></dir><file name="Action.php" hash="792f3fe4348f7d13bdf09562ec0d8b59"/><dir name="Request"><file name="Get.php" hash="2d6f510d4dcc171b8e44322b76a49f93"/><file name="Post.php" hash="a46b484a50ced15e2c02deb16bbeb6bb"/><file name="Xml.php" hash="73b3f04d29c2bd79c470d7b81cfc0390"/></dir><file name="Request.php" hash="7f15814e8a01982499563e0e056d2d52"/><dir name="Response"><file name="Data.php" hash="9dbe8e28a501feacb07a6ce2bcc4deb8"/><file name="Empty.php" hash="5988ec43c1756cf4acfedb212787b2b9"/><file name="Invalid.php" hash="8287280c3b99f64e08cf54a0bf65e4a1"/><file name="Message.php" hash="5b6d717c75014e798e619e3df2008d29"/><file name="Search.php" hash="96b9bdef60811c4d994cb457f0d355a9"/><file name="Timezone.php" hash="ffa4d48e42fa52d2c928e202c2af61fd"/></dir><file name="Response.php" hash="b2c244001dedd653e1f0953d6bf25e0e"/></dir><dir name="Catalog"><dir name="Model"><file name="Config.php" hash="57ae55e3cea3dd1b4c60dafcb06d5efd"/></dir></dir><dir name="CatalogSearch"><dir name="Layer"><dir name="Filter"><file name="Attribute.php" hash="104ac92f6ef59879b593a9f1baffd562"/><file name="Category.php" hash="d30670c5ca616dcb0c3f0dd2ffbb97ba"/><file name="Price.php" hash="4ca1e56183364670fb39a88ea5666c13"/></dir></dir><dir name="Resource"><dir name="Fulltext"><file name="Collection.php" hash="b80bc1ff2e539c4ff54ba9b04a53bae6"/></dir><dir name="Layer"><dir name="Filter"><file name="Attribute.php" hash="d675e50ad7c5277b9ca4100770ac16d3"/></dir></dir></dir></dir><dir name="Config"><dir name="Log"><file name="Level.php" hash="ef07b22f48c4ca72066bf90a1cf9ac27"/></dir></dir><file name="Cron.php" hash="0936cc2667e056182668c9f61fc171bc"/><file name="Notification.php" hash="771d45868668c5b925e3bf2202482b43"/><file name="Observer.php" hash="a4eedf47b7194cd56aa0b7cc069fb691"/><dir name="Order"><file name="Sync.php" hash="e8af97ae357bfa1380e9f733d88efd5c"/></dir><dir name="Product"><file name="Sync.php" hash="4f3fcd3df423aa085fe209dd51e8876e"/></dir><dir name="Resource"><dir name="Notification"><file name="Collection.php" hash="936a242678b1c89853149e9dc77b6aff"/></dir><file name="Notification.php" hash="f3206c5286bf6ccb4df268c54fcff0f5"/></dir><file name="Session.php" hash="a4ab94b093ba8a414fbfa031f0d2cabe"/><file name="Sync.php" hash="f9f6f4d1251d493944ab13f1467d5257"/><dir name="System"><dir name="Config"><dir name="Source"><dir name="Additional"><file name="Attributes.php" hash="d91e1af65488d647b035fbb100a4eb1d"/></dir><dir name="Boosting"><file name="Attribute.php" hash="52a633242ff145d8f3b74c6a573c303e"/></dir><file name="Frequency.php" hash="5866e9d92476cdaa8a5f27c5fa0dc48b"/><file name="Landingoptions.php" hash="058c81eb9faec57f6c77db697ff3279e"/><dir name="Log"><file name="Level.php" hash="ba2bee63eb18b966db6beade72293036"/></dir><dir name="Product"><file name="Attributes.php" hash="f99ced41d6504f1570a703e66743dd0b"/></dir><file name="Yesnoforced.php" hash="0cf0e7842afae56af0caf92c9490295b"/></dir></dir></dir></dir><dir name="Test"><dir name="Config"><file name="Base.php" hash="a1a8faa15e50cd1d394bc79ca94f712b"/></dir><dir name="Controller"><dir name="CatalogSearch"><dir name="fixtures"><file name="search_results.yaml" hash="29bfca0b08d91f8acc4f7114f1e40475"/></dir></dir><file name="CatalogSearch.php" hash="7f8d9583333f5ad7a383a994816aa7a5"/></dir><dir name="Helper"><file name="Api.php" hash="74864f6af98fbbf64cf2e83e0ff25389"/><file name="Compat.php" hash="8bae993e0da8f368eb50689f271446a5"/><dir name="Config"><dir name="fixtures"><file name="testGetOrderSyncEnabledFlag.yaml" hash="998fd5ff61adf624336ba054e1898754"/><file name="testGetOrderSyncFrequency.yaml" hash="52dffcad75b15761a695eb451b99c751"/><file name="testGetProductSyncEnabledFlag.yaml" hash="431cf53b919d319bee1444c88c53d399"/><file name="testGetProductSyncFrequency.yaml" hash="004e62dfa22c9abfd8dcf56c92270e19"/><file name="testIsExtensionEnabledDisabled.yaml" hash="5d706b347b4f32f87dc8eb4dd6102148"/><file name="testIsExtensionEnabledEnabled.yaml" hash="52c9eb8a4fad4d8f3ba25c68d35977f3"/></dir><dir name="providers"><file name="testIsOrderSyncEnabled.yaml" hash="8239d8d0835b3fc555ac22906acd96da"/><file name="testIsProductSyncEnabled.yaml" hash="71e7c11155746f8add52a5b3704f4113"/><file name="testIsTestModeEnabled.yaml" hash="f727b557a1a13f0e98b576e3a067690b"/></dir></dir><file name="Config.php" hash="6a29543849d2b606f20f792018124b23"/><dir name="Data"><dir name="providers"><file name="testBytesToHumanReadable.yaml" hash="7cdb8705e108715abc63fa2cfd81626b"/><file name="testGetLanguageFromLocale.yaml" hash="c301f8dc090142627f6af4e4222b7708"/><file name="testHumanReadableToBytes.yaml" hash="dd89ae5b6705cfcfbf42ba64432c0f0a"/><file name="testIsProductionDomain.yaml" hash="f88ef0f80073fdb9cbdbd020348527a7"/></dir></dir><file name="Data.php" hash="93bb38ec55380a6ec7d79008496cac34"/></dir><dir name="Model"><dir name="Api"><dir name="Action"><dir name="Addrecords"><dir name="providers"><file name="testValidateRequiredFields.yaml" hash="26eea8bfc58c60e4a2f07dcbddc2e6fd"/><file name="testValidateRequiredFieldsRecords.yaml" hash="590b7ec9b7c33debb740b8c6c6db717f"/><file name="testValidateRequiredFieldsRecordsAllowedEmpty.yaml" hash="f82933b3d219491015cef39a214543bd"/><file name="testValidateRequiredFieldsRecordsEmpty.yaml" hash="d5dcd5aad682db42b1352c35b8905d47"/><file name="testValidateRequiredFieldsRecordsOptional.yaml" hash="25092bb84ba311815ca33971a388256e"/></dir></dir><file name="Addrecords.php" hash="14f5c217fac3f82957f54916fa29b387"/><dir name="Adduser"><dir name="providers"><file name="testValidateRequiredFields.yaml" hash="841f068ff3ffd8081ccc91e8675f998c"/></dir></dir><file name="Adduser.php" hash="5189f29f08c4ed30d9b6ac83429de3d0"/><dir name="Addwebstore"><dir name="providers"><file name="testValidateRequiredFields.yaml" hash="e52324b58b76d2d63cfa83ee228e1b31"/></dir></dir><file name="Addwebstore.php" hash="fbeac1b8adc5df45ef08a0a3331e5fbc"/><dir name="Getuserdetail"><dir name="providers"><file name="testValidateRequiredFields.yaml" hash="297df49838c47536bc9ee928b27470b2"/></dir></dir><file name="Getuserdetail.php" hash="4733d1ec7fd5b3b765aa88f182adf390"/><dir name="Idsearch"><dir name="providers"><file name="testValidateRequiredFields.yaml" hash="f36c1a1a1e6d1f5a2d3a59d8d349036e"/></dir></dir><file name="Idsearch.php" hash="0f572e9621086df59e53c55054e3f9d7"/><dir name="Producttracking"><dir name="providers"><file name="testValidateRequiredFields.yaml" hash="8749e326906367a0d0c26128ca944d0b"/></dir></dir><file name="Producttracking.php" hash="c41b1cb2e23bb00d83703d5f1775d31e"/><file name="Startsession.php" hash="e952ec0fd56d4a4603f73e297fd219c4"/></dir><file name="Action.php" hash="14ce7e11eb3acfda2dfea539c8f5254b"/><dir name="Request"><dir name="Xml"><dir name="providers"><file name="testGetDataAsXml.yaml" hash="4aa16e3557481444ba39775ee0b99d29"/></dir></dir><file name="Xml.php" hash="279a5defe0c933540e9635455590d8b1"/></dir><file name="Request.php" hash="b0cbdfe6a023d0479dc1b1c7fc6869c0"/><dir name="Response"><dir name="Data"><dir name="providers"><file name="testIsSuccessful.yaml" hash="f085922f4329b3d6b19c1e0010d3ca22"/></dir></dir><file name="Data.php" hash="1ab05c7225658f54bd31f0c205d05a2b"/><file name="Empty.php" hash="9933c42aa9ff06977ca3e53d2de15857"/><file name="Invalid.php" hash="bb68f25a053b7e5fc024bce0f5286fcb"/><dir name="Message"><dir name="providers"><file name="testIsSuccessful.yaml" hash="d53562002270a0c4b58ca94733309a21"/></dir></dir><file name="Message.php" hash="135c0d136fa9d0e86158407fc59294c3"/><dir name="Timezone"><dir name="providers"><file name="testIsSuccessful.yaml" hash="67f35e7ee5081689a1eddb867ae6256d"/></dir></dir><file name="Timezone.php" hash="f9cb063ef1f506f656a25c6206899eb3"/><dir name="providers"><file name="response_testIsSuccessful.yaml" hash="ac214d5ebf1eccb89e8fa85e8e3cbb75"/></dir></dir><file name="Response.php" hash="dbcce43ccb2cdf47bce5c05fcf27e759"/><dir name="Test"><file name="Case.php" hash="ec6d3169b9e2bd26655687124be779dd"/></dir><dir name="data"><file name="data_response_data.xml" hash="cd2e5d3dcac402828b9e5f0b0b637c76"/><file name="data_response_failure.xml" hash="ba677628e68149e0283729c1c8e5e86a"/><file name="data_response_no_response.xml" hash="98944f0eda050221de59100f4b22f030"/><file name="data_response_success.xml" hash="e92d1cfcd4461dc8d3fcabe1f902b40b"/><file name="data_response_success_only.xml" hash="276055d04a3f112efe9e6f1c337d7699"/><file name="message_response_failure.xml" hash="ab63c55523b26bd53ea6abf426dd11f5"/><file name="message_response_missing_message.xml" hash="4a43490eda6f33804ca400da2f9cdac1"/><file name="message_response_missing_status.xml" hash="39103fec18a0be88e4ff00a8149c8ad4"/><file name="message_response_session_id.xml" hash="415f25f79a3795ae70b983504cbb784f"/><file name="message_response_success.xml" hash="3ac53566b3d187cb81eb04d40a195c50"/><file name="response_malformed.xml" hash="bfeb4e75829a42082a7935a8e7be491e"/><file name="response_noxml.xml" hash="2debfdcf79f03e4a65a667d21ef9de14"/><file name="response_valid.xml" hash="c915992330f0e4bd37bb629637139f98"/><file name="search_response_empty.xml" hash="601298402d21f3626052634cbbb0ae72"/><file name="search_response_paged.xml" hash="04203807a1d6df787c54afd84690a50e"/><file name="search_response_sorted.xml" hash="c3ee65658a56f65334417328c7fbdcac"/><file name="search_response_success.xml" hash="fea4e59e7ae6e68b387729e320d5f49c"/><file name="startsession_response_success.xml" hash="1865ee2da80359e5a84914c56cae2c8d"/><file name="timezone_response_no_data.xml" hash="276055d04a3f112efe9e6f1c337d7699"/><file name="timezone_response_no_status.xml" hash="bd695479b03e3607196f01f627af4a45"/><file name="timezone_response_with_failure.xml" hash="69a6b1fa7b2cf087d85be162064525b0"/><file name="timezone_response_with_success.xml" hash="e6888eca066741675d228ba1397b5554"/></dir></dir><dir name="Config"><dir name="Log"><file name="Level.php" hash="2032f568dc75c8a5ff63c7daaf8bd049"/></dir></dir><dir name="Notification"><dir name="fixtures"><file name="testLoad.yaml" hash="305afce09851bb40e8c990c138aff162"/></dir></dir><file name="Notification.php" hash="889ec647dbdb938741cfc75e1baa2bc2"/><dir name="Observer"><dir name="fixtures"><file name="testScheduleOrderSync.yaml" hash="9dc706c72f13c3dbf43add1b23d1edce"/></dir></dir><file name="Observer.php" hash="f47e6187bcc7f738f40865a5db663467"/><dir name="Order"><dir name="Sync"><dir name="fixtures"><file name="testAddOrderToQueue.yaml" hash="9fd8654cffa0c5e5ad8140825a116601"/><file name="testClearQueue.yaml" hash="2413c55c5e3f05e15668fbe7edbe0bb0"/><file name="testRun.yaml" hash="b0b694cbdbfbbd97880254841f57fcf9"/></dir></dir><file name="Sync.php" hash="d2166272904e003b6bea3862f66e0235"/></dir><dir name="Product"><dir name="Sync"><dir name="fixtures"><file name="testAddProducts.yaml" hash="3059d9bfa5e611542384f2177b693d0c"/><file name="testClearAllProducts.yaml" hash="7420a73a3db174f74f825a9267f9be96"/><file name="testDeleteProducts.yaml" hash="fdac9c823890a3308fa83564df2b2531"/><file name="testRun.yaml" hash="1890501df756f0e8813d66f5726cbbc8"/><file name="testUpdateProducts.yaml" hash="796f71e44f79c5e5bb14f8d860bcbcd5"/></dir></dir><file name="Sync.php" hash="bb0455f3b8ce4988f4da8e66d90a9ef8"/></dir><dir name="Sync"><dir name="providers"><file name="testSchedule.yaml" hash="97f1043c42eb74162ae97941dc920d8f"/></dir></dir><file name="Sync.php" hash="8c091e168e251761be7fb1d1d2149305"/><dir name="System"><dir name="Config"><dir name="Source"><dir name="Test"><dir name="providers"><file name="testIsValidSourceModel.yaml" hash="cfedc94014707ee5586dd212b4d1ee16"/></dir></dir><file name="Test.php" hash="3e915599d4f76a2fbbb20aed9ffdf968"/></dir></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><dir name="Klevu"><file name="NotificationsController.php" hash="e6a8f7ee4ca5463bb9b3df5fa901b6b1"/><dir name="Search"><file name="WizardController.php" hash="5de993f8afaa30dcabbbaeb255503383"/></dir><file name="SearchController.php" hash="fc8669460e70f2a7cca27714faaf15d5"/></dir></dir><file name="SearchController.php" hash="1cd14cf045fedd2957a2bfd6de4b6080"/></dir><dir name="etc"><file name="adminhtml.xml" hash="938a66de5c0de1d096d7c0cb97e980cd"/><file name="config.xml" hash="937c87d26206813274c7f4d6090e3ef3"/><file name="system.xml" hash="26bc11cf684cc93977735cfa4bff4fa9"/></dir><dir name="sql"><dir name="klevu_search_setup"><file name="mysql4-data-upgrade-1.1.1-1.1.2.php" hash="28513b00d6ee4ac0a2bd0716484ff687"/><file name="mysql4-data-upgrade-1.1.7-1.1.8.php" hash="dd74fc5c8209647d87b5757a0fad6805"/><file name="mysql4-install-1.0.0.php" hash="860c1991c2ebf804f939598e9932edf5"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="klevu"><dir name="search"><dir name="form"><dir name="field"><file name="array_readonly.phtml" hash="d3a3be4cf22f89d84bd73121a1adfa86"/><dir name="sync"><file name="button.phtml" hash="0459f187b2b98a9ff5a9433875b465e1"/><file name="logbutton.phtml" hash="d31dd881015319d78d9fb2b83874f296"/></dir></dir><file name="information.phtml" hash="971e5e3e0823a740774164f58e00084e"/></dir><file name="notifications.phtml" hash="090714ca57b11d24769c1baa1bcd4615"/><dir name="wizard"><file name="complete.phtml" hash="aeca8d1a3335b461f5e6bcbba08c22b6"/><dir name="config"><file name="button.phtml" hash="b1644ee569882b05c2d8cb2f1eecddbc"/></dir><dir name="configure"><file name="attributes.phtml" hash="d54f0a86e78390fbe66825d0771aad25"/><file name="store.phtml" hash="1345462de285ecfe0bbe5ae8c422cbe6"/><file name="user.phtml" hash="0dfc281b7e98f3d7338ece082f2dc1ec"/></dir><dir name="form"><dir name="field"><file name="array.phtml" hash="ccdccab02fbd9cfc2145f094d5e12a2e"/></dir></dir></dir></dir></dir></dir><dir name="layout"><dir name="klevu"><file name="search.xml" hash="f385a7290bad22fb212b440afa82d2a7"/></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="klevu"><file name="search.xml" hash="71f813aa8775943a17220ddc3c4b9a41"/></dir></dir><dir name="template"><dir name="klevu"><dir name="search"><file name="form_js.phtml" hash="0f685e77647449b5a752a6c90b8fd4b8"/><file name="klevulog.phtml" hash="4dfa41e3747b2281e66b949ff03d46e2"/><file name="product_tracking.phtml" hash="5f81bca7273a3f1197217b6e0015083e"/></dir></dir></dir></dir></dir></dir></target><target name="mageweb"><dir name="js"><dir name="klevu"><dir name="search"><dir name="lib"><file name="Wizard.js" hash="67e9ae1996da43d6e419853a1e096419"/></dir></dir></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="klevu"><dir name="search"><file name="notifications.css" hash="80954cfc49bb2bc45d2eafb7c28cedad"/><file name="wizard.css" hash="824f735715f3ed97ce98414b9895e46b"/></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="css"><dir name="klevu"><file name="klevu-landing-page-style.css" hash="b80caca63061ae1e1be4ec63d31b4260"/><file name="klevu-landing-responsive.css" hash="811e1bfece09ba7d5529fec850963ac6"/></dir></dir><dir name="images"><dir name="klevu"><file name="btn-gridview.png" hash="c3dd97f4f53e1dfe34b6c5b6b4237b8a"/><file name="btn-listview.png" hash="00aaaa62d67e252248d6075f53b04f7f"/><file name="ku-loader.gif" hash="6050f5bcf455cc8f0332dd65e72f8e52"/></dir></dir></dir></dir></dir></target></contents>
23
+ <compatible/>
24
+ <dependencies><required><php><min>5.3.0</min><max>5.6.0</max></php></required></dependencies>
25
+ </package>
skin/adminhtml/default/default/klevu/search/notifications.css ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ .klevu-notification:before,
2
+ .klevu-notification:after {
3
+ content: " ";
4
+ display: table;
5
+ clear: both;
6
+ }
7
+ .klevu-notification .dismiss {
8
+ float: right;
9
+ }
10
+
skin/adminhtml/default/default/klevu/search/wizard.css ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #klevu_search_wizard .magento_content {
2
+ height: auto !important;
3
+ min-height: 450px;
4
+ }
5
+
6
+ #klevu_search_wizard .divider {
7
+ height: auto;
8
+ text-align: center;
9
+ font-weight: bold;
10
+ background: none;
11
+ }
12
+
13
+ #klevu_search_wizard .loading {
14
+ margin: 50px 20px;
15
+ text-align: center;
16
+ }
17
+
18
+ #klevu_search_wizard .warning {
19
+ margin: 30px 0;
20
+ }
21
+
22
+ #klevu_search_wizard #klevu_search_wizard_select_store_form .form-list {
23
+ margin: 0 auto;
24
+ }
25
+
26
+ #klevu_search_wizard p.error-msg {
27
+ min-height: 23px;
28
+ padding: 8px 8px 2px 32px;
29
+ }
30
+
31
+ #klevu_search_wizard .button-set {
32
+ margin: 10px 5px;
33
+ }
34
+
35
+ #klevu_search_wizard .button-set:before,
36
+ #klevu_search_wizard .button-set:after {
37
+ content: ' ';
38
+ display: table;
39
+ }
40
+
41
+ #klevu_search_wizard .button-set:after {
42
+ clear: both;
43
+ }
44
+
45
+ #klevu_search_wizard .button-set button,
46
+ #klevu_search_wizard .button-set a {
47
+ float: right;
48
+ margin-left: 10px;
49
+ }
50
+
51
+ #klevu_search_wizard_configure_attributes_form .fieldset div.grid {
52
+ max-height: 250px;
53
+ overflow: scroll;
54
+ }
55
+
56
+ #klevu_search_wizard_summary_form .configuration-changes {
57
+ margin: 10px 40px;
58
+ }
59
+
60
+ #klevu_search_wizard_summary_form .configuration-changes {
61
+ margin: 10px 0;
62
+ }
63
+
64
+ #klevu_search_wizard_summary_form .configuration-changes li div {
65
+ font-weight: bold;
66
+ }
67
+
68
+ #klevu_search_wizard_summary_form .configuration-changes li dl {
69
+ margin-left: 20px;
70
+ }
71
+
72
+ #klevu_search_wizard_summary_form .configuration-changes li dt,
73
+ #klevu_search_wizard_summary_form .configuration-changes li dd {
74
+ float: left;
75
+ }
76
+
77
+ #klevu_search_wizard_summary_form .configuration-changes li dt {
78
+ clear: left;
79
+ width: 150px;
80
+ }
81
+
82
+ #klevu_search_wizard_summary_form .configuration-changes dl:before,
83
+ #klevu_search_wizard_summary_form .configuration-changes dl:after {
84
+ content: " ";
85
+ display: table;
86
+ clear: both;
87
+ }
88
+
89
+ .klevu_signup_form td.label{
90
+ width: 130px !important;
91
+ }
92
+
93
+ .klevu_signup_form td.label label{
94
+ width: 130px !important;
95
+ }
96
+
97
+ .klevu_signup_form #countyCode{
98
+ width:74px;
99
+ text-align:right;
100
+ }
101
+
102
+ .klevu_signup_form #contactNo{
103
+ width:190px;
104
+ text-align:right;
105
+ }
skin/frontend/base/default/css/klevu/klevu-landing-page-style.css ADDED
@@ -0,0 +1,618 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* CSS of Klevu Search Result Landing Page */
2
+
3
+ /* klevu container for fix width layout*/
4
+
5
+ .kuContainer{
6
+ width:100%;
7
+ margin:0 auto;
8
+ font-family:inherit;
9
+ font-size:12px;
10
+ }
11
+
12
+ /* klevu container for fluid layout*/
13
+ .kuProListing{
14
+ margin-top:20px;
15
+ margin-bottom:20px;
16
+ }
17
+
18
+ /* klevu filters */
19
+ .kuFilters{
20
+ float:left;
21
+ width:23%;
22
+ border:1px solid #ddd;
23
+ }
24
+
25
+ /* set the height of each filter*/
26
+ .kuFilterBox{
27
+ height:150px;
28
+ overflow:hidden;
29
+ }
30
+
31
+ /* heading of filters i.e brand, color */
32
+ .kuFilterHead{
33
+ padding: 8px;
34
+ font-weight: bold;
35
+ font-size: 12px;
36
+ text-align: left;
37
+ background-color: #eee;
38
+ border-bottom: 1px solid #ddd;
39
+ }
40
+
41
+ /* down arrow div in filters */
42
+ .kuShowOpt{
43
+ text-align:center;
44
+ padding:3px;
45
+ position:relative;
46
+ }
47
+
48
+ /* up arrow div in filters */
49
+ .kuHideOpt{
50
+ text-align:center;
51
+ padding:3px;
52
+ }
53
+
54
+ .kuShowOpt img, .kuHideOpt img{
55
+ width:auto !important;
56
+ display:inline-block !important;
57
+ }
58
+
59
+ /* down & up arrow link color in filters */
60
+ .kuShowOpt a,.kuHideOpt a{
61
+ color:#4d7abf;
62
+ text-decoration:none;
63
+ }
64
+
65
+ /* set list for filters */
66
+ .kuFilterNames ul{
67
+ margin:0px;
68
+ padding:0px;
69
+ margin-top:10px;
70
+ margin-left:0px !important;
71
+ }
72
+
73
+ /* style for each values in filter */
74
+ .kuFilterNames ul li{
75
+ list-style:none;
76
+ text-align:left;
77
+ width:99%;
78
+ display:inline-table;
79
+ margin:0px;
80
+ padding-left:0px;
81
+ margin-left:0px !important;
82
+ }
83
+
84
+ .kuFilterNames ul li a{
85
+ display: block;
86
+ position: relative;
87
+ overflow: hidden;
88
+ margin: 0 5px;
89
+ line-height:24px;
90
+ padding-left: 5px;
91
+ padding-right: 5px;
92
+ font-size: 13px;
93
+ text-decoration: none;
94
+ cursor: pointer;
95
+ font-style:normal;
96
+ }
97
+
98
+ /* set background color on hover of filter */
99
+ .kuFilterNames ul li a:hover{
100
+ background-color:#eee;
101
+ cursor: pointer;
102
+ }
103
+
104
+ /* set background color for selected filter */
105
+ .kuFilterNames ul li.kuSelected a{
106
+ background-color:#616161;
107
+ color: #fff;
108
+ }
109
+
110
+ /* label for the filter */
111
+ .kuFilterNames ul li a span.kuFilterLabel{
112
+ float:left;
113
+ width:86%;
114
+ margin:0px;
115
+ padding:0px;
116
+ font-weight:normal;
117
+ }
118
+
119
+ /* total nos of results available for filter */
120
+ .kuFilterNames ul li a span.kuFilterTotal{
121
+ float:right;
122
+ width:13%;
123
+ text-align:right;
124
+ }
125
+
126
+ /* shows cancel button if filter is selected */
127
+ .kuFilterNames ul li a span.kuFilterCancel{
128
+ float:right;
129
+ width:13%;
130
+ text-align:right;
131
+ font-size:10px;
132
+ }
133
+
134
+ /* klevu results box */
135
+ .kuResultList{
136
+ float:right;
137
+ width:75%;
138
+ }
139
+
140
+ /* div for Sorting, pagination, change result view icons*/
141
+ .kuSortHeader{
142
+ padding:10px;
143
+ margin:5px;
144
+ padding-top: 0px;
145
+ margin-top: 0px;
146
+ }
147
+
148
+ /* div to display total no of results */
149
+ .kuTotResults{
150
+ text-align:left;
151
+ margin-bottom:10px;
152
+ line-height:24px;
153
+ font-weight:bold;
154
+ font-size:14px;
155
+ }
156
+
157
+ .kuSortingOpt{
158
+ background-color:#eee;
159
+ border:1px solid #ddd;
160
+ padding:5px;
161
+ }
162
+
163
+ /* div to display sorting dropdown */
164
+ .kuSortby{
165
+ float:left;
166
+ width:25%;
167
+ }
168
+
169
+ /* label of sorting dropdown */
170
+ .kuSortby label{
171
+ display:inline;
172
+ }
173
+
174
+ /* sorting dropdown */
175
+ .kuSortby select{
176
+ display:inline;
177
+ height:25px;
178
+ width:100px;
179
+ }
180
+
181
+ /* div to display icons to change the view of result (grid/view) */
182
+ .kuView{
183
+ float:left;
184
+ width:15%;
185
+ text-align:center;
186
+ }
187
+
188
+ /* display GRID view icon */
189
+ .kuView .kuGridviewBtn{
190
+ background: #ededed url(../../images/klevu/btn-gridview.png) no-repeat 0 0;
191
+ cursor:pointer;
192
+ }
193
+
194
+ /* display LIST view icon */
195
+ .kuView .kuListviewBtn{
196
+ background: #ededed url(../../images/klevu/btn-listview.png) no-repeat 0 0;
197
+ cursor:pointer;
198
+ }
199
+
200
+ /* set width and height of view icons box*/
201
+ .kuView a{
202
+ display: inline-block;
203
+ border: 1px solid #D9D9D9;
204
+ width: 44px;
205
+ height: 27px;
206
+ margin-right: -4px;
207
+ text-align: center;
208
+ vertical-align: middle;
209
+ overflow: hidden;
210
+ }
211
+
212
+ /* changing background position on hover of GRID/LIST view icons */
213
+ .kuView a:hover{
214
+ border-color: #BBB;
215
+ background-position:0 -30px;
216
+ text-decoration: none;
217
+ }
218
+
219
+ /* change background position to set current view */
220
+ .kuView a.kuCurrent{
221
+ background-position:0 -60px;
222
+ border-color: #BBB;
223
+ }
224
+
225
+ /* dropdown to select no of results per page*/
226
+ .kuPerPage{
227
+ float:left;
228
+ width:23%;
229
+ margin-left:10px;
230
+ text-align:right;
231
+ }
232
+
233
+ .kuPerPage select{
234
+ width:54px !important;
235
+ height:25px;
236
+ }
237
+
238
+ /* div for pagination */
239
+ .kuPagination{
240
+ width:33%;
241
+ float:left;
242
+ margin-left:10px;
243
+ text-align:right;
244
+ }
245
+
246
+ /* style to display page nos in line */
247
+ .kuPagination a{
248
+ border: 1px solid #ddd;
249
+ margin:0px;
250
+ position: relative;
251
+ display: inline-block;
252
+ background:#eee;
253
+ padding:4px;
254
+ padding-left:8px;
255
+ padding-right:8px;
256
+ color: #444;
257
+ cursor: pointer;
258
+ text-decoration:none;
259
+ }
260
+
261
+ /* style on hover of page links */
262
+ .kuPagination a:hover{
263
+ background: #dadada;
264
+ }
265
+
266
+ /* style to show current page */
267
+ .kuPagination a.kuCurrent{
268
+ background: #fff;
269
+ -webkit-box-shadow:none;
270
+ -moz-box-shadow:none;
271
+ box-shadow:none;
272
+ border:1px solid #ddd;
273
+ font-weight:bold;
274
+ }
275
+
276
+ .kuClearLeft{
277
+ clear:left;
278
+ line-height:0px;
279
+ }
280
+
281
+
282
+ /* klevu results div */
283
+ .kuResults{
284
+ margin-top:10px;
285
+ }
286
+
287
+ /* styles for list view results */
288
+ .kuListView{
289
+ margin-top:10px;
290
+ }
291
+
292
+ .kuListView ul{
293
+ margin:0px;
294
+ padding:0px;
295
+ }
296
+
297
+ /* In LISTVIEW: list style for each result */
298
+ .kuListView ul li{
299
+ display:block;
300
+ width: 100%;
301
+ height: auto;
302
+ padding: 10px;
303
+ border-bottom: 1px solid #eee;
304
+ text-align:left;
305
+ }
306
+
307
+ /* In LISTVIEW: for wrapping the image in fixed size div */
308
+ .kuListView .klevuImgWrap{
309
+ float:left;
310
+ overflow:hidden;
311
+ width:15% !important;
312
+ height:120px !important;
313
+ text-align:center;
314
+ }
315
+
316
+ /* In GRIDVIEW: thumbnail of the product */
317
+ .kuListView img{
318
+ max-width:100% !important;
319
+ max-height:120px !important;
320
+ width:auto;
321
+ height:auto;
322
+ border:none;
323
+ outline:none;
324
+ }
325
+
326
+ /* In LISTVIEW: display product name and description */
327
+ .kuListView ul li .kuNameDesc{
328
+ float:left;
329
+ width:65%;
330
+ margin-left:5px;
331
+ }
332
+
333
+ .kuListView ul li .kuName{
334
+ padding:5px;
335
+ }
336
+
337
+ .kuListView ul li .kuDesc{
338
+ line-height:20px;
339
+ padding:5px;
340
+ font-style:normal;
341
+ }
342
+
343
+ /* In LISTVIEW: set product name color and font size */
344
+ .kuListView ul li .kuName a{
345
+ font-size:14px;
346
+ text-decoration:none;
347
+ color:inherit;
348
+ font-weight:bold;
349
+ font-style:normal;
350
+ }
351
+
352
+ .kuListView ul li .kuName a:hover{
353
+ text-decoration:underline;
354
+ }
355
+
356
+ /* In LISTVIEW: div to display saleprice and original price */
357
+ .kuListView ul li .kuPrice{
358
+ float:left;
359
+ width:15%;
360
+ margin-top:5px;
361
+ margin-bottom:5px;
362
+ padding:5px;
363
+ text-align:center;
364
+ }
365
+
366
+ /* In LISTVIEW: div to display saleprice */
367
+ .kuListView ul li .kuSalePrice{
368
+ font-weight:bold;
369
+ font-size:14px;
370
+ margin-bottom:5px;
371
+ }
372
+
373
+ /* In LISTVIEW: div to display original price with line-through style */
374
+ .kuListView ul li .kuOrigPrice{
375
+ font-size:13px;
376
+ text-decoration:line-through;
377
+ }
378
+
379
+ /* In LISTVIEW: set color to highlight search keyowrd in name and description */
380
+ .kuListView ul li strong{
381
+ color:#2980B9;
382
+ }
383
+
384
+ /* styles for grid view results */
385
+ .kuGridView{
386
+ margin-top:10px;
387
+ }
388
+
389
+ .kuGridView ul{
390
+ margin:0px;
391
+ padding:0px;
392
+ margin-left:0px;
393
+ }
394
+
395
+ /* In GRIDVIEW: list style for each result */
396
+ .kuGridView ul li{
397
+ display: inline-block;
398
+ width: 24%;
399
+ min-height: 275px;
400
+ padding: 0;
401
+ vertical-align: top;
402
+ border-top: 1px solid #ddd;
403
+ text-align:center;
404
+ margin:2px;
405
+ padding-top:15px;
406
+ margin-left:0px !important;
407
+ font-style:normal;
408
+ }
409
+
410
+ .kuGridView ul li:nth-child(1),.kuGridView ul li:nth-child(2),.kuGridView ul li:nth-child(3),.kuGridView ul li:nth-child(4){
411
+ border-top:none;
412
+ }
413
+
414
+ /* In GRIDVIEW: for wrapping the image in fixed size div */
415
+ .kuGridView .klevuImgWrap{
416
+ float:none;
417
+ overflow:hidden;
418
+ padding:5px;
419
+ width:90% !important;
420
+ height:140px !important;
421
+ text-align:center;
422
+ margin:0 auto;
423
+ }
424
+
425
+ /* In GRIDVIEW: thumbnail of the product */
426
+ .kuGridView img{
427
+ max-width:100% !important;
428
+ height:140px !important;
429
+ width:auto;
430
+ border:none;
431
+ outline:none;
432
+ display:inline-block !important;
433
+ }
434
+
435
+ /* In GRIDVIEW: remove float value for name and description div */
436
+ .kuGridView .kuNameDesc{
437
+ float:none;
438
+ }
439
+
440
+ /* In GRIDVIEW: discription is not displayed in grid layout*/
441
+ .kuGridView .kuDesc{
442
+ display:none;
443
+ }
444
+
445
+ .kuGridView ul li .kuName{
446
+ padding:5px;
447
+ min-height:45px;
448
+ }
449
+
450
+
451
+ /* CSS for add to cart button */
452
+ .kuGridView ul li .kuAddtocart{
453
+ display:block;
454
+ height:20px;
455
+ margin-top:20px;
456
+ }
457
+
458
+ .kuGridView ul li input[type="text"]{
459
+ display:inline-block;
460
+ width:25%;
461
+ float:left;
462
+ height:22px;
463
+ border:1px solid #ddd;
464
+ outline:none;
465
+ text-align:right;
466
+ }
467
+
468
+ .kuGridView ul li a.kuAddtocartBtn{
469
+ display:inline-block;
470
+ background: #616161;
471
+ float:right;
472
+ color: #fff;
473
+ width:72%;
474
+ padding-top:6px;
475
+ padding-bottom:6px;
476
+ margin-bottom: 0;
477
+ text-align: center;
478
+ vertical-align: middle;
479
+ cursor: pointer;
480
+ white-space: nowrap;
481
+ -webkit-user-select: none;
482
+ -moz-user-select: none;
483
+ -ms-user-select: none;
484
+ -o-user-select: none;
485
+ text-decoration:none;
486
+
487
+ }
488
+
489
+ .kuListView ul li .klevu-addtocart{
490
+ float:right;
491
+ display:block;
492
+ width:15%;
493
+ height:20px;
494
+ margin-top:20px;
495
+ text-align:center;
496
+ }
497
+
498
+
499
+ .kuListView ul li input[type="text"]{
500
+ width:5%;
501
+ float:none;
502
+ height:22px;
503
+ border:1px solid #ddd;
504
+ outline:none;
505
+ text-align:right;
506
+ }
507
+
508
+ .kuListView ul li a.kuAddtocartBtn{
509
+ background: #616161;
510
+ float:none;
511
+ color: #fff;
512
+ width:15%;
513
+ padding-top:6px;
514
+ padding-bottom:6px;
515
+ padding:6px;
516
+ margin-bottom: 0;
517
+ text-align: center;
518
+ vertical-align: middle;
519
+ cursor: pointer;
520
+ white-space: nowrap;
521
+ -webkit-user-select: none;
522
+ -moz-user-select: none;
523
+ -ms-user-select: none;
524
+ -o-user-select: none;
525
+ text-decoration:none;
526
+
527
+ }
528
+
529
+
530
+ /* In GRIDVIEW: set product name color and font size */
531
+ .kuGridView ul li .kuName a{
532
+ font-size:13px;
533
+ text-decoration:none;
534
+ color:inherit;
535
+ font-style:normal;
536
+ }
537
+
538
+ .kuGridView ul li .kuName a:hover{
539
+ text-decoration:underline;
540
+ }
541
+
542
+ /* In GRIDVIEW: div to display saleprice and original price */
543
+ .kuGridView ul li .kuPrice{
544
+ float:none;
545
+ margin-top:5px;
546
+ margin-bottom:5px;
547
+ padding:5px;
548
+ }
549
+
550
+ /* In GRIDVIEW: div to display saleprice */
551
+ .kuGridView ul li .kuSalePrice{
552
+ font-weight:bold;
553
+ font-size:14px;
554
+ margin-bottom:5px;
555
+ }
556
+
557
+ /* In GRIDVIEW: div to display original price with line-through style */
558
+ .kuGridView ul li .kuOrigPrice{
559
+ font-size:13px;
560
+ text-decoration:line-through;
561
+ }
562
+
563
+ /* In GRIDVIEW: set color to highlight search keyowrd in name and description */
564
+ .kuGridView ul li strong{
565
+ color:#2980B9;
566
+ }
567
+
568
+
569
+ /* pagination links at bottom of results */
570
+ .kuBottomPagi{
571
+ padding:5px;
572
+ background-color:#eee;
573
+ border:1px solid #ddd;
574
+ }
575
+
576
+ .kuBottomPagi .kuPerPage{
577
+ margin-left:0px;
578
+ text-align: left;
579
+ }
580
+
581
+ .kuBottomPagi .kuPagination{
582
+ width: 50%;
583
+ float: right;
584
+ margin-left: 10px;
585
+ }
586
+
587
+ .kuPagination a{
588
+ font-style:normal;
589
+ }
590
+
591
+ .kuClearBoth{
592
+ clear:both;
593
+ }
594
+
595
+ /* div to display No records found message */
596
+ .kuNoRecordFound{
597
+ text-align:center;
598
+ margin-top:10%;
599
+ margin-bottom:10%;
600
+ }
601
+
602
+ /* show variants */
603
+ .kuVariants{
604
+ font-size: 9px;
605
+ text-align: center;
606
+ margin-top:2px;
607
+ }
608
+
609
+ /* height for loader div */
610
+ #loader{
611
+ height:400px;
612
+ }
613
+
614
+ #loader img{
615
+ margin-top:10%;
616
+ display: inline-block;
617
+ width:auto !important;
618
+ }
skin/frontend/base/default/css/klevu/klevu-landing-responsive.css ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .kuContainer{
2
+ width:100% !important;
3
+ }
4
+
5
+ @media only screen and (max-width: 1024px) {
6
+ .kuSortby{
7
+ width:45%;
8
+ }
9
+
10
+ .kuView{
11
+ width:50%;
12
+ text-align:right;
13
+ }
14
+
15
+ .kuPerPage{
16
+ width:45%;
17
+ text-align:left;
18
+ margin-left:0px;
19
+ margin-top:10px;
20
+ }
21
+
22
+ .kuPagination{
23
+ width:50%;
24
+ margin-top:10px;
25
+ }
26
+
27
+ .kuGridView ul li{
28
+ width:31%;
29
+ }
30
+
31
+ .kuListView ul li .kuNameDesc{
32
+ width:80%;
33
+ }
34
+
35
+ .kuListView ul li .kuPrice{
36
+ float:none !important;
37
+ width:100%;
38
+ }
39
+
40
+ .kuListView ul li .kuPrice .kuSalePrice{
41
+ display:inline !important;
42
+ }
43
+
44
+ .kuListView ul li .kuPrice .kuOrigPrice{
45
+ display:inline !important;
46
+ }
47
+
48
+ .kuGridView ul li:nth-child(4){
49
+ border-top:1px solid #ddd;
50
+ }
51
+
52
+ }
53
+
54
+ @media only screen and (min-width: 641px) and (max-width: 768px) {
55
+ .kuResultList{
56
+ width:74%;
57
+ }
58
+
59
+ .kuGridView ul li{
60
+ width:31%;
61
+ }
62
+
63
+ .kuGridView ul li:nth-child(4){
64
+ border-top:1px solid #ddd;
65
+ }
66
+
67
+ }
68
+
69
+ @media only screen and (max-width: 640px) {
70
+ .kuFilters{
71
+ display:none !important;
72
+ }
73
+
74
+ .kuResultList{
75
+ float:none;
76
+ width:100%;
77
+ margin-left:0px;
78
+ }
79
+
80
+ .kuSortby{
81
+ width:50%;
82
+ height:50px;
83
+ }
84
+
85
+ .kuSortHeader{
86
+ margin:0px;
87
+ }
88
+
89
+ .kuView{
90
+ width:50%;
91
+ text-align:right;
92
+ height:50px;
93
+ }
94
+
95
+ .kuPerPage{
96
+ width:35%;
97
+ text-align:left;
98
+ margin:0 auto;
99
+ margin-top:0px;
100
+ }
101
+
102
+ .kuPagination{
103
+ width:65%;
104
+ margin:0 auto;
105
+ margin-top:0px;
106
+ text-align:right;
107
+ }
108
+
109
+ .kuGridView ul li{
110
+ width:31%;
111
+ }
112
+
113
+ .kuGridView ul li:nth-child(3){
114
+ border-top:none;
115
+ }
116
+
117
+ .kuGridView ul li:nth-child(4){
118
+ border-top:1px solid #ddd;
119
+ }
120
+
121
+ .kuBottomPagi .kuPerPage{
122
+ margin-left:0px;
123
+ text-align: center;
124
+ margin-bottom:5px;
125
+ }
126
+
127
+ .kuBottomPagi .kuPagination{
128
+ width: 100%;
129
+ float: none;
130
+ }
131
+
132
+ }
133
+
134
+ @media only screen and (max-width: 480px) {
135
+ .kuGridView ul li{
136
+ width:47%;
137
+ }
138
+
139
+ .kuGridView ul li:nth-child(3),.kuGridView ul li:nth-child(4){
140
+ border-top:1px solid #ddd;
141
+ }
142
+ }
skin/frontend/base/default/images/klevu/btn-gridview.png ADDED
Binary file
skin/frontend/base/default/images/klevu/btn-listview.png ADDED
Binary file
skin/frontend/base/default/images/klevu/ku-loader.gif ADDED
Binary file