Springbot - Version 1.2.1.2

Version Notes

Corrected logging for non-info class messages

Download this release

Release Info

Developer Springbot Integrations Team
Extension Springbot
Version 1.2.1.2
Comparing to
See all releases


Version 1.2.1.2

Files changed (257) hide show
  1. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Auth.php +9 -0
  2. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Login.php +40 -0
  3. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Login/Form.php +93 -0
  4. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Register.php +36 -0
  5. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Register/Form.php +72 -0
  6. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Connected.php +12 -0
  7. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Help.php +10 -0
  8. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Index.php +20 -0
  9. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Index/Messages.php +18 -0
  10. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Index/Terms.php +14 -0
  11. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Jobs.php +14 -0
  12. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Jobs/Grid.php +96 -0
  13. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Jobs/Status.php +75 -0
  14. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Login.php +10 -0
  15. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Logout.php +14 -0
  16. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Logs.php +19 -0
  17. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Notifications.php +32 -0
  18. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Problems.php +18 -0
  19. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Settings.php +29 -0
  20. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Settings/Edit/Form.php +43 -0
  21. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Status.php +13 -0
  22. app/code/community/Springbot/Bmbleb/Block/Adminhtml/Tabs.php +33 -0
  23. app/code/community/Springbot/Bmbleb/Helper/Account.php +145 -0
  24. app/code/community/Springbot/Bmbleb/Helper/Alerts.php +14 -0
  25. app/code/community/Springbot/Bmbleb/Helper/ApiCall.php +116 -0
  26. app/code/community/Springbot/Bmbleb/Helper/ApiResponse.php +59 -0
  27. app/code/community/Springbot/Bmbleb/Helper/BmblebProps.php +26 -0
  28. app/code/community/Springbot/Bmbleb/Helper/ChangePassword.php +55 -0
  29. app/code/community/Springbot/Bmbleb/Helper/Data.php +135 -0
  30. app/code/community/Springbot/Bmbleb/Helper/ExternalLogging.php +69 -0
  31. app/code/community/Springbot/Bmbleb/Helper/GetTermsOfService.php +54 -0
  32. app/code/community/Springbot/Bmbleb/Helper/PluginStatus.php +345 -0
  33. app/code/community/Springbot/Bmbleb/Model/Alert.php +63 -0
  34. app/code/community/Springbot/Bmbleb/Model/Alert/Priority.php +27 -0
  35. app/code/community/Springbot/Bmbleb/Model/Alert/Status.php +27 -0
  36. app/code/community/Springbot/Bmbleb/Model/Alert/Type.php +45 -0
  37. app/code/community/Springbot/Bmbleb/Model/Bmbleb.php +10 -0
  38. app/code/community/Springbot/Bmbleb/Model/Bmblebprops.php +38 -0
  39. app/code/community/Springbot/Bmbleb/Model/Mysql4/Bmbleb/Collection.php +10 -0
  40. app/code/community/Springbot/Bmbleb/Model/Status.php +15 -0
  41. app/code/community/Springbot/Bmbleb/Model/Sync.php +29 -0
  42. app/code/community/Springbot/Bmbleb/controllers/Adminhtml/HelpController.php +12 -0
  43. app/code/community/Springbot/Bmbleb/controllers/Adminhtml/IndexController.php +68 -0
  44. app/code/community/Springbot/Bmbleb/controllers/Adminhtml/JobsController.php +77 -0
  45. app/code/community/Springbot/Bmbleb/controllers/Adminhtml/LogsController.php +30 -0
  46. app/code/community/Springbot/Bmbleb/controllers/Adminhtml/ProblemsController.php +20 -0
  47. app/code/community/Springbot/Bmbleb/controllers/Adminhtml/SettingsController.php +83 -0
  48. app/code/community/Springbot/Bmbleb/controllers/HelpController.php +30 -0
  49. app/code/community/Springbot/Bmbleb/controllers/IndexController.php +16 -0
  50. app/code/community/Springbot/Bmbleb/controllers/LoginController.php +76 -0
  51. app/code/community/Springbot/Bmbleb/controllers/LogoutController.php +43 -0
  52. app/code/community/Springbot/Bmbleb/controllers/RegisterController.php +350 -0
  53. app/code/community/Springbot/Bmbleb/etc/config.xml +114 -0
  54. app/code/community/Springbot/BoneCollector/Model/HarvestAbstract.php +105 -0
  55. app/code/community/Springbot/BoneCollector/Model/HarvestAttribute/Observer.php +48 -0
  56. app/code/community/Springbot/BoneCollector/Model/HarvestCaptureSKU/Observer.php +504 -0
  57. app/code/community/Springbot/BoneCollector/Model/HarvestCart/Observer.php +161 -0
  58. app/code/community/Springbot/BoneCollector/Model/HarvestCategory/Observer.php +36 -0
  59. app/code/community/Springbot/BoneCollector/Model/HarvestCustomer/Observer.php +55 -0
  60. app/code/community/Springbot/BoneCollector/Model/HarvestProduct/Observer.php +93 -0
  61. app/code/community/Springbot/BoneCollector/Model/HarvestPurchase/Observer.php +160 -0
  62. app/code/community/Springbot/BoneCollector/Model/HarvestSubscriber/Observer.php +37 -0
  63. app/code/community/Springbot/BoneCollector/etc/config.xml +173 -0
  64. app/code/community/Springbot/Boss.php +176 -0
  65. app/code/community/Springbot/Combine/Helper/Attributes.php +171 -0
  66. app/code/community/Springbot/Combine/Helper/Cart.php +28 -0
  67. app/code/community/Springbot/Combine/Helper/Data.php +235 -0
  68. app/code/community/Springbot/Combine/Helper/Harvest.php +427 -0
  69. app/code/community/Springbot/Combine/Helper/Parser.php +234 -0
  70. app/code/community/Springbot/Combine/Helper/Redirect.php +133 -0
  71. app/code/community/Springbot/Combine/Helper/Store.php +40 -0
  72. app/code/community/Springbot/Combine/Helper/Trackable.php +67 -0
  73. app/code/community/Springbot/Combine/Model/Abstract.php +26 -0
  74. app/code/community/Springbot/Combine/Model/Api.php +173 -0
  75. app/code/community/Springbot/Combine/Model/Cron/Count.php +86 -0
  76. app/code/community/Springbot/Combine/Model/Cron/Manager/Status.php +116 -0
  77. app/code/community/Springbot/Combine/Model/Cron/Queue.php +89 -0
  78. app/code/community/Springbot/Combine/Model/Cron/Queue/Batch.php +80 -0
  79. app/code/community/Springbot/Combine/Model/Cron/Queue/Batch/Row.php +39 -0
  80. app/code/community/Springbot/Combine/Model/Cron/Worker.php +101 -0
  81. app/code/community/Springbot/Combine/Model/File/Io.php +146 -0
  82. app/code/community/Springbot/Combine/Model/File/Path.php +75 -0
  83. app/code/community/Springbot/Combine/Model/Harvest/Abstract.php +308 -0
  84. app/code/community/Springbot/Combine/Model/Harvest/AttributeSets.php +36 -0
  85. app/code/community/Springbot/Combine/Model/Harvest/Carts.php +27 -0
  86. app/code/community/Springbot/Combine/Model/Harvest/Categories.php +39 -0
  87. app/code/community/Springbot/Combine/Model/Harvest/CustomerAttributeSets.php +6 -0
  88. app/code/community/Springbot/Combine/Model/Harvest/Customers.php +19 -0
  89. app/code/community/Springbot/Combine/Model/Harvest/Guests.php +20 -0
  90. app/code/community/Springbot/Combine/Model/Harvest/Products.php +22 -0
  91. app/code/community/Springbot/Combine/Model/Harvest/Purchases.php +20 -0
  92. app/code/community/Springbot/Combine/Model/Harvest/Subscribers.php +18 -0
  93. app/code/community/Springbot/Combine/Model/Harvester.php +16 -0
  94. app/code/community/Springbot/Combine/Model/Mysql4/Cron/Count.php +5 -0
  95. app/code/community/Springbot/Combine/Model/Mysql4/Cron/Queue.php +5 -0
  96. app/code/community/Springbot/Combine/Model/Mysql4/Cron/Queue/Collection.php +5 -0
  97. app/code/community/Springbot/Combine/Model/Mysql4/Redirect.php +5 -0
  98. app/code/community/Springbot/Combine/Model/Mysql4/Redirect/Collection.php +5 -0
  99. app/code/community/Springbot/Combine/Model/Mysql4/Redirect/Order.php +5 -0
  100. app/code/community/Springbot/Combine/Model/Mysql4/Redirect/Order/Collection.php +5 -0
  101. app/code/community/Springbot/Combine/Model/Mysql4/Setup.php +5 -0
  102. app/code/community/Springbot/Combine/Model/Mysql4/Trackable.php +5 -0
  103. app/code/community/Springbot/Combine/Model/Mysql4/Trackable/Collection.php +5 -0
  104. app/code/community/Springbot/Combine/Model/Parser.php +6 -0
  105. app/code/community/Springbot/Combine/Model/Parser/Abstract.php +182 -0
  106. app/code/community/Springbot/Combine/Model/Parser/AttributeSet.php +50 -0
  107. app/code/community/Springbot/Combine/Model/Parser/Category.php +39 -0
  108. app/code/community/Springbot/Combine/Model/Parser/Customer.php +119 -0
  109. app/code/community/Springbot/Combine/Model/Parser/CustomerAttributeSet.php +29 -0
  110. app/code/community/Springbot/Combine/Model/Parser/Guest.php +92 -0
  111. app/code/community/Springbot/Combine/Model/Parser/Product.php +102 -0
  112. app/code/community/Springbot/Combine/Model/Parser/Purchase.php +169 -0
  113. app/code/community/Springbot/Combine/Model/Parser/Purchase/Item.php +127 -0
  114. app/code/community/Springbot/Combine/Model/Parser/Quote.php +80 -0
  115. app/code/community/Springbot/Combine/Model/Parser/Quote/Item.php +108 -0
  116. app/code/community/Springbot/Combine/Model/Parser/Subscriber.php +54 -0
  117. app/code/community/Springbot/Combine/Model/Redirect.php +32 -0
  118. app/code/community/Springbot/Combine/Model/Redirect/Order.php +17 -0
  119. app/code/community/Springbot/Combine/Model/Resource/Abstract.php +46 -0
  120. app/code/community/Springbot/Combine/Model/Resource/Cron/Count.php +68 -0
  121. app/code/community/Springbot/Combine/Model/Resource/Cron/Count/Collection.php +11 -0
  122. app/code/community/Springbot/Combine/Model/Resource/Cron/Queue.php +61 -0
  123. app/code/community/Springbot/Combine/Model/Resource/Cron/Queue/Collection.php +70 -0
  124. app/code/community/Springbot/Combine/Model/Resource/Redirect.php +13 -0
  125. app/code/community/Springbot/Combine/Model/Resource/Redirect/Collection.php +42 -0
  126. app/code/community/Springbot/Combine/Model/Resource/Redirect/Order.php +10 -0
  127. app/code/community/Springbot/Combine/Model/Resource/Redirect/Order/Collection.php +15 -0
  128. app/code/community/Springbot/Combine/Model/Resource/Setup.php +289 -0
  129. app/code/community/Springbot/Combine/Model/Resource/Trackable.php +32 -0
  130. app/code/community/Springbot/Combine/Model/Resource/Trackable/Collection.php +9 -0
  131. app/code/community/Springbot/Combine/Model/System/Config/Source/LogFormat.php +13 -0
  132. app/code/community/Springbot/Combine/Model/System/Config/Source/LogLevel.php +12 -0
  133. app/code/community/Springbot/Combine/Model/System/Config/Source/Stability.php +13 -0
  134. app/code/community/Springbot/Combine/Model/System/Config/Source/UrlType.php +13 -0
  135. app/code/community/Springbot/Combine/Model/Trackable.php +28 -0
  136. app/code/community/Springbot/Combine/etc/adminhtml.xml +22 -0
  137. app/code/community/Springbot/Combine/etc/config.xml +137 -0
  138. app/code/community/Springbot/Combine/etc/system.xml +343 -0
  139. app/code/community/Springbot/Combine/sql/combine_setup/mysql4-install-1.0.0.70.php +18 -0
  140. app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.0.0.70-1.0.0.84.php +39 -0
  141. app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.0.0.84-1.0.0.88.php +16 -0
  142. app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.0.0.88-1.2.0.0.php +39 -0
  143. app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.2.0.0-1.2.0.1.php +13 -0
  144. app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.2.0.1-1.2.1.0.php +49 -0
  145. app/code/community/Springbot/DataServices/HarvestingManager.php +448 -0
  146. app/code/community/Springbot/Log.php +142 -0
  147. app/code/community/Springbot/Services/Abstract.php +125 -0
  148. app/code/community/Springbot/Services/Cmd/Forecast.php +32 -0
  149. app/code/community/Springbot/Services/Cmd/Halt.php +14 -0
  150. app/code/community/Springbot/Services/Cmd/Harvest.php +316 -0
  151. app/code/community/Springbot/Services/Cmd/Healthcheck.php +19 -0
  152. app/code/community/Springbot/Services/Cmd/Parse.php +9 -0
  153. app/code/community/Springbot/Services/Cmd/Update.php +49 -0
  154. app/code/community/Springbot/Services/Harvest.php +36 -0
  155. app/code/community/Springbot/Services/Harvest/AttributeSets.php +29 -0
  156. app/code/community/Springbot/Services/Harvest/Carts.php +35 -0
  157. app/code/community/Springbot/Services/Harvest/Categories.php +47 -0
  158. app/code/community/Springbot/Services/Harvest/CustomerAttributeSets.php +24 -0
  159. app/code/community/Springbot/Services/Harvest/Customers.php +34 -0
  160. app/code/community/Springbot/Services/Harvest/Guests.php +46 -0
  161. app/code/community/Springbot/Services/Harvest/Products.php +36 -0
  162. app/code/community/Springbot/Services/Harvest/Purchases.php +35 -0
  163. app/code/community/Springbot/Services/Harvest/Subscribers.php +34 -0
  164. app/code/community/Springbot/Services/Log/Installer.php +11 -0
  165. app/code/community/Springbot/Services/Log/Purchase.php +51 -0
  166. app/code/community/Springbot/Services/Post.php +10 -0
  167. app/code/community/Springbot/Services/Post/Attribute.php +55 -0
  168. app/code/community/Springbot/Services/Post/AttributeSet.php +25 -0
  169. app/code/community/Springbot/Services/Post/Cart.php +22 -0
  170. app/code/community/Springbot/Services/Post/Category.php +16 -0
  171. app/code/community/Springbot/Services/Post/Customer.php +11 -0
  172. app/code/community/Springbot/Services/Post/Json.php +17 -0
  173. app/code/community/Springbot/Services/Post/Jsonstring.php +12 -0
  174. app/code/community/Springbot/Services/Post/Product.php +31 -0
  175. app/code/community/Springbot/Services/Post/Purchase.php +59 -0
  176. app/code/community/Springbot/Services/Post/Subscriber.php +11 -0
  177. app/code/community/Springbot/Services/Priority.php +11 -0
  178. app/code/community/Springbot/Services/Registry.php +83 -0
  179. app/code/community/Springbot/Services/Store/Finalize.php +28 -0
  180. app/code/community/Springbot/Services/Store/Register.php +109 -0
  181. app/code/community/Springbot/Services/Update/Abstract.php +49 -0
  182. app/code/community/Springbot/Services/Update/Connect.php +119 -0
  183. app/code/community/Springbot/Services/Update/Downloader.php +63 -0
  184. app/code/community/Springbot/Services/Update/Installer.php +57 -0
  185. app/code/community/Springbot/Services/Update/Package.php +148 -0
  186. app/code/community/Springbot/Services/Work/Cleanup.php +28 -0
  187. app/code/community/Springbot/Services/Work/Manager.php +113 -0
  188. app/code/community/Springbot/Services/Work/Report.php +26 -0
  189. app/code/community/Springbot/Services/Work/Restart.php +19 -0
  190. app/code/community/Springbot/Services/Work/Runner.php +32 -0
  191. app/code/community/Springbot/Services/Work/Stop.php +47 -0
  192. app/code/community/Springbot/Shadow/Helper/Listeners.php +21 -0
  193. app/code/community/Springbot/Shadow/Model/Listeners/Observer.php +197 -0
  194. app/code/community/Springbot/Shadow/Model/Timer.php +77 -0
  195. app/code/community/Springbot/Shadow/controllers/IndexController.php +38 -0
  196. app/code/community/Springbot/Shadow/etc/config.xml +74 -0
  197. app/code/community/Springbot/Util/Caller.php +56 -0
  198. app/code/community/Springbot/Util/Categories.php +54 -0
  199. app/code/community/Springbot/Util/Log/Rollover.php +120 -0
  200. app/code/community/Springbot/Util/Logger.php +73 -0
  201. app/code/community/Springbot/Util/Partition.php +23 -0
  202. app/design/adminhtml/base/default/layout/bmbleb.xml +81 -0
  203. app/design/adminhtml/default/default/layout/bmbleb.xml +131 -0
  204. app/design/adminhtml/default/default/template/bmbleb/alert/finished.phtml +9 -0
  205. app/design/adminhtml/default/default/template/bmbleb/alert/index.phtml +32 -0
  206. app/design/adminhtml/default/default/template/bmbleb/alert/next/form.phtml +65 -0
  207. app/design/adminhtml/default/default/template/bmbleb/auth.phtml +5 -0
  208. app/design/adminhtml/default/default/template/bmbleb/dashboard_loggedout.phtml +34 -0
  209. app/design/adminhtml/default/default/template/bmbleb/help/index.phtml +44 -0
  210. app/design/adminhtml/default/default/template/bmbleb/index.phtml +17 -0
  211. app/design/adminhtml/default/default/template/bmbleb/index/messages.phtml +11 -0
  212. app/design/adminhtml/default/default/template/bmbleb/index/terms.phtml +9 -0
  213. app/design/adminhtml/default/default/template/bmbleb/jobs.phtml +16 -0
  214. app/design/adminhtml/default/default/template/bmbleb/jobs/status.phtml +34 -0
  215. app/design/adminhtml/default/default/template/bmbleb/login.phtml +15 -0
  216. app/design/adminhtml/default/default/template/bmbleb/logout.phtml +36 -0
  217. app/design/adminhtml/default/default/template/bmbleb/logs/index.phtml +40 -0
  218. app/design/adminhtml/default/default/template/bmbleb/notifications.phtml +7 -0
  219. app/design/adminhtml/default/default/template/bmbleb/problems/index.phtml +43 -0
  220. app/design/adminhtml/default/default/template/bmbleb/status.phtml +12 -0
  221. app/design/adminhtml/default/default/template/bmbleb/tabs.phtml +13 -0
  222. app/etc/modules/Springbot.xml +48 -0
  223. package.xml +57 -0
  224. shell/springbot.php +160 -0
  225. skin/adminhtml/default/default/bmbleb/bmbleb.css +339 -0
  226. skin/adminhtml/default/default/bmbleb/images/arrows_up-down-large.png +0 -0
  227. skin/adminhtml/default/default/bmbleb/images/bmb-ctl.png +0 -0
  228. skin/adminhtml/default/default/bmbleb/images/check.png +0 -0
  229. skin/adminhtml/default/default/bmbleb/images/grn-bg.png +0 -0
  230. skin/adminhtml/default/default/bmbleb/images/h3-bg.png +0 -0
  231. skin/adminhtml/default/default/bmbleb/images/icon-alert.png +0 -0
  232. skin/adminhtml/default/default/bmbleb/images/icon-bmbleb.png +0 -0
  233. skin/adminhtml/default/default/bmbleb/images/icon-insights.png +0 -0
  234. skin/adminhtml/default/default/bmbleb/images/icon-status.png +0 -0
  235. skin/adminhtml/default/default/bmbleb/images/left-ico1.png +0 -0
  236. skin/adminhtml/default/default/bmbleb/images/left-ico2.png +0 -0
  237. skin/adminhtml/default/default/bmbleb/images/left-ico3.png +0 -0
  238. skin/adminhtml/default/default/bmbleb/images/left-ico4.png +0 -0
  239. skin/adminhtml/default/default/bmbleb/images/left-ico5.png +0 -0
  240. skin/adminhtml/default/default/bmbleb/images/left-ico6.png +0 -0
  241. skin/adminhtml/default/default/bmbleb/images/left-ico7.png +0 -0
  242. skin/adminhtml/default/default/bmbleb/images/left-ico8.png +0 -0
  243. skin/adminhtml/default/default/bmbleb/images/left-ico_demographics.png +0 -0
  244. skin/adminhtml/default/default/bmbleb/images/login-icn-b.png +0 -0
  245. skin/adminhtml/default/default/bmbleb/images/login-icn.png +0 -0
  246. skin/adminhtml/default/default/bmbleb/images/logo.png +0 -0
  247. skin/adminhtml/default/default/bmbleb/images/orng-bg.png +0 -0
  248. skin/adminhtml/default/default/bmbleb/images/plugin_dashboard_syncing.jpg +0 -0
  249. skin/adminhtml/default/default/bmbleb/images/register.png +0 -0
  250. skin/adminhtml/default/default/bmbleb/images/registration-bg-25.png +0 -0
  251. skin/adminhtml/default/default/bmbleb/images/registration-bg-50.png +0 -0
  252. skin/adminhtml/default/default/bmbleb/images/registration-bg.png +0 -0
  253. skin/adminhtml/default/default/bmbleb/images/spinner.gif +0 -0
  254. skin/adminhtml/default/default/bmbleb/images/springbot-ctl.png +0 -0
  255. skin/adminhtml/default/default/bmbleb/images/submit-btn-bg.png +0 -0
  256. skin/adminhtml/default/default/bmbleb/images/sync_icon.png +0 -0
  257. skin/adminhtml/default/default/bmbleb/images/white-check.png +0 -0
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Auth.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Auth extends Mage_Adminhtml_Block_Template
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setTemplate("bmbleb/auth.phtml");
8
+ }
9
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Login.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 12/10/11
6
+ * Time: 2:02 PM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Springbot_Bmbleb_Block_Adminhtml_Bmbleb_Login extends Mage_Adminhtml_Block_Widget_Form_Container
10
+ {
11
+ public function __construct()
12
+ {
13
+ parent::__construct();
14
+
15
+ $this->_blockGroup = 'bmbleb';
16
+ $this->_controller = 'adminhtml_bmbleb';
17
+ $this->_mode = 'login';
18
+
19
+ $this->_removeButton('back');
20
+ $this->_removeButton('reset');
21
+ $this->_removeButton('save');
22
+ //$this->_updateButton('save', 'label', Mage::helper('bmbleb')->__('Login'));
23
+
24
+ $this->_addButton('login', array(
25
+ 'label' => Mage::helper('bmbleb')->__('Login'),
26
+ 'onclick' => 'login_form.submit();',
27
+ //'class' => 'go' // could use class of 'save' for checkmark
28
+ ), 0, 100, 'footer');
29
+
30
+ }
31
+ /*
32
+ NOTE: the issue with sumitting is being caused because of this html
33
+ editForm = new varienForm('edit_form', '');
34
+ this is also the reason js validation is not working
35
+ */
36
+ public function getHeaderText()
37
+ {
38
+ return Mage::helper('bmbleb')->__('Log In');
39
+ }
40
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Login/Form.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 12/10/11
6
+ * Time: 6:45 AM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Springbot_Bmbleb_Block_Adminhtml_Bmbleb_Login_Form extends Mage_Adminhtml_Block_Widget_Form
10
+ {
11
+ protected function _prepareForm()
12
+ {
13
+ $form = new Varien_Data_Form();
14
+
15
+
16
+ $fieldset = $form->addFieldset('bmbleb_form', array('legend'=>Mage::helper('bmbleb')->__('Already Registered at www.springbot.com?')));
17
+
18
+ $fieldset->addField('email', 'text', array(
19
+ 'label' => Mage::helper('bmbleb')->__('Email'),
20
+ 'class' => 'required-entry',
21
+ 'required' => true,
22
+ 'name' => 'email'
23
+ ));
24
+
25
+ $fieldset->addField('password', 'password', array(
26
+ 'label' => Mage::helper('bmbleb')->__('Password'),
27
+ 'class' => 'required-entry',
28
+ 'required' => true,
29
+ 'name' => 'password',
30
+ ));
31
+
32
+ $fieldset->addField('link', 'note', array(
33
+ 'label' => '',
34
+ 'text' => '<a href="http://www.springbot.com">Need a Springbot Account? Click Here</a>',
35
+ ));
36
+
37
+ // Hide submit button so that we can press enter to submit
38
+ $submitButton = new Varien_Data_Form_Element_Submit(array('style' => 'position: absolute; left: -9999px; width: 1px; height: 1px;'));
39
+ $fieldset->addElement($submitButton);
40
+
41
+ $form->setMethod('post');
42
+ $form->setUseContainer(true);
43
+ $form->setId('login_form');
44
+ $form->setName('login_form');
45
+ $form->setAction($this->getUrl('*/login/login'));
46
+ $this->logPageVisit();
47
+ $this->setForm($form);
48
+ }
49
+ private function logPageVisit()
50
+ {
51
+ $this->configVars =array();
52
+ $springbotStoreID ='';
53
+
54
+ $storeID =Mage::app()->getStore()->getStoreId();
55
+ $storeURL =Mage::getStoreConfig('web/unsecure/base_url',$storeID);
56
+ if (empty($url)) { $url=Mage::getBaseDir(); }
57
+
58
+ $varIndex ='store_id_'.$storeID;
59
+ $this->configVars = Mage::getStoreConfig('springbot/config',$storeID);
60
+ if (isset($this->configVars[$varIndex])) {
61
+ $springbotStoreID =$this->configVars[$varIndex];
62
+ }
63
+ $eventDatetime =date("Y-m-d H:i:s ".'-0500');
64
+ $pri ='1';
65
+ $msg ='User is viewing Sprinnbgot Login Form from '.$storeURL;
66
+ $url = $this->fetchConfigVariable('api_url','https://api.springbot.com/').'api/logs';
67
+ $rawJSON='{"logs" :{"'.$springbotStoreID.'":{"store_id":"'.$springbotStoreID.'",'
68
+ .'"event_time":"' .$eventDatetime.'",'
69
+ .'"store_url":"' .$storeURL.'",'
70
+ .'"remote_addr":"",'
71
+ .'"priority":"' .$pri.'",'
72
+ .'"description":"'.$msg.'"'
73
+ .'}}}';
74
+
75
+ try {
76
+ $client = new Varien_Http_Client($url);
77
+ $client->setRawData($rawJSON);
78
+ $req = $client->request('POST');
79
+ } catch (Exception $e) {
80
+ Mage::log('Remote Springbot service unavailable!');
81
+ Mage::logException($e);
82
+ }
83
+ }
84
+ private function fetchConfigVariable($varName,$default_value='')
85
+ {
86
+ if (isset($this->configVars[$varName])) {
87
+ $rtnValue = $this->configVars[$varName];
88
+ } else {
89
+ $rtnValue = $default_value;
90
+ }
91
+ return $rtnValue;
92
+ }
93
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Register.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 12/09/11
6
+ * Time: 2:56 PM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Springbot_Bmbleb_Block_Adminhtml_Bmbleb_Register extends Mage_Adminhtml_Block_Widget_Form_Container
10
+ {
11
+ public function __construct()
12
+ {
13
+ parent::__construct();
14
+
15
+ $this->_blockGroup = 'bmbleb';
16
+ $this->_controller = 'adminhtml_bmbleb';
17
+ $this->_mode = 'register';
18
+
19
+ $this->_removeButton('back');
20
+ $this->_removeButton('reset');
21
+ $this->_removeButton('save');
22
+
23
+ $this->_addButton('register', array(
24
+ 'label' => Mage::helper('bmbleb')->__('Register Now'),
25
+ 'onclick' => 'register_form.submit();',
26
+ //'class' => 'go' // could use class of 'save' for checkmark
27
+ ), 0, 100, 'footer');
28
+
29
+
30
+ }
31
+
32
+ public function getHeaderText()
33
+ {
34
+ return Mage::helper('bmbleb')->__('Register');
35
+ }
36
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Bmbleb/Register/Form.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 12/10/11
6
+ * Time: 4:31 PM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Springbot_Bmbleb_Block_Adminhtml_Bmbleb_Register_Form extends Mage_Adminhtml_Block_Widget_Form
10
+ {
11
+ protected function _prepareForm()
12
+ {
13
+
14
+ $guid = Mage::getStoreConfig('bmbleb/config/store_guid',Mage::app()->getStore());
15
+ if (empty($guid) || $guid == '0' || $guid == ''){
16
+ $charid = strtoupper(md5(uniqid(rand(), true)));
17
+ $guid = substr($charid, 0, 8).'-'
18
+ .substr($charid, 8, 4).'-'
19
+ .substr($charid,12, 4).'-'
20
+ .substr($charid,16, 4).'-'
21
+ .substr($charid,20,12);
22
+ $config = new Mage_Core_Model_Config();
23
+ $config->saveConfig('bmbleb/config/store_guid', $guid, 'default', 0);
24
+ }
25
+ $form = new Varien_Data_Form();
26
+
27
+ $fieldset = $form->addFieldset('bmbleb_form', array('legend'=>Mage::helper('bmbleb')->__('Register Now for a Springbot Account')));
28
+
29
+ $fieldset->addField('uname', 'text', array(
30
+ 'label' => Mage::helper('bmbleb')->__('Your Name'),
31
+ 'class' => 'required-entry',
32
+ 'required' => true,
33
+ 'name' => 'uname',
34
+ ));
35
+ $fieldset->addField('email', 'text', array(
36
+ 'label' => Mage::helper('bmbleb')->__('Email'),
37
+ 'class' => 'required-entry',
38
+ 'required' => true,
39
+ 'name' => 'email',
40
+ ));
41
+
42
+ $fieldset->addField('password', 'password', array(
43
+ 'label' => Mage::helper('bmbleb')->__('Password'),
44
+ 'class' => 'required-entry',
45
+ 'required' => true,
46
+ 'name' => 'password',
47
+ ));
48
+
49
+ $fieldset->addField('passwordverify', 'password', array(
50
+ 'label' => Mage::helper('bmbleb')->__('Confirm Password'),
51
+ 'class' => 'required-entry',
52
+ 'required' => true,
53
+ 'name' => 'passwordverify',
54
+ ));
55
+
56
+ $fieldset->addField('storeguid', 'hidden', array(
57
+ 'value' => $guid,
58
+ 'name' => 'storeguid'
59
+ ));
60
+
61
+ /*
62
+ $form->setMethod('post');
63
+ $form->setUseContainer(true);
64
+ $form->setId('register_form');
65
+ $form->setName('register_form');
66
+ $form->setAction($this->getUrl('*/register/register'));
67
+
68
+ $this->setForm($form);
69
+ */
70
+
71
+ }
72
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Connected.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Connected extends Mage_Adminhtml_Block_Template
3
+ {
4
+ /**
5
+ * Block constructor
6
+ */
7
+ public function __construct()
8
+ {
9
+ parent::__construct();
10
+ $this->setTemplate("bmbleb/status.phtml");
11
+ }
12
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Help.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Help extends Mage_Adminhtml_Block_Template
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setTemplate("bmbleb/help/index.phtml");
8
+ }
9
+
10
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Index.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Index extends Mage_Adminhtml_Block_Template
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setTemplate("bmbleb/index.phtml");
8
+ }
9
+
10
+ public function didSyncThisSession(){
11
+
12
+ return false;
13
+
14
+ }
15
+
16
+ public function autoLauchSync(){
17
+
18
+ return false;
19
+ }
20
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Index/Messages.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Index_Messages extends Mage_Adminhtml_Block_Template
3
+ {
4
+
5
+ /**
6
+ * Block constructor
7
+ */
8
+ public function __construct()
9
+ {
10
+ parent::__construct();
11
+ $this->setTemplate("bmbleb/index/messages.phtml");
12
+ }
13
+
14
+
15
+
16
+
17
+
18
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Index/Terms.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Index_Terms extends Mage_Adminhtml_Block_Template
3
+ {
4
+
5
+ /**
6
+ * Block constructor
7
+ */
8
+ public function __construct()
9
+ {
10
+ parent::__construct();
11
+ $this->setTemplate("bmbleb/index/terms.phtml");
12
+ }
13
+
14
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Jobs.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Jobs extends Mage_Adminhtml_Block_Widget_Grid_Container
3
+ {
4
+ public function __construct()
5
+ {
6
+ $this->_controller = 'adminhtml_jobs';
7
+ $this->_blockGroup = 'bmbleb';
8
+ $this->_headerText = $this->__('Jobs');
9
+
10
+ parent::__construct();
11
+
12
+ $this->_removeButton('add');
13
+ }
14
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Jobs/Grid.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Bmbleb_Block_Adminhtml_Jobs_Grid extends Mage_Adminhtml_Block_Widget_Grid
4
+ {
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+ $this->setId('id');
9
+ $this->setDefaultDir('desc');
10
+ $this->setUseAjax(true);
11
+ $this->setSaveParametersInSession(true);
12
+ }
13
+
14
+ protected function _prepareCollection()
15
+ {
16
+ $collection = Mage::getResourceModel('combine/cron_queue_collection');
17
+ $this->setCollection($collection);
18
+
19
+ return parent::_prepareCollection();
20
+ }
21
+
22
+ protected function _prepareColumns() {
23
+ $this->addColumn('id', array(
24
+ 'header' => $this->__('Id'),
25
+ 'width' => '80',
26
+ 'align' => 'center',
27
+ 'index' => 'id'
28
+ ));
29
+
30
+ $this->addColumn('method', array(
31
+ 'header' => $this->__('Method'),
32
+ 'index' => 'method'
33
+ ));
34
+
35
+ $this->addColumn('args', array(
36
+ 'header' => $this->__('Arguments'),
37
+ 'index' => 'args'
38
+ ));
39
+
40
+ $this->addColumn('priority', array(
41
+ 'header' => $this->__('Priority'),
42
+ 'width' => '80',
43
+ 'index' => 'priority'
44
+ ));
45
+
46
+ $this->addColumn('attempts', array(
47
+ 'header' => $this->__('Attempts'),
48
+ 'width' => '80',
49
+ 'index' => 'attempts'
50
+ ));
51
+
52
+ $this->addColumn('error', array(
53
+ 'header' => $this->__('Last Error'),
54
+ 'width' => '80',
55
+ 'index' => 'error'
56
+ ));
57
+
58
+ $this->addColumn('locked_at', array(
59
+ 'header' => $this->__('locked_at'),
60
+ 'index' => 'locked_at',
61
+ 'type' => 'datetime'
62
+ ));
63
+
64
+ $this->addColumn('created_at', array(
65
+ 'header' => $this->__('locked_at'),
66
+ 'index' => 'created_at',
67
+ 'type' => 'datetime'
68
+ ));
69
+
70
+ return parent::_prepareColumns();
71
+ }
72
+
73
+ protected function _prepareMassaction()
74
+ {
75
+ $this->setMassactionIdField('id');
76
+ $this->getMassactionBlock()->setFormFieldName('job_ids');
77
+ $this->getMassactionBlock()->setUseSelectAll(false);
78
+
79
+ $this->getMassactionBlock()->addItem('run_jobs', array(
80
+ 'label'=> $this->__('Run selected jobs'),
81
+ 'url' => $this->getUrl('*/*/run'),
82
+ ));
83
+
84
+ $this->getMassactionBlock()->addItem('deleted_jobs', array(
85
+ 'label'=> $this->__('Deleted selected jobs'),
86
+ 'url' => $this->getUrl('*/*/delete'),
87
+ ));
88
+
89
+ return $this;
90
+ }
91
+
92
+ public function getGridUrl()
93
+ {
94
+ return $this->getUrl('*/*/grid', array('_current'=>true));
95
+ }
96
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Jobs/Status.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Bmbleb_Block_Adminhtml_Jobs_Status extends Mage_Adminhtml_Block_Template
4
+ {
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+ $this->setTemplate("bmbleb/jobs/status.phtml");
9
+ }
10
+
11
+ public function getManager()
12
+ {
13
+ return Mage::getModel('combine/cron_manager_status');
14
+ }
15
+
16
+ public function getAjaxToggleUrl()
17
+ {
18
+ return Mage::helper('adminhtml')->getUrl('*/*/toggleWorkerStatus');
19
+ }
20
+
21
+ public function renderAsJson()
22
+ {
23
+ return json_encode(array(
24
+ 'button_text' => $this->getButtonLabelText(),
25
+ 'label_text' => $this->getLabelText(),
26
+ ));
27
+ }
28
+
29
+ public function getLabelText()
30
+ {
31
+ if($this->getManager()->isActive()) {
32
+ return $this->_getEnabledMessage();
33
+ }
34
+ else if($this->getManager()->isBlocked()) {
35
+ return $this->_getBlockedMessage();
36
+ }
37
+ else {
38
+ return $this->_getDisabledMessage();
39
+ }
40
+ }
41
+
42
+ protected function _getEnabledMessage()
43
+ {
44
+ $mgr = $this->getManager();
45
+ return 'Manager is <span class="status-alert-' . $mgr->getStatus() . '">' .
46
+ $mgr->getStatus() . '</span>, and has been running for ' . $mgr->getRuntime() . ' seconds';
47
+ }
48
+
49
+ protected function _getBlockedMessage()
50
+ {
51
+ return 'The Manager is <span class="status-alert-' .
52
+ Springbot_Combine_Model_Cron_Manager_Status::INACTIVE . '">DISABLED</span>! ' .
53
+ 'There may be available jobs left in the queue but will not be processed until the work manger is restarted. ' .
54
+ 'This is likely because a stop work command was issued. ';
55
+ }
56
+
57
+ protected function _getDisabledMessage()
58
+ {
59
+ return 'The Manager is not running. ' .
60
+ 'This is most likely because there are no available jobs left to run. ' .
61
+ 'We will continue to queue jobs for syncing as they happen.';
62
+ }
63
+
64
+ public function getButtonLabelText()
65
+ {
66
+ $manager = $this->getManager();
67
+
68
+ if($manager->isActive()) {
69
+ $text = 'Deactivate Manager';
70
+ } else {
71
+ $text = 'Start Manager';
72
+ }
73
+ return $this->__($text);
74
+ }
75
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Login.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Login extends Mage_Adminhtml_Block_Template
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setTemplate("bmbleb/login.phtml");
8
+ }
9
+
10
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Logout.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Logout extends Mage_Adminhtml_Block_Template
3
+ {
4
+
5
+ /**
6
+ * Block constructor
7
+ */
8
+ public function __construct()
9
+ {
10
+ //parent::__construct();
11
+ //$this->setTemplate("bmbleb/login.phtml");
12
+ }
13
+
14
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Logs.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Logs extends Mage_Adminhtml_Block_Template
3
+ {
4
+ /**
5
+ * __construct
6
+ */
7
+ public function __construct()
8
+ {
9
+ $this->setTemplate("bmbleb/logs/index.phtml");
10
+ $this->_blockGroup = 'bmbleb';
11
+ parent::__construct();
12
+ }
13
+
14
+ public function getLogContent($logName)
15
+ {
16
+ return htmlspecialchars(Mage::helper('combine')->getLogContents($logName));
17
+ }
18
+
19
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Notifications.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Bmbleb_Block_Adminhtml_Notifications extends Mage_Adminhtml_Block_Template
4
+ {
5
+
6
+ /**
7
+ * Uses PluginStatus helper to determine if major problem needs to be displayed globally
8
+ */
9
+ public function getMessage()
10
+ {
11
+ if (Mage::getStoreConfig('springbot/config/show_notifications') == 1) {
12
+ if ($problems = Mage::helper('bmbleb/PluginStatus')->getGlobalPluginProblems()) {
13
+ $message = 'Springbot has encountered a small issue. ' .
14
+ '<a href="' . $this->getUrl('bmbleb/adminhtml_problems/index') . '">Click here to get more details</a>. ' .
15
+ 'You can turn off Springbot notifications in ' .
16
+ '<a href="' . $this->getUrl('adminhtml/system_config/edit/section/springbot') . '">Springbot configuration.</a>'
17
+ ;
18
+
19
+ return array('message' => $message, 'type' => 'error');
20
+ }
21
+ else if (Mage::helper('bmbleb/PluginStatus')->needsToLogin()) {
22
+ $message = 'Springbot has been installed successfully. ' .
23
+ '<a href="' . $this->getUrl('bmbleb/adminhtml_index/status') . '">Click here to login</a>. ' .
24
+ 'You can turn off Springbot notifications in ' .
25
+ '<a href="' . $this->getUrl('adminhtml/system_config/edit/section/springbot') . '">Springbot configuration.</a>'
26
+ ;
27
+ return array('message' => $message, 'type' => 'success');
28
+ }
29
+ }
30
+ return false;
31
+ }
32
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Problems.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Problems extends Mage_Adminhtml_Block_Template
3
+ {
4
+ public function __construct()
5
+ {
6
+ parent::__construct();
7
+ $this->setTemplate("bmbleb/problems/index.phtml");
8
+ }
9
+
10
+ /**
11
+ * Uses PluginStatus helper to determine if major problem needs to be displayed globally
12
+ */
13
+ public function getSolutions()
14
+ {
15
+ return Mage::helper('bmbleb/PluginStatus')->getAllPluginProblems();
16
+ }
17
+
18
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Settings.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ */
4
+ class Springbot_Bmbleb_Block_Adminhtml_Settings extends Mage_Adminhtml_Block_Widget_Form_Container
5
+ {
6
+ public function __construct()
7
+ {
8
+ $this->_blockGroup = 'bmbleb';
9
+ $this->_controller = 'adminhtml_settings';
10
+ $this->_headerText = Mage::helper('bmbleb')->__('Springbot Settings');
11
+
12
+ parent::__construct();
13
+
14
+ $this->_removeButton('reset');
15
+ $this->_removeButton('back');
16
+
17
+ }
18
+
19
+ /**
20
+ * Check permission for passed action
21
+ *
22
+ * @param string $action
23
+ * @return bool
24
+ */
25
+ protected function _isAllowedAction($action)
26
+ {
27
+ return Mage::getSingleton('admin/session')->isAllowed('bmbleb/adminhtml_settings/' . $action);
28
+ }
29
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Settings/Edit/Form.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ */
4
+ class Socketware_Bmbleb_Block_Adminhtml_Settings_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
5
+ {
6
+ protected function _prepareForm()
7
+ {
8
+ $form = new Varien_Data_Form();
9
+
10
+ $fieldset = $form->addFieldset('bmbleb_password', array('legend'=>Mage::helper('bmbleb')->__('Change your springbot account password - leave this blank if you do not want to change it.')));
11
+
12
+ $fieldset->addField('email', 'label', array(
13
+ 'label' => Mage::helper('bmbleb')->__('Account Email Address'),
14
+ 'value' => Mage::getStoreConfig('bmbleb/config/account_email',Mage::app()->getStore())
15
+ ));
16
+
17
+ $fieldset->addField('password', 'password', array(
18
+ 'label' => Mage::helper('bmbleb')->__('Password'),
19
+ //'class' => 'required-entry',
20
+ 'required' => false,
21
+ 'name' => 'password',
22
+ 'note' => Mage::helper('bmbleb')->__('Passwords must be more than 6 characters long and cannot contain spaces or special characters')
23
+ ));
24
+
25
+ $fieldset->addField('passwordverify', 'password', array(
26
+ 'label' => Mage::helper('bmbleb')->__('Confirm Password'),
27
+ //'class' => 'required-entry',
28
+ 'required' => false,
29
+ 'name' => 'passwordverify',
30
+ ));
31
+
32
+
33
+ $form->setMethod('post');
34
+ $form->setUseContainer(true);
35
+ $form->setId('edit_form');
36
+ $form->setName('edit_form');
37
+ $form->setAction($this->getUrl('*/*/post'));
38
+
39
+ $this->setForm($form);
40
+
41
+
42
+ }
43
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Status.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Status extends Mage_Adminhtml_Block_Template
3
+ {
4
+ /**
5
+ * Block constructor
6
+ */
7
+ public function __construct()
8
+ {
9
+ parent::__construct();
10
+ $this->setTemplate("bmbleb/status.phtml");
11
+ }
12
+
13
+ }
app/code/community/Springbot/Bmbleb/Block/Adminhtml/Tabs.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Block_Adminhtml_Tabs extends Mage_Adminhtml_Block_Template
3
+ {
4
+
5
+ /**
6
+ * Block constructor
7
+ */
8
+ public function __construct()
9
+ {
10
+ parent::__construct();
11
+ $this->setTemplate("bmbleb/tabs.phtml");
12
+ }
13
+
14
+ public function isActive($controller = '', $action = '')
15
+ {
16
+ return ($controller == $this->getRequest()->getControllerName() && ($action == '' || $action == $this->getRequest()->getActionName()));
17
+ }
18
+
19
+ // basic check for login status
20
+ public function isLoggedIn()
21
+ {
22
+ return Mage::helper('bmbleb/Account')->authorize('sync');
23
+ }
24
+
25
+ /**
26
+ * Uses PluginStatus helper to determine if major problem needs to be displayed globally
27
+ */
28
+ public function useExtendedAdmin()
29
+ {
30
+ return (Mage::getStoreConfig('springbot/advanced/extended_config') == 1);
31
+ }
32
+
33
+ }
app/code/community/Springbot/Bmbleb/Helper/Account.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * manage account functions for a session
5
+ * authenticate checks if email / password are valid and retrieves account
6
+ * authorize checks to see if account has permission to perform certain functions
7
+ * all other methods are private/protected
8
+ */
9
+ class Springbot_Bmbleb_Helper_Account extends Mage_Core_Helper_Abstract
10
+ {
11
+ public function getResyncOption()
12
+ {
13
+ return Mage::getSingleton('core/session')->getSpringbotResyncOption();
14
+ }
15
+
16
+ public function setResyncOption($value)
17
+ {
18
+ Mage::getSingleton('core/session')->setSpringbotResyncOption($value);
19
+ }
20
+ public function setAccount($account = array())
21
+ {
22
+ Mage::getSingleton('core/session')->setBmblebAccount($account);
23
+ }
24
+
25
+ public function getAccount()
26
+ {
27
+ return Mage::getSingleton('core/session')->getBmblebAccount();
28
+ }
29
+
30
+ public function setIsLoggedIn($value)
31
+ {
32
+ return Mage::getSingleton('core/session')->setBmblebIsloggedIn($value);
33
+ }
34
+
35
+ public function getIsLoggedIn()
36
+ {
37
+ return Mage::getSingleton('core/session')->getBmblebIsloggedIn();
38
+ }
39
+
40
+ public function getIsAuthenticated()
41
+ {
42
+ $email = $this->getSavedEmail();
43
+ $password = $this->getSavedPassword();
44
+
45
+ if($email && $password) {
46
+ $account = Mage::helper('combine')->checkCredentials($email, $password);
47
+ if($account['valid']) {
48
+ return true;
49
+ } else if(isset($account['message'])) {
50
+ Mage::getSingleton('adminhtml/session')->addError($account['message']);
51
+ }
52
+ }
53
+ return false;
54
+ }
55
+
56
+ protected function checkCredentials($email = '', $password = '')
57
+ {
58
+ $account = array('valid' => false);
59
+
60
+ $apiResponse = Mage::helper('bmbleb/ApiCall')->call("login", array('email' => $email,'password' => $password));
61
+ if ($apiResponse->getResponsecode() == "200"){
62
+ $account['valid'] = true;
63
+ $account['email'] = $email;
64
+ } elseif ($apiResponse->getResponsecode() == "400"){
65
+ $account['message'] = Mage::helper('bmbleb')->__('Unable to login with the email and password provided.');
66
+ } elseif ($apiResponse->getResponsecode() == "401"){
67
+ $account['message'] = Mage::helper('bmbleb')->__('Unable to login with the email and password provided.');
68
+ } else {
69
+ $account['message'] = Mage::helper('bmbleb')->__('Unable to check your login.');
70
+ }
71
+ $this->setAccount($account);
72
+
73
+ return $account;
74
+ }
75
+
76
+ public function authenticate($email = '', $password = '')
77
+ {
78
+ $result = false;
79
+
80
+ try {
81
+ $saveCredentials = true;
82
+ if ($email == '' && $password == ''){
83
+ return false;
84
+ }
85
+ $account = $this->checkCredentials($email, $password);
86
+ if ($account['valid']) {
87
+ $result = true;
88
+ if ($saveCredentials){
89
+ $this->setSavedAccountInformation($email, $password);
90
+ }
91
+ } else {
92
+ $this->setSavedAccountInformation('', '');
93
+ }
94
+ }
95
+ catch (Mage_Core_Exception $e) {
96
+ throw $e;
97
+ }
98
+
99
+ return $result;
100
+ }
101
+
102
+ public function authorize($feature = '')
103
+ {
104
+ if ($this->getIsLoggedIn() || $feature == ''){
105
+ return true;
106
+ } else {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ public function logout()
112
+ {
113
+ $this->setAccount(array());
114
+ $this->setIsLoggedIn(false);
115
+ }
116
+
117
+ public function getSavedEmail()
118
+ {
119
+ return Mage::getStoreConfig('springbot/config/account_email', Mage::app()->getStore());
120
+ }
121
+
122
+ public function getSavedPassword()
123
+ {
124
+ return Mage::helper('core')->decrypt(Mage::getStoreConfig('springbot/config/account_password', Mage::app()->getStore()));
125
+ }
126
+
127
+ public function getSavedSecurityToken()
128
+ {
129
+ return Mage::getStoreConfig('springbot/config/security_token', Mage::app()->getStore());
130
+ }
131
+
132
+ public function setSavedAccountInformation($email='', $password='', $secToken='')
133
+ {
134
+ $prevOwner=$this->getSavedEmail();
135
+
136
+ $config = new Mage_Core_Model_Config();
137
+
138
+ $config->saveConfig('springbot/config/account_email', $email, 'default', 0);
139
+ $config->saveConfig('springbot/config/account_password', Mage::helper('core')->encrypt($password), 'default', 0);
140
+ $config->saveConfig('springbot/config/security_token', $secToken, 'default', 0);
141
+
142
+ Mage::getConfig()->cleanCache();
143
+ Mage::getConfig()->reinit();
144
+ }
145
+ }
app/code/community/Springbot/Bmbleb/Helper/Alerts.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 1/6/12
6
+ * Time: 9:39 AM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Socketware_Bmbleb_Helper_Alerts
10
+ {
11
+
12
+
13
+
14
+ }
app/code/community/Springbot/Bmbleb/Helper/ApiCall.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Helper_ApiCall
3
+ {
4
+ private function getKey(){
5
+ return Mage::getStoreConfig('bmbleb/config/encrypt_key',Mage::app()->getStore());
6
+ }
7
+ private function getAPIHost(){
8
+ return Mage::getStoreConfig('bmbleb/config/api_url',Mage::app()->getStore());
9
+ }
10
+
11
+ public function call($APIMethod, $payloadAsArray)
12
+ {
13
+ try {
14
+ $rawData='';
15
+
16
+ $uri = $this->fetchConfigVariable('api_url','https://api.springbot.com/').'api/registration';
17
+ if (isset($payloadAsArray['uname'])) {
18
+ $rawData='{'
19
+ . '"name":"' .$payloadAsArray['uname'].'",'
20
+ . '"email":"' .$payloadAsArray['registeremail'].'",'
21
+ . '"password":"' .$payloadAsArray['registerpassword'].'",'
22
+ . '"password_confirmation":"'.$payloadAsArray['verifypassword'].'"'
23
+ .'}';
24
+ }
25
+ $client = new Varien_Http_Client($uri);
26
+ $client->setRawData($rawData);
27
+ $req = $client->request('POST');
28
+
29
+ $begJson =strpos($req,'{');
30
+ $endJson =strpos($req,'}',$begJson);
31
+ $jBuf =substr($req,$begJson,($endJson-$begJson-1));
32
+ $response =json_decode($jBuf,true);
33
+ if ($response['status']=='error') {
34
+ $errorCode ='999';
35
+ } else {
36
+ $errorCode ='200';
37
+ }
38
+
39
+ try{
40
+ //Populate ApiResponse object
41
+ $apiResponse = Mage::helper('bmbleb/ApiResponse');
42
+ $apiResponse->setFullresponse($req);
43
+ $apiResponse->setResponsecode($errorCode);
44
+ $apiResponse->setResponsecodedescription($response['status']);
45
+ $apiResponse->setMessage($response['message']);
46
+ return $apiResponse;
47
+
48
+ }catch (Exception $e) {
49
+ Mage::log($e);
50
+ }
51
+
52
+ } catch (Exception $e) {
53
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
54
+ Mage::log($e->getMessage());
55
+ }
56
+
57
+ $apiResponse = Mage::helper('bmbleb/ApiResponse');
58
+ $apiResponse->setFullresponse('');
59
+ $apiResponse->setResponsecode('500');
60
+ $apiResponse->setResponsecodedescription('Server Error');
61
+ $apiResponse->setMessage('An unknown error.');
62
+
63
+ return $apiResponse;
64
+ }
65
+
66
+ private function getEncrypt($sStr, $sKey = "")
67
+ {
68
+ if ($sKey==null || $sKey==""){$sKey = self::getKey();}
69
+ $sStr = self::pkcs5_pad($sStr, 16);
70
+
71
+ $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
72
+ $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
73
+
74
+ return base64_encode(
75
+ mcrypt_encrypt(
76
+ MCRYPT_RIJNDAEL_128,
77
+ $sKey,
78
+ $sStr,
79
+ MCRYPT_MODE_ECB,
80
+ $iv
81
+ )
82
+ );
83
+ }
84
+
85
+ private function getDecrypt($sStr, $sKey = "")
86
+ {
87
+ if ($sKey==null || $sKey==""){$sKey = self::getKey();}
88
+
89
+ $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
90
+ $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
91
+
92
+ return mcrypt_decrypt(
93
+ MCRYPT_RIJNDAEL_128,
94
+ $sKey,
95
+ base64_decode($sStr),
96
+ MCRYPT_MODE_ECB,
97
+ $iv
98
+ );
99
+ }
100
+
101
+ private function pkcs5_pad($text, $blocksize)
102
+ {
103
+ $pad = $blocksize - (strlen($text) % $blocksize);
104
+ return $text . str_repeat(chr($pad), $pad);
105
+ }
106
+ private function fetchConfigVariable($varName,$default_value='')
107
+ {
108
+ if (isset($this->configVars[$varName])) {
109
+ $rtnValue = $this->configVars[$varName];
110
+ } else {
111
+ $rtnValue = $default_value;
112
+ }
113
+ return $rtnValue;
114
+ }
115
+
116
+ }
app/code/community/Springbot/Bmbleb/Helper/ApiResponse.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Helper_ApiResponse
3
+ {
4
+ private $responsecode;
5
+ private $responsecodedescription;
6
+ private $message;
7
+ private $fullresponse;
8
+ private $customers;
9
+ private $usageLimits;
10
+
11
+ public function setMessage($message) {
12
+ $this->message = $message;
13
+ }
14
+
15
+ public function getMessage() {
16
+ return $this->message;
17
+ }
18
+
19
+ public function setResponsecode($responsecode) {
20
+ $this->responsecode = $responsecode;
21
+ }
22
+
23
+ public function getResponsecode() {
24
+ return $this->responsecode;
25
+ }
26
+
27
+ public function setResponsecodedescription($responsecodedescription) {
28
+ $this->responsecodedescription = $responsecodedescription;
29
+ }
30
+
31
+ public function getResponsecodedescription() {
32
+ return $this->responsecodedescription;
33
+ }
34
+
35
+ public function setFullresponse($fullresponse) {
36
+ $this->fullresponse = $fullresponse;
37
+ }
38
+
39
+ public function getFullresponse() {
40
+ return $this->fullresponse;
41
+ }
42
+
43
+ public function setCustomers($value) {
44
+ $this->customers = $value;
45
+ }
46
+
47
+ public function getCustomers() {
48
+ return $this->customers;
49
+ }
50
+
51
+ public function setUsageLimits($value) {
52
+ $this->usageLimits = $value;
53
+ }
54
+
55
+ public function getUsageLimits() {
56
+ return $this->usageLimits;
57
+ }
58
+
59
+ }
app/code/community/Springbot/Bmbleb/Helper/BmblebProps.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 12/2/11
6
+ * Time: 9:39 AM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Socketware_Bmbleb_Helper_BmblebProps{
10
+
11
+
12
+
13
+ public static function get($key) {
14
+ return Mage::getModel('bmbleb/bmblebprops')->getValueByKey($key);
15
+ }
16
+
17
+ public static function put($key, $value) {
18
+ return Mage::getModel('bmbleb/bmblebprops')->setValueByKey($key, $value);
19
+ }
20
+
21
+
22
+
23
+
24
+
25
+
26
+ }
app/code/community/Springbot/Bmbleb/Helper/ChangePassword.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 1/6/12
6
+ * Time: 9:39 AM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Socketware_Bmbleb_Helper_ChangePassword
10
+ {
11
+
12
+
13
+ public static function ChangePassword($newpassword){
14
+ try {
15
+
16
+ $payload = array();
17
+ $payload['newpassword'] = $newpassword;
18
+ $payload['storeguid'] = Mage::helper('bmbleb/Sync')->getStoreGuid();
19
+
20
+
21
+ $apiResponse = Mage::helper('bmbleb/ApiCall')->call("changepassword", $payload);
22
+ Mage::log("ChangePassword responsecode=" . $apiResponse->getResponsecode());
23
+ Mage::log("changepassword json: " . $apiResponse->getFullresponse());
24
+
25
+ if ($apiResponse->getResponsecode() == "200"){
26
+ Mage::log("Starting to parse changepassword json");
27
+ //Parse the response
28
+ //Note that JSON.php is an edited version (see bottom of file)
29
+ require_once('JSON.php'); // JSON parser
30
+ require_once('jsonpath-0.8.1.php'); // JSONPath evaluator
31
+ $parser = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
32
+ $o = $parser->decode($apiResponse->getFullresponse());
33
+ return true;
34
+
35
+ } elseif ($apiResponse->getResponsecode() == "400"){
36
+ return $apiResponse->getMessage();
37
+ //Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__('We\'re sorry, there\'s been an error: ' . $apiResponse->getMessage()));
38
+ } else {
39
+ return Mage::helper('bmbleb')->__('There was an error updating your password');
40
+ //Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__('We\'re sorry, there\'s been an error.'));
41
+ }
42
+ // end try/catch
43
+ }
44
+ catch (Mage_Core_Exception $e) {
45
+ Mage::log("Error changing password: Mage_Core_Exception " . $e->getMessage());
46
+ return $e->getMessage();
47
+ }
48
+ catch (Exception $e) {
49
+ Mage::log("Error changing password: Exception " . $e->getMessage());
50
+ return $e->getMessage();
51
+ }
52
+ // if passes through to here there was an error
53
+ return Mage::helper('bmbleb')->__('Unspecified error');
54
+ }
55
+ }
app/code/community/Springbot/Bmbleb/Helper/Data.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Springbot_Bmbleb_Helper_Data extends Mage_Core_Helper_Abstract
5
+ {
6
+
7
+ /**
8
+ * Helper method to only write debug logging when debug mode is enabled
9
+ * @return string|number
10
+ */
11
+ function debugLog($message = ''){
12
+ if (Mage::getStoreConfig('bmbleb/config/debug_enabled',Mage::app()->getStore())){
13
+ Mage::log($message);
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Helper method to wrap requests for current time so we can adjust for different magento versions
19
+ * @return string|number
20
+ */
21
+ function getTime(){
22
+ if (function_exists('now')){
23
+ return now(); // test just incase
24
+ } else if (method_exists('Varien_Date','now')){
25
+ return Varien_Date::now();
26
+ }
27
+ // fallback
28
+ return time();
29
+ }
30
+
31
+ function getDashboardInsightsStats(){
32
+ return $this->getSalesByAttributeCode('bmbleb_gender', 120);
33
+ }
34
+
35
+ function getSalesByAttributeCode($attributeCode = 'bmbmeb_age', $numberDaysInRange = 120){
36
+ /*
37
+ * Sales by Age Last 60 Days. Pie chart. Use the age groups that Rapleaf gives us (whatever's in the data already.) Calculate the total sales by customers of each age group over the last 60 days and put that value into a pie chart. I guess you'll have to sum them all and determine a percentage for each so that they total 100%.
38
+ */
39
+ $stats = array();
40
+ // load the table data with a custom query
41
+ $resource = Mage::getSingleton('core/resource');
42
+ $readConnection = $resource->getConnection('core_read');
43
+
44
+ // setup date range
45
+ $now = strtotime(Mage::helper('bmbleb')->getTime());
46
+ $format = 'Y-m-d H:i:s';
47
+ $currentEndDate = date($format, $now);
48
+ $previousStartDate = date($format, strtotime("- ".($numberDaysInRange)." days", $now));
49
+
50
+ $attribIdSelect = "SELECT a.attribute_id from " . $resource->getTableName('eav_attribute') . " a WHERE a.attribute_code = '" . $attributeCode . "'";
51
+ $query = "SELECT v.attribute_id, v.value, COUNT(o.entity_id) AS orders_count, SUM((IFNULL(o.base_grand_total, 0) - IFNULL(o.base_total_canceled, 0)) * IFNULL(o.base_to_global_rate, 0)) AS total_income_amount FROM ".$resource->getTableName('sales_flat_order')." AS o left join " . $resource->getTableName('customer_entity_varchar') . " v on o.customer_id = v.entity_id WHERE (o.state NOT IN ('pending_payment', 'new')) and o.created_at between '".$previousStartDate."' and '".$currentEndDate."' and v.attribute_id in (" . $attribIdSelect . ") group by v.value, v.attribute_id order by v.attribute_id";
52
+ $results = $readConnection->fetchAll($query);
53
+ foreach ($results as $row){
54
+ $attribValue = $row['value'];
55
+ if (!empty($attribValue) && !empty($attribValue)){
56
+ $stat = new Varien_Object();
57
+ $stat->setData(array('label' => $attribValue, 'value' => $row['total_income_amount']));
58
+ $stats[] = $stat;
59
+ }
60
+ }
61
+
62
+ return $stats;
63
+ }
64
+
65
+ function getStatsSocialNetworkLinks(){
66
+ $stats = array();
67
+
68
+ $collection = Mage::getResourceModel('customer/customer_collection');
69
+ $totalCustomers = $collection->count();
70
+
71
+ // NOTE: the OR was hard to track down. The key was the 'left' join - needed to pass through a null for the second parameter
72
+ $collection = Mage::getResourceModel('customer/customer_collection')
73
+ ->addAttributeToFilter(
74
+ array(
75
+ array('attribute' => 'bmbleb_twitter', 'notnull'=>true),
76
+ array('attribute' => 'bmbleb_facebook', 'notnull'=>true),
77
+ array('attribute' => 'bmbleb_linkedin', 'notnull'=>true)
78
+ ),
79
+ NULL,
80
+ 'left'
81
+ );
82
+ $hasOneOrMore = $collection->count();
83
+
84
+ $collection = Mage::getResourceModel('customer/customer_collection')
85
+ ->addAttributeToFilter('bmbleb_twitter', array('notnull'=>true))
86
+ ->addAttributeToFilter('bmbleb_facebook', array('notnull'=>true))
87
+ ->addAttributeToFilter('bmbleb_linkedin', array('notnull'=>true));
88
+ $hasThree = $collection->count();
89
+
90
+ $hasNone = $totalCustomers - $hasOneOrMore; // no links = total - one or more
91
+ $hasOneOrTwo = $hasOneOrMore - $hasThree; // one or two = one or more - has three
92
+
93
+ $stat = new Varien_Object();
94
+ $stat->setData(array('label' => 'No Links', 'value' => $hasNone));
95
+ $stats[] = $stat;
96
+
97
+ $stat = new Varien_Object();
98
+ $stat->setData(array('label' => '1 or 2 Links', 'value' => $hasOneOrTwo));
99
+ $stats[] = $stat;
100
+
101
+ $stat = new Varien_Object();
102
+ $stat->setData(array('label' => '3 Links', 'value' => $hasThree, 'selected' => true));
103
+ $stats[] = $stat;
104
+
105
+ return $stats;
106
+ }
107
+
108
+ function getInsightsStats(){
109
+ $collection = Mage::getResourceModel('customer/customer_collection');
110
+ $totalCustomers = $collection->count();
111
+
112
+ $collection = Mage::getResourceModel('customer/customer_collection')
113
+ ->addAttributeToFilter('bmbleb_facebook', array('notnull'=>true));
114
+ $totalFacebook = $collection->count();
115
+
116
+ $collection = Mage::getResourceModel('customer/customer_collection')
117
+ ->addAttributeToFilter('bmbleb_twitter', array('notnull'=>true));
118
+ $totalTwitter = $collection->count();
119
+
120
+ $collection = Mage::getResourceModel('customer/customer_collection')
121
+ ->addAttributeToFilter('bmbleb_linkedin', array('notnull'=>true));
122
+ $totalLinkedIn = $collection->count();
123
+
124
+ $stats = new Varien_Object();
125
+ $stats->setTotal($totalCustomers);
126
+ $stats->setFacebook($totalFacebook / $totalCustomers * 100);
127
+ $stats->setTwitter($totalTwitter / $totalCustomers * 100);
128
+ $stats->setLinkedIn($totalLinkedIn / $totalCustomers * 100);
129
+
130
+ return $stats;
131
+ }
132
+
133
+
134
+
135
+ }
app/code/community/Springbot/Bmbleb/Helper/ExternalLogging.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Helper_ExternalLogging
3
+ {
4
+ public function visibility($msg,$storeID='',$storeURL='',$pri='5')
5
+ {
6
+ Mage::log('Springbot: '.$msg);
7
+ $uri = $this->fetchConfigVariable('api_url','https://api.springbot.com/').'api/logs';
8
+ $filterChr =array('"','{','}'.'[',']',':');
9
+
10
+ try {
11
+ $rawJSON='{"logs" :{"'.$storeID.'":{"store_id":"'.$storeID.'",'
12
+ .'"event_time":"' .date("Y-m-d H:i:s ".'-0500').'",'
13
+ .'"store_url":"' .$storeURL.'",'
14
+ .'"priority":"' .$pri.'",'
15
+ .'"description":"'.str_replace($filterChr,' ',$msg).'"'
16
+ .'}}}';
17
+ $client = new Varien_Http_Client($uri);
18
+ $client->setRawData($rawJSON);
19
+ $response = json_decode($client->request('POST'),true);
20
+ if ($response['status']=='error') {
21
+ Mage::log('Springbot: '
22
+ .'Post to->'.$url.' '
23
+ .'JSON Buffer->'.$rawJSON.' '
24
+ .'Response->'.implode(' ',$response));
25
+ }
26
+ } catch (Exception $e) {
27
+ Mage::log('Springbot: '.$e->getMessage());
28
+ }
29
+ return;
30
+ }
31
+
32
+ public function log($toSend, $storeID, $priority = '5')
33
+ {
34
+ $uri = $this->fetchConfigVariable('api_url','https://api.springbot.com/').'api/logs';
35
+
36
+ try {
37
+ $client = new Varien_Http_Client($uri);
38
+ $client->setRawData($this->encodePayload($toSend, $storeID));
39
+ $response = json_decode($client->request('POST'),true);
40
+
41
+ if ($response['status']=='error') {
42
+ Mage::log('Springbot: '
43
+ .'Post to->'.$url.' '
44
+ .'JSON Buffer->'.$rawJSON.' '
45
+ .'Response->'.implode(' ',$response));
46
+ }
47
+ } catch (Exception $e) {
48
+ Mage::log('Springbot: '.$e->getMessage());
49
+ }
50
+ return;
51
+ }
52
+
53
+ public function encodePayload($array, $storeId, $priority = '5', $type = 'logs')
54
+ {
55
+ $array['event_time'] = date("Y-m-d H:i:s ".'-0500');
56
+ $array['priority'] = $priority;
57
+ $obj = new stdClass;
58
+ $str = new stdClass;
59
+ $str->$storeId = $array;
60
+ $obj->$type = $str;
61
+ return json_encode($obj);
62
+ }
63
+
64
+ private function fetchConfigVariable($varName,$defaultValue='')
65
+ {
66
+ $rtnValue = Mage::getStoreConfig('springbot/config/' . $varName);
67
+ return !empty($rtnValue) ? $rtnValue : $defaultValue;
68
+ }
69
+ }
app/code/community/Springbot/Bmbleb/Helper/GetTermsOfService.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by JetBrains PhpStorm.
4
+ * User: joereger
5
+ * Date: 1/6/12
6
+ * Time: 9:39 AM
7
+ * To change this template use File | Settings | File Templates.
8
+ */
9
+ class Socketware_Bmbleb_Helper_GetTermsOfService
10
+ {
11
+
12
+
13
+
14
+ public static function getTermsOfService(){
15
+ $tos = '';
16
+ try {
17
+
18
+ $payload = array();
19
+ $payload['storeguid'] = Mage::helper('bmbleb/Sync')->getStoreGuid();
20
+
21
+
22
+ $apiResponse = Mage::helper('bmbleb/ApiCall')->call("gettermsofservice", $payload);
23
+
24
+ if ($apiResponse->getResponsecode() == "200"){
25
+ //Parse the response
26
+ //Note that JSON.php is an edited version (see bottom of file)
27
+ require_once('JSON.php'); // JSON parser
28
+ require_once('jsonpath-0.8.1.php'); // JSONPath evaluator
29
+ $parser = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
30
+ $o = $parser->decode($apiResponse->getFullresponse());
31
+ $tos = $o['termsofservice'];
32
+ } elseif ($apiResponse->getResponsecode() == "400"){
33
+ // return the error message instead of using flash system
34
+ $tos = Mage::helper('bmbleb')->__('We\'re sorry, there\'s been an error loading the terms of service: ' . $apiResponse->getMessage());
35
+ } else {
36
+ // return the error message instead of using flash system
37
+ $tos = Mage::helper('bmbleb')->__('We\'re sorry, there\'s been an error loading the terms of service.');
38
+ }
39
+ // end try/catch
40
+ }
41
+ catch (Mage_Core_Exception $e) {
42
+ Mage::log("Error loading alerts: Mage_Core_Exception " . $e->getMessage());
43
+ // return the error message instead of using flash system
44
+ $tos = Mage::helper('bmbleb')->__('There\'s been an error loading the terms of service.');
45
+ }
46
+ catch (Exception $e) {
47
+ Mage::log("Error loading alerts: Exception " . $e->getMessage());
48
+ // return the error message instead of using flash system
49
+ $tos = Mage::helper('bmbleb')->__('There was an error loading the terms of service.');
50
+ }
51
+ return $tos;
52
+
53
+ }
54
+ }
app/code/community/Springbot/Bmbleb/Helper/PluginStatus.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Bmbleb_Helper_PluginStatus extends Mage_Core_Helper_Abstract
4
+ {
5
+ const REPORTED_PROBLEMS_HASH_CONFIG = 'springbot/config/reported_problems_hash';
6
+ const REPORT_PROBLEMS_INTERVAL_SECONDS = 604800; // Seven days in seconds
7
+ const TOO_MANY_HOURS = 3; // Minimum number of hours since harvest to display warning
8
+ const STORE_TIMESTAMP_GLOB = 'sb-cache-healthcheck-*.dat'; // Globbing string to find all
9
+ const STORE_GUID_CONFIG_PREFIX = 'store_guid_'; // Prefix for the store guid config name
10
+ const STORE_ID_CONFIG_PREFIX = 'store_id_'; // Prefix for the store id config name
11
+
12
+ /**
13
+ * Run all checks for fatal problems
14
+ *
15
+ * Returns any issues that would prevent a harvest that can be checked on each plugin page load, once a fatal
16
+ * plugin problem is detected the user is redirected to the problems page where they are presented with a more
17
+ * detailed list of problems and instructed to either login again or contact support.
18
+ */
19
+ public function getFatalPluginProblems()
20
+ {
21
+ return $this->_getPluginProblems(true, false, false);
22
+ }
23
+
24
+ /**
25
+ * Returns a list of potential problems that should be brought to the user's attention for the magento global
26
+ * notifications.
27
+ */
28
+ public function getGlobalPluginProblems()
29
+ {
30
+ return $this->_getPluginProblems(true, true, false);
31
+ }
32
+
33
+ /**
34
+ * Returns all problems (used for the problems page) so that they can see a detailed list of problems
35
+ * and possible fixes.
36
+ */
37
+ public function getAllPluginProblems()
38
+ {
39
+ return $this->_getPluginProblems(true, true, true);
40
+ }
41
+
42
+ /**
43
+ * Get a list of all potential plugin problems to display on the problems page
44
+ *
45
+ * Returns a detailed list of all issues (used on the problems page) so that the user may give the support team
46
+ * more information for troubleshooting what the actual issue may be.
47
+ */
48
+ private function _getPluginProblems($fatalProblems, $globalProblems, $possibleProblems)
49
+ {
50
+ $configVars = Mage::getStoreConfig('springbot/config');
51
+ $problems = array();
52
+
53
+
54
+ // Fatal problems are problems that will cause the Springbot section to redirect to the problems page
55
+ if ($fatalProblems) {
56
+
57
+ if ($this->_emailPasswordSet($configVars) && !$this->_harvestInFlight()) {
58
+ if (($missingGuids = $this->_getMissingStoreGuids($configVars))) {
59
+ $problems[] = array(
60
+ 'problem' => 'Missing GUIDs for the following stores: ' . $missingGuids,
61
+ 'solution' => 'This problem can usually be fixed by re-logging into your Springbot account. '
62
+ );
63
+ }
64
+ if ($this->_tokenIsInvalid($configVars)) {
65
+ $problems[] = array(
66
+ 'problem' => 'Security token is invalid',
67
+ 'solution' => 'This problem can usually be fixed by re-logging into your Springbot account. '
68
+ );
69
+ }
70
+ }
71
+
72
+ if (!$this->_logDirIsWritable()) {
73
+ $problems[] = array(
74
+ 'problem' => 'Magento log directory is not writable',
75
+ 'solution' => 'This server configuration problem often occurs when the owner of the directory "var/log" in your '
76
+ . 'Magento root folder is different than the user your webserver runs as. To fix this issue '
77
+ . 'navigate to your Magento directory and run the command "chown &lt;your webserver user&gt; var/log". '
78
+ );
79
+ }
80
+ if (!$this->_tmpDirIsWritable()) {
81
+ $problems[] = array(
82
+ 'problem' => 'Magento tmp directory is not writable',
83
+ 'solution' => 'This server configuration problem often occurs when the owner of the directory "var/tmp" in your '
84
+ . 'Magento root folder is different than the user your webserver runs as. To fix this issue '
85
+ . 'navigate to your Magento directory and run the command "chown &lt;your webserver user&gt; var/tmp". '
86
+ );
87
+ }
88
+ if (!$this->_logDirIsReadable()) {
89
+ $problems[] = array(
90
+ 'problem' => 'Magento log directory is not readable',
91
+ 'solution' => 'This server configuration problem often occurs when the owner of the directory "var/log" in your '
92
+ . 'Magento root folder is different than the user your webserver runs as. To fix this issue '
93
+ . 'navigate to your Magento directory and run the command "chown &lt;your webserver user&gt; var/log". '
94
+ );
95
+ }
96
+ if (!$this->_tmpDirIsReadable()) {
97
+ $problems[] = array(
98
+ 'problem' => 'Magento tmp directory is not readable',
99
+ 'solution' => 'This server configuration problem often occurs when the owner of the directory "var/tmp" in your '
100
+ . 'Magento root folder is different than the user your webserver runs as. To fix this issue '
101
+ . 'navigate to your Magento directory and run the command "chown &lt;your webserver user&gt; var/tmp". '
102
+ );
103
+ }
104
+ if ($secondsSinceHarvest = $this->_tooLongSinceCheckin()) {
105
+ $hoursSinceHarvest = round($secondsSinceHarvest / 60 / 60);
106
+ $problems[] = array(
107
+ 'problem' => 'It\'s been ' . $hoursSinceHarvest . ' hours since the Springbot plugin last checked in',
108
+ 'solution' => 'This problem occurs when the Springbot plugin has gone too long since it last '
109
+ . 'communicated with the Springbot server. This issue can often be resolved by simply '
110
+ . 're-logging in to your Springbot dashboard. '
111
+ );
112
+ }
113
+ if (!$this->_correctPhpPath()) {
114
+ $problems[] = array(
115
+ 'problem' => 'Incorrect PHP executable path',
116
+ 'solution' => 'This usually means that the default PHP path for your server cannot run in CLI mode. '
117
+ . 'To fix this issue locate the full path of the PHP executable that you would use to run cron '
118
+ . 'jobs and paste it into the "PHP Executable" setting in your Springbot configuration. '
119
+ );
120
+ }
121
+
122
+ }
123
+
124
+ // Global problems are problems that will cause a top notification on the Magento dashboard only
125
+ if ($globalProblems) {
126
+
127
+ }
128
+
129
+ // Report any plugin problems to the Springbot API once a week or for the very first time
130
+ $configModel = Mage::getModel('core/config');
131
+ if ($problems) {
132
+ $lastApiReportHash = Mage::getStoreConfig(self::REPORTED_PROBLEMS_HASH_CONFIG, Mage::app()->getStore());
133
+ $currentProblemsHash = md5(serialize($problems));
134
+
135
+ if ($lastApiReportHash != $currentProblemsHash) {
136
+ $configModel->saveConfig(self::REPORTED_PROBLEMS_HASH_CONFIG, $currentProblemsHash, 'default', 0);
137
+ $this->_postProblemsToApi($problems);
138
+ }
139
+ }
140
+
141
+ // Possible problems include issues which might not necessarily cause a problem, but may offer insight into other problems
142
+ if ($possibleProblems) {
143
+ if (!$this->_mediaDirIsWritable()) {
144
+ $problems[] = array(
145
+ 'problem' => 'The media directory is not writable',
146
+ 'solution' => 'The Magento media directory is not writable. While this should not affect the Springbot plugin '
147
+ . 'it may be evidence of further permission and configuration issues. Ideally the owner of the media '
148
+ . 'directory should be the same user that your webserver software runs as.'
149
+ );
150
+ }
151
+ }
152
+
153
+ return $problems;
154
+ }
155
+
156
+
157
+ public function needsToLogin() {
158
+ $configVars = Mage::getStoreConfig('springbot/config');
159
+ if ($this->_emailPasswordSet($configVars)) return false;
160
+ else return true;
161
+ }
162
+
163
+ /**
164
+ * Check to make sure user has logged in to avoid showing a problem notification before they even login
165
+ */
166
+ private function _emailPasswordSet($configVars)
167
+ {
168
+ if (
169
+ isset($configVars['account_email']) &&
170
+ isset($configVars['account_password']) &&
171
+ $configVars['account_email'] &&
172
+ $configVars['account_password']
173
+ ) {
174
+ return true;
175
+ }
176
+ else {
177
+ return false;
178
+ }
179
+ }
180
+
181
+ private function _harvestInFlight()
182
+ {
183
+ return Mage::helper('combine/harvest')->isHarvestRunning();
184
+ }
185
+
186
+ /**
187
+ * Check if token is valid. Ideally we would want to check the actual validity of the token but we avoid that since
188
+ * it would involve phoning home on each admin page load.
189
+ */
190
+ private function _tokenIsInvalid($configVars)
191
+ {
192
+ if ($configVars['security_token']) {
193
+ return false;
194
+ }
195
+ else {
196
+ return true;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Check if a GUID exists for every store
202
+ */
203
+ private function _getMissingStoreGuids($configVars)
204
+ {
205
+ $missingGuids = array();
206
+ foreach (Mage::app()->getStores() as $store) {
207
+ if (isset($configVars[self::STORE_ID_CONFIG_PREFIX . $store->getId()])) {
208
+ $storeId = $configVars[self::STORE_ID_CONFIG_PREFIX . $store->getId()];
209
+ }
210
+ if (isset($configVars[self::STORE_GUID_CONFIG_PREFIX . $store->getId()])) {
211
+ $storeGuid = $configVars[self::STORE_GUID_CONFIG_PREFIX . $store->getId()];
212
+ }
213
+ if (isset($storeId) && isset($storeGuid) && $storeId && !$storeGuid) {
214
+ $missingGuids[] = $store->getId();
215
+ }
216
+ }
217
+ if ($missingGuids) {
218
+ return implode(', ', $missingGuids);
219
+ }
220
+ else {
221
+ return false;
222
+ }
223
+
224
+ }
225
+
226
+ /**
227
+ * Check to see if its been a long time since the last checkin
228
+ */
229
+ private function _tooLongSinceCheckin()
230
+ {
231
+ $fileNameGlob = $this->_getFilenameGlob();
232
+ $currentTime = time();
233
+ $matches = glob($fileNameGlob);
234
+ $secondsSinceLastCheckin = null;
235
+ $mostRecentCheckin = null;
236
+ foreach ($matches as $match) {
237
+ $checkinTimestap = file_get_contents($match);
238
+ $secondsSinceLastCheckin = $currentTime - $checkinTimestap;
239
+ if (!$mostRecentCheckin || ($secondsSinceLastCheckin < $mostRecentCheckin)) {
240
+ $mostRecentCheckin = $secondsSinceLastCheckin;
241
+ }
242
+ }
243
+
244
+ if (($mostRecentCheckin === null) || ($mostRecentCheckin > (self::TOO_MANY_HOURS * 60 * 60))) {
245
+ return $mostRecentCheckin;
246
+ }
247
+ else {
248
+ return false;
249
+ }
250
+ }
251
+
252
+
253
+ /**
254
+ * Check to see if Magento tmp directory is writable
255
+ */
256
+ private function _tmpDirIsWritable()
257
+ {
258
+ return is_writable(Mage::getBaseDir() . '/var/tmp');
259
+ }
260
+
261
+ /**
262
+ * Check to see if Magento log directory is writable
263
+ */
264
+ private function _logDirIsWritable()
265
+ {
266
+ return is_writable(Mage::getBaseDir() . '/var/log');
267
+ }
268
+
269
+ /**
270
+ * Check to see if Magento tmp directory is writable
271
+ */
272
+ private function _tmpDirIsReadable()
273
+ {
274
+ return is_readable(Mage::getBaseDir() . '/var/tmp');
275
+ }
276
+
277
+ /**
278
+ * Check to see if Magento log directory is writable
279
+ */
280
+ private function _logDirIsReadable()
281
+ {
282
+ return is_readable(Mage::getBaseDir() . '/var/log');
283
+ }
284
+
285
+ /**
286
+ * Check to see if Magento log directory is writable
287
+ */
288
+ private function _mediaDirIsWritable()
289
+ {
290
+ return is_writable(Mage::getBaseDir('media'));
291
+ }
292
+
293
+ /**
294
+ * Take array of problems and post it to the Springbot API
295
+ */
296
+ private function _postProblemsToApi($problems)
297
+ {
298
+ try{
299
+ $baseStoreUrl = Mage::getStoreConfig('springbot/config/web/unsecure/base_url');
300
+ $data = array(
301
+ 'store_url' => $baseStoreUrl,
302
+ 'problems' => array(),
303
+ 'springbot_store_ids' => $this->_getSpringbotStoreIds()
304
+ );
305
+ foreach ($problems as $problem) {
306
+ $data['problems'][] = $problem['problem'];
307
+ }
308
+ $dataJson = json_encode($data);
309
+ $apiModel = Mage::getModel('combine/api');
310
+ $apiModel->call('installs', $dataJson, false);
311
+ } catch (Exception $e) {
312
+ // this call completing is not mission critical
313
+ Springbot_Log::error($e);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * There may not be any store IDs yet but return them if there are.
319
+ */
320
+ private function _getSpringbotStoreIds()
321
+ {
322
+ $springbotStoreIds = array();
323
+ foreach (Mage::app()->getStores() as $store) {
324
+ if ($springbotStoreId = Mage::getStoreConfig('springbot/config/store_id_' . $store->getId())) {
325
+ $springbotStoreIds[] = $springbotStoreId;
326
+ }
327
+ }
328
+ return $springbotStoreIds;
329
+ }
330
+
331
+ private function _getFilenameGlob()
332
+ {
333
+ return Mage::getBaseDir('tmp') . DS . self::STORE_TIMESTAMP_GLOB;
334
+ }
335
+
336
+ private function _correctPhpPath()
337
+ {
338
+ $phpPath = Mage::helper('combine/harvest')->getPhpExec();
339
+ ob_start();
340
+ Springbot_boss::spawn("{$phpPath} -r \"echo '<!-- PHP Test -->';\"", $output);
341
+ $result = ob_get_clean();
342
+ return trim($result) == '<!-- PHP Test -->';
343
+ }
344
+
345
+ }
app/code/community/Springbot/Bmbleb/Model/Alert.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Socketware_Bmbleb_Model_Alert extends Mage_Core_Model_Abstract
4
+ {
5
+
6
+ public function _construct()
7
+ {
8
+ parent::_construct();
9
+ $this->_init('bmbleb/alert');
10
+ }
11
+
12
+ /*
13
+ * Returns the next Alert that should be completed using the following logic:
14
+ * exclude all completed ones
15
+ * AND return new ones before skipped ones
16
+ * AND return higher priority ones first
17
+ * AND return the most recently updated ones first
18
+ */
19
+ public function getNextToDo()
20
+ {
21
+ $collection = Mage::getModel('bmbleb/alert')->getCollection()
22
+ ->addFieldToFilter('status', array('neq' => Socketware_Bmbleb_Model_Alert_Status::COMPLETED));
23
+ $collection->addOrder('status', 'asc')
24
+ ->addOrder('priority', 'desc')
25
+ ->addOrder('updated_at', 'desc');
26
+ return $collection->getFirstItem();
27
+ }
28
+
29
+ public function getStatusCounts()
30
+ {
31
+ $collection = Mage::getModel('bmbleb/alert')->getCollection()
32
+ ->removeAllFieldsFromSelect()
33
+ ->removeFieldFromSelect($this->getIdFieldName())
34
+ ->addFieldToSelect('status')
35
+ ->addExpressionFieldToSelect('total', 'COUNT({{status}})', 'status');
36
+ // NOTE: had to add the group by clause separately and to the select itself
37
+ $collection->getSelect()->group('status');
38
+
39
+ $stats = array(
40
+ 'new' => 0,
41
+ 'skipped' => 0,
42
+ 'completed' => 0,
43
+ 'total' => 0
44
+ );
45
+ foreach ($collection as $model){
46
+ switch ($model->getStatus()){
47
+ case Socketware_Bmbleb_Model_Alert_Status::UNOPENED:
48
+ $stats['new'] = $model->getTotal();
49
+ break;
50
+ case Socketware_Bmbleb_Model_Alert_Status::SKIPPED:
51
+ $stats['skipped'] = $model->getTotal();
52
+ break;
53
+ case Socketware_Bmbleb_Model_Alert_Status::COMPLETED:
54
+ $stats['completed'] = $model->getTotal();
55
+ break;
56
+ }
57
+ $stats['total'] += $model->getTotal();
58
+ }
59
+ return $stats;
60
+ }
61
+
62
+
63
+ }
app/code/community/Springbot/Bmbleb/Model/Alert/Priority.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alert type model
4
+ *
5
+ * @category Bmbleb
6
+ * @package Bmbleb_Alert
7
+ * @author Jeremy Dost <jdost@sharpdotinc.com>
8
+ */
9
+ class Socketware_Bmbleb_Model_Alert_Priority extends Mage_Core_Model_Abstract
10
+ {
11
+ const LOW = 2;
12
+ const MED = 3;
13
+ const HI = 4;
14
+
15
+ public function getOptions()
16
+ {
17
+ $options = new Varien_Object(array(
18
+ self::LOW => Mage::helper('bmbleb')->__('Low'),
19
+ self::MED => Mage::helper('bmbleb')->__('Medium'),
20
+ self::HI => Mage::helper('bmbleb')->__('High'),
21
+ ));
22
+ return $options->getData();
23
+ }
24
+
25
+
26
+ }
27
+
app/code/community/Springbot/Bmbleb/Model/Alert/Status.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alert type model
4
+ *
5
+ * @category Bmbleb
6
+ * @package Bmbleb_Alert
7
+ * @author Jeremy Dost <jdost@sharpdotinc.com>
8
+ */
9
+ class Socketware_Bmbleb_Model_Alert_Status extends Mage_Core_Model_Abstract
10
+ {
11
+ const UNOPENED = 0;
12
+ const SKIPPED = 1;
13
+ const COMPLETED = 2;
14
+
15
+ public function getOptions()
16
+ {
17
+ $options = new Varien_Object(array(
18
+ self::UNOPENED => Mage::helper('bmbleb')->__('New'),
19
+ self::SKIPPED => Mage::helper('bmbleb')->__('Skipped'),
20
+ self::COMPLETED => Mage::helper('bmbleb')->__('Completed'),
21
+ ));
22
+ return $options->getData();
23
+ }
24
+
25
+
26
+ }
27
+
app/code/community/Springbot/Bmbleb/Model/Alert/Type.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Alert type model
4
+ *
5
+ * @category Bmbleb
6
+ * @package Bmbleb_Alert
7
+ * @author Jeremy Dost <jdost@sharpdotinc.com>
8
+ */
9
+ class Socketware_Bmbleb_Model_Alert_Type extends Mage_Core_Model_Abstract
10
+ {
11
+ //These are the current possible alerttypeids
12
+ const LENGTHOFRESIDENCERESET = 200;
13
+ const OCCUPATION = 300;
14
+ const HOMEOWNERSTATUSRENTTOOWN = 400;
15
+ const HOMEOWNERSTATUSOWNTORENT = 410;
16
+ const MARITALSTATUSSINGLETOMARRIED = 500;
17
+ const MARITALSTATUSMARRIEDTOSINGLE = 510;
18
+ const CHILDRENNOTOYES = 600;
19
+ const EDUCATIONCOMPLETEDCOLLEGE = 700;
20
+ const EDUCATIONCOMPLETEDHIGHSCHOOL = 710;
21
+ const VEHICLENEW = 800;
22
+ const HIGHNETWORTHNOTOYES = 900;
23
+ const LOANTOVALUERATIOOVER80 = 1000;
24
+ const LOANTOVALUERATIOUNDER80 = 1010;
25
+ const TWITTERNEW = 1100;
26
+ const FACEBOOKNEW = 1200;
27
+ const LINKEDINNEW = 1300;
28
+ const IMAGEURLNEW = 1400;
29
+ const COMPANYCHANGE = 1500;
30
+ const TITLECHANGE = 1600;
31
+ const LOCATIONCHANGE = 1700;
32
+
33
+ public function getOptions()
34
+ {
35
+ $options = new Varien_Object(array(
36
+ self::TWITTERNEW => Mage::helper('bmbleb')->__('New Twitter'),
37
+ self::FACEBOOKNEW => Mage::helper('bmbleb')->__('New Facebook'),
38
+ self::LINKEDINNEW => Mage::helper('bmbleb')->__('New LinkedIn'),
39
+ ));
40
+ return $options->getData();
41
+ }
42
+
43
+
44
+ }
45
+
app/code/community/Springbot/Bmbleb/Model/Bmbleb.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Socketware_Bmbleb_Model_Bmbleb extends Mage_Core_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('bmbleb/bmbleb');
9
+ }
10
+ }
app/code/community/Springbot/Bmbleb/Model/Bmblebprops.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Socketware_Bmbleb_Model_Bmblebprops extends Mage_Core_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('bmbleb/bmblebprops');
9
+ }
10
+
11
+ public function getValueByKey($key) {
12
+ $props = Mage::getModel('bmbleb/bmblebprops')->getCollection();
13
+ foreach($props as $prop){
14
+ if ($prop->getKey() == $key){
15
+ return $prop->getValue();
16
+ }
17
+ }
18
+
19
+ return null;
20
+ }
21
+
22
+ public function setValueByKey($key, $value) {
23
+ //Delete any value for key currently present
24
+ $props = Mage::getModel('bmbleb/bmblebprops')->getCollection();
25
+ foreach($props as $prop){
26
+ if ($prop->getKey() == $key){
27
+ $prop->delete();
28
+ }
29
+ }
30
+ //Insert key/value pair
31
+ $prop = Mage::getModel('bmbleb/bmblebprops');
32
+ $prop->setKey($key);
33
+ $prop->setValue($value);
34
+ return $prop->save();
35
+ }
36
+
37
+
38
+ }
app/code/community/Springbot/Bmbleb/Model/Mysql4/Bmbleb/Collection.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Socketware_Bmbleb_Model_Mysql4_Bmbleb_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->_init('bmbleb/bmbleb');
9
+ }
10
+ }
app/code/community/Springbot/Bmbleb/Model/Status.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Socketware_Bmbleb_Model_Status extends Varien_Object
4
+ {
5
+ const STATUS_ENABLED = 1;
6
+ const STATUS_DISABLED = 2;
7
+
8
+ static public function getOptionArray()
9
+ {
10
+ return array(
11
+ self::STATUS_ENABLED => Mage::helper('bmbleb')->__('Enabled'),
12
+ self::STATUS_DISABLED => Mage::helper('bmbleb')->__('Disabled')
13
+ );
14
+ }
15
+ }
app/code/community/Springbot/Bmbleb/Model/Sync.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Socketware_Bmbleb_Model_Sync extends Mage_Core_Model_Abstract
4
+ {
5
+
6
+ const STATUS_STARTED = 0;
7
+ const STATUS_PROCESSING = 1;
8
+ const STATUS_PARTIALLY_COMPLETE = 2;
9
+ const STATUS_COMPLETED = 3;
10
+
11
+ public function _construct()
12
+ {
13
+ parent::_construct();
14
+ $this->_init('bmbleb/sync');
15
+ }
16
+
17
+ public function getParsedDetails()
18
+ {
19
+ if ($this->getDetails() != ''){
20
+ return Zend_Json::decode($this->getDetails());
21
+ }
22
+ return array();
23
+ }
24
+
25
+ public function setParsedDetails($dataArray = array())
26
+ {
27
+ $this->setDetails(Zend_Json::encode($dataArray));
28
+ }
29
+ }
app/code/community/Springbot/Bmbleb/controllers/Adminhtml/HelpController.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Adminhtml_HelpController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+
5
+ public function indexAction()
6
+ {
7
+ $this->loadLayout();
8
+ $this->_setActiveMenu('springbot_bmbleb/dashboard');
9
+ $this->renderLayout();
10
+ }
11
+
12
+ }
app/code/community/Springbot/Bmbleb/controllers/Adminhtml/IndexController.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Adminhtml_IndexController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ protected function _init()
5
+ {
6
+ if(!Mage::helper('bmbleb/Account')->getIsAuthenticated()) {
7
+ $this->_redirect('*/*/auth');
8
+ } else if(
9
+ $this->getRequest()->getParam('logout') &&
10
+ Mage::helper('bmbleb/Account')->getIsAuthenticated()
11
+ ) {
12
+ $this->_redirect('bmbleb/logout/logout');
13
+ return;
14
+ } elseif($this->getRequest()->getParam('harvest')) {
15
+ $this->_redirect('*/*/index');
16
+ } elseif($this->getRequest()->getParam('killharvest')) {
17
+ Springbot_Boss::halt();
18
+ $this->_redirect('*/*/status');
19
+ } elseif ($problems = Mage::helper('bmbleb/PluginStatus')->getFatalPluginProblems()) {
20
+ $this->_redirect('*/adminhtml_problems/index');
21
+ }
22
+ }
23
+
24
+ public function indexAction()
25
+ {
26
+ $this->loadLayout();
27
+ $this->_setActiveMenu('springbot_bmbleb');
28
+ $this->renderLayout();
29
+ $this->_redirect('*/*/status');
30
+ return;
31
+ }
32
+
33
+ public function connectedtospringbotAction()
34
+ {
35
+ $this->_init();
36
+ $this->loadLayout();
37
+ $this->_setActiveMenu('springbot_bmbleb');
38
+ $this->renderLayout();
39
+ return;
40
+ }
41
+
42
+ public function loginAction()
43
+ {
44
+ $this->_init();
45
+ $this->loadLayout();
46
+ $this->_setActiveMenu('springbot_bmbleb');
47
+ $this->renderLayout();
48
+ return;
49
+ }
50
+
51
+ public function authAction()
52
+ {
53
+ $this->loadLayout();
54
+ $this->_setActiveMenu('springbot_bmbleb');
55
+ $this->renderLayout();
56
+ return;
57
+ }
58
+
59
+ public function statusAction()
60
+ {
61
+ $this->_init();
62
+ $this->loadLayout();
63
+ $this->_setActiveMenu('springbot_bmbleb');
64
+ $this->renderLayout();
65
+ return;
66
+ }
67
+
68
+ }
app/code/community/Springbot/Bmbleb/controllers/Adminhtml/JobsController.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Adminhtml_JobsController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ public function indexAction()
5
+ {
6
+ $this->loadLayout();
7
+ $this->_setActiveMenu('springbot_bmbleb/dashboard');
8
+ $this->renderLayout();
9
+ }
10
+
11
+ public function toggleWorkerStatusAction()
12
+ {
13
+ Springbot_Log::debug('Toggling work manager status');
14
+ $manager = Mage::getModel('combine/cron_manager_status');
15
+ $manager->toggle();
16
+
17
+ sleep(1); // give everything a second to process
18
+
19
+ $this->getResponse()->setHeader('Content-type', 'application/json');
20
+ $this->getResponse()->setBody(
21
+ $this->getLayout()->createBlock('bmbleb/adminhtml_jobs_status')->renderAsJson()
22
+ );
23
+ }
24
+
25
+ public function statusAction()
26
+ {
27
+ $this->loadLayout();
28
+ $this->getResponse()->setBody(
29
+ $this->getLayout()->createBlock('bmbleb/adminhtml_jobs_status')->toHtml()
30
+ );
31
+ }
32
+
33
+ public function gridAction()
34
+ {
35
+ $this->loadLayout();
36
+ $this->getResponse()->setBody(
37
+ $this->getLayout()->createBlock('bmbleb/adminhtml_jobs_grid')->toHtml()
38
+ );
39
+ }
40
+
41
+ public function runAction()
42
+ {
43
+ if($jobIds = $this->getRequest()->getParam('job_ids')) {
44
+ foreach($jobIds as $jobId) {
45
+ try {
46
+ $job = $this->_loadJob($jobId);
47
+ $job->run();
48
+ } catch (Exception $e) {
49
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
50
+ }
51
+ }
52
+ }
53
+
54
+ $this->_redirect('*/*/index');
55
+ }
56
+
57
+ public function deleteAction()
58
+ {
59
+ $jobIds = $this->getRequest()->getParam('job_ids');
60
+
61
+ foreach($jobIds as $jobId) {
62
+ try {
63
+ $job = $this->_loadJob($jobId);
64
+ $job->delete();
65
+ } catch (Exception $e) {
66
+ Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
67
+ }
68
+ }
69
+
70
+ $this->_redirect('*/*/index');
71
+ }
72
+
73
+ protected function _loadJob($id)
74
+ {
75
+ return Mage::getModel('combine/cron_queue')->load($id);
76
+ }
77
+ }
app/code/community/Springbot/Bmbleb/controllers/Adminhtml/LogsController.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Adminhtml_LogsController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+
5
+ public function indexAction()
6
+ {
7
+ $this->loadLayout();
8
+ $this->renderLayout();
9
+ }
10
+
11
+ public function downloadAction() {
12
+ $logName = $this->getRequest()->getParam('name');;
13
+ $logPath = Mage::getBaseDir('log') . '/' . $logName;
14
+ if (!is_file($logPath) || !is_readable($logPath)) {
15
+ throw new Exception();
16
+ }
17
+ $this->getResponse()
18
+ ->setHttpResponseCode(200)
19
+ ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true )
20
+ ->setHeader('Pragma', 'public', true )
21
+ ->setHeader('Content-type', 'application/force-download')
22
+ ->setHeader('Content-Length', filesize($logPath))
23
+ ->setHeader('Content-Disposition', 'attachment' . '; filename=' . basename($logPath) );
24
+ $this->getResponse()->clearBody();
25
+ $this->getResponse()->sendHeaders();
26
+ readfile($logPath);
27
+ exit;
28
+ }
29
+
30
+ }
app/code/community/Springbot/Bmbleb/controllers/Adminhtml/ProblemsController.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Adminhtml_ProblemsController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+
5
+ public function indexAction()
6
+ {
7
+ if ($problems = Mage::helper('bmbleb/PluginStatus')->getAllPluginProblems()) {
8
+ Mage::getSingleton('core/session')->addError('If this problem persists please contact Springbot support.');
9
+ $this->loadLayout();
10
+ $this->_setActiveMenu('bmbleb/adminhtml_problems/index');
11
+ $this->renderLayout();
12
+ }
13
+ else {
14
+ Mage::getSingleton('core/session')->addSuccess('Springbot did not detect any errors.');
15
+ $this->_redirect('bmbleb/adminhtml_index/status');
16
+ }
17
+
18
+ }
19
+
20
+ }
app/code/community/Springbot/Bmbleb/controllers/Adminhtml/SettingsController.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_Adminhtml_SettingsController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ protected $_configVars;
5
+
6
+ const CONFIG_VAR_FAMILY = 'springbot';
7
+ const CONFIG_VAR_GROUP = 'config';
8
+
9
+ protected function _init()
10
+ {
11
+ $this->_configVars = Mage::getStoreConfig(self::CONFIG_VAR_FAMILY.'/'.self::CONFIG_VAR_GROUP,Mage::app()->getStore());
12
+ }
13
+
14
+ public function indexAction()
15
+ {
16
+ $this->_init();
17
+
18
+ $securityToken = $this->fetchConfigVariable('security_token');
19
+
20
+ if(empty($securityToken)) {
21
+ $auth = Mage::helper('bmbleb/Account')->authenticate(
22
+ $this->fetchConfigVariable('account_email'),
23
+ Mage::helper('core')->decrypt($this->fetchConfigVariable('account_password'))
24
+ );
25
+ } else {
26
+ $auth = true;
27
+ }
28
+
29
+ if ($auth) {
30
+ $bmbAcct=Mage::helper('bmbleb/Account');
31
+ $bmbAcct->setIsLoggedIn(true);
32
+ $this->_redirect('bmbleb/adminhtml_index/status');
33
+ return;
34
+ }
35
+
36
+ $this->_redirect('bmbleb/adminhtml_index/auth');
37
+ return;
38
+ }
39
+ public function postAction()
40
+ {
41
+ if ($data = $this->getRequest()->getPost()) {
42
+ // if both password fields are empty then do NOT attempt to update them
43
+ $password = $data['password'];
44
+ $passwordverify = $data['passwordverify'];
45
+ if ($password != '' || $passwordverify != ''){
46
+ // some extra validation
47
+ if (strlen($password) <= 6){
48
+ Mage::getSingleton('adminhtml/session')->addError('Passwords must be more than 6 characters long.');
49
+ } else if ($password != $passwordverify){
50
+ Mage::getSingleton('adminhtml/session')->addError('The passwords entered did not match.');
51
+ } else {
52
+ // validated - attempt save
53
+ $result = Mage::helper('bmbleb/ChangePassword')->ChangePassword($password);
54
+ if ($result === true){
55
+ // update the saved and session password too
56
+ $bmblebAccount = Mage::helper('bmbleb/Account');
57
+ $account = $bmblebAccount->getAccount();
58
+ $account['password'] = $password;
59
+ $bmblebAccount->setAccount($account);
60
+ $bmblebAccount->setSavedAccountInformation($account['email'], $password);
61
+
62
+ Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Your password was successfully updated.'));
63
+ } else {
64
+ // $result contains the error message
65
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__('We\'re sorry, there\'s been an error. ') . $result);
66
+ }
67
+ }
68
+ }
69
+ } else {
70
+ Mage::getSingleton('adminhtml/session')->addError('No data submitted');
71
+ }
72
+ $this->_redirect('*/*/index', array());
73
+ return;
74
+ }
75
+ private function fetchConfigVariable($varName, $default_value = '')
76
+ {
77
+ if (isset($this->_configVars[$varName])) {
78
+ return $this->_configVars[$varName];
79
+ } else {
80
+ return $default_value;
81
+ }
82
+ }
83
+ }
app/code/community/Springbot/Bmbleb/controllers/HelpController.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_HelpController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+
5
+ protected function _initAction() {
6
+ $this->_setActiveMenu('bmbleb/help/index');
7
+ return $this;
8
+ }
9
+
10
+ /*
11
+ * dashboard / login / register screen
12
+ */
13
+ public function indexAction()
14
+ {
15
+ $this->loadLayout();
16
+
17
+ $this->_addLeft(
18
+ $this->getLayout()->createBlock('adminhtml/template')
19
+ ->setTemplate('bmbleb/tabs.phtml'));
20
+
21
+ //Main template
22
+ $this->_addContent(
23
+ $this->getLayout()->createBlock('adminhtml/template', 'main')
24
+ ->setTemplate('bmbleb/help.phtml'));
25
+
26
+ $this->renderLayout();
27
+ }
28
+
29
+
30
+ }
app/code/community/Springbot/Bmbleb/controllers/IndexController.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_IndexController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ public function indexAction()
5
+ {
6
+ $this->loadLayout();
7
+ $this->renderLayout();
8
+ }
9
+
10
+ public function termsAction()
11
+ {
12
+ print $this->getLayout()->createBlock("bmbleb/Adminhtml_Index_Terms")->toHtml();
13
+ exit();
14
+ }
15
+ }
16
+
app/code/community/Springbot/Bmbleb/controllers/LoginController.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_LoginController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ const CONFIG_VAR_FAMILY ="springbot";
5
+ const CONFIG_VAR_GROUP ="config";
6
+ private $configVars =array();
7
+
8
+ protected function _initAction() {
9
+ $this->loadLayout();
10
+ return $this;
11
+ }
12
+ public function indexAction() {
13
+ // $this->_forward('edit');
14
+ $this->loadLayout()
15
+ ->_addContent($this->getLayout()->createBlock('bmbleb/adminhtml_bmbleb_login'))
16
+ ->renderLayout();
17
+ }
18
+ public function newAction() {
19
+ // $this->_forward('edit');
20
+ $this->loadLayout()
21
+ ->_addContent($this->getLayout()->createBlock('bmbleb/adminhtml_bmbleb_login'))
22
+ ->renderLayout();
23
+ }
24
+ public function editAction() {
25
+ $this->loadLayout()
26
+ ->_addContent($this->getLayout()->createBlock('bmbleb/adminhtml_bmbleb_login'))
27
+ ->renderLayout();
28
+ }
29
+ public function loginAction() {
30
+
31
+ $email = $this->getRequest()->getParam('email');
32
+ $pass = $this->getRequest()->getParam('password');
33
+
34
+ $bmblebAccount = Mage::helper('bmbleb/Account');
35
+ $bmblebAccount->setIsLoggedIn(false);
36
+ $url = $this->fetchConfigVariable('api_url','https://api.springbot.com/').'api/registration/login';
37
+
38
+ try {
39
+ $client = new Varien_Http_Client($url);
40
+ $client->setRawData('{"user_id":"'.$email.'", "password":"'.$pass.'"}');
41
+ $response = $client->request('POST');
42
+ $result = json_decode($response->getBody(),true);
43
+ } catch (Exception $e) {
44
+ Mage::log('Remote Springbot service unavailable!');
45
+ Mage::logException($e);
46
+ Mage::getSingleton('adminhtml/session')->addError('Service unavailable from ' . $url . ' please contact support@springbot.com.');
47
+ $this->_redirect('bmbleb/adminhtml_index/auth');
48
+ return;
49
+ }
50
+
51
+ if ($result['status']=='error') {
52
+ Mage::getSingleton('adminhtml/session')->addError($result['message'].' or service unavailable from '.$url);
53
+ $this->_redirect('bmbleb/adminhtml_index/auth');
54
+ } else {
55
+ if ($result['token']=='') {
56
+ Mage::getSingleton('adminhtml/session')->addError('Login denied by Springbot');
57
+ $this->_redirect('bmbleb/adminhtml_index/auth');
58
+ } else {
59
+ Mage::log('Email->'.$email.' Token->'.$result['token']);
60
+ $bmblebAccount->setSavedAccountInformation($email,$pass,$result['token']);
61
+ $this->_redirect('bmbleb/adminhtml_index/index');
62
+ }
63
+ }
64
+ }
65
+ private function fetchConfigVariable($varName,$default_value='')
66
+ {
67
+ $this->configVars = Mage::getStoreConfig(self::CONFIG_VAR_FAMILY.'/'.self::CONFIG_VAR_GROUP, Mage::app()->getStore());
68
+
69
+ if (isset($this->configVars[$varName])) {
70
+ $rtnValue = $this->configVars[$varName];
71
+ } else {
72
+ $rtnValue = $default_value;
73
+ }
74
+ return $rtnValue;
75
+ }
76
+ }
app/code/community/Springbot/Bmbleb/controllers/LogoutController.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Bmbleb_LogoutController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ public function indexAction()
5
+ {
6
+ //$this->_forward('logout');
7
+ return $this;
8
+ }
9
+
10
+ public function logoutAction()
11
+ {
12
+ $this->loadLayout();
13
+ $this->_setActiveMenu('springbot_bmbleb');
14
+ $this->renderLayout();
15
+ }
16
+
17
+ public function remoteDisconnectAction()
18
+ {
19
+ //Reset login status
20
+ $bmblebAccount = Mage::helper('bmbleb/Account');
21
+ $bmblebAccount->logout();
22
+ $bmblebAccount->setSavedAccountInformation('', '');
23
+
24
+ $this->_notifySpringbot();
25
+
26
+ Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('bmbleb')->__('You have been successfully logged out of your Springbot Account.'));
27
+
28
+ $this->_redirect('bmbleb/adminhtml_index/auth');
29
+ }
30
+
31
+ protected function _notifySpringbot()
32
+ {
33
+ $storeId = Mage::getStoreConfig('springbot/config/store_id_1');
34
+ $payload = array(
35
+ 'type' => 'disconnect',
36
+ 'store_id' => $storeId,
37
+ 'store_url' => Mage::getUrl(),
38
+ 'description' => 'Admin user logged out of springbot!',
39
+ );
40
+ Mage::helper('bmbleb/externalLogging')->log($payload, $storeId);
41
+ return;
42
+ }
43
+ }
app/code/community/Springbot/Bmbleb/controllers/RegisterController.php ADDED
@@ -0,0 +1,350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Bmbleb_RegisterController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+
6
+ protected function _initAction()
7
+ {
8
+ $domain = Mage::getBaseUrl();
9
+ Mage::helper('bmbleb/ExternalLogging')->visibility("Registration Controller initiated", '', $domain);
10
+
11
+ $regStatus = Mage::getStoreConfig('springbot/config/registration_status', Mage::app()->getStore());
12
+
13
+ if ($regStatus == 'complete') {
14
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__('Please register for your free Springbot account.'));
15
+ session_write_close();
16
+ $this->_redirect('bmbleb/adminhtml_index/status');
17
+ }
18
+ Mage::helper('bmbleb/ExternalLogging')->visibility("Null Registration detected", '', $domain);
19
+
20
+ $this->loadLayout()
21
+ ->_setActiveMenu('bmbleb/register');
22
+
23
+ return $this;
24
+ }
25
+
26
+ public function indexAction()
27
+ {
28
+ $this->_forward('edit');
29
+ }
30
+
31
+ public function editAction()
32
+ {
33
+
34
+ $this->_initAction();
35
+ $this->loadLayout();
36
+ $this->_addLeft(
37
+ $this->getLayout()->createBlock('adminhtml/template')
38
+ ->setTemplate('bmbleb/tabs.phtml'));
39
+
40
+ $this->_addContent($this->getLayout()->createBlock('bmbleb/adminhtml_bmbleb_register'));
41
+ $this->renderLayout();
42
+ }
43
+
44
+ public function newAction()
45
+ {
46
+ $this->_forward('edit');
47
+ }
48
+
49
+ public function registerAction()
50
+ {
51
+
52
+ $emptyString = 0;
53
+ $minimumNAMELength = 3;
54
+ $minimumPASSWORDLength = 6;
55
+ $validationMessage = '';
56
+
57
+ $uname = $this->getRequest()->getParam('uname');
58
+ $email = $this->getRequest()->getParam('email');
59
+ $password = $this->getRequest()->getParam('password');
60
+ $passwordverify = $this->getRequest()->getParam('passwordverify');
61
+
62
+ if (strlen($uname) == $emptyString) {
63
+ $validationMessage = $this->saveMessage($validationMessage, 'Your name is required');
64
+ }
65
+ if (strlen($email) == $emptyString) {
66
+ $validationMessage = $this->saveMessage($validationMessage, 'Email is required.');
67
+ }
68
+ if (strlen($password) == $emptyString) {
69
+ $validationMessage = $this->saveMessage($validationMessage, 'Password is required.');
70
+ }
71
+ if (strlen($passwordverify) == $emptyString) {
72
+ $validationMessage = $this->saveMessage($validationMessage, 'Confirm Password is required.');
73
+ }
74
+ if (strlen($uname) < $minimumNAMELength) {
75
+ $validationMessage = $this->saveMessage($validationMessage, "Your name must be at least $minimumNAMELength characters long.");
76
+ }
77
+ if (!$this->check_email_address($email)) {
78
+ $validationMessage = $this->saveMessage($validationMessage, 'Email is not valid.');
79
+ }
80
+ if (strlen($password) < $minimumPASSWORDLength) {
81
+ $validationMessage = $this->saveMessage($validationMessage, "Your password must be at least $minimumPASSWORDLength characters long.");
82
+ }
83
+ if ($password != $passwordverify) {
84
+ $validationMessage = $this->saveMessage($validationMessage, 'Your passwords must match.');
85
+ }
86
+ if ($validationMessage != '') {
87
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__($validationMessage));
88
+ $this->_redirect('bmbleb/adminhtml_index/auth');
89
+ return;
90
+ }
91
+ $payload = array();
92
+ $payload['registeremail'] = $email;
93
+ $payload['registerpassword'] = $password;
94
+ $payload['verifypassword'] = $passwordverify;
95
+ $payload['uname'] = $uname;
96
+
97
+ $apiResponse = Mage::helper('bmbleb/ApiCall')->call("registration", $payload);
98
+ if ($apiResponse->getResponsecode() == "200") {
99
+ $msg = $this->parseBody($apiResponse->getFullresponse());
100
+ if ($msg != '') {
101
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__($msg));
102
+ $this->_redirect('bmbleb/adminhtml_index/auth');
103
+ return;
104
+ }
105
+ Mage::getSingleton('core/session')->setBmblebJustRegistered(TRUE);
106
+ Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('bmbleb')->__('Your Springbot Connect account has been created.'));
107
+
108
+ $this->globalSettings($email, $password);
109
+ $this->registerAllStores($email, $password);
110
+
111
+ $this->_redirect('bmbleb/adminhtml_index/index');
112
+
113
+ } else {
114
+
115
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__('We\'re sorry, there\'s been an error: '
116
+ . '[' . $apiResponse->getResponsecode() . ']'
117
+ . '-' . $apiResponse->getMessage()));
118
+ $this->_redirect('bmbleb/adminhtml_index/auth');
119
+ }
120
+
121
+ }
122
+
123
+ function parseBody($body)
124
+ {
125
+ $msg = '';
126
+ $pos = strpos($body, '"status":"error"');
127
+ if ($pos > 0) {
128
+ $len = strlen($body);
129
+ $pos = strpos($body, '"message"', $pos);
130
+ $pos = $pos + 12;
131
+ $siz = $len - $pos - 3;
132
+ $msg = substr($body, $pos, $siz);
133
+ }
134
+ return $msg;
135
+ }
136
+
137
+ function saveMessage($msg, $newmsg)
138
+ {
139
+ if ($msg == '') {
140
+ return $newmsg;
141
+ } else {
142
+ return $msg;
143
+ }
144
+ }
145
+
146
+ function check_email_address($email)
147
+ {
148
+ if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
149
+ return true;
150
+ } else {
151
+ return false;
152
+ }
153
+ }
154
+
155
+ function registerAllStores($email, $password)
156
+ {
157
+ Mage::app()->getWebsites();
158
+ Mage::app()->getStores();
159
+
160
+ foreach (Mage::app()->getWebsites() as $website) {
161
+ foreach ($website->getGroups() as $group) {
162
+ $stores = $group->getStores();
163
+ foreach ($stores as $store) {
164
+ $this->registerEachStore($email,
165
+ $password,
166
+ $store['website_id'],
167
+ $store['store_id'],
168
+ $store['name'],
169
+ $store['code'],
170
+ $store['is_active']);
171
+ }
172
+ }
173
+ }
174
+ $this->commitVars(array('registration_status' => 'complete'));
175
+ }
176
+
177
+ function registerEachStore($email, $pswd, $webId, $storeId, $storeName, $storeCode, $storeActive)
178
+ {
179
+ $logMsg = '>WebID->' . $webId
180
+ . ' StoreID->' . $storeId
181
+ . ' StoreName->' . $storeName
182
+ . ' StoreCode->' . $storeCode
183
+ . ' StoreActive->' . $storeActive;
184
+
185
+ Mage::log('Register Store:' . $logMsg);
186
+
187
+ $apiClass = 'stores';
188
+ $baseUrl = Mage::getBaseUrl();
189
+ $logo_src = Mage::getStoreConfig('design/header/logo_src');
190
+ $logo_alt_tag = Mage::getStoreConfig('design/header/logo_alt');
191
+ $media_url = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA);
192
+
193
+ $url_components = explode('/', $baseUrl);
194
+ $siteBaseURL = $url_components[0] . '//' . $url_components[2];
195
+
196
+ $results = $this->RequestSecurityToken($email, $pswd);
197
+
198
+ if ($results['status'] == 'error') {
199
+ Mage::getSingleton('adminhtml/session')->addError(Mage::helper('bmbleb')->__("Error: " . $results['message']));
200
+ $this->_redirect('bmbleb/adminhtml_index/auth');
201
+ return;
202
+ }
203
+
204
+ if ($results['status'] == 'ok') {
205
+ $securityToken = $results['token'];
206
+ $charid = strtoupper(md5(uniqid(rand(), true)));
207
+ $guid = substr($charid, 0, 8) . '-'
208
+ . substr($charid, 8, 4) . '-'
209
+ . substr($charid, 12, 4) . '-'
210
+ . substr($charid, 16, 4) . '-'
211
+ . substr($charid, 20, 12);
212
+ $stores = array();
213
+ $stores['guid'] = $guid;
214
+ $stores['url'] = $siteBaseURL;
215
+ $stores['name'] = $storeName;
216
+ //ENABLE: In next release (after 1/19)
217
+ //
218
+ // $stores['logo_link'] =$media_url.'/'.$logo_src;
219
+ // $stores['logo_alt_tag'] =$logo_alt_tag;
220
+
221
+ $attributes = array();
222
+ $attributes['web_id'] = $webId;
223
+ $attributes['store_id'] = $storeId;
224
+ $attributes['store_name'] = $storeName;
225
+ $attributes['store_code'] = $storeCode;
226
+ $attributes['store_active'] = $storeActive;
227
+ $attributes['store_url'] = $siteBaseURL;
228
+ $stores['json_data'] = $this->formatJSON('', $attributes);
229
+ $rawData = '{"' . $apiClass . '": {"' . $guid . '":'
230
+ . $this->formatJSON('', $stores)
231
+ . '}}';
232
+ Mage::log('Register this store->' . $stores['json_data']);
233
+
234
+ $apiModel = Mage::getModel('combine/api');
235
+ $apiUrl = $apiModel->getApiUrl('stores');
236
+ $ch = curl_init($apiUrl);
237
+ $options = array(
238
+ CURLOPT_RETURNTRANSFER => true,
239
+ CURLOPT_HTTPHEADER => array('Content-type: application/json', 'X-AUTH-TOKEN:' . $securityToken),
240
+ CURLOPT_POSTFIELDS => $rawData
241
+ );
242
+ curl_setopt_array($ch, $options);
243
+ $buffer = curl_exec($ch);
244
+
245
+ $response = json_decode($buffer, true);
246
+ if ($response['status'] == 'ok') {
247
+ $springbot_storeId = array_search($guid, $response['stores']);
248
+ $vars = array(
249
+ 'store_guid' => $guid,
250
+ 'store_id' => $springbot_storeId,
251
+ 'product_cursor' => '0',
252
+ 'category_cursor' => '0',
253
+ 'security_token' => $securityToken
254
+ );
255
+ $this->commitVars($vars, $storeId);
256
+ }
257
+ }
258
+ }
259
+
260
+ function globalSettings($email, $password)
261
+ {
262
+ $encyptPswd = Mage::helper('core')->encrypt($password);
263
+
264
+ $vars = array(
265
+ 'initial_sync_inflight' => 'true',
266
+ 'registration_status' => 'new',
267
+ 'customer_sequence' => '5000000',
268
+ 'purchase_cursor' => '0',
269
+ 'account_email' => $email,
270
+ 'prev_account_email' => $email,
271
+ 'account_password' => $encyptPswd
272
+ );
273
+
274
+ $this->commitVars($vars);
275
+
276
+ return;
277
+ }
278
+
279
+ function commitVars($vars, $storeID = '')
280
+ {
281
+ $config = new Mage_Core_Model_Config();
282
+ foreach ($vars as $key => $val) {
283
+ $config->saveConfig($this->makeConfigKey($key, $storeID), $val, 'default', 0);
284
+ }
285
+ return;
286
+ }
287
+
288
+ function makeConfigKey($dataClass, $storeId = '')
289
+ {
290
+ $cKey = 'springbot/config/' . $dataClass;
291
+
292
+ if ($storeId != '') {
293
+ $cKey = $cKey . '_' . $storeId;
294
+ }
295
+ return $cKey;
296
+ }
297
+
298
+ function RequestSecurityToken($email, $pswd)
299
+ {
300
+ $header = array('Content-type: application/json');
301
+ $rawBuffer = '{"user_id":"' . $email . '", "password":"' . $pswd . '"}';
302
+ Mage::log('Login ' . $rawBuffer);
303
+
304
+ $apiModel = Mage::getModel('combine/api');
305
+ $apiUrl = $apiModel->getApiUrl('registration/login');
306
+ $ch = curl_init($apiUrl);
307
+ $options = array(
308
+ CURLOPT_RETURNTRANSFER => true,
309
+ CURLOPT_HTTPHEADER => $header,
310
+ CURLOPT_POSTFIELDS => $rawBuffer
311
+ );
312
+ curl_setopt_array($ch, $options);
313
+ $rs = curl_exec($ch);
314
+ curl_close($ch);
315
+
316
+ return json_decode($rs, true);
317
+
318
+ }
319
+
320
+ function formatJSON($dataID, $content)
321
+ {
322
+ $prefixLength = 0;;
323
+ $howMany = sizeof($content);
324
+ $pairCount = 0;
325
+ $closeArray = '';
326
+
327
+ if ($dataID != '') {
328
+ $json = '"' . $content[$dataID] . '":{';
329
+ } else {
330
+ $json = '{';
331
+ $closeArray = '}';
332
+ }
333
+ $dlm = '"';
334
+
335
+ foreach ($content as $key => $value) {
336
+ $dlm = '"';
337
+ $keySize = strlen($key);
338
+ $keyNoPrefix = substr($key, $prefixLength, $keySize - $prefixLength);
339
+ if ($keyNoPrefix == 'json_data' || $keyNoPrefix == 'line_items') {
340
+ $dlm = '';
341
+ }
342
+ $json = $json . '"' . $keyNoPrefix . '":' . $dlm . $value . $dlm;
343
+ $pairCount++;
344
+ if ($pairCount < $howMany) {
345
+ $json = $json . ',';
346
+ }
347
+ }
348
+ return $json . $closeArray;
349
+ }
350
+ }
app/code/community/Springbot/Bmbleb/etc/config.xml ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * @category Socketware
5
+ * @package Socketware_Bmbleb
6
+ * @author ModuleCreator
7
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Springbot_Bmbleb>
13
+ <version>0.1.0</version>
14
+ </Springbot_Bmbleb>
15
+ </modules>
16
+ <admin>
17
+ <routers>
18
+ <bmbleb>
19
+ <use>admin</use>
20
+ <args>
21
+ <module>Springbot_Bmbleb</module>
22
+ <frontName>bmbleb</frontName>
23
+ </args>
24
+ </bmbleb>
25
+ </routers>
26
+ </admin>
27
+ <adminhtml>
28
+ <menu>
29
+ <springbot_bmbleb module="bmbleb">
30
+ <title>Springbot</title>
31
+ <sort_order>71</sort_order>
32
+ <children>
33
+ <dashboard module="bmbleb">
34
+ <title>Dashboard</title>
35
+ <action>bmbleb/adminhtml_settings/index</action>
36
+ </dashboard>
37
+ </children>
38
+ </springbot_bmbleb>
39
+ </menu>
40
+ <acl>
41
+ <resources>
42
+ <all><title>Allow Everything</title></all>
43
+ <admin>
44
+ <children>
45
+ <springbot_bmbleb translate="title" module="bmbleb">
46
+ <title>Springbot</title>
47
+ <sort_order>300</sort_order>
48
+ <children>
49
+ <dashboard translate="title" module="bmbleb">
50
+ <title>Dashboard</title>
51
+ <sort_order>10</sort_order>
52
+ </dashboard>
53
+ </children>
54
+ </springbot_bmbleb>
55
+ </children>
56
+ </admin>
57
+ </resources>
58
+ </acl>
59
+ <layout>
60
+ <updates>
61
+ <bmbleb>
62
+ <file>bmbleb.xml</file>
63
+ </bmbleb>
64
+ </updates>
65
+ </layout>
66
+ </adminhtml>
67
+ <global>
68
+ <models>
69
+ <bmbleb>
70
+ <class>Sprigbot_Bmbleb_Model</class>
71
+ <resourceModel>bmbleb_mysql4</resourceModel>
72
+ </bmbleb>
73
+ <bmbleb_mysql4>
74
+ <class>Springbot_Bmbleb_Model_Mysql4</class>
75
+ </bmbleb_mysql4>
76
+ </models>
77
+ <resources>
78
+ <bmbleb_setup>
79
+ <setup>
80
+ <module>Springbot_Bmbleb</module>
81
+ </setup>
82
+ <connection>
83
+ <use>core_setup</use>
84
+ </connection>
85
+ </bmbleb_setup>
86
+ <bmbleb_write>
87
+ <connection>
88
+ <use>core_write</use>
89
+ </connection>
90
+ </bmbleb_write>
91
+ <bmbleb_read>
92
+ <connection>
93
+ <use>core_read</use>
94
+ </connection>
95
+ </bmbleb_read>
96
+ </resources>
97
+ <blocks>
98
+ <bmbleb>
99
+ <class>Springbot_Bmbleb_Block</class>
100
+ </bmbleb>
101
+ </blocks>
102
+ <helpers>
103
+ <bmbleb>
104
+ <class>Springbot_Bmbleb_Helper</class>
105
+ </bmbleb>
106
+ </helpers>
107
+ </global>
108
+ <default>
109
+ <bmbleb>
110
+ <config>
111
+ </config>
112
+ </bmbleb>
113
+ </default>
114
+ </config>
app/code/community/Springbot/BoneCollector/Model/HarvestAbstract.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_BoneCollector_Model_HarvestAbstract
4
+ {
5
+ const BOT_SERVICES = 'SpringbotServices.php';
6
+ const SPRINGBOT_DIRECTORY = '/app/code/community/Springbot/';
7
+ const LOG_DIRECTORY = '/var/log/';
8
+ const BOT_SERVICES_DIRECTORY = '/app/code/community/Springbot/DataServices/';
9
+ const BACKGROUND_MODE = '&';
10
+
11
+ /**
12
+ * The attributes which we want to listen for changes on
13
+ *
14
+ * @return array
15
+ */
16
+ protected $_attributes = array();
17
+
18
+ protected function _initObserver($observer)
19
+ {
20
+ if($event = $observer->getEvent()) {
21
+ Springbot_Log::debug($event->getName());
22
+ }
23
+ }
24
+
25
+ public function getPhpExec()
26
+ {
27
+ return Mage::helper('combine/harvest')->getPhpExec();
28
+ }
29
+
30
+ /**
31
+ * Get top level sku
32
+ *
33
+ * This aims to get the top level sku. The getSku method for the product
34
+ * model is overloaded providing the type instance version of the sku
35
+ * meaning that it gives the simple sku for configurable or grouped products
36
+ * we need to get the _data array directly and pass that sku up to ensure the
37
+ * parent sku.
38
+ *
39
+ * @param $observer Varient_Event_Observer
40
+ * @return string
41
+ */
42
+ public function getTopLevelSku($observer)
43
+ {
44
+ $product = $observer->getEvent()->getProduct();
45
+ return Mage::helper('combine/parser')->getTopLevelSku($product);
46
+ }
47
+
48
+ public function getCommand($method)
49
+ {
50
+ return $this->getPhpExec() . ' '
51
+ . $magentoRootDir
52
+ . self::SPRINGBOT_DIRECTORY
53
+ . self::BOT_SERVICES_DIRECTORY
54
+ . self::BOT_SERVICES . ' '
55
+ . $method . ' '
56
+ ;
57
+ }
58
+
59
+ public function doSend($object, $sessionKey)
60
+ {
61
+ $json = $object->toJson();
62
+ $hash = sha1($json);
63
+ $session = $this->_getSession();
64
+
65
+ if ($session->getData($sessionKey) == $hash) {
66
+ Springbot_Log::debug("Hash for {$sessionKey} is match, this object has already been posted, skipping");
67
+ return false;
68
+ } else {
69
+ $session->setData($sessionKey, $hash);
70
+ Springbot_Log::debug("Hash for {$sessionKey} does not match cache, sending");
71
+ return true;
72
+ }
73
+ }
74
+
75
+ protected function _getSession()
76
+ {
77
+ return Mage::getSingleton('core/session');
78
+ }
79
+
80
+ protected function _getAttributesToListenFor($extras = array())
81
+ {
82
+ return array_merge($this->_attributes, $extras);
83
+ }
84
+
85
+ protected function _entityChanged($model)
86
+ {
87
+ foreach($this->_getAttributesToListenFor() as $attribute) {
88
+ if($attribute != 'created_at' && $attribute != 'updated_at') {
89
+ if($this->_hasDataChangedFor($model, $attribute)) {
90
+ return true;
91
+ }
92
+ }
93
+ }
94
+ Springbot_Log::debug('Entity unchanged');
95
+ return false;
96
+ }
97
+
98
+ protected function _hasDataChangedFor($model, $field)
99
+ {
100
+ $newData = $model->getData($field);
101
+ $origData = $model->getOrigData($field);
102
+ return $newData!=$origData;
103
+ }
104
+
105
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestAttribute/Observer.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_BoneCollector_Model_HarvestAttribute_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
4
+ {
5
+ public function attributeSave($observer)
6
+ {
7
+ try {
8
+ $this->_initObserver($observer);
9
+ $attribute = $observer->getEvent()->getAttribute();
10
+
11
+ if($attribute->getIsUserDefined()) {
12
+ if($this->doSend($attribute, 'sb_eav_entity_attribute_obs_hash')) {
13
+ Springbot_Log::debug("Attempting to post parent attribute sets");
14
+ Springbot_Boss::scheduleJob('post:attribute', array('i' => $attribute->getAttributeId()), Springbot_Services_Priority::LISTENER, 'listener');
15
+ }
16
+ } else {
17
+ Springbot_Log::debug("Attribute is not user defined, skipping");
18
+ }
19
+ } catch (Exception $e) {
20
+ Springbot_Log::error($e);
21
+ }
22
+ }
23
+
24
+ public function attributeSetSave($observer)
25
+ {
26
+ try {
27
+ $attribute = $observer->getEvent()->getAttribute();
28
+ $set = $this->_getAttributeSet($attribute->getAttributeSetId());
29
+ if($this->doSend($set, 'sb_eav_entity_attribute_set_obs_hash')) {
30
+ $this->_initObserver($observer);
31
+ Springbot_Boss::scheduleJob('post:attributeSet', array('i' => $attribute->getAttributeSetId()), Springbot_Services_Priority::LISTENER, 'listener');
32
+ }
33
+ } catch (Exception $e) {
34
+ Springbot_Log::error($e);
35
+ }
36
+ }
37
+
38
+ protected function _getAttributeSet($id)
39
+ {
40
+ $helper = Mage::helper('combine/attributes');
41
+
42
+ // invalidate cache as attributes are added to set
43
+ $attrIds = $helper->getAttributesBySet($id)->getAllIds();
44
+
45
+ return $helper->getAttributeSetById($id)
46
+ ->setNestedAttributeIds($attrIds);
47
+ }
48
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestCaptureSKU/Observer.php ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoneCollector Event Listener (Capture SKU when Product Viewed)
4
+ *
5
+ * @version v1.0.0 - 5/28/2013
6
+ *
7
+ * @category Magento Integrations
8
+ * @package springbot
9
+ * @author William Seitz
10
+ * @division SpringBot Integration Team
11
+ * @support magentosupport@springbot.com
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ *
14
+ */
15
+ class Springbot_BoneCollector_Model_HarvestCaptureSKU_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
16
+ {
17
+ const VERSION = '1.2.0';
18
+ const METHOD = 'getsku';
19
+ const LISTENER_PUBLIC_NAME = 'CaptureSKU Harvesting Listener: ';
20
+ const ZERO = 0;
21
+
22
+ public function captureskuharvest($observer)
23
+ {
24
+ $controllerName = Mage::app()->getRequest()->getControllerName();
25
+ $actionName = Mage::app()->getRequest()->getActionName();
26
+ $routerName = Mage::app()->getRequest()->getRouteName();
27
+
28
+ if ($this->viewingProductDetailPage($controllerName,$actionName,$routerName)) {
29
+ $this->_initObserver($observer);
30
+ $productData = $observer->getEvent()->getProduct();
31
+ $entityViewed = $productData['sku'];
32
+ $eventDatetime = date("Y-m-d H:i:s");
33
+ $openModeAppend = 'a';
34
+ $eventHistoryFilename = Mage::getBaseDir().'/var/log/Springbot-EventHistory.csv';
35
+ $urlViewed = Mage::helper('core/url')->getCurrentUrl();
36
+ $user_agent = Mage::helper('core/http')->getHttpUserAgent();
37
+
38
+ if ($this->qualifyURL($urlViewed,$user_agent)) {
39
+ try {
40
+ $lastCatId = $this->_getLastCategory();
41
+ $visitorIP = Mage::helper('core/http')->getRemoteAddr(true);
42
+ $storeId = Mage::app()->getStore()->getStoreId();
43
+ $fHandle = fopen($eventHistoryFilename,$openModeAppend);
44
+ $viewedMessage = array('view',
45
+ $eventDatetime,
46
+ $urlViewed,
47
+ $entityViewed,
48
+ $visitorIP,
49
+ $storeId,
50
+ $lastCatId
51
+ );
52
+ fputcsv($fHandle,$viewedMessage,',');
53
+ fclose ($fHandle);
54
+
55
+ } catch (Exception $e) {
56
+ Mage::log('Unknown exception opening '.$eventHistoryFilename);
57
+ }
58
+ }
59
+ }
60
+ return;
61
+ }
62
+
63
+ private function viewingProductDetailPage($controllerName,$actionName,$routerName)
64
+ {
65
+ if ($controllerName == 'product'
66
+ && $actionName == 'view'
67
+ && $routerName == 'catalog') {
68
+ return true;
69
+ } else {
70
+ return false;
71
+ }
72
+ }
73
+
74
+ protected function _getLastCategory()
75
+ {
76
+ return Mage::helper('combine')->getLastCategoryId();
77
+ }
78
+
79
+ private function qualifyURL($url,$user_agent)
80
+ {
81
+ $rtn = true;
82
+
83
+ if (strpos($url,'/api') > self::ZERO
84
+ || strpos($url,'/ajax') > self::ZERO
85
+ || strpos($url,'/soap') > self::ZERO) {
86
+ $rtn = false;
87
+ } else {
88
+ $rtn = false;
89
+ if ($this->is_bot($user_agent) == false && $this->urlIsIPAddress($url)== false) {
90
+ $rtn = true;
91
+ }
92
+ }
93
+ return $rtn;
94
+ }
95
+
96
+ private function urlIsIPAddress($url)
97
+ {
98
+ $numericComponents = self::ZERO;
99
+ $ipComponents = explode('.',$url);
100
+
101
+ foreach($ipComponents as $ipVal) {
102
+ if (is_numeric($ipVal)) {
103
+ $numericComponents++;
104
+ }
105
+ }
106
+ if ($numericComponents == 4) {
107
+ return true;
108
+ } else {
109
+ return false;
110
+ }
111
+ }
112
+
113
+ private function is_bot($user_agent) {
114
+ $spiders = array(
115
+ "abot",
116
+ "dbot",
117
+ "ebot",
118
+ "hbot",
119
+ "kbot",
120
+ "lbot",
121
+ "mbot",
122
+ "nbot",
123
+ "obot",
124
+ "pbot",
125
+ "rbot",
126
+ "sbot",
127
+ "tbot",
128
+ "vbot",
129
+ "ybot",
130
+ "zbot",
131
+ "bot/",
132
+ "_bot",
133
+ ".bot",
134
+ "/bot",
135
+ "-bot",
136
+ ":bot",
137
+ "(bot",
138
+ "crawl",
139
+ "slurp",
140
+ "spider",
141
+ "seek",
142
+ "accoona",
143
+ "acoon",
144
+ "adressendeutschland",
145
+ "ah-ha.com",
146
+ "ahoy",
147
+ "altavista",
148
+ "ananzi",
149
+ "anthill",
150
+ "appie",
151
+ "arachnophilia",
152
+ "arale",
153
+ "araneo",
154
+ "aranha",
155
+ "architext",
156
+ "aretha",
157
+ "arks",
158
+ "asterias",
159
+ "atlocal",
160
+ "atn",
161
+ "atomz",
162
+ "augurfind",
163
+ "backrub",
164
+ "bannana_bot",
165
+ "baypup",
166
+ "bdfetch",
167
+ "big brother",
168
+ "biglotron",
169
+ "bjaaland",
170
+ "blackwidow",
171
+ "blaiz",
172
+ "blog",
173
+ "blo.",
174
+ "bloodhound",
175
+ "boitho",
176
+ "booch",
177
+ "bradley",
178
+ "butterfly",
179
+ "calif",
180
+ "cassandra",
181
+ "ccubee",
182
+ "cfetch",
183
+ "charlotte",
184
+ "churl",
185
+ "cienciaficcion",
186
+ "cmc",
187
+ "collective",
188
+ "comagent",
189
+ "combine",
190
+ "computingsite",
191
+ "csci",
192
+ "curl",
193
+ "cusco",
194
+ "daumoa",
195
+ "deepindex",
196
+ "delorie",
197
+ "depspid",
198
+ "deweb",
199
+ "die blinde kuh",
200
+ "digger",
201
+ "ditto",
202
+ "dmoz",
203
+ "docomo",
204
+ "download express",
205
+ "dtaagent",
206
+ "dwcp",
207
+ "ebiness",
208
+ "ebingbong",
209
+ "e-collector",
210
+ "ejupiter",
211
+ "emacs-w3 search engine",
212
+ "esther",
213
+ "evliya celebi",
214
+ "ezresult",
215
+ "falcon",
216
+ "felix ide",
217
+ "ferret",
218
+ "fetchrover",
219
+ "fido",
220
+ "findlinks",
221
+ "fireball",
222
+ "fish search",
223
+ "fouineur",
224
+ "funnelweb",
225
+ "gazz",
226
+ "gcreep",
227
+ "genieknows",
228
+ "getterroboplus",
229
+ "geturl",
230
+ "glx",
231
+ "goforit",
232
+ "golem",
233
+ "grabber",
234
+ "grapnel",
235
+ "gralon",
236
+ "griffon",
237
+ "gromit",
238
+ "grub",
239
+ "gulliver",
240
+ "hamahakki",
241
+ "harvest",
242
+ "havindex",
243
+ "helix",
244
+ "heritrix",
245
+ "hku www octopus",
246
+ "homerweb",
247
+ "htdig",
248
+ "html index",
249
+ "html_analyzer",
250
+ "htmlgobble",
251
+ "hubater",
252
+ "hyper-decontextualizer",
253
+ "ia_archiver",
254
+ "ibm_planetwide",
255
+ "ichiro",
256
+ "iconsurf",
257
+ "iltrovatore",
258
+ "image.kapsi.net",
259
+ "imagelock",
260
+ "incywincy",
261
+ "indexer",
262
+ "infobee",
263
+ "informant",
264
+ "ingrid",
265
+ "inktomisearch.com",
266
+ "inspector web",
267
+ "intelliagent",
268
+ "internet shinchakubin",
269
+ "ip3000",
270
+ "iron33",
271
+ "israeli-search",
272
+ "ivia",
273
+ "jack",
274
+ "jakarta",
275
+ "javabee",
276
+ "jetbot",
277
+ "jumpstation",
278
+ "katipo",
279
+ "kdd-explorer",
280
+ "kilroy",
281
+ "knowledge",
282
+ "kototoi",
283
+ "kretrieve",
284
+ "labelgrabber",
285
+ "lachesis",
286
+ "larbin",
287
+ "legs",
288
+ "libwww",
289
+ "linkalarm",
290
+ "link validator",
291
+ "linkscan",
292
+ "lockon",
293
+ "lwp",
294
+ "lycos",
295
+ "magpie",
296
+ "mantraagent",
297
+ "mapoftheinternet",
298
+ "marvin/",
299
+ "mattie",
300
+ "mediafox",
301
+ "mediapartners",
302
+ "mercator",
303
+ "merzscope",
304
+ "microsoft url control",
305
+ "minirank",
306
+ "miva",
307
+ "mj12",
308
+ "mnogosearch",
309
+ "moget",
310
+ "monster",
311
+ "moose",
312
+ "motor",
313
+ "multitext",
314
+ "muncher",
315
+ "muscatferret",
316
+ "mwd.search",
317
+ "myweb",
318
+ "najdi",
319
+ "nameprotect",
320
+ "nationaldirectory",
321
+ "nazilla",
322
+ "ncsa beta",
323
+ "nec-meshexplorer",
324
+ "nederland.zoek",
325
+ "netcarta webmap engine",
326
+ "netmechanic",
327
+ "netresearchserver",
328
+ "netscoop",
329
+ "newscan-online",
330
+ "nhse",
331
+ "nokia6682/",
332
+ "nomad",
333
+ "noyona",
334
+ "nutch",
335
+ "nzexplorer",
336
+ "objectssearch",
337
+ "occam",
338
+ "omni",
339
+ "open text",
340
+ "openfind",
341
+ "openintelligencedata",
342
+ "orb search",
343
+ "osis-project",
344
+ "pack rat",
345
+ "pageboy",
346
+ "pagebull",
347
+ "page_verifier",
348
+ "panscient",
349
+ "parasite",
350
+ "partnersite",
351
+ "patric",
352
+ "pear.",
353
+ "pegasus",
354
+ "peregrinator",
355
+ "pgp key agent",
356
+ "phantom",
357
+ "phpdig",
358
+ "picosearch",
359
+ "piltdownman",
360
+ "pimptrain",
361
+ "pinpoint",
362
+ "pioneer",
363
+ "piranha",
364
+ "plumtreewebaccessor",
365
+ "pogodak",
366
+ "poirot",
367
+ "pompos",
368
+ "poppelsdorf",
369
+ "poppi",
370
+ "popular iconoclast",
371
+ "psycheclone",
372
+ "publisher",
373
+ "python",
374
+ "rambler",
375
+ "raven search",
376
+ "roach",
377
+ "road runner",
378
+ "roadhouse",
379
+ "robbie",
380
+ "robofox",
381
+ "robozilla",
382
+ "rules",
383
+ "salty",
384
+ "sbider",
385
+ "scooter",
386
+ "scoutjet",
387
+ "scrubby",
388
+ "search.",
389
+ "searchprocess",
390
+ "semanticdiscovery",
391
+ "senrigan",
392
+ "sg-scout",
393
+ "shai'hulud",
394
+ "shark",
395
+ "shopwiki",
396
+ "sidewinder",
397
+ "sift",
398
+ "silk",
399
+ "simmany",
400
+ "site searcher",
401
+ "site valet",
402
+ "sitetech-rover",
403
+ "skymob.com",
404
+ "sleek",
405
+ "smartwit",
406
+ "sna-",
407
+ "snappy",
408
+ "snooper",
409
+ "sohu",
410
+ "speedfind",
411
+ "sphere",
412
+ "sphider",
413
+ "spinner",
414
+ "spyder",
415
+ "steeler/",
416
+ "suke",
417
+ "suntek",
418
+ "supersnooper",
419
+ "surfnomore",
420
+ "sven",
421
+ "sygol",
422
+ "szukacz",
423
+ "tach black widow",
424
+ "tarantula",
425
+ "templeton",
426
+ "/teoma",
427
+ "t-h-u-n-d-e-r-s-t-o-n-e",
428
+ "theophrastus",
429
+ "titan",
430
+ "titin",
431
+ "tkwww",
432
+ "toutatis",
433
+ "t-rex",
434
+ "tutorgig",
435
+ "twiceler",
436
+ "twisted",
437
+ "ucsd",
438
+ "udmsearch",
439
+ "url check",
440
+ "updated",
441
+ "vagabondo",
442
+ "valkyrie",
443
+ "verticrawl",
444
+ "victoria",
445
+ "vision-search",
446
+ "volcano",
447
+ "voyager/",
448
+ "voyager-hc",
449
+ "w3c_validator",
450
+ "w3m2",
451
+ "w3mir",
452
+ "walker",
453
+ "wallpaper",
454
+ "wanderer",
455
+ "wauuu",
456
+ "wavefire",
457
+ "web core",
458
+ "web hopper",
459
+ "web wombat",
460
+ "webbandit",
461
+ "webcatcher",
462
+ "webcopy",
463
+ "webfoot",
464
+ "weblayers",
465
+ "weblinker",
466
+ "weblog monitor",
467
+ "webmirror",
468
+ "webmonkey",
469
+ "webquest",
470
+ "webreaper",
471
+ "websitepulse",
472
+ "websnarf",
473
+ "webstolperer",
474
+ "webvac",
475
+ "webwalk",
476
+ "webwatch",
477
+ "webwombat",
478
+ "webzinger",
479
+ "wget",
480
+ "whizbang",
481
+ "whowhere",
482
+ "wild ferret",
483
+ "worldlight",
484
+ "wwwc",
485
+ "wwwster",
486
+ "xenu",
487
+ "xget",
488
+ "xift",
489
+ "xirq",
490
+ "yandex",
491
+ "yanga",
492
+ "yeti",
493
+ "yodao",
494
+ "zao/",
495
+ "zippp",
496
+ "zyborg"
497
+ );
498
+ foreach($spiders as $spider) {
499
+ if (stripos($user_agent, $spider) !== false ) return true;
500
+ }
501
+ return false;
502
+ }
503
+
504
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestCart/Observer.php ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoneCollector Event Listener (AddToCart SKU Harvest)
4
+ *
5
+ * @version v1.0.0 - 4/19/2013
6
+ *
7
+ * @category Magento Integrations
8
+ * @package springbot
9
+ * @author William Seitz
10
+ * @division SpringBot Integration Team
11
+ * @support magentosupport@springbot.com
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ *
14
+ */
15
+ class Springbot_BoneCollector_Model_HarvestCart_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
16
+ {
17
+ const ACTION_METHOD = 'atc';
18
+
19
+ /**
20
+ * This exists as a naive dependency injector, so we can set the
21
+ * local object for testing purposes
22
+ *
23
+ * @param $quote Mage_Sales_Model_Quote
24
+ * @return Springbot_Combine_Model_Parser_Quote
25
+ */
26
+ protected function _initParser($quote)
27
+ {
28
+ if(!isset($this->_parser)) {
29
+ $this->_parser = Mage::getModel('Springbot_Combine_Model_Parser_Quote', $quote);
30
+ }
31
+ return $this->_parser;
32
+ }
33
+
34
+ /**
35
+ * Push cart object to api
36
+ *
37
+ * @param Varien_Event_Observer $observer
38
+ */
39
+ public function addToCartHarvest($observer)
40
+ {
41
+ try {
42
+ $this->_initObserver($observer);
43
+ $quoteObject = $observer->getQuote();
44
+ $quote = $this->_initParser($quoteObject);
45
+
46
+ if (
47
+ $quote->getItemsCount() > 0 &&
48
+ ($quote->hasCustomerData() || Mage::getStoreConfig('springbot/config/send_cart_noemail')) &&
49
+ $quote->getStoreId()
50
+ ) {
51
+ $json = $quote->toJson();
52
+
53
+ if(Mage::helper('combine')->doSendQuote($json)) {
54
+
55
+ Mage::helper('combine/trackable')->addTrackable(
56
+ $quote->getCustomerEmail(),
57
+ 'cart_user_agent',
58
+ $_SERVER['HTTP_USER_AGENT'],
59
+ $quote->getQuoteId(),
60
+ $quote->getCustomerId()
61
+ );
62
+
63
+ Springbot_Boss::scheduleJob(
64
+ 'post:cart',
65
+ array(
66
+ 's' => Mage::app()->getStore()->getId(),
67
+ 'i' => $quote->getQuoteId(),
68
+ 'r' => Mage::helper('combine/redirect')->getRawEscapedCookie()
69
+ ), Springbot_Services_Priority::LISTENER, 'listener'
70
+ );
71
+
72
+ $this->insertRedirectIds($quote);
73
+ $this->createTrackables($quote);
74
+ }
75
+ }
76
+ } catch (Exception $e) {
77
+ Mage::logException($e);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Capture sku for add to cart
83
+ * Inserts line into event csv to push
84
+ *
85
+ * @param Varien_Event_Observer $observer
86
+ */
87
+ public function captureSku($observer)
88
+ {
89
+ $this->_initObserver($observer);
90
+ $quoteId = Mage::getSingleton("checkout/session")->getQuote()->getId();
91
+
92
+ $eventDatetime = date("Y-m-d H:i:s");
93
+ $openModeAppend = 'a';
94
+ $eventHistoryFilename = Mage::getBaseDir().'/var/log/Springbot-EventHistory.csv';
95
+
96
+ try {
97
+ $storeId = Mage::app()->getStore()->getStoreId();
98
+ $lastCatId = $this->_getLastCategory();
99
+ $fHandle = fopen($eventHistoryFilename,$openModeAppend);
100
+ $viewedMessage = array(
101
+ self::ACTION_METHOD,
102
+ $eventDatetime,
103
+ $this->getTopLevelSku($observer),
104
+ $quoteId,
105
+ $storeId,
106
+ Mage::helper('combine')->checkCategoryIdSanity($lastCatId, $observer->getEvent()->getProduct())
107
+ );
108
+ fputcsv($fHandle,$viewedMessage,',');
109
+ fclose ($fHandle);
110
+
111
+ } catch (Exception $e) {
112
+ Mage::logException($e);
113
+ Mage::log('Unknown exception opening '.$eventHistoryFilename);
114
+ }
115
+ return;
116
+ }
117
+
118
+ public function insertRedirectIds($quote)
119
+ {
120
+ if(Mage::helper('combine/redirect')->hasRedirectId()) {
121
+ Springbot_Log::debug("Insert redirect id for customer : {$quote->getCustomerEmail()}");
122
+ $params = array(
123
+ 'email' => $quote->getCustomerEmail(),
124
+ 'quote_id' => $quote->getQuoteId(),
125
+ 'customer_id' => $quote->getCustomerId(),
126
+ );
127
+
128
+ Mage::helper('combine/redirect')->insertRedirectIds($params);
129
+ }
130
+ }
131
+
132
+ public function createTrackables($quote)
133
+ {
134
+ $helper = Mage::helper('combine/trackable');
135
+ $model = Mage::getModel('combine/trackable');
136
+
137
+ if($helper->hasTrackables()) {
138
+ foreach($helper->getTrackables() as $type => $value) {
139
+ $model->setData(array(
140
+ 'email' => $quote->getCustomerEmail(),
141
+ 'type' => $type,
142
+ 'value' => $value,
143
+ 'quote_id' => $quote->getQuoteId(),
144
+ 'customer_id' => $quote->getCustomerId(),
145
+ ));
146
+ $model->createOrUpdate();
147
+ }
148
+ }
149
+ }
150
+
151
+ public function setParser($parser)
152
+ {
153
+ $this->_parser = $parser;
154
+ }
155
+
156
+ protected function _getLastCategory()
157
+ {
158
+ return Mage::helper('combine')->getLastCategoryId();
159
+ }
160
+
161
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestCategory/Observer.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_BoneCollector_Model_HarvestCategory_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
4
+ {
5
+ public function saveCategory($observer)
6
+ {
7
+ $this->_initObserver($observer);
8
+
9
+ $categoryId = $observer->getEvent()->getCategory()->getEntityId();
10
+
11
+ if(!empty($categoryId)) {
12
+ Springbot_Boss::scheduleJob('post:category', array('i' => $categoryId), Springbot_Services_Priority::LISTENER, 'listener');
13
+ }
14
+ }
15
+
16
+
17
+ public function deleteCategory($observer)
18
+ {
19
+ try{
20
+ $category = $observer->getEvent()->getCategory();
21
+
22
+ $this->_initObserver($observer);
23
+ foreach(Mage::helper('combine/harvest')->mapStoreIds($category) as $mapped) {
24
+ $deleted = array(
25
+ 'store_id' => $mapped->getStoreId(),
26
+ 'cat_id' => $category->getEntityId(),
27
+ 'is_deleted' => true,
28
+ );
29
+ }
30
+
31
+ Mage::helper('combine/harvest')->deleteRemote($deleted, 'categories');
32
+ } catch (Exception $e) {
33
+ Mage::logException($e);
34
+ }
35
+ }
36
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestCustomer/Observer.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_BoneCollector_Model_HarvestCustomer_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
4
+ {
5
+ protected $_customer;
6
+
7
+ public function saveCustomer($observer)
8
+ {
9
+ try {
10
+ $this->_initObserver($observer);
11
+ $this->_customer = $observer->getEvent()->getCustomer();
12
+
13
+ if ($this->_entityChanged($this->_customer)) {
14
+ $customerId = $this->_customer->getId();
15
+ Springbot_Boss::scheduleJob('post:customer', array('i' => $customerId), Springbot_Services_Priority::LISTENER, 'listener');
16
+ }
17
+ } catch (Exception $e) {
18
+ Springbot_Log::error($e);
19
+ }
20
+ }
21
+
22
+ public function deleteCustomer($observer)
23
+ {
24
+ try {
25
+ // Runs blocking in session to guarantee record existence
26
+ $customer = $observer->getEvent()->getCustomer();
27
+
28
+ $this->_initObserver($observer);
29
+ Mage::getModel('Springbot_Services_Post_Customer')->setData(array(
30
+ 'start_id' => $customer->getId(),
31
+ 'delete' => true,
32
+ ))->run();
33
+
34
+ } catch (Exception $e) {
35
+ Springbot_Log::error($e);
36
+ }
37
+ }
38
+
39
+ protected function _getAttributesToListenFor($extras = array())
40
+ {
41
+ $codes = array();
42
+ $h = Mage::helper('combine/attributes');
43
+ $attributes = $h->getCustomerCustomAttributes($h->getCustomerAttributeSet());
44
+
45
+ foreach($attributes as $attribute) {
46
+ $codes[] = $attribute->getAttributeCode();
47
+ }
48
+
49
+ // Ensure we test for change in group
50
+ $codes[] = 'group_id';
51
+
52
+ return parent::_getAttributesToListenFor($codes);
53
+ }
54
+ }
55
+
app/code/community/Springbot/BoneCollector/Model/HarvestProduct/Observer.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoneCollector Event Listener (Product Harvest)
4
+ *
5
+ * @version v1.0.0 - 12/28/2012
6
+ *
7
+ * @category Magento Integrations
8
+ * @package springbot
9
+ * @author William Seitz
10
+ * @division SpringBot Integration Team
11
+ * @support magentosupport@springbot.com
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ *
14
+ */
15
+ class Springbot_BoneCollector_Model_HarvestProduct_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
16
+ {
17
+ protected $_product;
18
+
19
+ protected $_attributes = array(
20
+ 'entity_id',
21
+ 'sku',
22
+ 'attribute_set_id',
23
+ 'description',
24
+ 'full_description',
25
+ 'short_description',
26
+ 'image',
27
+ 'url_key',
28
+ 'small_image',
29
+ 'thumbnail',
30
+ 'status',
31
+ 'visibility',
32
+ 'price',
33
+ 'special_price',
34
+ 'image_label',
35
+ );
36
+
37
+ public function harvestProduct($observer)
38
+ {
39
+ try {
40
+ $this->_product = $observer->getEvent()->getProduct();
41
+
42
+ if ($this->_entityChanged($this->_product)) {
43
+ $this->_initObserver($observer);
44
+ Springbot_Boss::scheduleJob('post:product', array('i' => $this->_product->getId()), Springbot_Services_Priority::LISTENER, 'listener');
45
+ }
46
+
47
+ } catch (Exception $e) {
48
+ Mage::logException($e);
49
+ }
50
+ }
51
+
52
+ public function deleteProduct($observer)
53
+ {
54
+ $this->_initObserver($observer);
55
+
56
+ try{
57
+ $this->_product = $observer->getEvent()->getProduct();
58
+ $entity_id = $this->_product->getId();
59
+ $storeIds = $this->_product->getStoreIds();
60
+
61
+ foreach(Mage::helper('combine/harvest')->mapStoreIds($this->_product) as $mapped) {
62
+ $post[] = array(
63
+ 'store_id' => $mapped->getStoreId(),
64
+ 'entity_id' => $entity_id,
65
+ 'sku' => $this->_getSkuFailsafe($this->_product),
66
+ 'is_deleted' => true,
67
+ );
68
+ }
69
+
70
+ Mage::helper('combine/harvest')->deleteRemote($post, 'products');
71
+ } catch (Exception $e) {
72
+ Mage::logException($e);
73
+ }
74
+ }
75
+
76
+ protected function _getSkuFailsafe($product)
77
+ {
78
+ if ($sku = $product->getSku()) {
79
+ return $sku;
80
+ }
81
+ else {
82
+ return Springbot_Boss::NO_SKU_PREFIX . $product->getEntityId();
83
+ }
84
+ }
85
+
86
+ protected function _getAttributesToListenFor($extras = array())
87
+ {
88
+ return parent::_getAttributesToListenFor(
89
+ Mage::helper('combine/parser')->getCustomAttributeNames($this->_product)
90
+ );
91
+
92
+ }
93
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestPurchase/Observer.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Visitor BoneCollector Event Listener
4
+ *
5
+ * @version v1.0.0 - 12/28/2012
6
+ *
7
+ * @category Magento Integrations
8
+ * @package springbot
9
+ * @author William Seitz
10
+ * @division SpringBot Integration Team
11
+ * @support magentosupport@springbot.com
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ *
14
+ */
15
+ class Springbot_BoneCollector_Model_HarvestPurchase_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
16
+ {
17
+ const METHOD = 'purchase';
18
+ const COOKIE_NAME = 'springbot_redirect_queue';
19
+ const STDLIST_BASENAME = 'SpringbotServices-BoneCollector-Purchase';
20
+
21
+ protected $_order;
22
+
23
+ public function purchaseHarvest($observer)
24
+ {
25
+ if($this->_getCheckoutSession()->getSpringbotLogPurchaseAction() === true) {
26
+ $this->callPurchaseLogAction($observer);
27
+ $this->_getCheckoutSession()->unsSpringbotLogPurchaseAction();
28
+ }
29
+
30
+ if($this->_doSendPurchase($observer)) {
31
+ $this->_purchaseHarvest($observer, true);
32
+ }
33
+ }
34
+
35
+ public function purchaseHarvestAdmin($observer)
36
+ {
37
+ $this->_purchaseHarvest($observer, false);
38
+ }
39
+
40
+ public function purchaseLogAction($observer)
41
+ {
42
+ $this->_initObserver($observer);
43
+ $this->_getCheckoutSession()->setSpringbotLogPurchaseAction(true);
44
+ }
45
+
46
+ public function callPurchaseLogAction($observer)
47
+ {
48
+ try {
49
+ $this->_initObserver($observer);
50
+ $this->_order = $observer->getEvent()->getOrder();
51
+
52
+ Springbot_Boss::scheduleJob(
53
+ 'log:purchase',
54
+ array(
55
+ 'i' => $this->_order->getId(),
56
+ 'c' => $this->_getLastCategory(),
57
+ ),
58
+ Springbot_Services_Priority::LISTENER, 'listener'
59
+ );
60
+
61
+ } catch (Exception $e) {
62
+ Mage::logException($e);
63
+ }
64
+ }
65
+
66
+ protected function _purchaseHarvest($observer, $frontend = true)
67
+ {
68
+ try {
69
+ $this->_initObserver($observer);
70
+ $this->_order = $observer->getEvent()->getOrder();
71
+ $this->updateTrackables();
72
+
73
+ if($frontend) {
74
+ $this->_logUserAgent();
75
+ }
76
+ Springbot_Boss::scheduleJob('post:purchase',
77
+ array(
78
+ 'i' => $this->_order->getEntityId(),
79
+ 'c' => $this->_getLastCategory(),
80
+ 'r' => $this->getRedirectIds($frontend),
81
+ ),
82
+ Springbot_Services_Priority::LISTENER, 'listener'
83
+ );
84
+
85
+ } catch (Exception $e) {
86
+ Mage::logException($e);
87
+ }
88
+ }
89
+
90
+ protected function _logUserAgent() {
91
+ Mage::helper('combine/trackable')->addTrackable(
92
+ $this->_order->getCustomerEmail(),
93
+ 'purchase_user_agent',
94
+ $_SERVER['HTTP_USER_AGENT'],
95
+ $this->_order->getQuoteId(),
96
+ $this->_order->getCustomerId()
97
+ );
98
+ }
99
+
100
+ public function updateTrackables()
101
+ {
102
+ $helper = Mage::helper('combine/trackable');
103
+ $params = $helper->getTrackables();
104
+ $quoteId = $this->_order->getQuoteId();
105
+
106
+ foreach($this->getTrackablesForQuote($quoteId) as $trackable) {
107
+ $trackable->setOrderId($this->_order->getId())
108
+ ->setCustomerId($this->_order->getCustomerId())
109
+ ->save();
110
+ }
111
+ }
112
+
113
+ public function getTrackablesForQuote($quoteId)
114
+ {
115
+ return Mage::getModel('combine/trackable')->getCollection()
116
+ ->addFieldToFilter('quote_id', $quoteId);
117
+ }
118
+
119
+ protected function _doSendPurchase($observer)
120
+ {
121
+ $order = $observer->getEvent()->getOrder();
122
+ $hash = sha1($order->toJson());
123
+ $session = $this->_getCheckoutSession();
124
+
125
+ if($session->getSpringbotOrderHash() == $hash) {
126
+ Springbot_Log::debug("Purchase hash is match, this object has already been posted, skipping");
127
+ return false;
128
+ } else {
129
+ $session->setSpringbotOrderHash($hash);
130
+ Springbot_Log::debug("Purchase hash does not match cache, sending purchase");
131
+ return true;
132
+ }
133
+ }
134
+
135
+ protected function _getCustomerEmail()
136
+ {
137
+ return $this->_order->getCustomerEmail();
138
+ }
139
+
140
+ protected function _getLastCategory()
141
+ {
142
+ return Mage::helper('combine')->getLastCategoryId();
143
+ }
144
+
145
+ protected function _getCheckoutSession()
146
+ {
147
+ return Mage::getSingleton('checkout/session');
148
+ }
149
+
150
+ public function getRedirectIds($frontend = true)
151
+ {
152
+ $redirects = $frontend ? Mage::helper('combine/redirect')->getRedirectIds() : array();
153
+
154
+ if($dbRedirects = Mage::helper('combine/redirect')->getRedirectsByEmail($this->_getCustomerEmail(), $this->_order->getCreatedAt())) {
155
+ $redirects = array_unique(array_merge($redirects, $dbRedirects));
156
+ }
157
+
158
+ return array_values($redirects);
159
+ }
160
+ }
app/code/community/Springbot/BoneCollector/Model/HarvestSubscriber/Observer.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_BoneCollector_Model_HarvestSubscriber_Observer extends Springbot_BoneCollector_Model_HarvestAbstract
4
+ {
5
+ public function saveSubscriber($observer)
6
+ {
7
+ try {
8
+ $this->_initObserver($observer);
9
+ $subscriberId = $observer->getEvent()->getSubscriber()->getId();
10
+
11
+ Springbot_Boss::scheduleJob(
12
+ 'post:subscriber',
13
+ array('i' => $subscriberId),
14
+ Springbot_Services_Priority::LISTENER,
15
+ 'listener'
16
+ );
17
+
18
+ } catch (Exception $e) {
19
+ Springbot_Log::error($e);
20
+ }
21
+ }
22
+
23
+ public function deleteSubscriber($observer)
24
+ {
25
+ try {
26
+ // Runs blocking in session to guarantee record existence
27
+ $this->_initObserver($observer);
28
+ Mage::getModel('Springbot_Services_Post_Subscriber')->setData(array(
29
+ 'start_id' => $observer->getEvent()->getSubscriber()->getId(),
30
+ 'delete' => true,
31
+ ))->run();
32
+ } catch (Exception $e) {
33
+ Springbot_Log::error($e);
34
+ }
35
+ }
36
+ }
37
+
app/code/community/Springbot/BoneCollector/etc/config.xml ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * @category springbot
5
+ * @package springbot_BoneCollector
6
+ * @author springbot integration team
7
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+ */
9
+ -->
10
+ <config>
11
+
12
+ <global>
13
+ <models>
14
+ <bonecollector>
15
+ <class>Springbot_BoneCollector_Model</class>
16
+ </bonecollector>
17
+ </models>
18
+ <events>
19
+ <catalog_product_save_after>
20
+ <observers>
21
+ <springbot_bonecollector_product_observer>
22
+ <type>singleton</type>
23
+ <class>Springbot_BoneCollector_Model_HarvestProduct_Observer</class>
24
+ <method>harvestProduct</method>
25
+ </springbot_bonecollector_product_observer>
26
+ </observers>
27
+ </catalog_product_save_after>
28
+ <catalog_product_delete_before>
29
+ <observers>
30
+ <springbot_bonecollector_productdelete_observer>
31
+ <type>singleton</type>
32
+ <class>Springbot_BoneCollector_Model_HarvestProduct_Observer</class>
33
+ <method>deleteProduct</method>
34
+ </springbot_bonecollector_productdelete_observer>
35
+ </observers>
36
+ </catalog_product_delete_before>
37
+ <catalog_category_save_after>
38
+ <observers>
39
+ <springbot_bonecollector_categorysave_observer>
40
+ <type>singleton</type>
41
+ <class>Springbot_BoneCollector_Model_HarvestCategory_Observer</class>
42
+ <method>saveCategory</method>
43
+ </springbot_bonecollector_categorysave_observer>
44
+ </observers>
45
+ </catalog_category_save_after>
46
+ <catalog_category_delete_after>
47
+ <observers>
48
+ <springbot_bonecollector_categorysave_observer>
49
+ <type>singleton</type>
50
+ <class>Springbot_BoneCollector_Model_HarvestCategory_Observer</class>
51
+ <method>deleteCategory</method>
52
+ </springbot_bonecollector_categorysave_observer>
53
+ </observers>
54
+ </catalog_category_delete_after>
55
+ <customer_save_after>
56
+ <observers>
57
+ <springbot_bonecollector_customersave_observer>
58
+ <type>singleton</type>
59
+ <class>Springbot_BoneCollector_Model_HarvestCustomer_Observer</class>
60
+ <method>saveCustomer</method>
61
+ </springbot_bonecollector_customersave_observer>
62
+ </observers>
63
+ </customer_save_after>
64
+ <customer_delete_before>
65
+ <observers>
66
+ <springbot_bonecollector_customersave_observer>
67
+ <type>singleton</type>
68
+ <class>Springbot_BoneCollector_Model_HarvestCustomer_Observer</class>
69
+ <method>deleteCustomer</method>
70
+ </springbot_bonecollector_customersave_observer>
71
+ </observers>
72
+ </customer_delete_before>
73
+ <newsletter_subscriber_save_after>
74
+ <observers>
75
+ <springbot_bonecollector_customersave_observer>
76
+ <type>singleton</type>
77
+ <class>Springbot_BoneCollector_Model_HarvestSubscriber_Observer</class>
78
+ <method>saveSubscriber</method>
79
+ </springbot_bonecollector_customersave_observer>
80
+ </observers>
81
+ </newsletter_subscriber_save_after>
82
+ <newsletter_subscriber_delete_before>
83
+ <observers>
84
+ <springbot_bonecollector_customersave_observer>
85
+ <type>singleton</type>
86
+ <class>Springbot_BoneCollector_Model_HarvestSubscriber_Observer</class>
87
+ <method>deleteSubscriber</method>
88
+ </springbot_bonecollector_customersave_observer>
89
+ </observers>
90
+ </newsletter_subscriber_delete_before>
91
+ </events>
92
+ </global>
93
+ <frontend>
94
+ <events>
95
+ <sales_order_place_after>
96
+ <observers>
97
+ <springbot_bonecollector_purchase_observer>
98
+ <type>singleton</type>
99
+ <class>Springbot_BoneCollector_Model_HarvestPurchase_Observer</class>
100
+ <method>purchaseLogAction</method>
101
+ </springbot_bonecollector_purchase_observer>
102
+ </observers>
103
+ </sales_order_place_after>
104
+ <sales_order_save_after>
105
+ <observers>
106
+ <springbot_bonecollector_purchase_observer>
107
+ <type>singleton</type>
108
+ <class>Springbot_BoneCollector_Model_HarvestPurchase_Observer</class>
109
+ <method>purchaseHarvest</method>
110
+ </springbot_bonecollector_purchase_observer>
111
+ </observers>
112
+ </sales_order_save_after>
113
+ <sales_quote_save_after>
114
+ <observers>
115
+ <springbot_bonecollector_addtocart_observer>
116
+ <type>singleton</type>
117
+ <class>Springbot_BoneCollector_Model_HarvestCart_Observer</class>
118
+ <method>addToCartHarvest</method>
119
+ </springbot_bonecollector_addtocart_observer>
120
+ </observers>
121
+ </sales_quote_save_after>
122
+ <catalog_controller_product_view>
123
+ <observers>
124
+ <springbot_bonecollector_capturesku_observer>
125
+ <type>singleton</type>
126
+ <class>Springbot_BoneCollector_Model_HarvestCaptureSKU_Observer</class>
127
+ <method>captureskuharvest</method>
128
+ </springbot_bonecollector_capturesku_observer>
129
+ </observers>
130
+ </catalog_controller_product_view>
131
+ <checkout_cart_add_product_complete>
132
+ <observers>
133
+ <springbot_bonecollector_addskutocart_observer>
134
+ <type>singleton</type>
135
+ <class>Springbot_BoneCollector_Model_HarvestCart_Observer</class>
136
+ <method>captureSku</method>
137
+ </springbot_bonecollector_addskutocart_observer>
138
+ </observers>
139
+ </checkout_cart_add_product_complete>
140
+ </events>
141
+ </frontend>
142
+ <adminhtml>
143
+ <events>
144
+ <sales_order_save_after>
145
+ <observers>
146
+ <springbot_bonecollector_purchase_observer>
147
+ <type>singleton</type>
148
+ <class>Springbot_BoneCollector_Model_HarvestPurchase_Observer</class>
149
+ <method>purchaseHarvestAdmin</method>
150
+ </springbot_bonecollector_purchase_observer>
151
+ </observers>
152
+ </sales_order_save_after>
153
+ <catalog_entity_attribute_save_commit_after>
154
+ <observers>
155
+ <springbot_bonecollector_attribute_observer>
156
+ <type>singleton</type>
157
+ <class>Springbot_BoneCollector_Model_HarvestAttribute_Observer</class>
158
+ <method>attributeSave</method>
159
+ </springbot_bonecollector_attribute_observer>
160
+ </observers>
161
+ </catalog_entity_attribute_save_commit_after>
162
+ <eav_entity_attribute_save_commit_after>
163
+ <observers>
164
+ <springbot_bonecollector_attribute_observer>
165
+ <type>singleton</type>
166
+ <class>Springbot_BoneCollector_Model_HarvestAttribute_Observer</class>
167
+ <method>attributeSetSave</method>
168
+ </springbot_bonecollector_attribute_observer>
169
+ </observers>
170
+ </eav_entity_attribute_save_commit_after>
171
+ </events>
172
+ </adminhtml>
173
+ </config>
app/code/community/Springbot/Boss.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Boss
4
+ {
5
+ private static $_loggers;
6
+ private static $_currentStore;
7
+
8
+ const EVENT_FILENAME = 'Springbot-EventHistory.csv';
9
+ const SOURCE_BULK_HARVEST = 'BH';
10
+ const SOURCE_OBSERVER = 'OB';
11
+ const DATE_FORMAT = 'Y-m-d H:i:s';
12
+ const NO_SKU_PREFIX = '_sbentity-';
13
+
14
+
15
+ /**
16
+ * Schedule cron job
17
+ *
18
+ * @param string $method
19
+ * @param array $args
20
+ * @param int $priority
21
+ * @param string $queue
22
+ * @param int $storeId
23
+ * @param bool $requiresAuth
24
+ */
25
+ public static function scheduleJob($method, $args, $priority, $queue = 'default', $storeId = null, $requiresAuth = true)
26
+ {
27
+ if($requiresAuth && !self::storeIdsExist()) {
28
+ Springbot_Log::debug('Not authenticated, job not queued');
29
+ }
30
+ else {
31
+ $cronner = Mage::getModel('combine/cron_queue');
32
+ $cronner->setData(array(
33
+ 'method' => $method,
34
+ 'args' => json_encode($args),
35
+ 'priority' => $priority,
36
+ 'command_hash' => sha1($method . json_encode($args)),
37
+ 'queue' => $queue,
38
+ 'store_id' => $storeId
39
+ ));
40
+
41
+ $cronner->insertIgnore();
42
+ self::startWorkManager();
43
+ }
44
+ }
45
+
46
+ public static function startWorkManager()
47
+ {
48
+ $status = Mage::getModel('combine/cron_manager_status');
49
+ if(
50
+ !$status->isBlocked() &&
51
+ !$status->isActive()
52
+ ) {
53
+ Springbot_Boss::internalCallback('work:manager');
54
+ }
55
+ }
56
+
57
+ /**
58
+ *
59
+ *
60
+ * @param string $method
61
+ * @param array $args
62
+ * @param bool $background
63
+ */
64
+ public static function internalCallback($method, $args = array(), $background = true)
65
+ {
66
+ $bkg = $background ? '&' : '';
67
+ $fmt = self::buildFlags($args);
68
+ $php = Mage::helper('combine/harvest')->getPhpExec();
69
+ $dir = Mage::getBaseDir();
70
+ $err = Mage::helper('combine')->getSpringbotErrorLog();
71
+ $log = Mage::helper('combine')->getSpringbotLog();
72
+ $nohup = Mage::helper('combine')->nohup();
73
+ $nice = Mage::helper('combine')->nice();
74
+
75
+ $cmd = "{$nohup} {$nice} {$php} {$dir}/shell/springbot.php {$fmt} {$method} >> {$log} 2>> {$err} {$bkg}";
76
+ return self::spawn($cmd);
77
+ }
78
+
79
+ /**
80
+ * Build cli flags from arg array
81
+ *
82
+ * @param array $args
83
+ * @return string
84
+ */
85
+ public static function buildFlags($args)
86
+ {
87
+ $fmt = array();
88
+
89
+ foreach($args as $flag => $arg) {
90
+ if(is_int($flag)) {
91
+ $flag = $arg;
92
+ $arg = '';
93
+ }
94
+ $fmt[] = "-$flag $arg";
95
+ }
96
+ return implode(' ', $fmt);
97
+ }
98
+
99
+ /**
100
+ * Spawn system callback with any available system command
101
+ *
102
+ * @param string $command
103
+ * @param int $return_var
104
+ */
105
+ public static function spawn($command, &$return_var = 0)
106
+ {
107
+ Springbot_Log::debug($command);
108
+ if(function_exists('system')) {
109
+ $ret = system($command, $return_var);
110
+ } else if(function_exists('exec')) {
111
+ $ret = exec($command, $return_var);
112
+ } else if(function_exists('passthru')) {
113
+ $ret = passthru($command, $return_var);
114
+ } else if(function_exists('shell_exec')) {
115
+ $ret = shell_exec($command);
116
+ } else {
117
+ throw new Exception('Program execution function not found!');
118
+ }
119
+ Springbot_Log::debug($ret);
120
+ return $ret;
121
+ }
122
+
123
+ public static function launchHarvest()
124
+ {
125
+ Mage::helper('combine/harvest')->truncateEngineLogs();
126
+ Springbot_Boss::internalCallback('cmd:harvest');
127
+ }
128
+
129
+ /**
130
+ * This method kills all processes which contain 'Harvest' in the command by default.
131
+ * Use carefully!
132
+ *
133
+ * @param string $toHalt
134
+ */
135
+ public static function halt()
136
+ {
137
+ $queueDb = new Springbot_Combine_Model_Mysql4_Cron_Queue;
138
+ $queueDb->removeHarvestRows();
139
+ }
140
+
141
+ public static function haltStore($storeId)
142
+ {
143
+ $queueDb = new Springbot_Combine_Model_Mysql4_Cron_Queue;
144
+ $queueDb->removeStoreHarvestRows($storeId);
145
+ }
146
+
147
+ public static function setActive($storeId)
148
+ {
149
+ self::$_currentStore = $storeId;
150
+ }
151
+
152
+ public static function getEventHistoryFilename()
153
+ {
154
+ return Mage::getBaseDir('var') . DS . 'log' . DS . self::EVENT_FILENAME;
155
+ }
156
+
157
+ public static function isCron()
158
+ {
159
+ return Mage::getStoreConfig('springbot/cron/enabled');
160
+ }
161
+
162
+ public static function storeIdsExist()
163
+ {
164
+ $configValues = Mage::getStoreConfig('springbot/config');
165
+ $storeIdStr = 'store_id_';
166
+ foreach ($configValues as $configName => $configValue) {
167
+ if(substr($configName, 0, strlen($storeIdStr)) == $storeIdStr) {
168
+ if (!is_numeric($configValue)) {
169
+ return false;
170
+ }
171
+ }
172
+ }
173
+ return true;
174
+ }
175
+
176
+ }
app/code/community/Springbot/Combine/Helper/Attributes.php ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // @TODO test for store id
4
+
5
+ class Springbot_Combine_Helper_Attributes extends Mage_Core_Helper_Abstract
6
+ {
7
+ public function getAttributeSets($type = 'catalog_product')
8
+ {
9
+ $entityTypeId = Mage::getModel('eav/entity')
10
+ ->setType($type)
11
+ ->getTypeId();
12
+ return Mage::getModel('eav/entity_attribute_set')
13
+ ->getCollection()
14
+ ->setEntityTypeFilter($entityTypeId);
15
+ }
16
+
17
+ public function getCustomerAttributeSet()
18
+ {
19
+ return $this->getCustomerAttributeSets()->getFirstItem();
20
+ }
21
+
22
+ public function getCustomerAttributeSets()
23
+ {
24
+ return $this->getAttributeSets('customer');
25
+ }
26
+
27
+ public function getAttributeSetById($id)
28
+ {
29
+ return Mage::getModel('eav/entity_attribute_set')->load($id);
30
+ }
31
+
32
+ public function getAttributesBySet($attributeSet)
33
+ {
34
+ if(!is_int($attributeSet)) {
35
+ $attributeSet = $this->_resolveSet($attributeSet);
36
+ }
37
+ return Mage::getResourceModel('eav/entity_attribute_collection')
38
+ ->setAttributeSetFilter($attributeSet)
39
+ ->addSetInfo();
40
+ }
41
+
42
+ public function getCustomAttributesBySet($attributeSet)
43
+ {
44
+ $collection = $this->getAttributesBySet($attributeSet);
45
+ return $this->filterNonUserDefined($collection);
46
+ }
47
+
48
+ public function getCustomerCustomAttributes($attributeSet)
49
+ {
50
+ $collection = $this->getAttributesBySet($attributeSet);
51
+ return $this->filterSystemForCustomers($collection);
52
+ }
53
+
54
+ public function getAttributeGroupsBySet($attributeSet)
55
+ {
56
+ return Mage::getModel('eav/entity_attribute_group')
57
+ ->getResourceCollection()
58
+ ->setAttributeSetFilter($this->_resolveSet($attributeSet));
59
+ }
60
+
61
+ public function getProductAttributesByGroup($group)
62
+ {
63
+ return Mage::getResourceModel('catalog/product_attribute_collection')
64
+ ->setAttributeGroupFilter($group->getId())
65
+ ->addVisibleFilter()
66
+ ->checkConfigurableProducts();
67
+ }
68
+
69
+ public function getAllSetsForAttribute($attribute)
70
+ {
71
+ if(is_object($attribute)) {
72
+ $attribute = $attribute->getAttributeId();
73
+ }
74
+ $collection = Mage::getModel('eav/entity_attribute')
75
+ ->getCollection()
76
+ ->addSetInfo()
77
+ ->addFieldToFilter('attribute_id', $attribute);
78
+
79
+ if($attribute = $collection->getFirstItem()) {
80
+ $sets = $attribute->getAttributeSetInfo();
81
+ if(is_array($sets)) {
82
+ return array_keys($sets);
83
+ }
84
+ }
85
+ }
86
+
87
+ public function getOptionText($attribute, $value)
88
+ {
89
+ foreach($this->getOptionsByAttribute($attribute) as $option) {
90
+ if(isset($option['value']) && $option['value'] == $value) {
91
+ return $option['label'];
92
+ }
93
+ }
94
+ }
95
+
96
+ public function getOptionsByAttribute($attribute)
97
+ {
98
+ try {
99
+ if($attribute->usesSource()) {
100
+ return Mage::getResourceModel('eav/entity_attribute_option_collection')
101
+ ->setAttributeFilter($attribute->getId())
102
+ ->setStoreFilter(0,false)
103
+ ->toOptionArray();
104
+ }
105
+ } catch (Exception $e) {
106
+ // onward! We don't stop for poor api design.
107
+ }
108
+ }
109
+
110
+ public function getParsedAttributesBySet($set)
111
+ {
112
+ return $this->parseAttributes($this->getCustomAttributesBySet($set));
113
+ }
114
+
115
+ public function parseAttributes($_attributes)
116
+ {
117
+ foreach($_attributes as $attr) {
118
+ $toInsert = array(
119
+ 'label' => $attr->getFrontendLabel(),
120
+ 'attribute_id' => $attr->getAttributeId(),
121
+ 'attribute_code' => $attr->getAttributeCode(),
122
+ );
123
+
124
+ if($options = $this->getOptionsByAttribute($attr)) {
125
+ $toInsert['options'] = $this->pluckOptions($options);
126
+ }
127
+ $attributes[] = $toInsert;
128
+ }
129
+ return $attributes;
130
+ }
131
+
132
+ public function pluckOptions($options)
133
+ {
134
+ return array_values($this->pluck($options, 'label'));
135
+ }
136
+
137
+ public function pluck($array, $field)
138
+ {
139
+ $_array = array();
140
+ foreach($array as $item) {
141
+ $_array[] = $item[$field];
142
+ }
143
+ return $_array;
144
+ }
145
+
146
+ public function filterNonUserDefined($collection)
147
+ {
148
+ return $collection->addFieldToFilter('is_user_defined', array('gt' => 0));
149
+ }
150
+
151
+ public function filterSystemForCustomers($collection)
152
+ {
153
+ $table = Mage::getSingleton('core/resource')->getTableName('customer/eav_attribute');
154
+
155
+ $collection->getSelect()->join(
156
+ array('additional_table' => $table),
157
+ 'additional_table.attribute_id = main_table.attribute_id',
158
+ 'is_system'
159
+ )->where('is_system = 0');
160
+
161
+ return $collection;
162
+ }
163
+
164
+ protected function _resolveSet($attributeSet)
165
+ {
166
+ if($attributeSet instanceof Mage_Eav_Model_Entity_Attribute_Set) {
167
+ $attributeSet = $attributeSet->getAttributeSetId();
168
+ }
169
+ return $attributeSet;
170
+ }
171
+ }
app/code/community/Springbot/Combine/Helper/Cart.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Cart extends Mage_Core_Helper_Abstract
4
+ {
5
+ public function setQuote($quoteId, $suppliedSecurityHash) {
6
+ if (Mage::getStoreConfig('springbot/cart_restore/do_restore') == 1) {
7
+ if ($quote = Mage::getModel('sales/quote')->load($quoteId)) {
8
+ $cartCount = Mage::helper('checkout/cart')->getSummaryCount();
9
+ if ($cartCount == 0) {
10
+
11
+ $quote->setIsActive(true)->save();
12
+ $token = Mage::getStoreConfig('springbot/config/security_token');
13
+
14
+ $correctSecurityHash = sha1($quoteId . $token);
15
+ if ($suppliedSecurityHash == $correctSecurityHash) {
16
+
17
+ if (Mage::getStoreConfig('springbot/cart_restore/retain_coupon') == 0) {
18
+ $quote->setCouponCode('');
19
+ $quote->save();
20
+ }
21
+
22
+ Mage::getSingleton('checkout/session')->setQuoteId($quoteId);
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
app/code/community/Springbot/Combine/Helper/Data.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+ public function formatDateTime($date = null)
6
+ {
7
+ $_date = new DateTime($date, new DateTimeZone('UTC'));
8
+ return $_date->format(DateTime::ATOM);
9
+ }
10
+
11
+ public function getStoreGuid($storeId)
12
+ {
13
+ $guid = Mage::getStoreConfig('springbot/config/store_guid_' . $storeId);
14
+ if (empty($guid)) {
15
+ $charid = strtoupper(md5(uniqid(rand(), true)));
16
+ $guid = substr($charid, 0, 8).'-'
17
+ .substr($charid, 8, 4).'-'
18
+ .substr($charid,12, 4).'-'
19
+ .substr($charid,16, 4).'-'
20
+ .substr($charid,20,12);
21
+ }
22
+ return $guid;
23
+ }
24
+
25
+ public function apiPostWrapped($model, $struct, $arrayWrap = false)
26
+ {
27
+ if($arrayWrap) {
28
+ $struct = array($struct);
29
+ }
30
+ $api = Mage::getModel('combine/api');
31
+ $payload = $api->wrap($model, $struct);
32
+ return $api->reinit()->call($model, $payload);
33
+ }
34
+
35
+ public function checkCredentials($email = null, $password = null)
36
+ {
37
+ $return = array('valid' => false);
38
+
39
+ try {
40
+ $this->requestSecurityToken($email, $password, true);
41
+ $return['valid'] = true;
42
+ } catch (Exception $e) {
43
+ $return['message'] = $e->getMessage();
44
+ }
45
+ return $return;
46
+ }
47
+
48
+ public function requestSecurityToken($email = null, $password = null, $force = false)
49
+ {
50
+ $token = Mage::getStoreConfig('springbot/config/security_token');
51
+ if($token && !$force) {
52
+ return $token;
53
+ }
54
+
55
+ $payload = $this->_resolvePassword($email, $password);
56
+
57
+ $response = Mage::getModel('combine/api')->call('registration/login', json_encode($payload), false);
58
+
59
+ if(!isset($response['token'])) {
60
+ throw new Exception($response['message']);
61
+ }
62
+ return $response['token'];
63
+ }
64
+
65
+ protected function _resolvePassword($email = null, $password = null)
66
+ {
67
+ if(is_null($email) || is_null($password)) {
68
+ $payload = array(
69
+ 'user_id' => Mage::getStoreConfig('springbot/config/account_email'),
70
+ 'password' => Mage::helper('core')->decrypt(Mage::getStoreConfig('springbot/config/account_password')),
71
+ );
72
+ } else {
73
+ $payload = array(
74
+ 'user_id' => $email,
75
+ 'password' => $password,
76
+ );
77
+ }
78
+ return $payload;
79
+ }
80
+
81
+ public function doSendQuote($json)
82
+ {
83
+ $obj = json_decode($json);
84
+ $items = sha1(json_encode((isset($obj->line_items)) ? $obj->line_items : array()));
85
+
86
+ if(strcmp(Mage::getSingleton('core/session')->getSpringbotPostedQuoteItems(), $items) !== 0) {
87
+ Mage::getSingleton('core/session')->setSpringbotPostedQuoteItems($items);
88
+ return true;
89
+ } else {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ public function escapeShell($arg)
95
+ {
96
+ if(function_exists('escapeshellarg')) {
97
+ return escapeshellarg($arg);
98
+ } else {
99
+ return "'" . str_replace("'", "'\"'\"'", $arg) . "'";
100
+ }
101
+ }
102
+
103
+ public function getMicroTime()
104
+ {
105
+ $mtime = explode(" ",microtime());
106
+ return $mtime[1] + $mtime[0];
107
+ }
108
+
109
+ public function isJson($string)
110
+ {
111
+ return is_string($string) && json_decode($string) != null;
112
+ }
113
+
114
+ public function getLastCategoryId()
115
+ {
116
+ $this->setLastCategoryId();
117
+ return Mage::getSingleton('core/session')->getSpringbotLastCategoryId();
118
+ }
119
+
120
+ public function setLastCategoryId()
121
+ {
122
+ $product = Mage::registry('current_product');
123
+ $category = Mage::registry('current_category');
124
+
125
+ if($categoryId = $this->resolveCategoryId($category, $product)) {
126
+ Mage::getSingleton('core/session')->setSpringbotLastCategoryId($categoryId);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Resolve category id to set in session
132
+ *
133
+ * We are handling multiple states:
134
+ * cat && prod => check if cat makes sense
135
+ * prod => pop cat id from prod
136
+ * cat => use cat id
137
+ * !cat && !prod => use cached
138
+ *
139
+ * @param Mage_Catalog_Model_Category $category
140
+ * @param Mage_Catalog_Model_Product $product
141
+ * @return int|null
142
+ */
143
+ public function resolveCategoryId($category, $product)
144
+ {
145
+ $categoryId = null;
146
+ if((isset($product) || isset($category)))
147
+ {
148
+ if(isset($product) && isset($category))
149
+ {
150
+ $productCatIds = $product->getCategoryIds();
151
+ $categoryId = $category->getId();
152
+
153
+ if(!in_array($categoryId, $productCatIds))
154
+ {
155
+ $categoryId = array_pop($productCatIds);
156
+ }
157
+ }
158
+ else if(isset($product))
159
+ {
160
+ $productCatIds = $product->getCategoryIds();
161
+ $categoryId = array_pop($productCatIds);
162
+ }
163
+ else if (isset($category))
164
+ {
165
+ $categoryId = $category->getId();
166
+ }
167
+ }
168
+ return $categoryId;
169
+ }
170
+
171
+ public function checkCategoryIdSanity($categoryId, $product)
172
+ {
173
+ if(!$product instanceof Varien_Object) {
174
+ $product = Mage::getModel('catalog/product')->load($product);
175
+ }
176
+ return $this->resolveCategoryId(
177
+ new Varien_Object(array('id' => $categoryId)),
178
+ $product
179
+ );
180
+ }
181
+
182
+ public function getSpringbotErrorLog()
183
+ {
184
+ return Mage::getBaseDir('var') . DS . 'log' . DS . Springbot_Log::ERRFILE;
185
+ }
186
+
187
+ public function getSpringbotLog()
188
+ {
189
+ return Mage::getBaseDir('var') . DS . 'log' . DS . Springbot_Log::LOGFILE;
190
+ }
191
+
192
+ public function isEmpty($obj)
193
+ {
194
+ return count((array) $obj) == 0;
195
+ }
196
+
197
+ public function nohup()
198
+ {
199
+ return Mage::getStoreConfig('springbot/advanced/nohup') ? 'nohup' : '';
200
+ }
201
+
202
+ public function nice()
203
+ {
204
+ return Mage::getStoreConfig('springbot/advanced/nice') ? 'nice' : '';
205
+ }
206
+
207
+ public function getLogContents($logName)
208
+ {
209
+ $maxRecSize = 65536;
210
+ if (empty($logName)) {
211
+ $fullFilename = Mage::getBaseDir('log') . DS . Springbot_Log::LOGFILE;
212
+ } elseif (strpos($logName, '/') === 0){
213
+ $fullFilename = $logName;
214
+ } else {
215
+ $fullFilename = Mage::getBaseDir('log') . '/' . $logName;
216
+ }
217
+
218
+ $buffer = '';
219
+ if(file_exists($fullFilename)) {
220
+ if (($fHandle = fopen($fullFilename, 'r')) !== FALSE) {
221
+ $fSize = filesize($fullFilename)/1024;
222
+ if ($fSize > 32) {
223
+ fseek($fHandle, 1024*($fSize-32));
224
+ }
225
+ while (!feof($fHandle)) {
226
+ $buffer .= fgets($fHandle,$maxRecSize) . ' ';
227
+ }
228
+ fclose ($fHandle);
229
+ } else {
230
+ $buffer='Open failed on '.$fullFilename;
231
+ }
232
+ }
233
+ return $buffer;
234
+ }
235
+ }
app/code/community/Springbot/Combine/Helper/Harvest.php ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Harvest extends Mage_Core_Helper_Abstract
4
+ {
5
+ protected $_ignores;
6
+ protected $_defines;
7
+ protected $_rulesBuilt = false;
8
+ protected $_phpExec;
9
+ protected $_harvestId;
10
+
11
+ public function initRemoteHarvest($id)
12
+ {
13
+ $api = Mage::getModel('combine/api');
14
+
15
+ Springbot_Log::debug("Query remote service for harvest id");
16
+
17
+ $response = $api->get('harvests/new', array('store_id' => $this->getSpringbotStoreId($id)));
18
+
19
+ if(isset($response['harvest_id'])) {
20
+ $this->_harvestId = $response['harvest_id'];
21
+ } else {
22
+ Springbot_Log::debug("Harvest id not present in response");
23
+ Springbot_Log::debug($response);
24
+ }
25
+
26
+ return $this->_harvestId;
27
+ }
28
+
29
+ public function reinit()
30
+ {
31
+ $this->_buildStoreRules();
32
+ return $this;
33
+ }
34
+
35
+ /**
36
+ * Map store ids to given model
37
+ *
38
+ * This method intends to apply all appropriate store ids to an object (namely
39
+ * categories and products), but could be anything. If the callback fails to
40
+ * provide any store ids, we failover to provide the originally supplied model.
41
+ *
42
+ * @param Varien_Object $model
43
+ * @param mixed $storeIds
44
+ * @param string $callback
45
+ * @return array<Varien_Object>
46
+ */
47
+ public function mapStoreIds($model, $storeIds = null, $callback = 'getStoreIds')
48
+ {
49
+ $output = array();
50
+
51
+ if(!$storeIds) {
52
+ $storeIds = $model->{$callback}();
53
+ }
54
+
55
+ foreach($storeIds as $id) {
56
+ if($id) {
57
+ $_model = clone $model;
58
+ $_model = $_model->setStoreId($id)->load($_model->getId());
59
+ $output[] = $_model;
60
+ }
61
+ }
62
+
63
+ return !empty($output) ? $output : array($model);
64
+ }
65
+
66
+ /**
67
+ * Get PHP executable
68
+ *
69
+ * @return string
70
+ */
71
+ public function getPhpExec()
72
+ {
73
+ if(!isset($this->_phpExec)) {
74
+ $php = Mage::getStoreConfig('springbot/config/php_exec');
75
+
76
+ if((empty($php))) {
77
+ // This prevents the system command from outputting to apache
78
+ ob_start();
79
+ if(empty($php) || !file_exists($php)) {
80
+ $php = Springbot_Boss::spawn('which php5 2> /dev/null');
81
+ }
82
+ if(empty($php) || !file_exists($php)) {
83
+ $php = Springbot_Boss::spawn('which php 2> /dev/null');
84
+ }
85
+ if(empty($php) || !file_exists($php)) {
86
+ $php = 'php';
87
+ }
88
+ ob_end_clean();
89
+ }
90
+ $this->_phpExec = $php;
91
+ }
92
+ return $this->_phpExec;
93
+ }
94
+
95
+ /**
96
+ * Get last collection primary id
97
+ *
98
+ * @param Varien_Data_Collection $collection
99
+ * @return int
100
+ */
101
+ public function getLastCollectionId($collection, $dir = 'DESC')
102
+ {
103
+ $collection->clear();
104
+
105
+ $id = $this->getIdFieldName($collection);
106
+
107
+ $collection->getSelect()
108
+ ->reset(Zend_Db_Select::ORDER)
109
+ ->order("$id $dir")
110
+ ->limit(1);
111
+ return $collection->getFirstItem()->getData($id);
112
+ }
113
+
114
+ public function getFirstCollectionId($collection)
115
+ {
116
+ return $this->getLastCollectionId($collection, 'ASC');
117
+ }
118
+
119
+ /**
120
+ * Get id field name for collection
121
+ *
122
+ * Attepmt to get id field name (sql primary key) for collection through
123
+ * existing methods, then failing over to inspecting an the resource itself.
124
+ * Mainly done this way for subscribers.
125
+ *
126
+ * @param Varien_Data_Collection $collection
127
+ * @param string $default | optional
128
+ * @return string
129
+ */
130
+ public function getIdFieldName($collection, $default = 'entity_id')
131
+ {
132
+ if(method_exists($collection, 'getIdFieldName')) {
133
+ $id = $collection->getIdFieldName();
134
+ }
135
+
136
+ if(is_null($id) && method_exists($collection, 'getRowIdFieldName')) {
137
+ $id = $collection->getRowIdFieldName();
138
+ }
139
+
140
+ if(is_null($id)) {
141
+ try {
142
+ $id = $collection->getResource()->getIdFieldName();
143
+ } catch (Exception $e) {}
144
+ }
145
+
146
+ if(is_null($id)) {
147
+ $id = $default;
148
+ }
149
+
150
+ return $id;
151
+ }
152
+
153
+ /**
154
+ * Partition collection
155
+ *
156
+ * This could be done a little more efficiently through the use of limit and next
157
+ * commands, but we run the risk of utilizing more memory than we want to for large collections.
158
+ * In this method we split the collection up, not caring for density, so we might 'harvest'
159
+ * countless blank segments.
160
+ *
161
+ * @param Varien_Data_Collection $collection
162
+ * @return array<string>
163
+ */
164
+ public function partitionCollection($collection, $segmentSize = null)
165
+ {
166
+ $class = get_class($collection);
167
+ Springbot_Log::debug("Parititoning {$class} with select:");
168
+ Springbot_Log::debug((string) $collection->getSelect());
169
+
170
+ $idFieldName = $this->getIdFieldName($collection);
171
+ $sampleSize = self::getSampleSize();
172
+ $reverseSample = self::getReverseSample();
173
+
174
+ $size = is_null($segmentSize) ? $this->_getMaxSegmentSize() : $segmentSize;
175
+ $segments = array();
176
+
177
+ // If getting just a sample of each entity, get the $sampleSize most recent entities
178
+ if ($sampleSize && !$reverseSample) {
179
+ $maxEntities = $sampleSize;
180
+ }
181
+ else {
182
+ $maxEntities = null;
183
+ }
184
+
185
+ $lastId = $this->getLastCollectionId($collection);
186
+ $firstId = $this->getFirstCollectionId($collection);
187
+
188
+ Springbot_Log::debug("Partitioning collection from $firstId to $lastId");
189
+
190
+ if ($lastId) {
191
+ $blockCount = 0;
192
+ do {
193
+ $nextId = $this->_getLowestEntityId($collection, $idFieldName, $lastId, $size);
194
+
195
+ if($nextId < $firstId) {
196
+ $nextId = $firstId;
197
+ } else if (!$nextId) {
198
+ $nextId = 0;
199
+ }
200
+
201
+ $segments[] = new Springbot_Util_Partition($nextId, $lastId);
202
+ $lastId = $nextId;
203
+ $blockCount++;
204
+ if ($maxEntities && (($blockCount * $size) > $maxEntities)) break;
205
+ } while ($nextId > $firstId);
206
+ }
207
+
208
+ return $segments;
209
+ }
210
+
211
+ private function _getLowestEntityId($collection, $idFieldName, $lastId, $size)
212
+ {
213
+ $collection->clear();
214
+ $collection->getSelect()
215
+ ->reset(Zend_Db_Select::COLUMNS)
216
+ ->reset(Zend_Db_Select::WHERE)
217
+ ->reset(Zend_Db_Select::ORDER)
218
+ ->columns($idFieldName)
219
+ ->where("{$idFieldName} < {$lastId}")
220
+ ->order("{$idFieldName} DESC")
221
+ ->limit(1, $size);
222
+
223
+ $result = $collection->getFirstItem();
224
+ if ($result) {
225
+ return $result[$idFieldName];
226
+ }
227
+ else {
228
+ return 0;
229
+ }
230
+ }
231
+
232
+
233
+ public static function getSampleSize()
234
+ {
235
+ if ($sampleSize = Mage::getStoreConfig('springbot/config/sample_size')) {
236
+ return $sampleSize;
237
+ }
238
+ else {
239
+ return null;
240
+ }
241
+ }
242
+
243
+
244
+ public static function getReverseSample()
245
+ {
246
+ if ($reverseSample = Mage::getStoreConfig('springbot/config/reverse_sample')) {
247
+ return $reverseSample;
248
+ }
249
+ else {
250
+ return false;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Forecast collection count
256
+ *
257
+ * @param Varien_Data_Collection $collection
258
+ * @param int $storeId
259
+ * @param string $label
260
+ * @param int $harvestId
261
+ */
262
+ public function forecast($collection, $storeId, $label, $harvestId = null)
263
+ {
264
+ try {
265
+ $size = $collection->getSize();
266
+ $message = "{$size} {$label} will be harvested from store : {$storeId}/{$this->getSpringbotStoreId($storeId)}";
267
+
268
+ Springbot_Log::harvest($message, true, $storeId);
269
+
270
+ if(!is_null($harvestId)) {
271
+ $this->reportHarvestCount(array(
272
+ 'store_id' => $this->getSpringbotStoreId($storeId),
273
+ 'type' => $label,
274
+ 'estimate' => $size,
275
+ ), $harvestId);
276
+ }
277
+
278
+ } catch (Exception $e) {
279
+ Springbot_Log::error($e);
280
+ Springbot_Log::harvest("Unknown quantity of {$label} to harvest!");
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Helper method to delete remote objects
286
+ *
287
+ * @param array $post
288
+ * @param string $method
289
+ */
290
+ public function deleteRemote(array $post, $method)
291
+ {
292
+ $serialized = json_encode($post);
293
+ $file = Mage::getModel('combine/file_io');
294
+ $file->write(sha1($serialized) . '.json', $serialized);
295
+
296
+ Springbot_Boss::scheduleJob(
297
+ 'post:json',
298
+ array(
299
+ 'n' => $file->getBaseFilename(),
300
+ 'm' => $method,
301
+ ),
302
+ 0,
303
+ 'listener'
304
+ );
305
+ }
306
+
307
+ public function reportHarvestCount($params, $id = null)
308
+ {
309
+ Springbot_Log::debug("Reporting harvest count for store_id => $id");
310
+
311
+ $api = Mage::getModel('combine/api');
312
+ $payload = $api->wrap('harvest_segments', array($params));
313
+ $id = is_null($id) ? $this->getHarvestId() : $id;
314
+
315
+ if(!is_null($id)) {
316
+ return $api->put("harvests/{$id}", $payload);
317
+ }
318
+ }
319
+
320
+ public function getStoreUrl($storeId)
321
+ {
322
+ $store = Mage::app()->getStore($storeId);
323
+ $url = $store->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
324
+
325
+ if($store->getStoreInUrl()) {
326
+ $url .= $store->getCode();
327
+ }
328
+
329
+ return preg_replace('/\/$/', '', $url);
330
+ }
331
+
332
+ public function getSpringbotStoreId($id)
333
+ {
334
+ $storeIDIndex = 'store_id_' . $id;
335
+ $botId = Mage::getStoreConfig('springbot/config/' . $storeIDIndex);
336
+
337
+ if(!isset($botId)) {
338
+ Springbot_Log::debug("Tried to find config for key : $storeIDIndex");
339
+ }
340
+ return $botId;
341
+ }
342
+
343
+ public function getStoresToHarvest()
344
+ {
345
+ $_stores = array();
346
+ foreach (Mage::app()->getWebsites() as $website) {
347
+ foreach ($website->getGroups() as $group) {
348
+ foreach ($group->getStores() as $store) {
349
+ if ($this->doHarvestStore($store->getStoreId())) {
350
+ $_stores[$store->getStoreId()] = $store;
351
+ }
352
+ }
353
+ }
354
+ }
355
+ ksort($_stores, SORT_NUMERIC);
356
+ return $_stores;
357
+ }
358
+
359
+ public function doHarvestStore($storeId)
360
+ {
361
+ $harvest = true;
362
+ if(!$this->_rulesBuilt) {
363
+ $this->_buildStoreRules();
364
+ }
365
+ if(isset($this->_defines)) {
366
+ $harvest = (in_array($storeId, $this->_defines));
367
+ }
368
+ if(isset($this->_ignores)) {
369
+ $harvest = !(in_array($storeId, $this->_ignores));
370
+ }
371
+ return $harvest;
372
+ }
373
+
374
+ public function isHarvestRunning()
375
+ {
376
+ $jobs = Mage::getModel('combine/cron_queue')->getCollection();
377
+
378
+ $jobs->addFieldToFilter('queue', array('neq' => 'listener'))
379
+ ->addFieldToFilter('attempts', 0);
380
+
381
+ if($jobs->getSize() > 0) {
382
+ return true;
383
+ } else {
384
+ return false;
385
+ }
386
+ }
387
+
388
+ public function truncateEngineLogs()
389
+ {
390
+ @file_put_contents(Mage::getBaseDir('log') . DS . Springbot_Log::LOGFILE, '');
391
+ @file_put_contents(Mage::getBaseDir('log') . DS . Springbot_Log::ERRFILE, '');
392
+ }
393
+
394
+ public function getHarvestId()
395
+ {
396
+ return $this->_harvestId;
397
+ }
398
+
399
+ public function setHarvestId($id)
400
+ {
401
+ $this->_harvestId = $id;
402
+ return $this;
403
+ }
404
+
405
+ protected function _buildStoreRules()
406
+ {
407
+ $ignores = Mage::getStoreConfig('springbot/config/ignore_store_list');
408
+ if(!empty($ignores)) {
409
+ Springbot_Log::harvest('Ignore stores : ' . $ignores);
410
+ $this->_ignores =explode(',', $ignores);
411
+ }
412
+
413
+ $defines = Mage::getStoreConfig('springbot/config/define_store_list');
414
+ if(!empty($defines)) {
415
+ Springbot_Log::harvest('Explicitly defined stores : ' . $defines);
416
+ $this->_defines = explode(',', $defines);
417
+ }
418
+
419
+ $this->_rulesBuilt = true;
420
+ }
421
+
422
+ protected function _getMaxSegmentSize()
423
+ {
424
+ $size = Mage::getStoreConfig('springbot/config/segment_size');
425
+ return $size ? $size : 100;
426
+ }
427
+ }
app/code/community/Springbot/Combine/Helper/Parser.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Parser extends Mage_Core_Helper_Abstract
4
+ {
5
+ protected $_transEmails;
6
+
7
+ public function getParentSkus($entityId)
8
+ {
9
+ $parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($entityId);
10
+
11
+ $parentCollection = Mage::getResourceModel('catalog/product_collection')
12
+ ->addFieldToFilter('entity_id', array('in' => $parentIds))
13
+ ->addAttributeToSelect('sku');
14
+ $parentSkusArray = $parentCollection->getColumnValues('sku');
15
+ $parentIdsArray = $parentCollection->getColumnValues('entity_id');
16
+
17
+ foreach ($parentSkusArray as $index => $sku) {
18
+ if (!$sku) {
19
+ $parentSkusArray[$index] = Springbot_Boss::NO_SKU_PREFIX . $parentIdsArray[$index];
20
+ }
21
+ }
22
+
23
+ return $parentSkusArray;
24
+ }
25
+
26
+ /**
27
+ * Get top level sku
28
+ *
29
+ * This aims to get the top level sku. The getSku method for the product
30
+ * model is overloaded providing the type instance version of the sku
31
+ * meaning that it gives the simple sku for configurable or grouped products
32
+ * we need to get the _data array directly and pass that sku up to ensure the
33
+ * parent sku.
34
+ *
35
+ * @param $product
36
+ * @return string
37
+ */
38
+ public function getTopLevelSku($product)
39
+ {
40
+ if($product instanceof Mage_Catalog_Model_Product) {
41
+ $data = $product->getData();
42
+ if (isset($data['sku']) && $data['sku']) {
43
+ return $data['sku'];
44
+ }
45
+ else {
46
+ return $this->_getSkuFailsafe($product);
47
+ }
48
+ }
49
+ }
50
+
51
+ protected function _getSkuFailsafe($product)
52
+ {
53
+ if ($sku = $product->getSku()) {
54
+ return $sku;
55
+ }
56
+ else {
57
+ return Springbot_Boss::NO_SKU_PREFIX . $product->getEntityId();
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Gets accessible sku, product is visbible from frontend
63
+ *
64
+ * @param Mage_Sales_Model_Order_Item
65
+ * @return string
66
+ */
67
+ public function getAccessibleSkuFromSalesItem($item)
68
+ {
69
+ $product = Mage::getModel('catalog/product')->load($item->getProductId());
70
+ if($product) {
71
+ if(!$this->isAccessible($product)) {
72
+ Springbot_Log::debug('Product not visible - attempt to find parent');
73
+ $product = $this->getParentProductFromSalesItem($item);
74
+ }
75
+ return $this->getTopLevelSku($product);
76
+ }
77
+ }
78
+
79
+ public function isAccessible($product)
80
+ {
81
+ return $product instanceof Mage_Catalog_Model_Product &&
82
+ !($product->getVisibility() == Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE ||
83
+ $product->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_DISABLED);
84
+ }
85
+
86
+ /**
87
+ * Uses config data to get parent product for purchased simple
88
+ *
89
+ * @param Mage_Sales_Model_Order_Item
90
+ * @return Mage_Catalog_Model_Product
91
+ */
92
+ public function getParentProductFromSalesItem($item)
93
+ {
94
+ $values = $item->getBuyRequest()->toArray();
95
+
96
+ if($type = $item->getOptionsByCode('product_type')) {
97
+ if($parentProductId = $type->getProductId()) {
98
+ $product = Mage::getModel('catalog/product')->load($parentProductId);
99
+ }
100
+ }
101
+ else if($item->hasParentItemId()) {
102
+ $product = $item->getParentItem()->getProduct();
103
+ }
104
+ else if (isset($values['super_product_config']) && ($values['super_product_config']['product_type'] == 'grouped')) {
105
+ $parentProductId = $values['super_product_config']['product_id'];
106
+ $product = Mage::getModel('catalog/product')->load($parentProductId);
107
+ }
108
+ else {
109
+ $product = $item->getProduct();
110
+ }
111
+ return $product;
112
+ }
113
+
114
+ public function getChildProductIds($product)
115
+ {
116
+ $ids = Mage::getModel('catalog/product_type_configurable')->getChildrenIds($product->getId());
117
+ if(isset($ids[0]) && is_array($ids[0])) {
118
+ $ids = $ids[0];
119
+ }
120
+ return $ids;
121
+ }
122
+
123
+ public function getCustomAttributeNames($product)
124
+ {
125
+ return array_keys($this->getCustomAttributes($product));
126
+ }
127
+
128
+ public function getCustomAttributes($product, $len = -1)
129
+ {
130
+ $return = array();
131
+ $helper = Mage::helper('combine/attributes');
132
+ $attributes = $helper->getCustomAttributesBySet($product->getAttributeSetId());
133
+
134
+ foreach($attributes as $attribute) {
135
+ $code = $attribute->getAttributeCode();
136
+
137
+ if($attribute->usesSource()) {
138
+ $value = $product->getAttributeText($code);
139
+ } else {
140
+ $value = $product->getData($code);
141
+ }
142
+
143
+ $return[$code] = $len > 0 ? substr($value, 0, $len) : $value;
144
+ }
145
+
146
+ return $return;
147
+ }
148
+
149
+ public function hasImage($product)
150
+ {
151
+ if($product instanceof Mage_Catalog_Model_Product) {
152
+ if(($image = $product->getImage()) != 'no_selection' && $image ) {
153
+ return true;
154
+ } else if(($image = $product->getSmallImage()) != 'no_selection' && $image ) {
155
+ return true;
156
+ } else if(($image = $product->getThumbnail()) != 'no_selection' && $image ) {
157
+ return true;
158
+ } else if($product instanceof Mage_Catalog_Model_Product) {
159
+ if($gallery = $product->getMediaGalleryImages()) {
160
+ return $gallery->count() > 0;
161
+ }
162
+ }
163
+ }
164
+ return false;
165
+ }
166
+
167
+ public function getLandingUrl($product)
168
+ {
169
+ if($product instanceof Mage_Catalog_Model_Product) {
170
+ $linkType = Mage::getStoreConfig('springbot/advanced/product_url_type');
171
+ if ($linkType == 'id_path') {
172
+ $url = Mage::getUrl('catalog/product/view', array(
173
+ 'id' => $product->getId(),
174
+ '_store' => $product->getStoreId(),
175
+ ));
176
+ } else if ($linkType == 'in_store') {
177
+ $url = $product->getUrlInStore();
178
+ } else if ($uri = $product->getUrlPath() && $linkType == 'default') {
179
+ $url = Mage::helper('combine/harvest')->getStoreUrl($product->getStoreId()) . '/' . $product->getUrlPath();
180
+ } else {
181
+ $url = $product->getProductUrl(false);
182
+ }
183
+ // remove calling script from url (Mage logic ftw)
184
+ $url = preg_replace('/\/springbot.php\//', '/', $url);
185
+ }
186
+ return $url;
187
+ }
188
+
189
+ public function getImageUrl($product)
190
+ {
191
+ if($product instanceof Mage_Catalog_Model_Product) {
192
+ if((Mage::getStoreConfig('springbot/images/use_cached_images'))) {
193
+ $img = Mage::helper('catalog/image')->init($product, 'image');
194
+ if($size = Mage::getStoreConfig('springbot/images/pixel_width')) {
195
+ $img->resize($size);
196
+ }
197
+ return (string) $img;
198
+ } else if(($image = $product->getImage()) != 'no_selection' && $image ) {
199
+ // main
200
+ } else if(($image = $product->getSmallImage()) != 'no_selection' && $image ) {
201
+ // small
202
+ } else if(($image = $product->getThumbnail()) != 'no_selection' && $image ) {
203
+ // thumbnail
204
+ } else if ($product->getMediaGalleryImages() && $product->getMediaGalleryImages()->getSize() > 0) {
205
+ // First item from gallery
206
+ $image = $product->getMediaGalleryImages()->getFirstItem()->getUrl();
207
+ } else {
208
+ // if all else fails, build cached image
209
+ return null;
210
+ }
211
+
212
+ if(strpos($image, DS) !== 0) {
213
+ $image = DS . $image;
214
+ }
215
+ return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA) . 'catalog/product' . $image;
216
+ }
217
+ }
218
+
219
+ public function isTransactionalEmail($email)
220
+ {
221
+ return array_search($email, $this->_getTransEmails()) !== false;
222
+ }
223
+
224
+ protected function _getTransEmails()
225
+ {
226
+ if(!isset($this->_transEmails)) {
227
+ foreach(Mage::getStoreConfig('trans_email') as $k => $v) {
228
+ if(isset($v['email'])) { $this->_transEmails[] = $v['email']; }
229
+ }
230
+ }
231
+ return $this->_transEmails;
232
+ }
233
+
234
+ }
app/code/community/Springbot/Combine/Helper/Redirect.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Redirect extends Mage_Core_Helper_Abstract
4
+ {
5
+ const COOKIE_NAME = 'springbot_redirect_queue';
6
+
7
+ public function insertRedirectIds($params, $ids = null)
8
+ {
9
+ if(is_null($ids)) {
10
+ $ids = $this->getRedirectIds();
11
+ } else if (is_string($ids)) {
12
+ $ids = $this->getRedirectIds($ids);
13
+ }
14
+
15
+ foreach($ids as $id) {
16
+ Springbot_Log::debug("Insert redirect_id : $id");
17
+ $redirect = Mage::getModel('combine/redirect');
18
+
19
+ $redirect->setData($params)
20
+ ->setRedirectId($id)
21
+ ->insertIgnore();
22
+ }
23
+ }
24
+
25
+ public function checkAllRedirectTables()
26
+ {
27
+ $resource = Mage::getSingleton('core/resource');
28
+ $tables = array(
29
+ $resource->getTableName('combine/redirect'),
30
+ $resource->getTableName('combine/redirect_order'),
31
+ );
32
+
33
+ foreach($tables as $table) {
34
+ if($this->checkTable($table)) {
35
+ return;
36
+ }
37
+ }
38
+ }
39
+
40
+ public function checkTable($table)
41
+ {
42
+ if(!Mage::getSingleton('core/resource')->getConnection('core_read')->showTableStatus($table)) {
43
+ Mage::logException(new Exception("{$table} table does not exist. Rerunning Springbot update 1.0.0.70->1.2.0.0."));
44
+ $setup = new Springbot_Combine_Model_Resource_Setup('combine_setup');
45
+ $setup->reinstallSetupScript('1.0.0.70', '1.2.0.0');
46
+ return true;
47
+ }
48
+ }
49
+
50
+ public function getRedirectIds($raw = null)
51
+ {
52
+ if(is_null($raw)) {
53
+ $raw = $this->getRawCookie();
54
+ }
55
+ $queue = explode($this->determineDelimiter($raw), trim($raw));
56
+ return $this->sanitizeMongo($queue);
57
+ }
58
+
59
+ public function getLastId()
60
+ {
61
+ $ids = $this->getRedirectIds();
62
+ if(count($ids)) {
63
+ return $ids[0];
64
+ }
65
+ }
66
+
67
+ public function sanitizeMongo($array)
68
+ {
69
+ $output = array();
70
+ foreach($array as $value) {
71
+ if(empty($value)) { continue; }
72
+ if(preg_match("/^[0-9a-fA-F]{24}$/", $value)) {
73
+ $output[] = $value;
74
+ } else {
75
+ $ip = Mage::helper('core/http')->getRemoteAddr();
76
+ Springbot_Log::error(new Exception("{$value} attempted to pass as cookie param from {$ip}. Possible insertion attack."));
77
+ }
78
+ }
79
+ return array_reverse($output);
80
+ }
81
+
82
+ public function encodeEscapeCookie($array)
83
+ {
84
+ return Mage::helper('combine')->escapeShell(implode('%7', $array));
85
+ }
86
+
87
+ public function getRedirectsByEmail($email, $dateLimit = null)
88
+ {
89
+ $collection = Mage::getModel('combine/redirect')->getCollection()->loadByEmail($email);
90
+
91
+ if(!is_null($dateLimit)) {
92
+ $collection->addFieldToFilter('created_at', array('to' => $dateLimit));
93
+ }
94
+
95
+ if($collection instanceof Varien_Data_Collection && $collection->getSize() > 0) {
96
+ return array_values($collection->getColumnValues('redirect_id'));
97
+ }
98
+ }
99
+
100
+ public function getRedirectByOrderId($id)
101
+ {
102
+ return Mage::getModel('combine/redirect')->getCollection()
103
+ ->joinOrderIds()
104
+ ->addFieldToFilter('order_id', $id)->getFirstItem();
105
+ }
106
+
107
+ public function getRawEscapedCookie()
108
+ {
109
+ if($cookie = $this->getRawCookie()) {
110
+ return Mage::helper('combine')->escapeShell($cookie);
111
+ }
112
+ }
113
+
114
+ public function getRawCookie()
115
+ {
116
+ return Mage::getModel('core/cookie')->get(self::COOKIE_NAME);
117
+ }
118
+
119
+ public function hasRedirectId()
120
+ {
121
+ $raw = $this->getRawCookie();
122
+ return !empty($raw);
123
+ }
124
+
125
+ public function determineDelimiter($str)
126
+ {
127
+ if (substr_count($str,'%7') > 0) {
128
+ return '%7';
129
+ } else {
130
+ return '|';
131
+ }
132
+ }
133
+ }
app/code/community/Springbot/Combine/Helper/Store.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Store extends Mage_Core_Helper_Abstract
4
+ {
5
+ protected $_storeId;
6
+
7
+ public function setStore($store)
8
+ {
9
+ if($store instanceof Mage_Core_Model_Store) {
10
+ $store = $store->getStoreId();
11
+ }
12
+ $this->_storeId = $store;
13
+ return $this;
14
+ }
15
+
16
+ public function getGuid()
17
+ {
18
+ return $this->getValue('springbot/config/store_guid_' . $this->_storeId);
19
+ }
20
+
21
+ public function getSpringbotStoreId()
22
+ {
23
+ return $this->getValue('springbot/config/store_id_' . $this->_storeId);
24
+ }
25
+
26
+ public function getAccountEmail()
27
+ {
28
+ return $this->getValue('springbot/config/account_email');
29
+ }
30
+
31
+ public function getValue($path)
32
+ {
33
+ return Mage::getStoreConfig($path, $this->getStoreId());
34
+ }
35
+
36
+ public function getStoreId()
37
+ {
38
+ return $this->_storeId;
39
+ }
40
+ }
app/code/community/Springbot/Combine/Helper/Trackable.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Helper_Trackable extends Mage_Core_Helper_Abstract
4
+ {
5
+ const SB_COOKIE = '_sbtk';
6
+
7
+ public function getTrackables()
8
+ {
9
+ $sbCookie = $this->getCookie();
10
+ return json_decode(base64_decode($sbCookie));
11
+ }
12
+
13
+ public function getCookie()
14
+ {
15
+ return Mage::getModel('core/cookie')->get(self::SB_COOKIE);
16
+ }
17
+
18
+ public function hasTrackables()
19
+ {
20
+ $sb = $this->getCookie();
21
+ return !empty($sb);
22
+ }
23
+
24
+ public function addTrackable($customerEmail, $type, $value, $quoteId, $customerId) {
25
+ $model = Mage::getModel('combine/trackable');
26
+ $model->setData(
27
+ array(
28
+ 'email' => $customerEmail,
29
+ 'type' => $type,
30
+ 'value' => $value,
31
+ 'quote_id' => $quoteId,
32
+ 'customer_id' => $customerId
33
+ )
34
+ );
35
+ $model->createOrUpdate();
36
+ }
37
+
38
+ public function getTrackablesHashByOrder($orderId)
39
+ {
40
+ $collection = Mage::getModel('combine/trackable')->getCollection()
41
+ ->addFieldToFilter('order_id', $orderId);
42
+
43
+ return $this->_buildHash($collection);
44
+ }
45
+
46
+ public function getTrackablesHashByQuote($quoteId)
47
+ {
48
+ $collection = Mage::getModel('combine/trackable')->getCollection()
49
+ ->addFieldToFilter('quote_id', $quoteId);
50
+
51
+ return $this->_buildHash($collection);
52
+ }
53
+
54
+ protected function _buildHash($collection)
55
+ {
56
+ $hash = new stdClass();
57
+
58
+ foreach($collection as $item) {
59
+ $hash->{$item->getType()} = $item->getValue();
60
+ }
61
+
62
+ if(!Mage::helper('combine')->isEmpty($hash)) {
63
+ return $hash;
64
+ }
65
+ }
66
+
67
+ }
app/code/community/Springbot/Combine/Model/Abstract.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Abstract extends Mage_Core_Model_Abstract
4
+ {
5
+ /**
6
+ * Insert ignore into collection
7
+ */
8
+ public function insertIgnore()
9
+ {
10
+ try {
11
+ if($this->_validate()) {
12
+ $this->_getResource()->insertIgnore($this);
13
+ }
14
+ } catch(Exception $e) {
15
+ $this->_getResource()->rollBack();
16
+ Springbot_Log::error($e);
17
+ }
18
+
19
+ return $this;
20
+ }
21
+
22
+ protected function _validate()
23
+ {
24
+ return true;
25
+ }
26
+ }
app/code/community/Springbot/Combine/Model/Api.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Api extends Varien_Object
4
+ {
5
+ const SUCCESSFUL_RESPONSE = 'ok';
6
+ const HTTP_CONTENT_TYPE = 'Content-type: application/json';
7
+ const TOTAL_POST_FAIL_LIMIT = 32;
8
+ const RETRY_LIMIT = 3;
9
+
10
+ protected $_securityToken;
11
+ protected $_client;
12
+ protected $_header;
13
+ protected $_url;
14
+ protected $_retries = 0;
15
+ protected $_requestStart;
16
+
17
+ public function wrap($model, array $data)
18
+ {
19
+ $transport = new stdClass();
20
+ $transport->$model = $data;
21
+
22
+ return Zend_Json::encode($transport,
23
+ false,
24
+ array('enableJsonExprFinder' => true)
25
+ );
26
+ }
27
+
28
+ public function reinit()
29
+ {
30
+ $this->_retries = 0;
31
+ return $this;
32
+ }
33
+
34
+ public function get($method, $param = array(), $authenticate = true)
35
+ {
36
+ $url = $method . '?' . http_build_query($param);
37
+ return $this->call($url, false, $authenticate, Varien_Http_Client::GET);
38
+ }
39
+
40
+ public function put($method, $payload, $authenticate = true)
41
+ {
42
+ return $this->call($method, $payload, $authenticate, Varien_Http_Client::PUT);
43
+ }
44
+
45
+ public function call($method, $payload = false, $authenticate = true, $httpMethod = Varien_Http_Client::POST)
46
+ {
47
+ $result = array();
48
+ $client = $this->getClient($httpMethod);
49
+ $client->setUri($this->getApiUrl($method));
50
+ Springbot_Log::debug("Calling Springbot api method : $method | " . $client->getUri(true));
51
+
52
+ if($authenticate) {
53
+ $this->authenticate();
54
+ $client->setHeaders('X-AUTH-TOKEN:' . $this->_securityToken);
55
+ }
56
+
57
+ if($payload) {
58
+ $client->setRawData(utf8_encode($payload));
59
+ }
60
+
61
+ try {
62
+ // stop Zend_Http_Client from dumping to stream on error
63
+ ob_start();
64
+ $this->_startProfile();
65
+ $response = $client->request();
66
+ $this->_stopProfile();
67
+ ob_end_clean();
68
+
69
+ if($response->isSuccessful()) {
70
+ $result = json_decode($response->getBody(),true);
71
+ if ($result['status'] != self::SUCCESSFUL_RESPONSE) {
72
+ Springbot_Log::harvest($response->getBody());
73
+ }
74
+ }
75
+ Springbot_Log::http($payload);
76
+ } catch (Exception $e) {
77
+ Springbot_Log::error($e);
78
+ $code = isset($result['status']) ? $result['status'] : 'null';
79
+ throw new Exception("$method call failed with code: $code");
80
+ }
81
+
82
+ return $result;
83
+ }
84
+
85
+
86
+ public function hasToken()
87
+ {
88
+ $token = Mage::getStoreConfig('springbot/config/security_token');
89
+ if(!empty($token)) {
90
+ $this->_securityToken = $token;
91
+ }
92
+ return isset($this->_securityToken);
93
+ }
94
+
95
+ public function authenticate()
96
+ {
97
+ if(!$this->hasToken()) {
98
+ $credentials = $this->_getApiCredentials();
99
+ $result = $this->call('registration/login', $credentials, false);
100
+
101
+ if(!isset($result['token'])) {
102
+ throw new Exception('Token not available in api response. Please check springbot credentials.');
103
+ }
104
+
105
+ $this->_securityToken = $result['token'];
106
+ }
107
+ return;
108
+ }
109
+
110
+ public function getApiUrl($method = '')
111
+ {
112
+ if(!isset($this->_url)) {
113
+ $url = Mage::getStoreConfig('springbot/config/api_url', Mage::app()->getStore());
114
+ if(!isset($url)) {
115
+ $url = 'https://api.springbot.com/';
116
+ }
117
+ $this->_url = $url . 'api/';
118
+ }
119
+ return $this->_url . $method;
120
+ }
121
+
122
+ public function getClient($method = Varien_Http_Client::POST)
123
+ {
124
+ if(!isset($this->_client)) {
125
+ $this->_client = new Zend_Http_Client();
126
+ $this->_client->setMethod($method);
127
+ $this->_client->setHeaders(self::HTTP_CONTENT_TYPE);
128
+ }
129
+ return $this->_client;
130
+ }
131
+
132
+ public function getLastStatus()
133
+ {
134
+ return $this->_responseCode;
135
+ }
136
+
137
+ protected function _getApiCredentials()
138
+ {
139
+ $post = array(
140
+ 'user_id' => $this->_getAccountEmail(),
141
+ 'password' => $this->_getAccountPassword(),
142
+ );
143
+ return json_encode($post);
144
+ }
145
+
146
+ protected function _getAccountEmail()
147
+ {
148
+ return Mage::getStoreConfig('springbot/config/account_email');
149
+ }
150
+
151
+ protected function _getAccountPassword()
152
+ {
153
+ $passwd = Mage::getStoreConfig('springbot/config/account_password');
154
+ return Mage::helper('core')->decrypt($passwd);
155
+ }
156
+
157
+ protected function _getSecurityToken()
158
+ {
159
+ $this->_securityToken = Mage::getStoreConfig('springbot/config/security_token');
160
+ return $this->_securityToken;
161
+ }
162
+
163
+ protected function _startProfile()
164
+ {
165
+ $this->_requestStart = Mage::helper('combine')->getMicroTime();
166
+ }
167
+
168
+ protected function _stopProfile()
169
+ {
170
+ $time = Mage::helper('combine')->getMicroTime() - $this->_requestStart;
171
+ Springbot_Log::debug("Request completed in $time sec");
172
+ }
173
+ }
app/code/community/Springbot/Combine/Model/Cron/Count.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Cron_Count extends Springbot_Combine_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/cron_count');
8
+ }
9
+
10
+ public function increaseCount($storeId, $harvestId, $entityType, $increment)
11
+ {
12
+ $collection = Mage::getModel('combine/cron_count')->getCollection();
13
+ $countResource = Mage::getResourceModel('combine/cron_count');
14
+
15
+ // Query to see if count already exists
16
+ $rowCount = $collection->addFieldToFilter('store_id', $storeId)
17
+ ->addFieldToFilter('harvest_id', $harvestId)
18
+ ->addFieldToFilter('entity', $entityType)
19
+ ->getSize();
20
+
21
+ // If it doesn't exist yet, create a new row
22
+ if($rowCount == 0) {
23
+ $countResource->createCountRow($storeId, $harvestId, $entityType, $increment);
24
+ }
25
+ else {
26
+ $countResource->increaseCountRow($storeId, $harvestId, $entityType, $increment);
27
+ }
28
+ }
29
+
30
+ public function getProcessedCount($storeId, $harvestId, $entityType)
31
+ {
32
+ $countRow = $this->_getEntityCountItem($storeId, $harvestId, $entityType);
33
+ if ($countRow) {
34
+ return $countRow->getCount();
35
+ }
36
+ else {
37
+ return 0;
38
+ }
39
+ }
40
+
41
+ public function getEntityStartTime($storeId, $harvestId, $entityType)
42
+ {
43
+ $countRow = $this->_getEntityCountItem($storeId, $harvestId, $entityType);
44
+ if ($countRow) {
45
+ return $countRow->getCreatedAt();
46
+ }
47
+ else {
48
+ return 0;
49
+ }
50
+ }
51
+
52
+ public function getEntityCompletedTime($storeId, $harvestId, $entityType)
53
+ {
54
+ $countRow = $this->_getEntityCountItem($storeId, $harvestId, $entityType);
55
+ if ($countRow) {
56
+ return $countRow->getCompleted();
57
+ }
58
+ else {
59
+ return 0;
60
+ }
61
+ }
62
+
63
+
64
+ private function _getEntityCountItem($storeId, $harvestId, $entityType)
65
+ {
66
+ $collection = Mage::getModel('combine/cron_count')->getCollection();
67
+ $countRow = $collection
68
+ ->addFieldToFilter('store_id', $storeId)
69
+ ->addFieldToFilter('harvest_id', $harvestId)
70
+ ->addFieldToFilter('entity', $entityType)
71
+ ->getFirstItem();
72
+ if ($countRow->hasCount()) {
73
+ return $countRow;
74
+ }
75
+ else {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ public function setCompletedTime($storeId, $harvestId, $entityType)
81
+ {
82
+ $countResource = Mage::getResourceModel('combine/cron_count');
83
+ $countResource->setCompletedTime($storeId, $harvestId, $entityType);
84
+ }
85
+
86
+ }
app/code/community/Springbot/Combine/Model/Cron/Manager/Status.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Cron_Manager_Status extends Varien_Object
4
+ {
5
+ const ACTIVE = 'active';
6
+ const INACTIVE = 'inactive';
7
+ const BLOCKER = 'springbot-work-mgr.block';
8
+
9
+ public function isActive()
10
+ {
11
+ $pid = $this->getPid();
12
+ return !empty($pid) && file_exists("/proc/$pid");
13
+ }
14
+
15
+ public function toggle()
16
+ {
17
+ if($this->isActive()) {
18
+ Springbot_Log::debug('Work manager active, halting');
19
+ $this->issueWorkBlocker();
20
+ $this->haltManager();
21
+ } else {
22
+ Springbot_Log::debug('Work manager inactive, starting');
23
+ $this->removeWorkBlocker();
24
+ Springbot_Boss::startWorkManager();
25
+ }
26
+ }
27
+
28
+ public function isBlocked()
29
+ {
30
+ return file_exists($this->_getBlockFile());
31
+ }
32
+
33
+ public function haltManager()
34
+ {
35
+ Springbot_Boss::internalCallback('work:stop', array('p' => $this->getPid()));
36
+ }
37
+
38
+ public function issueWorkBlocker()
39
+ {
40
+ file_put_contents($this->_getBlockFile(), '');
41
+ }
42
+
43
+ public function removeWorkBlocker()
44
+ {
45
+ if($this->isBlocked()) {
46
+ unlink($this->_getBlockFile());
47
+ }
48
+ }
49
+
50
+ public function getStatus()
51
+ {
52
+ return $this->isActive() ? self::ACTIVE : self::INACTIVE;
53
+ }
54
+
55
+ public function getRuntime()
56
+ {
57
+ $filename = $this->_getWorkmanagerFilename();
58
+ if(file_exists($filename)) {
59
+ return time() - filectime($filename);
60
+ }
61
+ }
62
+
63
+ public function getActiveWorkerPids()
64
+ {
65
+ $ids = array();
66
+ foreach($this->_getWorkerFiles() as $file) {
67
+ $matches = array();
68
+ preg_match('/\d+$/', $file, $matches);
69
+ if(isset($matches[0])) {
70
+ $ids[] = $matches[0];
71
+ }
72
+ }
73
+ return $ids;
74
+ }
75
+
76
+ public function getPid()
77
+ {
78
+ $filename = $this->_getWorkmanagerFilename();
79
+ if(file_exists($filename)) {
80
+ return file_get_contents($filename);
81
+ } else {
82
+ return null;
83
+ }
84
+ }
85
+
86
+ public function getSched()
87
+ {
88
+ if($pid = $this->getPid()) {
89
+ $sched = array();
90
+ $handler = fopen('/proc/' . $pid . '/sched', 'r');
91
+ while($line = fgets($handler)) {
92
+ $pieces = array();
93
+ if(preg_match('/^(\S+)\s+:\s+([0-9.]+)$/', $line, $pieces)) {
94
+ $sched[$pieces[1]] = $pieces[2];
95
+ }
96
+ }
97
+ fclose($handler);
98
+ return $sched;
99
+ }
100
+ }
101
+
102
+ protected function _getBlockFile()
103
+ {
104
+ return Mage::getBaseDir('tmp') . DS . self::BLOCKER;
105
+ }
106
+
107
+ protected function _getWorkmanagerFilename()
108
+ {
109
+ return Mage::getBaseDir('tmp') . DS . Springbot_Services_Work_Manager::WORKMANAGER_FILENAME;
110
+ }
111
+
112
+ private function _getWorkerFiles()
113
+ {
114
+ return glob(Mage::getBaseDir('tmp') . DS . 'springbotworker*');
115
+ }
116
+ }
app/code/community/Springbot/Combine/Model/Cron/Queue.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Cron_Queue extends Springbot_Combine_Model_Abstract
4
+ {
5
+ const FAILED_JOB_PRIORITY = 9;
6
+
7
+ public function _construct()
8
+ {
9
+ $this->_init('combine/cron_queue');
10
+ }
11
+
12
+ public function save()
13
+ {
14
+ if($this->_validate()) {
15
+ return parent::save();
16
+ } else {
17
+ Springbot_Log::debug(__CLASS__." invalid, not saving!");
18
+ Springbot_Log::debug($this->getData());
19
+ }
20
+ }
21
+
22
+ protected function _validate()
23
+ {
24
+ return $this->hasMethod();
25
+ }
26
+
27
+ protected function _pre()
28
+ {
29
+ $this->addData(array(
30
+ 'attempts' => $this->getAttempts() + 1,
31
+ 'run_at' => now(),
32
+ 'locked_at' => now(),
33
+ 'locked_by' => getmypid(),
34
+ 'error' => null
35
+ ));
36
+ $this->save();
37
+ }
38
+
39
+ public function run()
40
+ {
41
+ Springbot_Log::debug("Running ".__CLASS__);
42
+ $return = true;
43
+ $class = $this->getInstance();
44
+ $class->setData($this->getParsedArgs());
45
+ $this->_pre();
46
+
47
+ try {
48
+ $class->run();
49
+ } catch (Exception $e) {
50
+ $this->setError($e->getMessage());
51
+ // Lower priority for failed job - keeping order intact
52
+ $this->setPriority($this->getPriority() + Springbot_Services_Priority::FAILED);
53
+ $return = false;
54
+ if ($this->getAttempts() >= Springbot_Combine_Model_Resource_Cron_Queue_Collection::ATTEMPT_LIMIT) {
55
+ Springbot_Log::remote(
56
+ "Job failed multiple times. Method: {$this->getMethod()}, Args: {$this->getArgs()}, Error: {$this->getError()}",
57
+ $this->getStoreId(),
58
+ self::FAILED_JOB_PRIORITY
59
+ );
60
+ }
61
+ }
62
+ $this->_post();
63
+ return $return;
64
+ }
65
+
66
+ protected function _post()
67
+ {
68
+ if(!$this->hasError()) {
69
+ $this->delete();
70
+ } else {
71
+ $this->addData(array(
72
+ 'locked_at' => null,
73
+ 'locked_by' => null,
74
+ ))->save();
75
+ }
76
+ }
77
+
78
+ public function getInstance()
79
+ {
80
+ return Springbot_Services_Registry::getInstance($this->getMethod());
81
+ }
82
+
83
+ public function getParsedArgs()
84
+ {
85
+ $args = (array) json_decode($this->getArgs());
86
+ return Springbot_Services_Registry::parseOpts($args);
87
+ }
88
+
89
+ }
app/code/community/Springbot/Combine/Model/Cron/Queue/Batch.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Cron_Queue_Batch extends Varien_Object
4
+ {
5
+ protected $_stack = array();
6
+
7
+ public function schedule($method, $args, $priority, $queue = 'default', $storeId = null, $requiresAuth = true)
8
+ {
9
+ $this->push(array(
10
+ 'method' => $method,
11
+ 'args' => json_encode($args),
12
+ 'priority' => $priority,
13
+ 'command_hash' => sha1($method . json_encode($args)),
14
+ 'queue' => $queue,
15
+ 'store_id' => $storeId
16
+ ));
17
+ return $this;
18
+ }
19
+
20
+ public function insert()
21
+ {
22
+ $sql = $this->toSql();
23
+ if($rows = $this->_rowCount()) {
24
+ Springbot_Log::info("Inserting {$rows} rows into {$this->_getTablename()}");
25
+ //Springbot_Log::debug("Running batch insert : {$sql}");
26
+ $rows = $this->_getWriter()->exec($sql);
27
+ }
28
+ Springbot_Log::info("{$rows} rows inserted");
29
+ Springbot_Boss::startWorkManager();
30
+ }
31
+
32
+ public function push($args)
33
+ {
34
+ if($this->_isValid($args)) {
35
+ //Springbot_Log::debug("Pushing {$args['method']} : {$args['args']}");
36
+ $row = $this->_getRowModel();
37
+ $row->setData($args);
38
+ $this->_stack[] = $row;
39
+ }
40
+ return $this;
41
+ }
42
+
43
+ public function toSql()
44
+ {
45
+ $columns = $this->_getRowModel()->getColumnNames();
46
+ $table = $this->_getTablename();
47
+ $rows = $this->_rowsToSql();
48
+ return "INSERT IGNORE INTO {$table} ({$columns}) VALUES {$rows}";
49
+ }
50
+
51
+ protected function _isValid($args)
52
+ {
53
+ return isset($args['method']);
54
+ }
55
+
56
+ protected function _rowsToSql()
57
+ {
58
+ return implode(', ', $this->_stack);
59
+ }
60
+
61
+ protected function _rowCount()
62
+ {
63
+ return count($this->_stack);
64
+ }
65
+
66
+ protected function _getRowModel()
67
+ {
68
+ return Mage::getModel('combine/cron_queue_batch_row');
69
+ }
70
+
71
+ protected function _getTablename()
72
+ {
73
+ return Mage::getSingleton('core/resource')->getTableName('springbot_cron_queue');
74
+ }
75
+
76
+ protected function _getWriter()
77
+ {
78
+ return Mage::getSingleton('core/resource')->getConnection('core_write');
79
+ }
80
+ }
app/code/community/Springbot/Combine/Model/Cron/Queue/Batch/Row.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Cron_Queue_Batch_Row extends Varien_Object
4
+ {
5
+ protected $_schema = array(
6
+ 'method',
7
+ 'args',
8
+ 'priority',
9
+ 'command_hash',
10
+ 'queue',
11
+ 'store_id',
12
+ );
13
+
14
+ public function getSchema()
15
+ {
16
+ return $this->_schema;
17
+ }
18
+
19
+ public function getColumnNames()
20
+ {
21
+ return '`' . implode('`,`', $this->getSchema()) . '`';
22
+ }
23
+
24
+ public function __toString()
25
+ {
26
+ $res = $this->_getResource();
27
+
28
+ foreach($this->getSchema() as $column) {
29
+ $value = $this->getData($column);
30
+ $quoted[] = !empty($value) ? $res->quote($value) : 'NULL';
31
+ }
32
+ return '(' . implode(',', $quoted) . ')';
33
+ }
34
+
35
+ protected function _getResource()
36
+ {
37
+ return Mage::getSingleton('core/resource')->getConnection('core_write');
38
+ }
39
+ }
app/code/community/Springbot/Combine/Model/Cron/Worker.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Cron_Worker extends Mage_Core_Model_Abstract
4
+ {
5
+ public function run($isForeman = true, $jobId = null)
6
+ {
7
+ Springbot_Log::debug("Starting worker for pid => " . getmypid());
8
+ try{
9
+ if($jobId) {
10
+ // Run only a specific job
11
+ $job = $this->getJob($jobId);
12
+ if($job->hasId()) {
13
+ $job->run();
14
+ }
15
+ }
16
+ else {
17
+ $count = 0;
18
+ $sleepInterval = $this->getSleepInterval();
19
+ $maxJobs = $this->getMaxJobs();
20
+ do {
21
+ if($job = $this->getNextJob($isForeman)) {
22
+ Springbot_Log::debug("Running job #$count for pid => " . getmypid());
23
+ $job->run();
24
+ $count++;
25
+ sleep($sleepInterval);
26
+ } else {
27
+ Springbot_Log::debug("No more jobs found");
28
+ }
29
+ } while ($job && ($count < $maxJobs));
30
+ }
31
+ } catch (Exception $e) {
32
+ Springbot_Log::error($e);
33
+ }
34
+ }
35
+
36
+ public function cronRun()
37
+ {
38
+ if(Springbot_Boss::isCron()) {
39
+ $status = Mage::getModel('combine/cron_manager_status');
40
+ if(!($status->isBlocked() && $status->isActive())) {
41
+ $this->run(true);
42
+ }
43
+ }
44
+ }
45
+
46
+ public function getSleepInterval()
47
+ {
48
+ if (!$sleepInterval = Mage::getStoreConfig('springbot/advanced/sleep_interval')) {
49
+ $sleepInterval = 1;
50
+ }
51
+ return $sleepInterval;
52
+ }
53
+
54
+ public function getMaxJobs()
55
+ {
56
+ if (!$maxJobs = Mage::getStoreConfig('springbot/cron/max_jobs')) {
57
+ $maxJobs = 10;
58
+ }
59
+ return $maxJobs;
60
+ }
61
+
62
+ public function getBulkJobsToRun($queue)
63
+ {
64
+ return $this->_getCollection()->getPriorityJobs($this->getMaxJobs(), $queue);
65
+ }
66
+
67
+ public function getActiveCount()
68
+ {
69
+ return $this->_getCollection()->getActiveCount();
70
+ }
71
+
72
+ protected function _cleanup()
73
+ {
74
+ foreach($this->_getFailedJobs() as $job) {
75
+ Springbot_Log::debug("Removing failed job {$job->getMethod()}");
76
+ $job->delete();
77
+ }
78
+ }
79
+
80
+ protected function _getFailedJobs()
81
+ {
82
+ return $this->_getCollection()
83
+ ->addFieldToFilter('error', array('notnull' => true));
84
+ }
85
+
86
+ protected function _getCollection()
87
+ {
88
+ return Mage::getModel('combine/cron_queue')->getCollection();
89
+ }
90
+
91
+ public function getNextJob($isForeman)
92
+ {
93
+ return $this->_getCollection()->getNextJob($isForeman);
94
+ }
95
+
96
+ public function getJob($jobId)
97
+ {
98
+ return Mage::getModel('combine/cron_queue')->load($jobId);
99
+ }
100
+
101
+ }
app/code/community/Springbot/Combine/Model/File/Io.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_File_Io
4
+ {
5
+ const TRUNCATE = 'w';
6
+ const APPEND = 'a';
7
+ const READ = 'r';
8
+
9
+ protected $_filename;
10
+ protected $_resource;
11
+ protected $_mode;
12
+ protected $_path;
13
+
14
+ public function __destruct()
15
+ {
16
+ @fclose($this->_resource);
17
+ }
18
+
19
+ protected function _init($filename, $mode)
20
+ {
21
+ $this->setFilename($this->_getPath()->resolve($filename, 'tmp'));
22
+ $this->_setMode($mode);
23
+
24
+ if(empty($this->_filename)) {
25
+ throw new Exception('Filename required for I/O.');
26
+ }
27
+ $this->_openFile();
28
+ }
29
+
30
+ public function write($filename, $content)
31
+ {
32
+ $this->_init($filename, self::TRUNCATE);
33
+
34
+ if(fwrite($this->_getResource(), $content) === false) {
35
+ throw new Exception('Writing to file ' . $this->_filename . ' failed.');
36
+ }
37
+ }
38
+
39
+ public function read($filename)
40
+ {
41
+ $this->_init($filename, self::READ);
42
+
43
+ if(!($content = fread($this->_getResource(), $this->_getStreamLength()))) {
44
+ throw new Exception('Reading from file ' . $this->_filename . ' failed.');
45
+ }
46
+ return $content;
47
+ }
48
+
49
+ public function exists($filename)
50
+ {
51
+ $path = $this->_getPath()->resolve($filename, 'tmp');
52
+
53
+ return @file_exists($path);
54
+ }
55
+
56
+ public function delete()
57
+ {
58
+ @unlink($this->_filename);
59
+ }
60
+
61
+ public function getBaseFilename()
62
+ {
63
+ return basename($this->_filename);
64
+ }
65
+
66
+ public function getFilename()
67
+ {
68
+ return $this->_filename;
69
+ }
70
+
71
+ public function setFilename($filename)
72
+ {
73
+ $this->_filename = $filename;
74
+ return $this;
75
+ }
76
+
77
+ protected function _openFile()
78
+ {
79
+ if(empty($this->_filename)) {
80
+ throw new Exception('Filename required to open file.');
81
+ }
82
+
83
+ if(!file_exists($this->_filename) && $this->_doCreate()) {
84
+ $this->_createFile();
85
+ }
86
+
87
+ $this->_resource = $this->_getResource();
88
+
89
+ if(!$this->_resource) {
90
+ throw new Exception('Could not open file for reading.');
91
+ }
92
+
93
+ return $this;
94
+ }
95
+
96
+ protected function _createFile()
97
+ {
98
+ @file_put_contents($this->_filename, '');
99
+ @chmod($this->_filename, 0777);
100
+ if(!file_exists($this->_filename)) {
101
+ throw new Exception('Invalid filename.');
102
+ }
103
+ return $this;
104
+ }
105
+
106
+ protected function _getResource()
107
+ {
108
+ if(!isset($this->_resource)) {
109
+ $this->_resource = @fopen($this->_filename, $this->_getMode());
110
+ }
111
+ return $this->_resource;
112
+ }
113
+
114
+ protected function _getPath()
115
+ {
116
+ if(!isset($this->_path)) {
117
+ $this->_path = Mage::getModel('combine/file_path');
118
+ }
119
+ return $this->_path;
120
+ }
121
+
122
+ protected function _setMode($mode)
123
+ {
124
+ $this->_mode = $mode;
125
+ return $this;
126
+ }
127
+
128
+ protected function _getMode()
129
+ {
130
+ if(!isset($this->_mode)) {
131
+ throw new Exception('No mode set for I/O.');
132
+ }
133
+ return $this->_mode;
134
+ }
135
+
136
+ protected function _getStreamLength()
137
+ {
138
+ $filesize = @filesize($this->_filename);
139
+ return $filesize ? $filesize : 1024;
140
+ }
141
+
142
+ protected function _doCreate()
143
+ {
144
+ return $this->_getMode() != self::READ;
145
+ }
146
+ }
app/code/community/Springbot/Combine/Model/File/Path.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_File_Path
4
+ {
5
+ protected $_baseDir;
6
+ protected $_filename;
7
+ protected $_type;
8
+
9
+ public function getBaseDir($type = 'base')
10
+ {
11
+ if(isset($this->_type)) {
12
+ $type = $this->_type;
13
+ }
14
+ if(!isset($this->_baseDir)) {
15
+ $this->_baseDir = Mage::getBaseDir($type);
16
+ }
17
+ return $this->_baseDir;
18
+ }
19
+
20
+ public function setBaseDir($path)
21
+ {
22
+ $this->_baseDir = $path;
23
+ return $this;
24
+ }
25
+
26
+ public function setBaseDirType($type)
27
+ {
28
+ $this->_type = $type;
29
+ return $this;
30
+ }
31
+
32
+ public function setFilename($name)
33
+ {
34
+ $this->_filename = $name;
35
+ return $this;
36
+ }
37
+
38
+ public function getFilename()
39
+ {
40
+ return $this->_filename;
41
+ }
42
+
43
+ public function getAbsolutePath()
44
+ {
45
+ return $this->getBaseDir() . DS . $this->getFilename();
46
+ }
47
+
48
+ public function resolve($filename = null, $type = null)
49
+ {
50
+ $filename = is_null($filename) ? $this->_filename : $filename;
51
+ $type = is_null($type) ? $this->_type : $type;
52
+
53
+ if(!isset($filename)) {
54
+ throw new Exception('Filename required as property or argument.');
55
+ }
56
+ if(!isset($type)) {
57
+ throw new Exception('Directory type required.');
58
+ }
59
+
60
+ $this->setFilename($filename);
61
+
62
+ return $this->getBaseDir($type) . DS . $this->getFilename();
63
+ }
64
+
65
+ public function isWriteable($path = null)
66
+ {
67
+ if(is_null($path)) {
68
+ $path = $this->getBaseDir();
69
+ }
70
+ if(!file_exists($path)) {
71
+ throw new Exception('File or directory does not exist.');
72
+ }
73
+ return is_writable($path);
74
+ }
75
+ }
app/code/community/Springbot/Combine/Model/Harvest/Abstract.php ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Abstract
4
+ {
5
+ protected $_collection;
6
+ protected $_iterator;
7
+ protected $_https;
8
+ protected $_model;
9
+ protected $_parser;
10
+ protected $_parserModel;
11
+ protected $_segmentQueue = array();
12
+ protected $_total = 0;
13
+ protected $_apiController;
14
+ protected $_apiModel;
15
+ protected $_mageModel;
16
+ protected $_rowId = 'entity_id';
17
+ protected $_segmentSize = 250;
18
+ protected $_segmentMin = 0;
19
+ protected $_segmentMax = 0;
20
+ protected $_delete = false;
21
+ protected $_storeId;
22
+ protected $_dataSource;
23
+
24
+ /**
25
+ * Central controller for harvest
26
+ *
27
+ * @return Springbot_Combine_Model_Harvest_Abstract
28
+ */
29
+ public function harvest()
30
+ {
31
+ if($this->getCount()) {
32
+ $this->_getIterator()->walk(
33
+ $this->getSelect(),
34
+ array(array($this, 'step'))
35
+ );
36
+ // Post leftover segment
37
+ $this->_total += count($this->_segmentQueue);
38
+ $this->postSegment();
39
+ }
40
+ return $this;
41
+ }
42
+
43
+ /**
44
+ * Set delete param for all records
45
+ *
46
+ * @return Springbot_Combine_Model_Harvest_Abstract
47
+ */
48
+ public function delete()
49
+ {
50
+ $this->_delete = true;
51
+ return $this->harvest();
52
+ }
53
+
54
+ /**
55
+ * Post single defined model
56
+ *
57
+ * We must post as a single element in array to handle downstream
58
+ * formatting concerns.
59
+ *
60
+ * @param Mage_Core_Model_Abstract $model
61
+ */
62
+ public function post($model)
63
+ {
64
+ $parsed = array($this->parse($model));
65
+ $payload = $this->_getApi()->wrap($this->_getApiModel(), $parsed);
66
+ $this->_getApi()->reinit()->call($this->_getApiController(), $payload);
67
+ }
68
+
69
+ /**
70
+ * Push a model onto the segment queue
71
+ *
72
+ * @param Mage_Core_Model_Abstract $model
73
+ * @return Springbot_Combine_Model_Harvest_Abstract
74
+ */
75
+ public function push($model)
76
+ {
77
+ unset($this->_parser); //reinit
78
+ $this->setDataSource(Springbot_Boss::SOURCE_OBSERVER);
79
+ $this->_segmentQueue[] = $this->parse($model);
80
+ return $this;
81
+ }
82
+
83
+ /**
84
+ * Gets row id based on class config
85
+ *
86
+ * @param array $row
87
+ * @return int
88
+ */
89
+ protected function _getRowId($row)
90
+ {
91
+ $id = null;
92
+ if(isset($row[$this->_rowId])) {
93
+ $id = $row[$this->_rowId];
94
+ $this->_setSegmentMinMax($id);
95
+ }
96
+ return $id;
97
+ }
98
+
99
+ /**
100
+ * Set min/max for current segment
101
+ *
102
+ * @param int $id
103
+ */
104
+ protected function _setSegmentMinMax($id)
105
+ {
106
+ if($id < $this->_segmentMin || !$this->_segmentMin) {
107
+ $this->_segmentMin = $id;
108
+ }
109
+ if($id > $this->_segmentMax) {
110
+ $this->_segmentMax = $id;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Step callback referenced in harvester
116
+ *
117
+ * @param array $args
118
+ */
119
+ public function step($args)
120
+ {
121
+ if(count($this->_segmentQueue) >= $this->_getMaxSegmentSize()) {
122
+ $this->_total += $this->_getMaxSegmentSize();
123
+ $this->postSegment();
124
+ }
125
+
126
+ try {
127
+ if(isset($args['row'])) {
128
+ $id = $this->_getRowId($args['row']);
129
+ $model = $this->loadMageModel($id);
130
+ $this->_segmentQueue[] = $this->parse($model);
131
+ }
132
+ } catch (Exception $e) {
133
+ Springbot_Log::error($e);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Parse caller for dependent parser method
139
+ *
140
+ * @param Mage_Core_Model_Abstract $model
141
+ * @return Zend_Json_Expr
142
+ */
143
+ public function parse($model)
144
+ {
145
+ $parsed = $this->_getParser($model)->parse($model);
146
+ if($this->_delete) {
147
+ $parsed->setIsDeleted(true);
148
+ }
149
+ $parsed->setDataSource($this->getDataSource());
150
+ $json = $parsed->toJson();
151
+ return $parsed->getData();
152
+ }
153
+
154
+ /**
155
+ * Loads mage model to parse
156
+ *
157
+ * @param int $entityId
158
+ * @return Mage_Core_Model_Abstract
159
+ */
160
+ public function loadMageModel($entityId)
161
+ {
162
+ if(!isset($this->_model)) {
163
+ $this->_model = Mage::getModel($this->_getMageModel());
164
+ }
165
+ return $this->_model->load($entityId);
166
+ }
167
+
168
+ /**
169
+ * Post segment to api
170
+ */
171
+ public function postSegment()
172
+ {
173
+ if(count($this->_segmentQueue) > 0) {
174
+ $payload = $this->_getApi()->wrap($this->_getApiModel(), $this->_segmentQueue);
175
+ $this->_getApi()->reinit()->call($this->_getApiController(), $payload);
176
+
177
+ $this->_clearSegment();
178
+ }
179
+ }
180
+
181
+ protected function _clearSegment()
182
+ {
183
+ unset($this->_segmentQueue);
184
+ }
185
+
186
+ public function setStoreId($id)
187
+ {
188
+ $this->_storeId = $id;
189
+ return $this;
190
+ }
191
+
192
+ public function setDataSource($source)
193
+ {
194
+ $this->_dataSource = $source;
195
+ return $this;
196
+ }
197
+
198
+ public function setDelete($bool)
199
+ {
200
+ $this->_delete = $bool;
201
+ return $this;
202
+ }
203
+
204
+ public function getHarvesterName()
205
+ {
206
+ return ucfirst($this->_apiModel);
207
+ }
208
+
209
+ public function getCollection()
210
+ {
211
+ if(!isset($this->_collection)) {
212
+ throw new Exception("Collection not found!");
213
+ }
214
+ return $this->_collection;
215
+ }
216
+
217
+ public function setCollection(Varien_Data_Collection $collection)
218
+ {
219
+ $this->_collection = $collection;
220
+ return $this;
221
+ }
222
+
223
+ public function getSelect()
224
+ {
225
+ return $this->getCollection()->getSelect();
226
+ }
227
+
228
+ public function getCount()
229
+ {
230
+ return $this->getCollection()->getSize();
231
+ }
232
+
233
+ public function getProcessedCount()
234
+ {
235
+ return $this->_total;
236
+ }
237
+
238
+ public function getSegmentMin()
239
+ {
240
+ return $this->_segmentMin;
241
+ }
242
+
243
+ public function getSegmentMax()
244
+ {
245
+ return $this->_segmentMax;
246
+ }
247
+
248
+ public function getDataSource()
249
+ {
250
+ return $this->_dataSource;
251
+ }
252
+
253
+ protected function _getMageModel()
254
+ {
255
+ if(!isset($this->_mageModel)) {
256
+ throw new Exception('Please set Magento model to parse!');
257
+ }
258
+ return $this->_mageModel;
259
+ }
260
+
261
+ protected function _getParser($model)
262
+ {
263
+ if(!isset($this->_parserModel)) {
264
+ throw new Exception('Please set parser type.');
265
+ }
266
+ if(!isset($this->_parser)) {
267
+ $this->_parser = Mage::getModel($this->_parserModel, $model);
268
+ }
269
+ return $this->_parser;
270
+ }
271
+
272
+ protected function _getApiController()
273
+ {
274
+ if(!isset($this->_apiController)) {
275
+ throw new Exception('Please set api controller to send to.');
276
+ }
277
+ return $this->_apiController;
278
+ }
279
+
280
+ protected function _getApiModel()
281
+ {
282
+ if(!isset($this->_apiModel)) {
283
+ throw new Exception('Please set remote model to send to.');
284
+ }
285
+ return $this->_apiModel;
286
+ }
287
+
288
+ protected function _getMaxSegmentSize()
289
+ {
290
+ return $this->_segmentSize;
291
+ }
292
+
293
+ protected function _getApi()
294
+ {
295
+ if(!isset($this->_api)) {
296
+ $this->_api = Mage::getModel('combine/api');
297
+ }
298
+ return $this->_api;
299
+ }
300
+
301
+ protected function _getIterator()
302
+ {
303
+ if(!isset($this->_iterator)) {
304
+ $this->_iterator = Mage::getSingleton('core/resource_iterator');
305
+ }
306
+ return $this->_iterator;
307
+ }
308
+ }
app/code/community/Springbot/Combine/Model/Harvest/AttributeSets.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_AttributeSets extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_parserModel = 'combine/parser_attributeSet';
6
+ protected $_apiController = 'attribute_sets';
7
+ protected $_apiModel = 'attribute_sets';
8
+ protected $_rowId = 'attribute_set_id';
9
+ protected $_helper;
10
+
11
+ public function parse($model)
12
+ {
13
+ $parser = $this->_getParser($model)->setMageStoreId($this->_storeId);
14
+ $parsed = $parser->parse($model);
15
+
16
+ if($this->_delete) {
17
+ $parsed->setIsDeleted(true);
18
+ }
19
+ $parsed->setDataSource($this->getDataSource());
20
+ $json = $parsed->toJson();
21
+ return $parsed->getData();
22
+ }
23
+
24
+ public function loadMageModel($id)
25
+ {
26
+ return $this->_getHelper()->getAttributeSetById($id);
27
+ }
28
+
29
+ protected function _getHelper()
30
+ {
31
+ if(!isset($this->_helper)) {
32
+ $this->_helper = Mage::helper('combine/attributes');
33
+ }
34
+ return $this->_helper;
35
+ }
36
+ }
app/code/community/Springbot/Combine/Model/Harvest/Carts.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Carts extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'sales/quote';
6
+ protected $_parserModel = 'combine/parser_quote';
7
+ protected $_apiController = 'carts';
8
+ protected $_apiModel = 'carts';
9
+
10
+
11
+ public function loadMageModel($entityId)
12
+ {
13
+ $this->_model = Mage::getModel($this->_getMageModel());
14
+ $this->_model->setStoreId($this->_storeId);
15
+ $this->_model->load($entityId);
16
+ return $this->_model;
17
+ }
18
+
19
+ public function parse($model)
20
+ {
21
+ $parser = $this->_getParser($model)->parse($model);
22
+ $parser->setDataSource($this->getDataSource());
23
+ return $parser->getData();
24
+ }
25
+
26
+
27
+ }
app/code/community/Springbot/Combine/Model/Harvest/Categories.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Categories extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'catalog/category';
6
+ protected $_parserModel = 'combine/parser_category';
7
+ protected $_apiController = 'categories';
8
+ protected $_apiModel = 'categories';
9
+
10
+ /**
11
+ * Parse caller for dependent parser method
12
+ *
13
+ * @param Mage_Core_Model_Abstract $model
14
+ * @return Zend_Json_Expr
15
+ */
16
+ public function parse($model)
17
+ {
18
+ $parsed = $this->_getParser($model)
19
+ ->setMageStoreId($this->_storeId)
20
+ ->parse($model);
21
+
22
+ if($this->_delete) {
23
+ $parsed->setIsDeleted(true);
24
+ }
25
+ $parsed->setDataSource($this->getDataSource());
26
+ $json = $parsed->toJson();
27
+ return $parsed->getData();
28
+ }
29
+
30
+ public function loadMageModel($entityId)
31
+ {
32
+ if(!isset($this->_model)) {
33
+ $this->_model = Mage::getModel($this->_getMageModel());
34
+ }
35
+ $this->_model->setStoreId($this->_storeId);
36
+ $this->_model->load($entityId);
37
+ return $this->_model;
38
+ }
39
+ }
app/code/community/Springbot/Combine/Model/Harvest/CustomerAttributeSets.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_CustomerAttributeSets extends Springbot_Combine_Model_Harvest_AttributeSets implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_parserModel = 'combine/parser_customerAttributeSet';
6
+ }
app/code/community/Springbot/Combine/Model/Harvest/Customers.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Customers extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'customer/customer';
6
+ protected $_parserModel = 'combine/parser_customer';
7
+ protected $_apiController = 'customers';
8
+ protected $_apiModel = 'customers';
9
+
10
+ public function loadMageModel($entityId)
11
+ {
12
+ if(!isset($this->_model)) {
13
+ $this->_model = Mage::getModel('customer/customer');
14
+ }
15
+ // This unsets addresses so we can reuse this model
16
+ $this->_model->cleanAllAddresses();
17
+ return $this->_model->load($entityId);
18
+ }
19
+ }
app/code/community/Springbot/Combine/Model/Harvest/Guests.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Guests extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'sales/order';
6
+ protected $_parserModel = 'combine/parser_guest';
7
+ protected $_apiController = 'customers';
8
+ protected $_apiModel = 'customers';
9
+
10
+ public function loadMageModel($entityId)
11
+ {
12
+ if(!isset($this->_model)) {
13
+ $this->_model = Mage::getModel('sales/order');
14
+ }
15
+ $this->_model->unsetData();
16
+ $this->_model->reset();
17
+
18
+ return $this->_model->load($entityId);
19
+ }
20
+ }
app/code/community/Springbot/Combine/Model/Harvest/Products.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Products extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'catalog/product';
6
+ protected $_parserModel = 'combine/parser_product';
7
+ protected $_apiController = 'products';
8
+ protected $_apiModel = 'products';
9
+ protected $_segmentSize = 100;
10
+
11
+
12
+ public function loadMageModel($entityId)
13
+ {
14
+ if(!isset($this->_model)) {
15
+ $this->_model = Mage::getModel($this->_getMageModel());
16
+ }
17
+ $this->_model->cleanCache()->reset();
18
+ $this->_model->setStoreId($this->_storeId);
19
+ $this->_model->load($entityId);
20
+ return $this->_model;
21
+ }
22
+ }
app/code/community/Springbot/Combine/Model/Harvest/Purchases.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Purchases extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'sales/order';
6
+ protected $_parserModel = 'combine/parser_purchase';
7
+ protected $_apiController = 'purchases';
8
+ protected $_apiModel = 'purchases';
9
+ protected $_segmentSize = 100;
10
+
11
+ public function loadMageModel($entityId)
12
+ {
13
+ if(!isset($this->_model)) {
14
+ $this->_model = Mage::getModel($this->_getMageModel());
15
+ }
16
+ $this->_model->unsetData();
17
+ $this->_model->reset();
18
+ return $this->_model->load($entityId);
19
+ }
20
+ }
app/code/community/Springbot/Combine/Model/Harvest/Subscribers.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Harvest_Subscribers extends Springbot_Combine_Model_Harvest_Abstract implements Springbot_Combine_Model_Harvester
4
+ {
5
+ protected $_mageModel = 'newsletter/subscriber';
6
+ protected $_parserModel = 'combine/parser_subscriber';
7
+ protected $_apiController = 'customers';
8
+ protected $_apiModel = 'customers';
9
+ protected $_rowId = 'subscriber_id';
10
+
11
+ public function parse($model)
12
+ {
13
+ if($this->_delete) {
14
+ $model->setSubscriberStatus(Mage_Newsletter_Model_Subscriber::STATUS_UNSUBSCRIBED);
15
+ }
16
+ return parent::parse($model);
17
+ }
18
+ }
app/code/community/Springbot/Combine/Model/Harvester.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ interface Springbot_Combine_Model_Harvester
4
+ {
5
+ public function getCollection();
6
+
7
+ public function setCollection(Varien_Data_Collection $collection);
8
+
9
+ public function harvest();
10
+
11
+ public function step($args);
12
+
13
+ public function parse($model);
14
+
15
+ public function loadMageModel($entityId);
16
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Cron/Count.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Cron_Count extends Springbot_Combine_Model_Resource_Cron_Count
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Cron/Queue.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Cron_Queue extends Springbot_Combine_Model_Resource_Cron_Queue
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Cron/Queue/Collection.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Cron_Queue_Collection extends Springbot_Combine_Model_Resource_Cron_Queue_Collection
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Redirect.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Redirect extends Springbot_Combine_Model_Resource_Redirect
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Redirect/Collection.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Redirect_Collection extends Springbot_Combine_Model_Resource_Redirect_Collection
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Redirect/Order.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Redirect_Order extends Springbot_Combine_Model_Resource_Redirect_Order
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Redirect/Order/Collection.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Redirect_Order_Collection extends Springbot_Combine_Model_Resource_Redirect_Order_Collection
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Setup.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Setup extends Springbot_Combine_Model_Resource_Setup
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Trackable.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Trackable extends Springbot_Combine_Model_Resource_Trackable
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Mysql4/Trackable/Collection.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Mysql4_Trackable_Collection extends Springbot_Combine_Model_Resource_Trackable_Collection
4
+ {
5
+ }
app/code/community/Springbot/Combine/Model/Parser.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ interface Springbot_Combine_Model_Parser
4
+ {
5
+ public function parse($parsable);
6
+ }
app/code/community/Springbot/Combine/Model/Parser/Abstract.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Abstract extends Varien_Object
4
+ {
5
+ protected $_parsed = false;
6
+ protected $_attrProtected = array();
7
+
8
+ /**
9
+ * Redeclaration of Varien_Object::toJson
10
+ *
11
+ * @array $attributes array of attributes to include
12
+ * @return string
13
+ */
14
+ public function toJson(array $arrAttributes = array())
15
+ {
16
+ if(!$this->isParsed()) {
17
+ $this->_parse();
18
+ }
19
+ $this->prune();
20
+ return parent::toJson($arrAttributes);
21
+ }
22
+
23
+ public function reinit()
24
+ {
25
+ unset($this->_storeId);
26
+ }
27
+
28
+ public function prune()
29
+ {
30
+ $this->_data = $this->_prune($this->_data);
31
+ return $this;
32
+ }
33
+
34
+ protected function _prune($array)
35
+ {
36
+ if(is_array($array)) {
37
+ foreach($array as $key => $value) {
38
+ if(is_array($value)) {
39
+ $array[$key] = $this->_prune($value);
40
+ }
41
+ if(empty($value)) {
42
+ unset($array[$key]);
43
+ } else if(is_string($value)) {
44
+ $array[$key] = trim($array[$key]);
45
+ }
46
+ }
47
+ }
48
+ return $array;
49
+ }
50
+
51
+ public function getCustomAttributes()
52
+ {
53
+ $return = array();
54
+ $helper = Mage::helper('combine/attributes');
55
+ $model = $this->_getAccessor();
56
+ $attributes = $helper->getAttributesBySet($model->getAttributeSetId());
57
+
58
+ foreach($attributes as $attribute) {
59
+ $code = $attribute->getAttributeCode();
60
+
61
+ if(!$this->_isProtected($code) && $model->hasData($code)) {
62
+ if($attribute->usesSource()) {
63
+ $value = $helper->getOptionText($attribute, $model->getData($code));
64
+ } else {
65
+ $value = $model->getData($code);
66
+ }
67
+
68
+ $return[$code] = $value;
69
+ }
70
+ }
71
+
72
+ return $return;
73
+ }
74
+
75
+ public function setIsParsed()
76
+ {
77
+ $this->_parsed = true;
78
+ return $this;
79
+ }
80
+
81
+ public function isParsed()
82
+ {
83
+ return $this->_parsed;
84
+ }
85
+
86
+ public function getAccessibleSku($item)
87
+ {
88
+ if($product = $item->getProduct()) {
89
+ if(
90
+ $product->getVisibility() == Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE ||
91
+ $product->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_DISABLED
92
+ ) {
93
+ Springbot_Log::debug('Product not visible - attempt to find parent');
94
+ $product = $this->getParentProduct($item);
95
+ }
96
+ return $this->_getHelper()->getTopLevelSku($product);
97
+ }
98
+ }
99
+
100
+ protected function _getLandingUrl($product)
101
+ {
102
+ return $this->_getHelper()->getLandingUrl($product);
103
+ }
104
+
105
+ protected function _parse()
106
+ {
107
+ return $this->setIsParsed();
108
+ }
109
+
110
+ protected function _formatDateTime($date)
111
+ {
112
+ $_date = new DateTime($date, new DateTimeZone('UTC'));
113
+ return $_date->format(DateTime::ATOM);
114
+ }
115
+
116
+ protected function _getAccessor()
117
+ {
118
+ if(!isset($this->_accessor)) {
119
+ throw new Exception('Please set _accessor in Class ' . __CLASS__);
120
+ }
121
+ return $this->{$this->_accessor};
122
+ }
123
+
124
+ public function getMageStoreId()
125
+ {
126
+ if(!isset($this->_storeId)) {
127
+ $this->_storeId = $this->_getAccessor()->getStoreId();
128
+ }
129
+ return $this->_storeId;
130
+ }
131
+
132
+ public function setMageStoreId($storeId)
133
+ {
134
+ $this->_storeId = $storeId;
135
+ return $this;
136
+ }
137
+
138
+ public function getSpringbotStoreId()
139
+ {
140
+ return $this->_getSpringbotStoreId($this->getMageStoreId());
141
+ }
142
+
143
+ protected function _getSpringbotStoreId($id)
144
+ {
145
+ return Mage::helper('combine/harvest')->getSpringbotStoreId($id);
146
+ }
147
+
148
+ protected function _isProtected($attrCode)
149
+ {
150
+ return in_array($attrCode, $this->_attrProtected);
151
+ }
152
+
153
+ protected function _getHelper()
154
+ {
155
+ return Mage::helper('combine/parser');
156
+ }
157
+
158
+ protected function _getBaseAmt($field, $model = null)
159
+ {
160
+ if(is_null($model)) {
161
+ $model = $this->_getAccessor();
162
+ }
163
+ return ($amt = $model->getData("base_{$field}")) ? $amt : $model->getData($field);
164
+ }
165
+
166
+ protected function _getProduct($id)
167
+ {
168
+ return Mage::getModel('catalog/product')->load($id);
169
+ }
170
+
171
+
172
+ protected function _getSkuFailsafe($product)
173
+ {
174
+ if ($sku = $product->getSku()) {
175
+ return $sku;
176
+ }
177
+ else {
178
+ return Springbot_Boss::NO_SKU_PREFIX .$product->getEntityId();
179
+ }
180
+ }
181
+
182
+ }
app/code/community/Springbot/Combine/Model/Parser/AttributeSet.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_AttributeSet extends Springbot_Combine_Model_Parser_Abstract implements Springbot_Combine_Model_Parser
4
+ {
5
+ protected $_helper;
6
+ protected $_set;
7
+ protected $_storeId;
8
+ protected $_accessor = '_set';
9
+ protected $_type = 'product';
10
+
11
+ public function __construct(Mage_Eav_Model_Entity_Attribute_Set $set)
12
+ {
13
+ $this->_set = $set;
14
+ $this->_parse();
15
+ }
16
+
17
+ public function parse($set)
18
+ {
19
+ $this->_set = $set;
20
+ $this->_parse();
21
+
22
+ return $this;
23
+ }
24
+
25
+ protected function _parse()
26
+ {
27
+ $this->_data = array(
28
+ 'store_id' => $this->getSpringbotStoreId(),
29
+ 'attribute_set_id' => $this->_set->getAttributeSetId(),
30
+ 'name' => $this->_set->getAttributeSetName(),
31
+ 'type' => $this->_type,
32
+ 'attribute_items' => $this->_parseAttributes(),
33
+ );
34
+
35
+ return parent::_parse();
36
+ }
37
+
38
+ protected function _parseAttributes()
39
+ {
40
+ return Mage::helper('combine/attributes')->getParsedAttributesBySet($this->_set);
41
+ }
42
+
43
+ protected function _getHelper()
44
+ {
45
+ if(!isset($this->_helper)) {
46
+ $this->_helper = Mage::helper('combine/attributes');
47
+ }
48
+ return $this->_helper;
49
+ }
50
+ }
app/code/community/Springbot/Combine/Model/Parser/Category.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Category extends Springbot_Combine_Model_Parser_Abstract
4
+ {
5
+ protected $_accessor = '_category';
6
+ protected $_category;
7
+ protected $_storeId;
8
+
9
+ public function __construct(Mage_Catalog_Model_Category $category)
10
+ {
11
+ $this->_category = $category;
12
+ $this->_parse();
13
+ }
14
+
15
+ public function parse($category)
16
+ {
17
+ $this->_category = $category;
18
+ $this->_parse();
19
+
20
+ return $this;
21
+ }
22
+
23
+ protected function _parse()
24
+ {
25
+ $this->setData(array(
26
+ 'cat_id' => $this->_category->getEntityId(),
27
+ 'path' => $this->_category->getPath(),
28
+ 'level' => $this->_category->getLevel(),
29
+ 'store_id' => $this->getSpringbotStoreId(),
30
+ 'cat_name' => $this->_category->getName(),
31
+ 'deleted' => $this->_category->getDeleted(),
32
+ 'json_data' => array(
33
+ 'url_path' => $this->_category->getUrlPath(),
34
+ 'is_active' => $this->_category->getIsActive(),
35
+ )
36
+ ));
37
+ return parent::_parse();
38
+ }
39
+ }
app/code/community/Springbot/Combine/Model/Parser/Customer.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Customer extends Springbot_Combine_Model_Parser_Abstract
4
+ {
5
+ const TYPE = 'REGISTERED';
6
+
7
+ protected $_customer;
8
+ protected $_sales;
9
+ protected $_attrProtected = array('password_hash', 'default_billing', 'default_shipping');
10
+ protected $_accessor = '_customer';
11
+
12
+ public function __construct(Mage_Customer_Model_Customer $customer)
13
+ {
14
+ $this->_customer = $customer;
15
+ $this->_parse();
16
+ }
17
+
18
+ public function getSalesModel()
19
+ {
20
+ if(!isset($this->_sales) || !($this->_sales instanceof Mage_Sales_Model_Order)) {
21
+ $this->_sales = Mage::getModel('sales/order');
22
+ }
23
+ return $this->_sales;
24
+ }
25
+
26
+ public function setSalesModel($sales)
27
+ {
28
+ $this->_sales = $sales;
29
+ return $this;
30
+ }
31
+
32
+ public function hasCustomerPurchased()
33
+ {
34
+ return $this->_getCustomerOrderCollection()->getSize() > 0;
35
+ }
36
+
37
+ public function parse($customer)
38
+ {
39
+ $this->_customer = $customer;
40
+ $this->_parse();
41
+
42
+ return $this;
43
+ }
44
+
45
+ protected function _parse()
46
+ {
47
+ $this->setData(array(
48
+ 'customer_id' => $this->_customer->getEntityId(),
49
+ 'first_name' => $this->_customer->getFirstname(),
50
+ 'last_name' => $this->_customer->getLastname(),
51
+ 'email' => $this->_getEmail(),
52
+ 'store_id' => $this->_getSpringbotStoreId($this->_customer->getStore()->getId()),
53
+ 'has_purchase' => $this->hasCustomerPurchased(),
54
+ 'json_data' => $this->_getAddressData(),
55
+ 'custom_attribute_set_id' => $this->_customer->getAttributeSetId(),
56
+ 'custom_attributes' => $this->getCustomAttributes(),
57
+ 'customer_type' => self::TYPE,
58
+ ));
59
+
60
+ return parent::_parse();
61
+ }
62
+
63
+ public function getCustomAttributes()
64
+ {
65
+ $attributes = parent::getCustomAttributes();
66
+
67
+ $attributes['group_id'] = $this->_customer->getGroupId();
68
+ $attributes['group'] = $this->_getCustomerGroupName();
69
+
70
+ return $attributes;
71
+ }
72
+
73
+ protected function _getAddressData()
74
+ {
75
+ if($address = $this->_customer->getDefaultBillingAddress()) {
76
+ $class = 'billing';
77
+ } else if ($address = $this->_customer->getDefaultShippingAddress()) {
78
+ $class = 'shipping';
79
+ } else {
80
+ return new stdClass;
81
+ }
82
+
83
+ return array(
84
+ 'telephone' => $address->getTelephone(),
85
+ 'street' => $address->getStreet1(),
86
+ 'city' => $address->getCity(),
87
+ 'state' => $address->getRegionCode(),
88
+ 'postal_code' => $address->getPostcode(),
89
+ 'country_code' => $address->getCountry(),
90
+ 'company' => $address->getCompany(),
91
+ 'class' => $class,
92
+ );
93
+ }
94
+
95
+ protected function _getCustomerOrderCollection()
96
+ {
97
+ return $this->getSalesModel()->getCollection()
98
+ ->addAttributeToFilter('customer_id', $this->_customer->getEntityId());
99
+ }
100
+
101
+ protected function _getCustomerGroupName()
102
+ {
103
+ $groupId = $this->_customer->getGroupId();
104
+ $group = Mage::getModel('customer/group')->load($groupId);
105
+ return $group->getCode();
106
+ }
107
+
108
+ private function _getEmail() {
109
+ $email = $this->_customer->getEmail();
110
+ // If no email is found use the email stored in the session by the javascript listener
111
+ if (!$email) {
112
+ if ($quote = Mage::getSingleton('checkout/session')->getQuote()) {
113
+ $email = $quote->getCustomerEmail();
114
+ }
115
+ }
116
+
117
+ return $email;
118
+ }
119
+ }
app/code/community/Springbot/Combine/Model/Parser/CustomerAttributeSet.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_CustomerAttributeSet extends Springbot_Combine_Model_Parser_AttributeSet implements Springbot_Combine_Model_Parser
4
+ {
5
+ protected $_type = 'customer';
6
+
7
+ protected function _parseAttributes()
8
+ {
9
+ $helper = $this->_getHelper();
10
+ $attributes = $helper->parseAttributes($helper->getCustomerCustomAttributes($this->_set));
11
+
12
+ $customerGroupModel = new Mage_Customer_Model_Group();
13
+ $customerGroups = $customerGroupModel->getCollection()->toOptionHash();
14
+ $optionsArray = array();
15
+ foreach ($customerGroups as $key => $customerGroup){
16
+ $optionsArray[] = $customerGroup;
17
+ }
18
+
19
+ $attributes[] = array(
20
+ 'label' => 'Group',
21
+ 'attribute_id' => 9000000,
22
+ 'attribute_code' => 'group',
23
+ 'options' => $optionsArray
24
+ );
25
+
26
+ return $attributes;
27
+
28
+ }
29
+ }
app/code/community/Springbot/Combine/Model/Parser/Guest.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Guest extends Springbot_Combine_Model_Parser_Abstract
4
+ {
5
+ const TYPE = 'GUEST';
6
+ const OFFSET = 100000000;
7
+
8
+ protected $_order;
9
+
10
+ public function __construct(Mage_Sales_Model_Order $order)
11
+ {
12
+ $this->_order = $order;
13
+ $this->_address = $this->_order->getShippingAddress();
14
+ $this->_parse();
15
+ }
16
+
17
+ public function parse($order)
18
+ {
19
+ $this->_order = $order;
20
+ $this->_address = $this->_order->getShippingAddress();
21
+ $this->_parse();
22
+ return $this;
23
+ }
24
+
25
+ protected function _parse()
26
+ {
27
+ if(!is_object($this->_address)) { return false; }
28
+
29
+ $this->_data = array(
30
+ 'customer_id' => $this->_createCustomerId(),
31
+ 'first_name' => $this->_address->getFirstname(),
32
+ 'last_name' => $this->_address->getLastname(),
33
+ 'email' => $this->_getEmail(),
34
+ 'store_id' => $this->_getSpringbotStoreId($this->_order->getStoreId()),
35
+ 'has_purchase' => true,
36
+ 'json_data' => $this->_getAddressData(),
37
+ 'customer_type' => self::TYPE
38
+ );
39
+
40
+ return parent::_parse();
41
+ }
42
+
43
+ protected function _createCustomerId()
44
+ {
45
+ return self::OFFSET + $this->_order->getEntityId();
46
+ }
47
+
48
+ protected function _getAddressData()
49
+ {
50
+ if($address = $this->_order->getBillingAddress()) {
51
+ $class = 'billing';
52
+ } else if ($address = $this->_order->getShippingAddress()) {
53
+ $class = 'shipping';
54
+ } else {
55
+ // No default addresses found
56
+ return;
57
+ }
58
+
59
+ return array(
60
+ 'street' => $address->getStreet1(),
61
+ 'city' => $address->getCity(),
62
+ 'state' => $address->getRegionCode(),
63
+ 'postal_code' => $address->getPostcode(),
64
+ 'country_code' => $address->getCountry(),
65
+ 'company' => $address->getCompany(),
66
+ 'class' => $class,
67
+ 'guest' => true,
68
+ );
69
+ }
70
+
71
+ protected function _getEmail()
72
+ {
73
+ $email = $this->_order->getCustomerEmail();
74
+
75
+ if($this->hasAddress() && !isset($email)) {
76
+ $email = $this->_address->getEmail();
77
+ }
78
+
79
+ if (!$email) {
80
+ if ($quote = Mage::getSingleton('checkout/session')->getQuote()) {
81
+ $email = $quote->getCustomerEmail();
82
+ }
83
+ }
84
+
85
+ return $email;
86
+ }
87
+
88
+ public function hasAddress()
89
+ {
90
+ return isset($this->_address);
91
+ }
92
+ }
app/code/community/Springbot/Combine/Model/Parser/Product.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Product extends Springbot_Combine_Model_Parser_Abstract implements Springbot_Combine_Model_Parser
4
+ {
5
+ protected $_accessor = '_product';
6
+ protected $_product;
7
+
8
+ public function __construct(Mage_Catalog_Model_Product $product)
9
+ {
10
+ $this->_product = $product;
11
+ $this->_parse();
12
+ }
13
+
14
+ public function parse($product)
15
+ {
16
+ $this->_product = $product;
17
+ $this->_parse();
18
+
19
+ return $this;
20
+ }
21
+
22
+ public function _parse()
23
+ {
24
+ $p = $this->_product;
25
+ $categories = Springbot_Util_Categories::forProduct($this->_product);
26
+
27
+ $this->setData(array(
28
+ 'store_id' => $this->getSpringbotStoreId(),
29
+ 'entity_id' => $p->getId(),
30
+ 'sku' => $this->_getSku(),
31
+ 'sku_fulfillment' => $p->getSku(),
32
+ 'category_ids' => $p->getCategoryIds(),
33
+ 'root_category_ids' => $categories->getRoots(),
34
+ 'all_category_ids' => $categories->getAll(),
35
+ 'full_description' => $p->getDescription(),
36
+ 'short_description' => $p->getShortDescription(),
37
+ 'image_url' => $this->getImageUrl(),
38
+ 'landing_url' => $this->getLandingUrl(),
39
+ 'name' => $p->getName(),
40
+ 'url_key' => $p->getUrlKey(),
41
+ 'is_deleted' => false,
42
+ 'status' => $p->getStatus(),
43
+ 'created_at' => $this->_formatDateTime($p->getCreatedAt()),
44
+ 'updated_at' => $this->_formatDateTime($p->getUpdatedAt()),
45
+ 'catalog_created_at' => $this->_formatDateTime($p->getCreatedAt()),
46
+ 'catalog_updated_at' => $this->_formatDateTime($p->getUpdatedAt()),
47
+ 'json_data' => array(
48
+ 'unit_price' => $p->getPrice(),
49
+ 'msrp' => $p->getMsrp(),
50
+ 'sale_price' => $p->getSpecialPrice(),
51
+ 'unit_cost' => 0,
52
+ 'image_label' => $p->getImageLabel(),
53
+ 'unit_wgt' => $p->getWeight(),
54
+ 'type' => $p->getTypeId(),
55
+ 'visibility' => $p->getVisibility(),
56
+ 'cat_id_list' => $this->_implodeCategoryIds(),
57
+ ),
58
+ 'custom_attribute_set_id' => $p->getAttributeSetId(),
59
+ 'custom_attributes' => $this->getCustomAttributes(),
60
+ ));
61
+ return parent::_parse();
62
+ }
63
+
64
+
65
+
66
+ public function getCustomAttributes()
67
+ {
68
+ return $this->_getHelper()->getCustomAttributes($this->_product);
69
+ }
70
+
71
+ protected function _implodeCategoryIds()
72
+ {
73
+ $ids = $this->_product->getCategoryIds();
74
+
75
+ if($ids && count($ids) > 0) {
76
+ $ids = implode(',', $ids);
77
+ }
78
+ return $ids;
79
+ }
80
+
81
+ protected function _getSku()
82
+ {
83
+ $parents = Mage::helper('combine/parser')->getParentSkus($this->_product->getId());
84
+ if(sizeof($parents) > 0) {
85
+ $sku = implode('|', $parents);
86
+ } else {
87
+ $sku = $this->_getSkuFailsafe($this->_product);
88
+ }
89
+ return $sku;
90
+ }
91
+
92
+ public function getImageUrl()
93
+ {
94
+ return $this->_getHelper()->getImageUrl($this->_product);
95
+ }
96
+
97
+ public function getLandingUrl()
98
+ {
99
+ return parent::_getLandingUrl($this->_product);
100
+ }
101
+ }
102
+
app/code/community/Springbot/Combine/Model/Parser/Purchase.php ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Purchase extends Springbot_Combine_Model_Parser_Abstract implements Springbot_Combine_Model_Parser
4
+ {
5
+ protected $_accessor = '_purchase';
6
+ protected $_purchase;
7
+ protected $_lineItems;
8
+
9
+ public function __construct(Mage_Sales_Model_Order $order)
10
+ {
11
+ $this->_lineItems = null;
12
+ $this->_purchase = $order;
13
+ $this->_parse();
14
+ }
15
+
16
+ public function parse($purchase)
17
+ {
18
+ $this->_lineItems = null;
19
+ $this->_purchase = $purchase;
20
+ $this->_parse();
21
+
22
+ return $this;
23
+ }
24
+
25
+ protected function _parse()
26
+ {
27
+ $model = $this->_purchase;
28
+
29
+ $this->setData(array(
30
+ 'purchase_id' => $model->getIncrementId(),
31
+ 'entity_id' => $model->getId(),
32
+ 'email' => $this->_getEmail($model),
33
+ 'quote_id' => $model->getQuoteId(),
34
+ 'redirect_mongo_id' => $this->_getRedirectMongoId(),
35
+ 'redirect_mongo_ids' => $this->_getRedirectMongoIds(),
36
+ 'sb_params' => $this->_getSbParams($model->getQuoteId()),
37
+ 'store_id' => $this->_getSpringbotStoreId($model->getStoreId()),
38
+ 'customer_id' => $this->_getCustomerId(),
39
+ 'order_date_time' => $model->getCreatedAt(),
40
+ 'order_gross' => $this->_getBaseAmt('grand_total'),
41
+ 'order_discount' => $this->_getBaseAmt('discount_amount'),
42
+ 'order_paid' => $this->_getOrderPaid(),
43
+ 'shipping' => $this->_getBaseAmt('shipping_amount'),
44
+ 'sales_tax' => $this->_getBaseAmt('tax_amount'),
45
+ 'pay_method' => $this->_getPaymentMethod(),
46
+ 'guest' => $model->getCustomerIsGuest() ? 'Y' : 'N',
47
+ 'order_state' => $model->getState(),
48
+ 'order_status' => $model->getStatus(),
49
+ 'line_items' => $this->_getLineItems(),
50
+ 'attribute_items' => $this->_getAttributeArray(),
51
+ 'json_data' => $this->_getJsonData(),
52
+ ));
53
+
54
+ return parent::_parse();
55
+ }
56
+
57
+ protected function _getRedirectMongoId()
58
+ {
59
+ if($modelId = $this->_purchase->getRedirectMongoId()) {
60
+ return $modelId;
61
+ }
62
+ return Mage::helper('combine/redirect')->getRedirectByOrderId($this->_purchase->getId())->getRedirectId();
63
+ }
64
+
65
+ protected function _getRedirectMongoIds()
66
+ {
67
+ if($modelIds = $this->_purchase->getRedirectMongoIds()) {
68
+ return array_values($modelIds);
69
+ }
70
+ return Mage::helper('combine/redirect')->getRedirectsByEmail($this->_purchase->getCustomerEmail(), $this->_purchase->getCreatedAt());
71
+ }
72
+
73
+ protected function _getSbParams($quoteId)
74
+ {
75
+ return Mage::helper('combine/trackable')->getTrackablesHashByQuote($quoteId);
76
+ }
77
+
78
+ protected function _getLineItems()
79
+ {
80
+ if(!isset($this->_lineItems)) {
81
+ $lineItems = array();
82
+ $items = $this->_purchase->getAllVisibleItems();
83
+
84
+ if(count($items) > 0) {
85
+ foreach($items as $item) {
86
+ $lineItems[] = Mage::getModel('combine/parser_purchase_item', $item)->getData();
87
+ }
88
+ }
89
+ $this->_lineItems = $lineItems;
90
+ }
91
+ return $this->_lineItems;
92
+ }
93
+
94
+ protected function _getAttributeArray()
95
+ {
96
+ $attrs = array();
97
+ foreach($this->_lineItems as $item)
98
+ {
99
+ $id = isset($item['attribute_set_id']) ? $item['attribute_set_id'] : null;
100
+ foreach($item['attributes'] as $key => $value) {
101
+ if(!empty($value)) {
102
+ $attrs[] = "$key:$value:$id";
103
+ }
104
+ }
105
+ }
106
+ return $attrs;
107
+ }
108
+
109
+ protected function _getJsonData()
110
+ {
111
+ $model = $this->_purchase;
112
+
113
+ return array(
114
+ 'order_dow' => date('D', strtotime($model->getCreatedAt())),
115
+ 'currency' => $model->getOrderCurrencyCode(),
116
+ 'ship_service' => $model->getShippingDescription(),
117
+ 'ip' => $model->getRemoteIp(),
118
+ 'gift_msg' => (bool) $model->getGiftMessage(),
119
+ 'guest' => (bool) $model->getCustomerIsGuest(),
120
+ 'free_shipping' => (bool) $model->getFreeShipping(),
121
+ 'coupon_code' => $model->getCouponCode(),
122
+ );
123
+ }
124
+
125
+ protected function _getCustomerId()
126
+ {
127
+ if($id = $this->_purchase->getCustomerId()) {
128
+ return $id;
129
+ } else {
130
+ $offset = Springbot_Combine_Model_Parser_Guest::OFFSET;
131
+ return $offset + $this->_purchase->getEntityId();
132
+ }
133
+ }
134
+
135
+ protected function _getOrderPaid()
136
+ {
137
+ $payment = $this->_getPayment();
138
+ if($amt = $payment->getBaseAmountOrdered()) {
139
+ // using payment amt ordered
140
+ } else if ($amt = $payment->getBaseAmountAuthorized()) {
141
+ // using payment authorized amt
142
+ } else {
143
+ $amt = $this->_getBaseAmt('grand_total');
144
+ }
145
+ return $amt;
146
+ }
147
+
148
+ protected function _getPaymentMethod()
149
+ {
150
+ $payment = $this->_getPayment();
151
+ return is_object($payment) ? $payment->getMethod() : null;
152
+ }
153
+
154
+ protected function _getPayment()
155
+ {
156
+ $payment = $this->_purchase->getPayment();
157
+ if(empty($payment)) {
158
+ $payment = Mage::getModel('sales/order_payment');
159
+ }
160
+ return $payment;
161
+ }
162
+
163
+ private function _getEmail($model)
164
+ {
165
+ $email = $model->getCustomerEmail();
166
+
167
+ return $email;
168
+ }
169
+ }
app/code/community/Springbot/Combine/Model/Parser/Purchase/Item.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Purchase_Item extends Springbot_Combine_Model_Parser_Abstract implements Springbot_Combine_Model_Parser
4
+ {
5
+ protected $_item;
6
+ protected $_accessor = '_item';
7
+ protected $_actualProduct;
8
+ protected $_parentProduct;
9
+
10
+ public function __construct(Mage_Sales_Model_Order_Item $item)
11
+ {
12
+ $this->_item = $item;
13
+ $this->_actualProduct = null;
14
+ $this->_parentProduct = null;
15
+ $this->_parse();
16
+ }
17
+
18
+ public function parse($item)
19
+ {
20
+ $this->_item = $item;
21
+ $this->_actualProduct = null;
22
+ $this->_parentProduct = null;
23
+ $this->_parse();
24
+ return $this;
25
+ }
26
+
27
+ public function _parse()
28
+ {
29
+ $item = $this->_item;
30
+
31
+ $parent = $this->getParentProduct();
32
+ $actual = $this->getActualProduct();
33
+
34
+ $categories = Springbot_Util_Categories::forProduct($parent);
35
+
36
+ $this->_data = array(
37
+ 'sku' => $this->_getSkuFailsafe($parent),
38
+ 'sku_fulfillment' => $item->getSku(),
39
+ 'qty_ordered' => $item->getQtyOrdered(),
40
+ 'landing_url' => $this->getLandingUrl(),
41
+ 'image_url' => $this->getImageUrl(),
42
+ 'wgt' => $item->getWeight(),
43
+ 'name' => $item->getName(),
44
+ 'desc' => $item->getDescription(),
45
+ 'sell_price' => $this->_getBaseAmt('row_total', $item),
46
+ 'product_id' => (int) $item->getProductId(),
47
+ 'product_type' => $item->getProductType(),
48
+ 'category_ids' => $parent->getCategoryIds(),
49
+ 'root_category_ids' => $categories->getRoots(),
50
+ 'all_category_ids' => $categories->getAll(),
51
+ 'attribute_set_id' => (int) $actual->getAttributeSetId(),
52
+ 'attributes' => $this->_getProductAttributes(),
53
+ );
54
+ }
55
+
56
+ public function getParentProduct()
57
+ {
58
+ $item = $this->_item;
59
+
60
+ if(!isset($this->_parentProduct))
61
+ {
62
+ if($config = $item->getProductOptionByCode('super_product_config'))
63
+ {
64
+ $parentProductId = isset($config['product_id']) ? $config['product_id'] : null;
65
+ }
66
+ else if($item->hasParentItemId())
67
+ {
68
+ $parentProductId = $item->getParentItem()->getProductId();
69
+ }
70
+ if(!isset($parentProductId))
71
+ {
72
+ $parentProductId = $item->getProductId();
73
+ }
74
+ $this->_parentProduct = $this->_getProduct($parentProductId);
75
+ }
76
+ return $this->_parentProduct;
77
+ }
78
+
79
+ public function getActualProduct()
80
+ {
81
+ $item = $this->_item;
82
+
83
+ if(!isset($this->_actualProduct)) {
84
+ if($item->getProductType() == 'simple') {
85
+ $this->_actualProduct = Mage::getModel('catalog/product')->load($item->getProductId());
86
+ }
87
+ else {
88
+ $this->_actualProduct = Mage::getModel('catalog/product')->load($item->getProductId());
89
+
90
+ foreach($item->getOrder()->getAllItems() as $_item) {
91
+ if($item->getSku() == $_item->getSku()) {
92
+ $this->_actualProduct = Mage::getModel('catalog/product')->load($_item->getProductId());
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return $this->_actualProduct;
98
+ }
99
+
100
+ protected function _getProductAttributes()
101
+ {
102
+ $collection = array();
103
+ return $this->_getHelper()->getCustomAttributes($this->getActualProduct(), 50);
104
+ }
105
+
106
+ public function getLandingUrl()
107
+ {
108
+ $product = $this->getActualProduct();
109
+
110
+ if(!$this->_getHelper()->isAccessible($product)) {
111
+ $product = $this->getParentProduct();
112
+ }
113
+
114
+ return $this->_getLandingUrl($product);
115
+ }
116
+
117
+ public function getImageUrl()
118
+ {
119
+ $product = $this->getActualProduct();
120
+
121
+ if(!$this->_getHelper()->hasImage($product)) {
122
+ $product = $this->getParentProduct();
123
+ }
124
+
125
+ return $this->_getHelper()->getImageUrl($product);
126
+ }
127
+ }
app/code/community/Springbot/Combine/Model/Parser/Quote.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Quote extends Springbot_Combine_Model_Parser_Abstract
4
+ {
5
+ protected $_quote;
6
+ protected $_accessor = '_quote';
7
+ protected $_items = array();
8
+
9
+ public function __construct(Mage_Sales_Model_Quote $quote)
10
+ {
11
+ $this->_items = array();
12
+ $this->_quote = $quote;
13
+ $this->_parse();
14
+ }
15
+
16
+ public function parse(Mage_Sales_Model_Quote $quote)
17
+ {
18
+ $this->_items = array();
19
+ $this->_quote = $quote;
20
+ $this->_parse();
21
+ return $this;
22
+ }
23
+
24
+ public function getItemsCount()
25
+ {
26
+ return count($this->_items);
27
+ }
28
+
29
+ public function hasCustomerData()
30
+ {
31
+ return $this->_getEmail();
32
+ }
33
+
34
+ protected function _parse()
35
+ {
36
+ $this->setData(array(
37
+ 'quote_id' => $this->_quote->getEntityId(),
38
+ 'sb_params' => $this->_getSbParams($this->_quote->getEntityId()),
39
+ 'store_id' => $this->_getSpringbotStoreId($this->_quote->getStoreId()),
40
+ 'customer_id' => $this->_quote->getCustomerId(),
41
+ 'quote_created' => $this->_formatDateTime($this->_quote->getCreatedAt()),
42
+ 'quote_updated' => $this->_formatDateTime($this->_quote->getUpdatedAt()),
43
+ 'quote_converted' => $this->_formatDateTime($this->_quote->getConvertedAt()),
44
+ 'customer_email' => $this->_getEmail(),
45
+ 'customer_prefix' => $this->_quote->getCustomerPrefix(),
46
+ 'customer_firstname' => $this->_quote->getCustomerFirstname(),
47
+ 'customer_middlename' => $this->_quote->getCustomerMiddlename(),
48
+ 'customer_lastname' => $this->_quote->getCustomerLastname(),
49
+ 'customer_suffix' => $this->_quote->getCustomerSuffix(),
50
+ 'json_data' => array(
51
+ 'checkout_method' => $this->_quote->getCheckoutMethod(),
52
+ 'customer_is_guest' => $this->_quote->getCustomerIsGuest(),
53
+ 'remote_ip' => $this->_quote->getRemoteIp(),
54
+ ),
55
+ 'line_items' => $this->_getLineItems(),
56
+ ));
57
+
58
+ return parent::_parse();
59
+ }
60
+
61
+ protected function _getLineItems()
62
+ {
63
+ if($items = $this->_quote->getAllVisibleItems()) {
64
+ foreach($items as $item) {
65
+ $this->_items[] = Mage::getModel('combine/parser_quote_item', $item)->getData();
66
+ }
67
+ }
68
+ return $this->_items;
69
+ }
70
+
71
+ public function _getEmail() {
72
+ $email = $this->_quote->getCustomerEmail();
73
+ return $email;
74
+ }
75
+
76
+ protected function _getSbParams($quoteId)
77
+ {
78
+ return Mage::helper('combine/trackable')->getTrackablesHashByQuote($quoteId);
79
+ }
80
+ }
app/code/community/Springbot/Combine/Model/Parser/Quote/Item.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Quote_Item extends Springbot_Combine_Model_Parser_Abstract
4
+ {
5
+ protected $_item;
6
+ protected $_accessor = '_item';
7
+ protected $_parentProduct;
8
+ protected $_actualProduct;
9
+
10
+ public function __construct(Mage_Sales_Model_Quote_Item $item)
11
+ {
12
+ $this->_item = $item;
13
+ $this->_parentProduct = null;
14
+ $this->_actualProduct = null;
15
+ $this->_parse();
16
+ }
17
+
18
+ public function parse(Mage_Sales_Model_Quote_Item $item)
19
+ {
20
+ $this->_item = $item;
21
+ $this->_parentProduct = null;
22
+ $this->_actualProduct = null;
23
+ $this->_parse();
24
+ return $this;
25
+ }
26
+
27
+ protected function _parse()
28
+ {
29
+ $item = $this->_item;
30
+
31
+ $parent = $this->getParentProduct();
32
+ $actual = $this->getActualProduct();
33
+
34
+ $this->_data = array(
35
+ 'sku' => $this->_getSkuFailsafe($parent),
36
+ 'sku_fulfillment' => $item->getSku(),
37
+ 'entity_id' => $item->getProductId(),
38
+ 'landing_url' => $this->getLandingUrl(),
39
+ 'image_url' => $this->getImageUrl(),
40
+ 'qty' => $item->getQty(),
41
+ 'product_type' => $item->getProductType(),
42
+ );
43
+ return parent::_parse();
44
+ }
45
+
46
+ public function getParentProduct()
47
+ {
48
+ if(!isset($this->_parentProduct))
49
+ {
50
+ $item = $this->_item;
51
+
52
+ if($type = $item->getOptionByCode('product_type'))
53
+ {
54
+ if($parentProductId = $type->getProductId())
55
+ {
56
+ $this->_parentProduct = Mage::getModel('catalog/product')->load($parentProductId);
57
+ }
58
+ }
59
+ else if($item->hasParentItemId())
60
+ {
61
+ $this->_parentProduct = $item->getParentItem()->getProduct();
62
+ }
63
+ else
64
+ {
65
+ $this->_parentProduct = $item->getProduct();
66
+ }
67
+ }
68
+ return $this->_parentProduct;
69
+ }
70
+
71
+ public function getActualProduct()
72
+ {
73
+ if(!isset($this->_actualProduct))
74
+ {
75
+ if($option = $this->_item->getOptionByCode('simple_product'))
76
+ {
77
+ $this->_actualProduct = Mage::getModel('catalog/product')->load($option->getProductId());
78
+ }
79
+ else
80
+ {
81
+ $this->_actualProduct = $this->_item->getProduct();
82
+ }
83
+ }
84
+ return $this->_actualProduct;
85
+ }
86
+
87
+ public function getLandingUrl()
88
+ {
89
+ $product = $this->getActualProduct();
90
+
91
+ if(!$this->_getHelper()->isAccessible($product)) {
92
+ $product = $this->getParentProduct();
93
+ }
94
+
95
+ return $this->_getLandingUrl($product);
96
+ }
97
+
98
+ public function getImageUrl()
99
+ {
100
+ $product = $this->getActualProduct();
101
+
102
+ if(!$this->_getHelper()->hasImage($product)) {
103
+ $product = $this->getParentProduct();
104
+ }
105
+
106
+ return $this->_getHelper()->getImageUrl($product);
107
+ }
108
+ }
app/code/community/Springbot/Combine/Model/Parser/Subscriber.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Parser_Subscriber extends Springbot_Combine_Model_Parser_Abstract
4
+ {
5
+ const TYPE = 'SUBSCRIBER';
6
+ const SUBSCRIBER_MODE = 'NL';
7
+
8
+ protected $_subscriber;
9
+
10
+ public function __construct(Mage_Newsletter_Model_Subscriber $subscriber)
11
+ {
12
+ $this->_subscriber = $subscriber;
13
+ $this->_parse();
14
+ }
15
+
16
+ public function isCustomer()
17
+ {
18
+ $customerId = $this->_subscriber->getCustomerId();
19
+
20
+ return !empty($customerId);
21
+ }
22
+
23
+ public function parse($subscriber)
24
+ {
25
+ $this->_subscriber = $subscriber;
26
+ $this->_parse();
27
+
28
+ return $this;
29
+ }
30
+
31
+ protected function _parse()
32
+ {
33
+ $this->setCustomerId($this->_buildCustomerId())
34
+ ->setSubscriberId($this->_subscriber->getSubscriberId())
35
+ ->setStoreId($this->_getSpringbotStoreId($this->_subscriber->getStoreId()))
36
+ ->setEmail($this->_subscriber->getSubscriberEmail())
37
+ ->setOptinStatus($this->_subscriber->getSubscriberStatus())
38
+ ->setSubscriberMode(self::SUBSCRIBER_MODE)
39
+ ->setCustomerType(self::TYPE)
40
+ ;
41
+ return parent::_parse();
42
+ }
43
+
44
+ protected function _buildCustomerId()
45
+ {
46
+ $customerId = $this->_subscriber->getCustomerId();
47
+
48
+ if(empty($customerId)) {
49
+ $id = $this->_subscriber->getSubscriberId();
50
+ $customerId = (int) str_pad($id, 9, '9', STR_PAD_LEFT);
51
+ }
52
+ return $customerId;
53
+ }
54
+ }
app/code/community/Springbot/Combine/Model/Redirect.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Redirect extends Springbot_Combine_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/redirect');
8
+ Mage::helper('combine/redirect')->checkAllRedirectTables();
9
+ }
10
+
11
+ public function save()
12
+ {
13
+ if($this->_validate()) {
14
+ Springbot_Log::debug("Save redirect id : {$this->getRedirectId()} for order : {$this->getOrderId()}");
15
+ parent::save();
16
+ }
17
+ }
18
+
19
+ protected function _validate()
20
+ {
21
+ return $this->hasRedirectId() && !empty($this->_data['redirect_id']);
22
+ }
23
+
24
+ public function getAttributionIds()
25
+ {
26
+ $collection = Mage::getModel('combine/redirect')->getCollection()->loadByEmail($this->getEmail());
27
+
28
+ $ids = $collection->getAllIds();
29
+
30
+ return $ids;
31
+ }
32
+ }
app/code/community/Springbot/Combine/Model/Redirect/Order.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Redirect_Order extends Springbot_Combine_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/redirect_order');
8
+ Mage::helper('combine/redirect')->checkTable($this->getMainTable());
9
+ }
10
+
11
+ protected function _validate()
12
+ {
13
+ $entity = $this->getRedirectEntityId();
14
+ $orderId = $this->getOrderId();
15
+ return !(empty($entity) || empty($orderId));
16
+ }
17
+ }
app/code/community/Springbot/Combine/Model/Resource/Abstract.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Springbot_Combine_Model_Resource_Abstract extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+ public function insertIgnore(Mage_Core_Model_Abstract $object)
6
+ {
7
+ try {
8
+ $table = $this->getMainTable();
9
+ $bind = $this->_prepareDataForSave($object);
10
+ $this->_insertIgnore($table, $bind);
11
+ } catch (Exception $e) {
12
+ Mage::logException($e);
13
+ }
14
+ }
15
+
16
+ protected function _insertIgnore($table, array $bind)
17
+ {
18
+ $adapter = $this->_getWriteAdapter();
19
+
20
+ // extract and quote col names from the array keys
21
+ $cols = array();
22
+ $vals = array();
23
+ foreach ($bind as $col => $val) {
24
+ $cols[] = $adapter->quoteIdentifier($col, true);
25
+ $vals[] = '?';
26
+ }
27
+
28
+ // build the statement
29
+ $sql = "INSERT IGNORE INTO "
30
+ . $adapter->quoteIdentifier($table, true)
31
+ . ' (' . implode(', ', $cols) . ') '
32
+ . 'VALUES (' . implode(', ', $vals) . ')';
33
+
34
+ Springbot_Log::debug($sql);
35
+ Springbot_Log::debug('BIND : '.implode(', ', $bind));
36
+
37
+ // execute the statement and return the number of affected rows
38
+ $stmt = $adapter->query($sql, array_values($bind));
39
+ return $stmt->rowCount();
40
+ }
41
+
42
+ protected function _getHelper()
43
+ {
44
+ return Mage::helper('combine/redirect');
45
+ }
46
+ }
app/code/community/Springbot/Combine/Model/Resource/Cron/Count.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Cron_Count extends Springbot_Combine_Model_Resource_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/cron_count', 'id');
8
+ }
9
+
10
+ public function createCountRow($storeId, $harvestId, $entityType, $count)
11
+ {
12
+ $countItem = Mage::getModel('combine/cron_count');
13
+ $countItem->setData(array(
14
+ 'store_id' => $storeId,
15
+ 'harvest_id' => $harvestId,
16
+ 'entity' => $entityType,
17
+ 'count' => $count
18
+ ));
19
+ $this->insertIgnore($countItem);
20
+ }
21
+
22
+ public function increaseCountRow($storeId, $harvestId, $entityType, $count)
23
+ {
24
+ $adapter = $this->_getWriter();
25
+ $table = $this->getMainTable();
26
+ $sql = "UPDATE `{$table}` SET `count` = `count` + :count WHERE `store_id` = :store_id AND `harvest_id` = :harvest_id AND `entity` = :entity_type";
27
+ $binds = array(
28
+ 'count' => $count,
29
+ 'store_id' => $storeId,
30
+ 'harvest_id' => $harvestId,
31
+ 'entity_type' => $entityType,
32
+ );
33
+
34
+ $stmt = $adapter->query($sql, $binds);
35
+ return $stmt->rowCount();
36
+ }
37
+
38
+ public function setCompletedTime($storeId, $harvestId, $entityType)
39
+ {
40
+ $adapter = $this->_getWriter();
41
+ $table = $this->getMainTable();
42
+ $sql = "UPDATE `{$table}` SET `completed` = NOW() WHERE `store_id` = :store_id AND `harvest_id` = :harvest_id AND `entity` = :entity_type";
43
+ $binds = array(
44
+ 'store_id' => $storeId,
45
+ 'harvest_id' => $harvestId,
46
+ 'entity_type' => $entityType,
47
+ );
48
+ $stmt = $adapter->query($sql, $binds);
49
+ }
50
+
51
+ public function clearStoreCounts($storeId)
52
+ {
53
+ $adapter = $this->_getWriter();
54
+ $table = $this->getMainTable();
55
+ $sql = "DELETE FROM `{$table}` WHERE `store_id` = :store_id";
56
+ $binds = array(
57
+ 'store_id' => $storeId,
58
+ );
59
+
60
+ $stmt = $adapter->query($sql, $binds);
61
+ return $stmt->rowCount();
62
+ }
63
+
64
+ protected function _getWriter()
65
+ {
66
+ return Mage::getSingleton('core/resource')->getConnection('core_write');
67
+ }
68
+ }
app/code/community/Springbot/Combine/Model/Resource/Cron/Count/Collection.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Cron_Count_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/cron_count');
8
+ }
9
+
10
+
11
+ }
app/code/community/Springbot/Combine/Model/Resource/Cron/Queue.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Cron_Queue extends Springbot_Combine_Model_Resource_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/cron_queue', 'id');
8
+ }
9
+
10
+ public function removeHarvestRows()
11
+ {
12
+ $cronQueueTable = Mage::getSingleton('core/resource')->getTableName('springbot_cron_queue');
13
+ $write = $this->_getWriter();
14
+ $write->query("DELETE FROM `{$cronQueueTable}` WHERE `queue` != 'listener';");
15
+ }
16
+
17
+ public function removeStoreHarvestRows($storeId)
18
+ {
19
+ $cronQueueTable = Mage::getSingleton('core/resource')->getTableName('springbot_cron_queue');
20
+ $write = $this->_getWriter();
21
+ $sql = $write->quoteInto("DELETE FROM `{$cronQueueTable}` WHERE `store_id` = ?", $storeId);
22
+ $write->query($sql);
23
+ }
24
+
25
+ public function lockRows($rowIds)
26
+ {
27
+ $cronQueueTable = Mage::getSingleton('core/resource')->getTableName('springbot_cron_queue');
28
+ $write = $this->_getWriter();
29
+ $lockedAt = now();
30
+ $lockedBy = getmypid();
31
+ $idsString = implode(', ', $rowIds);
32
+ $write->query("UPDATE `{$cronQueueTable}` SET `locked_at` = '{$lockedAt}', `locked_by` = {$lockedBy} WHERE `id` IN ({$idsString});");
33
+ }
34
+
35
+ public function unlockOldRows($hoursOld)
36
+ {
37
+ $cronQueueTable = Mage::getSingleton('core/resource')->getTableName('springbot_cron_queue');
38
+ $write = $this->_getWriter($cronQueueTable);
39
+ $write->query("UPDATE `{$cronQueueTable}` SET `locked_at` = NULL, `locked_by` = NULL WHERE `locked_at` < DATE_SUB(NOW(), INTERVAL {$hoursOld} HOUR)");
40
+ }
41
+
42
+ public function unlockOrphanedRows($activeIds)
43
+ {
44
+ $cronQueueTable = Mage::getSingleton('core/resource')->getTableName('springbot_cron_queue');
45
+ $write = $this->_getWriter($cronQueueTable);
46
+ $sql = "UPDATE `{$cronQueueTable}` SET `locked_at` = NULL, `locked_by` = NULL WHERE `locked_by` IS NOT NULL AND `locked_by` ";
47
+
48
+ if(count($activeIds)) {
49
+ $sql = $write->quoteInto($sql . " NOT IN (?)", $activeIds);
50
+ }
51
+
52
+ Springbot_Log::debug($sql);
53
+
54
+ $write->query($sql);
55
+ }
56
+
57
+ protected function _getWriter()
58
+ {
59
+ return Mage::getSingleton('core/resource')->getConnection('core_write');
60
+ }
61
+ }
app/code/community/Springbot/Combine/Model/Resource/Cron/Queue/Collection.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Cron_Queue_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ const ATTEMPT_LIMIT = 10;
6
+
7
+ public function _construct()
8
+ {
9
+ $this->_init('combine/cron_queue');
10
+ }
11
+
12
+ /**
13
+ * Get jobs based on priority and created at
14
+ * FIFO, ordered on priority (0 is top priority)
15
+ *
16
+ * @param int|string|null $limit
17
+ */
18
+ public function getPriorityJobs($limit, $queue = null, $isForeman = true)
19
+ {
20
+ $this->getSelect()
21
+ ->where('locked_at IS NULL')
22
+ ->where('attempts < ?', $this->getAttemptLimit());
23
+
24
+ // Only foreman can process the default queue
25
+ if(!$isForeman) {
26
+ $this->getSelect()->where("queue != 'default'");
27
+ }
28
+
29
+ $this->getSelect()
30
+ ->order(array('priority ASC', 'id ASC'));
31
+
32
+ if(!empty($limit)) {
33
+ $this->getSelect()->limit($limit);
34
+ }
35
+
36
+ if ($queue) {
37
+ $this->getSelect()->where('queue = "' . $queue .'" ');
38
+ }
39
+
40
+ Springbot_Log::debug((string) $this->getSelect());
41
+
42
+ return $this;
43
+ }
44
+
45
+ public function getNextJob($isForeman)
46
+ {
47
+ $col = $this->getPriorityJobs(1, null, $isForeman);
48
+
49
+ if($col && $col->getSize() > 0) {
50
+ return $col->getFirstItem();
51
+ } else {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ public function hasJobs()
57
+ {
58
+ return $this->getPriorityJobs(1)->getSize() > 0;
59
+ }
60
+
61
+ public function getAttemptLimit()
62
+ {
63
+ return self::ATTEMPT_LIMIT;
64
+ }
65
+
66
+ public function getActiveCount()
67
+ {
68
+ return $this->addFieldToFilter('locked_at', array('notnull' => true))->getSize();
69
+ }
70
+ }
app/code/community/Springbot/Combine/Model/Resource/Redirect.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Redirect extends Springbot_Combine_Model_Resource_Abstract
4
+ {
5
+ protected $_redirectOrderTable;
6
+
7
+ public function _construct()
8
+ {
9
+ $this->_init('combine/redirect', 'id');
10
+ Mage::helper('combine/redirect')->checkAllRedirectTables();
11
+ $this->_redirectOrderTable = $this->getTable('combine/redirect_order');
12
+ }
13
+ }
app/code/community/Springbot/Combine/Model/Resource/Redirect/Collection.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Redirect_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/redirect');
8
+ }
9
+
10
+ protected function _initSelect()
11
+ {
12
+ parent::_initSelect();
13
+ Mage::helper('combine/redirect')->checkAllRedirectTables();
14
+ }
15
+
16
+ public function loadByEmail($email)
17
+ {
18
+ $this->getSelect()->order('id DESC');
19
+
20
+ $this->addFieldToFilter('email', $email);
21
+
22
+ return $this;
23
+ }
24
+
25
+ public function loadByKey($email, $redirectId)
26
+ {
27
+ Springbot_Log::debug("Loading redirect for unique key {$email} : {$redirectId}");
28
+ return $this->addFieldToFilter('email', $email)
29
+ ->addFieldToFilter('redirect_id', $redirectId)
30
+ ->getFirstItem();
31
+ }
32
+
33
+ public function joinOrderIds()
34
+ {
35
+ $this->getSelect()->join(
36
+ array('at_order_id' => $this->getTable('combine/redirect_order')),
37
+ 'main_table.id = at_order_id.redirect_entity_id',
38
+ 'at_order_id.order_id'
39
+ );
40
+ return $this;
41
+ }
42
+ }
app/code/community/Springbot/Combine/Model/Resource/Redirect/Order.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Redirect_Order extends Springbot_Combine_Model_Resource_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/redirect_order', 'id');
8
+ Mage::helper('combine/redirect')->checkTable($this->getMainTable());
9
+ }
10
+ }
app/code/community/Springbot/Combine/Model/Resource/Redirect/Order/Collection.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Redirect_Order_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/redirect_order');
8
+ }
9
+
10
+ protected function _initSelect()
11
+ {
12
+ parent::_initSelect();
13
+ Mage::helper('combine/redirect')->checkTable($this->getMainTable());
14
+ }
15
+ }
app/code/community/Springbot/Combine/Model/Resource/Setup.php ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Setup extends Mage_Core_Model_Resource_Setup
4
+ {
5
+ protected $_app;
6
+ protected $_api;
7
+ protected $_config;
8
+ protected $_data = array();
9
+
10
+ public function toJson($attributes = array())
11
+ {
12
+ $obj = new stdClass();
13
+ $obj->installs = array($this->_data);
14
+ return json_encode($obj);
15
+ }
16
+
17
+ public function reinstallSetupScript($fromVersion, $toVersion)
18
+ {
19
+ Mage::log("Reinstall $fromVersion to $toVersion");
20
+ $files = $this->_getAvailableDbFiles(self::TYPE_DB_UPGRADE, $fromVersion, $toVersion);
21
+
22
+ try {
23
+ foreach($files as $file) {
24
+ $fileName = $file['fileName'];
25
+ Mage::log("Reinstall $fileName");
26
+ $conn = $this->getConnection();
27
+ include $fileName;
28
+ }
29
+ } catch (Exception $e) {
30
+ Mage::logException($e);
31
+ }
32
+ }
33
+
34
+ public function resendInstallLog()
35
+ {
36
+ $this->getSiteDetails();
37
+ $this->setDefaultPhpPath();
38
+ $this->submit();
39
+ }
40
+
41
+ public function getData()
42
+ {
43
+ return $this->_data;
44
+ }
45
+
46
+ public function bugOut()
47
+ {
48
+ echo Zend_Json::prettyPrint($this->toJson());
49
+ }
50
+
51
+ public function getSiteDetails()
52
+ {
53
+ try{
54
+ $this->fetchConfig();
55
+ } catch (Exception $e) {
56
+ Mage::logException($e);
57
+ $this->_setData('type', 'magento')
58
+ ->_setData('error', 'General failure on install.');
59
+ }
60
+ return true;
61
+ }
62
+
63
+ public function setDefaultPhpPath()
64
+ {
65
+ $phpPaths = $this->_getPhpExecutablePaths();
66
+ if(isset($phpPaths['php-cli']) && ($phpPaths['php-cli'])) {
67
+ Springbot_Log::debug('Setting default php path to ' . $phpPaths['php-cli']);
68
+ Mage::getModel('core/config')->saveConfig('springbot/config/php_exec', $phpPaths['php-cli'] );
69
+ }
70
+ elseif(isset($phpPaths['php5']) && ($phpPaths['php5'])) {
71
+ Springbot_Log::debug('Setting default php path to ' . $phpPaths['php5']);
72
+ Mage::getModel('core/config')->saveConfig('springbot/config/php_exec', $phpPaths['php5'] );
73
+ }
74
+ }
75
+
76
+ public function fetchConfig()
77
+ {
78
+ $this->_getApp()->reinitStores();
79
+ $config = $this->_getConfig();
80
+ $config->getResourceModel()->loadToXml($config);
81
+
82
+ $this->_setData('type', 'magento')
83
+ ->_setData('version', $this->getVersion())
84
+ ->_setData('primary_url', $this->getPrimaryUrl())
85
+ ->_setData('modules', $this->getExtensions())
86
+ ->_setData('store_details', $this->getStoreDetails())
87
+ ->_setData('system_info', $this->getSystemDetails());
88
+ return $this;
89
+ }
90
+
91
+ public function submit()
92
+ {
93
+ $this->_getApi()->call('installs', $this->toJson(), false);
94
+ }
95
+
96
+ public function getVersion()
97
+ {
98
+ return Mage::getVersion();
99
+ }
100
+
101
+ public function getPrimaryUrl()
102
+ {
103
+ return $this->_getApp()
104
+ ->getStore(Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID)
105
+ ->getConfig(Mage_Core_Model_Store::XML_PATH_UNSECURE_BASE_URL);
106
+ }
107
+
108
+ public function getStoreDetails()
109
+ {
110
+ $stores = array();
111
+ foreach($this->_getStores() as $store) {
112
+ if($store instanceof Mage_Core_Model_Store) {
113
+ $stores[] = array(
114
+ 'id' => $store->getId(),
115
+ 'name' => $store->getName(),
116
+ 'base_url' => $store->getConfig(Mage_Core_Model_Store::XML_PATH_UNSECURE_BASE_URL),
117
+ 'transactional_emails' => $store->getConfig('trans_email'),
118
+ 'contact_information' => $store->getConfig('general/store_information'),
119
+ );
120
+ }
121
+ }
122
+ return $stores;
123
+ }
124
+
125
+ public function getSystemDetails()
126
+ {
127
+ return array(
128
+ 'php_version' => phpversion(),
129
+ 'host' => php_uname(),
130
+ 'php_exec_healthcheck' => $this->_checkPhpExec(),
131
+ 'functions_exist' => array (
132
+ 'system' => $this->_checkFunction('system'),
133
+ 'exec' => $this->_checkFunction('exec'),
134
+ 'escapeshellarg' => $this->_checkFunction('escapeshellarg'),
135
+ 'escapeshellcmd' => $this->_checkFunction('escapeshellcmd'),
136
+ 'passthru' => $this->_checkFunction('passthru'),
137
+ 'shell_exec' => $this->_checkFunction('shell_exec'),
138
+ 'proc' => array(
139
+ 'proc_close' => $this->_checkFunction('proc_close'),
140
+ 'proc_get_status' => $this->_checkFunction('proc_get_status'),
141
+ 'proc_nice' => $this->_checkFunction('proc_nice'),
142
+ 'proc_open' => $this->_checkFunction('proc_open'),
143
+ 'proc_terminate' => $this->_checkFunction('proc_terminate'),
144
+ ),
145
+ 'pcntl' => array(
146
+ 'pcntl_alarm' => $this->_checkFunction('pctnl_alarm'),
147
+ 'pcntl_errno' => $this->_checkFunction('pctnl_errno'),
148
+ 'pcntl_exec' => $this->_checkFunction('pctnl_exec'),
149
+ 'pcntl_fork' => $this->_checkFunction('pctnl_fork'),
150
+ 'pcntl_get_last_error' => $this->_checkFunction('pctnl_get_last_error'),
151
+ 'pcntl_getpriority' => $this->_checkFunction('pctnl_getpriority'),
152
+ 'pcntl_setpriority' => $this->_checkFunction('pctnl_setpriority'),
153
+ 'pcntl_signal_dispatch' => $this->_checkFunction('pctnl_signal_dispatch'),
154
+ 'pcntl_signal' => $this->_checkFunction('pctnl_signal'),
155
+ 'pcntl_sigprocmask' => $this->_checkFunction('pctnl_sigprocmask'),
156
+ 'pcntl_sigtimedwait' => $this->_checkFunction('pctnl_sigtimedwait'),
157
+ 'pcntl_sigwaitinfo' => $this->_checkFunction('pctnl_sigwaitinfo'),
158
+ 'pcntl_strerror' => $this->_checkFunction('pctnl_strerror'),
159
+ 'pcntl_wait' => $this->_checkFunction('pctnl_wait'),
160
+ 'pcntl_waitpid' => $this->_checkFunction('pctnl_waitpid'),
161
+ 'pcntl_wexitstatus' => $this->_checkFunction('pctnl_wexitstatus'),
162
+ 'pcntl_wifexited' => $this->_checkFunction('pctnl_wifexited'),
163
+ 'pcntl_wifsignaled' => $this->_checkFunction('pctnl_wifsignaled'),
164
+ 'pcntl_wifstopped' => $this->_checkFunction('pctnl_wifstopped'),
165
+ 'pcntl_wstopsig' => $this->_checkFunction('pctnl_wstopsig'),
166
+ 'pcntl_wtermsig' => $this->_checkFunction('pctnl_wtermsig'),
167
+ )
168
+ ),
169
+ 'phpinfo' => $this->_phpinfoArray(true),
170
+ 'php_executable_paths' => $this->_getPhpExecutablePaths(),
171
+ );
172
+ }
173
+
174
+ public function getExtensions()
175
+ {
176
+ $versions = new stdClass();
177
+ $modules = $this->_getConfig()->getNode('modules')->children();
178
+ if($modules) {
179
+ foreach($modules as $name => $meta) {
180
+ if(strpos($name, 'Mage') !== 0) {
181
+ $versions->$name = $meta;
182
+ }
183
+ }
184
+ }
185
+ return $versions;
186
+ }
187
+
188
+ protected function _setData($type, $value = null)
189
+ {
190
+ $this->_data[$type] = $value;
191
+ return $this;
192
+ }
193
+
194
+ protected function _getApp()
195
+ {
196
+ if(!isset($this->_app)) {
197
+ $this->_app = Mage::app();
198
+ }
199
+ return $this->_app;
200
+ }
201
+
202
+ protected function _getConfig()
203
+ {
204
+ if(!isset($this->_config)) {
205
+ $this->_config = Mage::getConfig();
206
+ }
207
+ return $this->_config;
208
+ }
209
+
210
+ protected function _getStores()
211
+ {
212
+ return $this->_getApp()->getStores();
213
+ }
214
+
215
+ protected function _getApi()
216
+ {
217
+ if(!isset($this->_api)) {
218
+ $this->_api = Mage::getModel('combine/api');
219
+ }
220
+ return $this->_api;
221
+ }
222
+
223
+ protected function _checkFunction($func)
224
+ {
225
+ return function_exists($func) ? 'true' : 'false';
226
+ }
227
+
228
+ protected function _checkPhpExec()
229
+ {
230
+ ob_start();
231
+ $check = system("/usr/bin/php -r \"echo 'ok';\"");
232
+ ob_end_clean();
233
+ return $check == "ok" ? "ok" : "could not execute php as shell";
234
+ }
235
+
236
+ protected function _getPhpExecutablePaths()
237
+ {
238
+ $returnVar = null;
239
+ ob_start();
240
+ $outputs = array(
241
+ 'php' => Springbot_Boss::spawn("which -a php", $returnVar),
242
+ 'php5' => Springbot_Boss::spawn("which -a php5", $returnVar),
243
+ 'php-cli' => Springbot_Boss::spawn("which -a php-cli", $returnVar),
244
+ );
245
+ ob_end_clean();
246
+ return $outputs;
247
+ }
248
+
249
+ protected function _phpinfoArray($return=false){
250
+ /* Andale! Andale! Yee-Hah! */
251
+ ob_start();
252
+ phpinfo(-1);
253
+
254
+ $pi = preg_replace(
255
+ array('#^.*<body>(.*)</body>.*$#ms', '#<h2>PHP License</h2>.*$#ms',
256
+ '#<h1>Configuration</h1>#', "#\r?\n#", "#</(h1|h2|h3|tr)>#", '# +<#',
257
+ "#[ \t]+#", '#&nbsp;#', '# +#', '# class=".*?"#', '%&#039;%',
258
+ '#<tr>(?:.*?)" src="(?:.*?)=(.*?)" alt="PHP Logo" /></a>'
259
+ .'<h1>PHP Version (.*?)</h1>(?:\n+?)</td></tr>#',
260
+ '#<h1><a href="(?:.*?)\?=(.*?)">PHP Credits</a></h1>#',
261
+ '#<tr>(?:.*?)" src="(?:.*?)=(.*?)"(?:.*?)Zend Engine (.*?),(?:.*?)</tr>#',
262
+ "# +#", '#<tr>#', '#</tr>#'),
263
+ array('$1', '', '', '', '</$1>' . "\n", '<', ' ', ' ', ' ', '', ' ',
264
+ '<h2>PHP Configuration</h2>'."\n".'<tr><td>PHP Version</td><td>$2</td></tr>'.
265
+ "\n".'<tr><td>PHP Egg</td><td>$1</td></tr>',
266
+ '<tr><td>PHP Credits Egg</td><td>$1</td></tr>',
267
+ '<tr><td>Zend Engine</td><td>$2</td></tr>' . "\n" .
268
+ '<tr><td>Zend Egg</td><td>$1</td></tr>', ' ', '%S%', '%E%'),
269
+ ob_get_clean());
270
+
271
+ $sections = explode('<h2>', strip_tags($pi, '<h2><th><td>'));
272
+ unset($sections[0]);
273
+
274
+ $pi = array();
275
+ foreach($sections as $section){
276
+ $n = substr($section, 0, strpos($section, '</h2>'));
277
+ preg_match_all(
278
+ '#%S%(?:<td>(.*?)</td>)?(?:<td>(.*?)</td>)?(?:<td>(.*?)</td>)?%E%#',
279
+ $section, $askapache, PREG_SET_ORDER);
280
+ foreach($askapache as $m) {
281
+ if(isset($m[2])) {
282
+ $pi[$n][$m[1]]=(!isset($m[3])||$m[2]==$m[3])?$m[2]:array_slice($m,2);
283
+ }
284
+ }
285
+ }
286
+
287
+ return ($return === false) ? print_r($pi) : $pi;
288
+ }
289
+ }
app/code/community/Springbot/Combine/Model/Resource/Trackable.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Trackable extends Mage_Core_Model_Mysql4_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/trackable', 'id');
8
+ }
9
+
10
+ public function create(Springbot_Combine_Model_Trackable $object)
11
+ {
12
+ $adapter = $this->_getWriteAdapter();
13
+
14
+ $select = $adapter->select()
15
+ ->from($this->getMainTable())
16
+ ->where('quote_id = ?', $object->getQuoteId())
17
+ ->where('type = ?', $object->getType());
18
+
19
+ if(!($row = $adapter->fetchRow($select))) {
20
+ Springbot_Log::debug("Creating trackable {$object->getType()} : {$object->getValue()}");
21
+ $adapter->insert($this->getMainTable(), $object->getData());
22
+ $object->setId($adapter->lastInsertId($this->getMainTable()));
23
+ return $object;
24
+ } else {
25
+ Springbot_Log::debug("Trackable {$object->getType()} : {$object->getValue()} exists, updating");
26
+ $object->setId($row['id']);
27
+
28
+ $this->save($object);
29
+ return $object;
30
+ }
31
+ }
32
+ }
app/code/community/Springbot/Combine/Model/Resource/Trackable/Collection.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Resource_Trackable_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/trackable');
8
+ }
9
+ }
app/code/community/Springbot/Combine/Model/System/Config/Source/LogFormat.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_System_Config_Source_LogFormat
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 'simple', 'label'=>Mage::helper('combine')->__('Simple')),
9
+ array('value' => 'default', 'label'=>Mage::helper('combine')->__('Default')),
10
+ array('value' => 'expanded', 'label'=>Mage::helper('combine')->__('Expanded')),
11
+ );
12
+ }
13
+ }
app/code/community/Springbot/Combine/Model/System/Config/Source/LogLevel.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_System_Config_Source_LogLevel
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => '6', 'label'=>Mage::helper('combine')->__('Info')),
9
+ array('value' => '7', 'label'=>Mage::helper('combine')->__('Debug')),
10
+ );
11
+ }
12
+ }
app/code/community/Springbot/Combine/Model/System/Config/Source/Stability.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_System_Config_Source_Stability
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 'stable', 'label'=>Mage::helper('combine')->__('Stable')),
9
+ array('value' => 'beta', 'label'=>Mage::helper('combine')->__('Beta')),
10
+ //array('value' => 'alpha', 'label'=>Mage::helper('combine')->__('Alpha')),
11
+ );
12
+ }
13
+ }
app/code/community/Springbot/Combine/Model/System/Config/Source/UrlType.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_System_Config_Source_UrlType
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 'default', 'label'=>Mage::helper('combine')->__('Default')),
9
+ array('value' => 'id_path', 'label'=>Mage::helper('combine')->__('Id Path')),
10
+ array('value' => 'in_store', 'label'=>Mage::helper('combine')->__('In Store')),
11
+ );
12
+ }
13
+ }
app/code/community/Springbot/Combine/Model/Trackable.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Combine_Model_Trackable extends Mage_Core_Model_Abstract
4
+ {
5
+ public function _construct()
6
+ {
7
+ $this->_init('combine/trackable');
8
+ }
9
+
10
+ public function createOrUpdate()
11
+ {
12
+ if($this->_validate()) {
13
+ if($this->getResource()->create($this)) {
14
+ } else {
15
+ $this->save();
16
+ }
17
+ }
18
+ return $this;
19
+ }
20
+
21
+ protected function _validate()
22
+ {
23
+ return !empty($this->_data['email']) &&
24
+ !empty($this->_data['type']) &&
25
+ !empty($this->_data['value']) &&
26
+ !empty($this->_data['quote_id']);
27
+ }
28
+ }
app/code/community/Springbot/Combine/etc/adminhtml.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <admin>
6
+ <children>
7
+ <system>
8
+ <children>
9
+ <config>
10
+ <children>
11
+ <springbot translate="title" module="combine">
12
+ <title>Springbot</title>
13
+ </springbot>
14
+ </children>
15
+ </config>
16
+ </children>
17
+ </system>
18
+ </children>
19
+ </admin>
20
+ </resources>
21
+ </acl>
22
+ </config>
app/code/community/Springbot/Combine/etc/config.xml ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * @category springbot
5
+ * @package springbot_Combine
6
+ * @author springbot integration team
7
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Springbot_Combine>
13
+ <version>1.2.1.2</version>
14
+ </Springbot_Combine>
15
+ </modules>
16
+ <global>
17
+ <models>
18
+ <combine>
19
+ <class>Springbot_Combine_Model</class>
20
+ <resourceModel>combine_resource</resourceModel>
21
+ </combine>
22
+ <combine_resource>
23
+ <class>Springbot_Combine_Model_Resource</class>
24
+ <deprecatedNode>combine_mysql4</deprecatedNode>
25
+ <entities>
26
+ <redirect>
27
+ <table>springbot_redirect</table>
28
+ </redirect>
29
+ <redirect_order>
30
+ <table>springbot_redirect_order</table>
31
+ </redirect_order>
32
+ <trackable>
33
+ <table>springbot_trackable</table>
34
+ </trackable>
35
+ <cron_queue>
36
+ <table>springbot_cron_queue</table>
37
+ </cron_queue>
38
+ <cron_count>
39
+ <table>springbot_cron_count</table>
40
+ </cron_count>
41
+ </entities>
42
+ </combine_resource>
43
+ </models>
44
+ <helpers>
45
+ <combine>
46
+ <class>Springbot_Combine_Helper</class>
47
+ </combine>
48
+ </helpers>
49
+ <resources>
50
+ <combine_setup>
51
+ <setup>
52
+ <module>Springbot_Combine</module>
53
+ <class>Springbot_Combine_Model_Resource_Setup</class>
54
+ </setup>
55
+ <connection>
56
+ <use>core_setup</use>
57
+ </connection>
58
+ </combine_setup>
59
+ <combine_write>
60
+ <connection>
61
+ <use>core_write</use>
62
+ </connection>
63
+ </combine_write>
64
+ <combine_read>
65
+ <connection>
66
+ <use>core_read</use>
67
+ </connection>
68
+ </combine_read>
69
+ </resources>
70
+ </global>
71
+ <default>
72
+ <springbot>
73
+ <config>
74
+ <segment_size>25</segment_size>
75
+ <show_notifications>1</show_notifications>
76
+ <remote_update>0</remote_update>
77
+ <use_cached_image>0</use_cached_image>
78
+ <remote_update>0</remote_update>
79
+ <stability>stable</stability>
80
+ <email_selector>billing:email,login-email,newsletter</email_selector>
81
+ <email_selector_classes>validate-email</email_selector_classes>
82
+ <sent_store_noemail>0</sent_store_noemail>
83
+ </config>
84
+ <debug>
85
+ <log_format>default</log_format>
86
+ <log_level>6</log_level>
87
+ <expire_time_days>30</expire_time_days>
88
+ <filesize_limit>104857600</filesize_limit>
89
+ </debug>
90
+ <advanced>
91
+ <nice>1</nice>
92
+ <nohup>1</nohup>
93
+ <worker_count>5</worker_count>
94
+ <sleep_interval>1</sleep_interval>
95
+ <extended_config>0</extended_config>
96
+ <product_url_type>default</product_url_type>
97
+ </advanced>
98
+ <cart_restore>
99
+ <do_restore>1</do_restore>
100
+ <retain_coupon>1</retain_coupon>
101
+ </cart_restore>
102
+ <cron>
103
+ <enabled>0</enabled>
104
+ <max_jobs>10</max_jobs>
105
+ </cron>
106
+ </springbot>
107
+ </default>
108
+ <crontab>
109
+ <jobs>
110
+ <springbot_cron_worker>
111
+ <schedule><cron_expr>* * * * *</cron_expr></schedule>
112
+ <run><model>combine/cron_worker::cronRun</model></run>
113
+ </springbot_cron_worker>
114
+ </jobs>
115
+ </crontab>
116
+ <adminhtml>
117
+ <acl>
118
+ <resources>
119
+ <admin>
120
+ <children>
121
+ <system>
122
+ <children>
123
+ <config>
124
+ <children>
125
+ <springbot translate="title" module="combine">
126
+ <title>Springbot</title>
127
+ </springbot>
128
+ </children>
129
+ </config>
130
+ </children>
131
+ </system>
132
+ </children>
133
+ </admin>
134
+ </resources>
135
+ </acl>
136
+ </adminhtml>
137
+ </config>
app/code/community/Springbot/Combine/etc/system.xml ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <config>
4
+ <tabs>
5
+ <springbot translate="label">
6
+ <label>Springbot</label>
7
+ <sort_order>88888</sort_order>
8
+ <show_in_default>1</show_in_default>
9
+ <show_in_website>0</show_in_website>
10
+ <show_in_store>0</show_in_store>
11
+ </springbot>
12
+ </tabs>
13
+ <sections>
14
+ <springbot translate="label" module="combine">
15
+ <label>Harvest</label>
16
+ <tab>springbot</tab>
17
+ <frontend_type>text</frontend_type>
18
+ <sort_order>1000</sort_order>
19
+ <show_in_default>1</show_in_default>
20
+ <show_in_website>0</show_in_website>
21
+ <show_in_store>0</show_in_store>
22
+ <groups>
23
+ <config translate="label">
24
+ <label>System Configuration</label>
25
+ <frontend_type>text</frontend_type>
26
+ <sort_order>150</sort_order>
27
+ <show_in_default>1</show_in_default>
28
+ <show_in_website>0</show_in_website>
29
+ <show_in_store>0</show_in_store>
30
+ <fields>
31
+ <php_exec translate="label">
32
+ <label>PHP Executable</label>
33
+ <frontend_type>text</frontend_type>
34
+ <sort_order>10</sort_order>
35
+ <show_in_default>1</show_in_default>
36
+ <show_in_website>0</show_in_website>
37
+ <show_in_store>0</show_in_store>
38
+ <comment>Use this to define the PHP executable (including any runtime options) that you wish Springbot to use. If you don't know what this means, leave it blank!</comment>
39
+ </php_exec>
40
+ <segment_size translate="label">
41
+ <label>Segment Size</label>
42
+ <frontend_type>text</frontend_type>
43
+ <sort_order>12</sort_order>
44
+ <show_in_default>1</show_in_default>
45
+ <show_in_website>0</show_in_website>
46
+ <show_in_store>0</show_in_store>
47
+ <comment>Defines the maximum segment inspected during harvest</comment>
48
+ </segment_size>
49
+ <ignore_store_list translate="label">
50
+ <label>Ignore Store List</label>
51
+ <frontend_type>text</frontend_type>
52
+ <sort_order>20</sort_order>
53
+ <show_in_default>1</show_in_default>
54
+ <show_in_website>0</show_in_website>
55
+ <show_in_store>0</show_in_store>
56
+ <comment>Comma-separated list of stores to ignore. Leave blank to harvest all stores.</comment>
57
+ </ignore_store_list>
58
+ <define_store_list translate="label">
59
+ <label>Explicitly Define Stores to Harvest</label>
60
+ <frontend_type>text</frontend_type>
61
+ <sort_order>30</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <show_in_website>0</show_in_website>
64
+ <show_in_store>0</show_in_store>
65
+ <comment>Comma-separated list of stores to harvest. Leave blank to harvest all stores. Ignores will supercede the values in this list.</comment>
66
+ </define_store_list>
67
+ <account_email translate="label">
68
+ <label>Springbot Username</label>
69
+ <frontend_type>text</frontend_type>
70
+ <sort_order>40</sort_order>
71
+ <show_in_default>1</show_in_default>
72
+ <show_in_website>0</show_in_website>
73
+ </account_email>
74
+ <account_password translate="label">
75
+ <label>Springbot Password</label>
76
+ <frontend_type>obscure</frontend_type>
77
+ <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
78
+ <sort_order>50</sort_order>
79
+ <show_in_default>1</show_in_default>
80
+ <show_in_website>0</show_in_website>
81
+ </account_password>
82
+ <remote_update translate="label">
83
+ <label>Allow Remote Update</label>
84
+ <frontend_type>select</frontend_type>
85
+ <source_model>adminhtml/system_config_source_yesno</source_model>
86
+ <sort_order>60</sort_order>
87
+ <show_in_default>1</show_in_default>
88
+ <show_in_website>0</show_in_website>
89
+ <show_in_store>0</show_in_store>
90
+ </remote_update>
91
+ <stability translate="label">
92
+ <label>Minimum Acceptable Stability</label>
93
+ <frontend_type>select</frontend_type>
94
+ <source_model>combine/system_config_source_stability</source_model>
95
+ <sort_order>70</sort_order>
96
+ <show_in_default>1</show_in_default>
97
+ <show_in_website>0</show_in_website>
98
+ <show_in_store>0</show_in_store>
99
+ <size>2</size>
100
+ </stability>
101
+ <show_notifications translate="label">
102
+ <label>Show Notifications</label>
103
+ <frontend_type>select</frontend_type>
104
+ <source_model>adminhtml/system_config_source_yesno</source_model>
105
+ <sort_order>80</sort_order>
106
+ <show_in_default>1</show_in_default>
107
+ <show_in_website>0</show_in_website>
108
+ <show_in_store>0</show_in_store>
109
+ </show_notifications>
110
+ <email_selector translate="label">
111
+ <label>Email Javascript Selector</label>
112
+ <frontend_type>text</frontend_type>
113
+ <sort_order>90</sort_order>
114
+ <show_in_default>1</show_in_default>
115
+ <show_in_website>0</show_in_website>
116
+ <comment>Comma separated list of IDs for the frontend field where a user enters their email address.</comment>
117
+ </email_selector>
118
+ <email_selector translate="label">
119
+ <label>Email Javascript Selector Classes</label>
120
+ <frontend_type>text</frontend_type>
121
+ <sort_order>100</sort_order>
122
+ <show_in_default>1</show_in_default>
123
+ <show_in_website>0</show_in_website>
124
+ <comment>Comma separated list of Classes for the frontend field where a user enters their email address.</comment>
125
+ </email_selector>
126
+ <security_token translate="label">
127
+ <label>Security Token</label>
128
+ <frontend_type>text</frontend_type>
129
+ <sort_order>110</sort_order>
130
+ <show_in_default>1</show_in_default>
131
+ <show_in_website>0</show_in_website>
132
+ <comment>Springbot supplied security token.</comment>
133
+ </security_token>
134
+
135
+ </fields>
136
+ </config>
137
+ <images translate="label">
138
+ <label>Images</label>
139
+ <frontend_type>text</frontend_type>
140
+ <sort_order>250</sort_order>
141
+ <show_in_default>1</show_in_default>
142
+ <show_in_website>0</show_in_website>
143
+ <show_in_store>0</show_in_store>
144
+ <fields>
145
+ <use_cached_images translate="label">
146
+ <label>Use Cached Images</label>
147
+ <frontend_type>select</frontend_type>
148
+ <source_model>adminhtml/system_config_source_yesno</source_model>
149
+ <sort_order>10</sort_order>
150
+ <show_in_default>1</show_in_default>
151
+ <show_in_website>0</show_in_website>
152
+ <comment>Selecting yes will generate a cached image on your server. Clearing image cache will require a full reharvest. If you do not know what this means, this should be left off.</comment>
153
+ </use_cached_images>
154
+ <!--<pixel_width translate="label">
155
+ <label>Image Pixel Width</label>
156
+ <frontend_type>text</frontend_type>
157
+ <sort_order>20</sort_order>
158
+ <show_in_default>1</show_in_default>
159
+ <show_in_website>0</show_in_website>
160
+ <can_be_empty>1</can_be_empty>
161
+ <depends>
162
+ <use_cached_images>1</use_cached_images>
163
+ </depends>
164
+ <comment>This sets pixel width for cached images.</comment>
165
+ </pixel_width>-->
166
+ </fields>
167
+ </images>
168
+ <debug translate="label">
169
+ <label>Debug</label>
170
+ <frontend_type>text</frontend_type>
171
+ <sort_order>350</sort_order>
172
+ <show_in_default>1</show_in_default>
173
+ <show_in_website>0</show_in_website>
174
+ <show_in_store>0</show_in_store>
175
+ <fields>
176
+ <log_http translate="label">
177
+ <label>Log HTTP Requests</label>
178
+ <frontend_type>select</frontend_type>
179
+ <source_model>adminhtml/system_config_source_yesno</source_model>
180
+ <sort_order>10</sort_order>
181
+ <show_in_default>1</show_in_default>
182
+ <show_in_website>0</show_in_website>
183
+ <show_in_store>0</show_in_store>
184
+ <comment>&lt;strong style="color:red"&gt;Warning!&lt;/strong&gt; Please monitor disk space if setting to Debug level, filesize may grow rapidly.</comment>
185
+ </log_http>
186
+ <log_level translate="label">
187
+ <label>Log Level</label>
188
+ <frontend_type>select</frontend_type>
189
+ <source_model>combine/system_config_source_logLevel</source_model>
190
+ <sort_order>20</sort_order>
191
+ <show_in_default>1</show_in_default>
192
+ <show_in_website>0</show_in_website>
193
+ <show_in_store>0</show_in_store>
194
+ <comment>&lt;strong style="color:red"&gt;Warning!&lt;/strong&gt; For debugging purposes only! It will create very large log files quickly on a busy site.</comment>
195
+ </log_level>
196
+ <log_format translate="label">
197
+ <label>Log Format</label>
198
+ <frontend_type>select</frontend_type>
199
+ <source_model>combine/system_config_source_logFormat</source_model>
200
+ <sort_order>25</sort_order>
201
+ <show_in_default>1</show_in_default>
202
+ <show_in_website>0</show_in_website>
203
+ <show_in_store>0</show_in_store>
204
+ </log_format>
205
+ <expire_time_days translate="label">
206
+ <label>Delete Log Files after X Days</label>
207
+ <frontend_type>text</frontend_type>
208
+ <sort_order>30</sort_order>
209
+ <show_in_default>1</show_in_default>
210
+ <show_in_website>0</show_in_website>
211
+ </expire_time_days>
212
+ <filesize_limit translate="label">
213
+ <label>Filesize Limit</label>
214
+ <frontend_type>text</frontend_type>
215
+ <sort_order>40</sort_order>
216
+ <show_in_default>1</show_in_default>
217
+ <show_in_website>0</show_in_website>
218
+ <comment>Filesize limit in bytes for when to roll log files over</comment>
219
+ </filesize_limit>
220
+ </fields>
221
+ </debug>
222
+ <cron translate="label">
223
+ <label>Cron</label>
224
+ <frontend_type>text</frontend_type>
225
+ <sort_order>450</sort_order>
226
+ <show_in_default>1</show_in_default>
227
+ <show_in_website>0</show_in_website>
228
+ <show_in_store>0</show_in_store>
229
+ <fields>
230
+ <enabled translate="label">
231
+ <label>Run in Cron Mode</label>
232
+ <frontend_type>select</frontend_type>
233
+ <source_model>adminhtml/system_config_source_yesno</source_model>
234
+ <sort_order>10</sort_order>
235
+ <show_in_default>1</show_in_default>
236
+ <show_in_website>0</show_in_website>
237
+ <show_in_store>0</show_in_store>
238
+ </enabled>
239
+ <max_jobs translate="label">
240
+ <label>Max jobs to run per instance</label>
241
+ <frontend_type>text</frontend_type>
242
+ <sort_order>20</sort_order>
243
+ <show_in_default>1</show_in_default>
244
+ <show_in_website>0</show_in_website>
245
+ <comment>This is the maximum number of jobs the cron worker will attempt to run each time it is called. Leave blank for unlimited.</comment>
246
+ </max_jobs>
247
+ </fields>
248
+ </cron>
249
+ <advanced translate="label">
250
+ <label>Advanced Config</label>
251
+ <frontend_type>text</frontend_type>
252
+ <sort_order>550</sort_order>
253
+ <show_in_default>1</show_in_default>
254
+ <show_in_website>0</show_in_website>
255
+ <show_in_store>0</show_in_store>
256
+ <fields>
257
+ <nohup translate="label">
258
+ <label>Nohup</label>
259
+ <frontend_type>select</frontend_type>
260
+ <source_model>adminhtml/system_config_source_yesno</source_model>
261
+ <sort_order>70</sort_order>
262
+ <show_in_default>1</show_in_default>
263
+ <show_in_website>0</show_in_website>
264
+ <show_in_store>0</show_in_store>
265
+ </nohup>
266
+ <nice translate="label">
267
+ <label>Nice</label>
268
+ <frontend_type>select</frontend_type>
269
+ <source_model>adminhtml/system_config_source_yesno</source_model>
270
+ <sort_order>80</sort_order>
271
+ <show_in_default>1</show_in_default>
272
+ <show_in_website>0</show_in_website>
273
+ <show_in_store>0</show_in_store>
274
+ </nice>
275
+ <worker_count translate="label">
276
+ <label>Worker Count</label>
277
+ <frontend_type>text</frontend_type>
278
+ <sort_order>90</sort_order>
279
+ <show_in_default>1</show_in_default>
280
+ <show_in_website>0</show_in_website>
281
+ <show_in_store>0</show_in_store>
282
+ </worker_count>
283
+ <sleep_interval translate="label">
284
+ <label>Sleep Interval</label>
285
+ <frontend_type>text</frontend_type>
286
+ <sort_order>100</sort_order>
287
+ <show_in_default>1</show_in_default>
288
+ <show_in_website>0</show_in_website>
289
+ <show_in_store>0</show_in_store>
290
+ </sleep_interval>
291
+ <extended_config translate="label">
292
+ <label>Extended Configuration</label>
293
+ <frontend_type>select</frontend_type>
294
+ <source_model>adminhtml/system_config_source_yesno</source_model>
295
+ <sort_order>110</sort_order>
296
+ <show_in_default>1</show_in_default>
297
+ <show_in_website>0</show_in_website>
298
+ <show_in_store>0</show_in_store>
299
+ </extended_config>
300
+ <product_url_type translate="label">
301
+ <label>Product Url Type</label>
302
+ <frontend_type>select</frontend_type>
303
+ <source_model>combine/system_config_source_urlType</source_model>
304
+ <sort_order>120</sort_order>
305
+ <show_in_default>1</show_in_default>
306
+ <show_in_website>0</show_in_website>
307
+ <show_in_store>0</show_in_store>
308
+ </product_url_type>
309
+ </fields>
310
+ </advanced>
311
+ <cart_restore>
312
+ <label>Restore Carts</label>
313
+ <frontend_type>text</frontend_type>
314
+ <sort_order>560</sort_order>
315
+ <show_in_default>1</show_in_default>
316
+ <show_in_website>0</show_in_website>
317
+ <show_in_store>0</show_in_store>
318
+ <fields>
319
+ <do_restore translate="label">
320
+ <label>Restore Carts</label>
321
+ <frontend_type>select</frontend_type>
322
+ <source_model>adminhtml/system_config_source_yesno</source_model>
323
+ <sort_order>90</sort_order>
324
+ <show_in_default>1</show_in_default>
325
+ <show_in_website>0</show_in_website>
326
+ <show_in_store>0</show_in_store>
327
+ </do_restore>
328
+ <retain_coupon translate="label">
329
+ <label>Restore Coupons</label>
330
+ <frontend_type>select</frontend_type>
331
+ <source_model>adminhtml/system_config_source_yesno</source_model>
332
+ <sort_order>110</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
+ </retain_coupon>
337
+ </fields>
338
+ </cart_restore>
339
+ </groups>
340
+ </springbot>
341
+ </sections>
342
+ </config>
343
+
app/code/community/Springbot/Combine/sql/combine_setup/mysql4-install-1.0.0.70.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Springbot_Combine_Model_Resource_Setup */
5
+
6
+ $installer->startSetup();
7
+
8
+ try
9
+ {
10
+ $installer->getSiteDetails();
11
+ $installer->setDefaultPhpPath();
12
+ if(!Mage::getStoreConfig('springbot/debug/skip_install_log')) {
13
+ $installer->submit();
14
+ }
15
+ } catch (Exception $e) {
16
+ Mage::logException($e);
17
+ }
18
+ $installer->endSetup();
app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.0.0.70-1.0.0.84.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Springbot_Combine_Model_Resource_Setup */
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->getConnection()->beginTransaction();
9
+
10
+ $installer->run("
11
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('combine/redirect')}`
12
+ (
13
+ `id` INT(11) unsigned NOT NULL auto_increment,
14
+ `email` VARCHAR(255) NOT NULL,
15
+ `redirect_id` CHAR(24) NOT NULL,
16
+ `quote_id` INT(11) DEFAULT NULL,
17
+ `customer_id` INT(11) DEFAULT NULL,
18
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
19
+ PRIMARY KEY (`id`),
20
+ UNIQUE KEY `UNQ_COMPOUND_EMAIL_REDIRECT_ID` (`email`, `redirect_id`),
21
+ KEY `IDX_EMAIL` (`email`)
22
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
23
+ ");
24
+
25
+ $installer->run("
26
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('combine/redirect_order')}`
27
+ (
28
+ `id` INT(11) unsigned NOT NULL auto_increment,
29
+ `redirect_entity_id` INT(11) NOT NULL,
30
+ `order_id` INT(11) DEFAULT NULL,
31
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
32
+ PRIMARY KEY (`id`),
33
+ UNIQUE KEY `UNQ_COMPOUND_REDIRECT_ENTITY_ORDER_ID` (`redirect_entity_id`, `order_id`),
34
+ KEY `IDX_REDIRECT_ENTITY_ID` (`redirect_entity_id`),
35
+ KEY `IDX_ORDER_ID` (`order_id`)
36
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
37
+ ");
38
+
39
+ $installer->endSetup();
app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.0.0.84-1.0.0.88.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Springbot_Combine_Model_Resource_Setup */
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->getConnection()->beginTransaction();
9
+
10
+ $installer->run("
11
+ DELETE FROM `{$installer->getTable('combine/redirect_order')}` WHERE order_id IS NULL;
12
+ ALTER TABLE `{$installer->getTable('combine/redirect_order')}` MODIFY COLUMN order_id int(11) NOT NULL;
13
+ ");
14
+
15
+ $installer->endSetup();
16
+
app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.0.0.88-1.2.0.0.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Springbot_Combine_Model_Resource_Setup */
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->getConnection()->beginTransaction();
9
+
10
+ $session = Mage::getSingleton('core/session');
11
+
12
+ try {
13
+ $installer->run("
14
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('combine/trackable')}`
15
+ (
16
+ `id` INT(11) unsigned NOT NULL auto_increment,
17
+ `email` VARCHAR(255) NOT NULL,
18
+ `type` VARCHAR(255) NOT NULL,
19
+ `value` CHAR(24) NOT NULL,
20
+ `quote_id` INT(11) DEFAULT NULL,
21
+ `order_id` INT(11) DEFAULT NULL,
22
+ `customer_id` INT(11) DEFAULT NULL,
23
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
24
+ PRIMARY KEY (`id`),
25
+ KEY `IDX_EMAIL` (`email`),
26
+ KEY `IDX_QUOTE_ID` (`quote_id`),
27
+ KEY `IDX_ORDER_ID` (`order_id`)
28
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
29
+ ");
30
+ } catch (Exception $e) {
31
+ Mage::log('Install failed clear and retry');
32
+ if(!$session->getSbReinstall()) {
33
+ $session->setSbReinstall(true);
34
+ $installer->reinstallSetupScript('1.0.0.70', '1.2.0.0');
35
+ }
36
+ }
37
+
38
+ $session->setSbReinstall(false);
39
+ $installer->endSetup();
app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.2.0.0-1.2.0.1.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Springbot_Combine_Model_Resource_Setup */
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->getConnection()->beginTransaction();
9
+
10
+ $installer->run("ALTER TABLE `{$installer->getTable('combine/trackable')}` MODIFY COLUMN `value` varchar(255) NOT NULL;");
11
+
12
+ $installer->endSetup();
13
+
app/code/community/Springbot/Combine/sql/combine_setup/mysql4-upgrade-1.2.0.1-1.2.1.0.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Springbot_Combine_Model_Resource_Setup */
5
+
6
+ $installer->startSetup();
7
+
8
+ $installer->getConnection()->beginTransaction();
9
+
10
+ try {
11
+ $installer->run("
12
+ DROP TABLE IF EXISTS `{$installer->getTable('combine/cron_queue')}`;
13
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('combine/cron_queue')}`
14
+ (
15
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
16
+ `method` VARCHAR(255) NOT NULL,
17
+ `args` TEXT NOT NULL,
18
+ `store_id` INT NOT NULL,
19
+ `command_hash` CHAR(40) NOT NULL,
20
+ `queue` VARCHAR(255) NOT NULL DEFAULT 'default',
21
+ `priority` INT UNSIGNED NOT NULL DEFAULT 5,
22
+ `attempts` INT UNSIGNED NOT NULL DEFAULT 0,
23
+ `run_at` DATETIME NULL,
24
+ `locked_at` DATETIME NULL,
25
+ `locked_by` VARCHAR(255) NULL,
26
+ `error` TEXT NULL,
27
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
28
+ UNIQUE (`command_hash`),
29
+ PRIMARY KEY (`id`),
30
+ KEY `IDX_PRIORITY_CREATED_AT` (`priority`, `created_at`)
31
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
32
+ DROP TABLE IF EXISTS `{$installer->getTable('combine/cron_count')}`;
33
+ CREATE TABLE IF NOT EXISTS `{$installer->getTable('combine/cron_count')}`
34
+ (
35
+ `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
36
+ `entity` VARCHAR(255) NOT NULL,
37
+ `store_id` INT NOT NULL,
38
+ `harvest_id` CHAR(40) NOT NULL,
39
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
40
+ `completed` TIMESTAMP NULL DEFAULT NULL,
41
+ `count` INT UNSIGNED NOT NULL DEFAULT 0,
42
+ PRIMARY KEY (`id`)
43
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
44
+ ");
45
+ } catch (Exception $e) {
46
+ Mage::log('Springbot 1.2.0.0-1.2.1.0 update failed!');
47
+ }
48
+
49
+ $installer->endSetup();
app/code/community/Springbot/DataServices/HarvestingManager.php ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Harvesting Manager
4
+ *
5
+ * @version v1.0.45 - 12/26/2012
6
+ *
7
+ * @category Magento Integrations
8
+ * @package springbot
9
+ * @author William Seitz
10
+ * @division SpringBot Integration Team
11
+ * @support magentosupport@springbot.com
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ *
14
+ */
15
+
16
+ class Springbot_DataServices_HarvestingManager
17
+ {
18
+ const MAGENTO_PACKAGE_VERSION = '1.2.1.2';
19
+ const ZERO = 0;
20
+ const SUCCESSFUL_RESPONSE = 'ok';
21
+ const DATE_FORMAT = 'Y-m-d H:i:s';
22
+
23
+ private $rootAppPath = '';
24
+ private $eventHistoryFilename = '';
25
+
26
+ public function __construct()
27
+ {
28
+ $this->rootAppPath = Mage::getBaseDir();
29
+ $this->eventHistoryFilename = $this->rootAppPath . '/var/log/Springbot-EventHistory.csv';
30
+ }
31
+
32
+ public function harvestHealthCheck($storeId)
33
+ {
34
+ $this->springbotStoreID = Mage::helper('combine/harvest')->getSpringbotStoreId($storeId);
35
+ $result = Mage::getModel('combine/api')->call('harvest_master', '{"store_id":"'.$this->springbotStoreID.'","version":"'.self::MAGENTO_PACKAGE_VERSION.'"}');
36
+
37
+ echo "Harvest HealthCheck store: {$storeId}/{$this->springbotStoreID}" . PHP_EOL;
38
+
39
+ if ($result['status']==self::SUCCESSFUL_RESPONSE) {
40
+
41
+ foreach ($result['commands'] as $cmd) {
42
+ $this->showMessage('Command->'.$cmd['command'].' is being processed.');
43
+ switch (trim(strtolower($cmd['command']))) {
44
+ case 'get_log':
45
+ if (isset($cmd['data']['log_name'])) {
46
+ $this->getlog($cmd['data']['log_name']);
47
+ } else {
48
+ $this->getlog();
49
+ }
50
+ break;
51
+
52
+ case 'setvar':
53
+ case 'set_var':
54
+ $this->setConfigVar($cmd['data']['var_name'], $cmd['data']['var_value']);
55
+ Mage::getConfig()->cleanCache();
56
+ break;
57
+
58
+ case 'post':
59
+ $type = isset($cmd['data']['type']) ? $cmd['data']['type'] : null;
60
+ $id = isset($cmd['data']['entity_id']) ? $cmd['data']['entity_id'] : null;
61
+ $this->postItem($type, $id);
62
+
63
+ case 'package_update':
64
+ $version = isset($cmd['data']['package_version']) ? $cmd['data']['package_version'] : null;
65
+ $this->updatePackage($version);
66
+ break;
67
+
68
+ case 'full_harvest':
69
+ case 'launch_full_harvest':
70
+ Springbot_Boss::launchHarvest();
71
+ break;
72
+
73
+ case 'partial_harvest':
74
+ $type = isset($cmd['data']['type']) ? $cmd['data']['type'] : null;
75
+ $this->launchPartialHarvest($storeId, $type);
76
+ break;
77
+
78
+ case 'resume_harvest':
79
+ Mage::getConfig()->cleanCache();
80
+ Mage::getConfig()->reinit();
81
+ $this->resumeHarvest();
82
+ break;
83
+
84
+ case 'file_replace':
85
+ $this->fileReplace($cmd['data']['target_dir'],$cmd['data']['source']);
86
+ break;
87
+
88
+ case 'adroll_turn_on':
89
+ $this->setAdrollFeature($cmd['store_id'],$cmd,true);
90
+ break;
91
+
92
+ case 'adroll_turn_off':
93
+ $this->setAdrollFeature($cmd['store_id'],$cmd,false);
94
+ break;
95
+
96
+ case 'kill_harvest':
97
+ Springbot_Boss::halt();
98
+ break;
99
+
100
+ case 'table_data':
101
+ $this->logTableData();
102
+ break;
103
+
104
+ case 'forecast':
105
+ Springbot_Services_Cmd_Forecast::forecastAllStores();
106
+ break;
107
+
108
+ case 'skip_store_harvest':
109
+ Springbot_Boss::haltStore($cmd['store_id']);
110
+ break;
111
+
112
+ }
113
+ }
114
+ $this->deliverEventLog();
115
+ }
116
+ return;
117
+ }
118
+
119
+ public function getlog($logName = '')
120
+ {
121
+ $buffer = Mage::helper('combine')->getLogContents($logName);
122
+
123
+ $logData = array(
124
+ 'logs' => array(
125
+ array(
126
+ 'store_id' => $this->springbotStoreID,
127
+ 'description' => $buffer,
128
+ ),
129
+ ),
130
+ );
131
+
132
+ $method = 'logs';
133
+
134
+ Mage::getModel('combine/api')->call($method, json_encode($logData), false);
135
+
136
+ if (isset($result['status'])) {
137
+ if ($result['status']==self::SUCCESSFUL_RESPONSE) {
138
+ $error_msg='was successfully delivered';
139
+ } else {
140
+ $error_msg='delivery failed ->'.$result['status'];
141
+ }
142
+ }
143
+ $this->showMessage('['.__METHOD__.'] '.' '.$error_msg);
144
+ }
145
+
146
+ private function setConfigVar($varName,$varValue)
147
+ {
148
+ if(!preg_match('/.*\/.*\/.*/', $varName)) {
149
+ $varName = 'springbot/config/' . $varName;
150
+ }
151
+ if (!empty($varName)) {
152
+ $this->set_config($varName, $varValue);
153
+ }
154
+ return;
155
+ }
156
+
157
+ private function deliverEventLog()
158
+ {
159
+ $maxRecord = 8192;
160
+ $delimiter = ',';
161
+ $eventHistoryArchiveFilename = $this->rootAppPath.'/var/log/Springbot-EventHistory-Archive.csv';
162
+
163
+ if (file_exists($eventHistoryArchiveFilename)) {
164
+ $this->showMessage('Purge existing archive '.$eventHistoryArchiveFilename);
165
+ unlink ($eventHistoryArchiveFilename);
166
+ }
167
+ $this->showMessage('Snapshot '.$this->eventHistoryFilename.' -> '.$eventHistoryArchiveFilename);
168
+ copy($this->eventHistoryFilename, $eventHistoryArchiveFilename);
169
+ $handle = fopen($this->eventHistoryFilename, 'w');
170
+ if ($handle) {
171
+ $this->showMessage('Erasing '.$this->eventHistoryFilename);
172
+ fclose($handle);
173
+ } else {
174
+ $this->showMessage('Open/Erase failed on '.$this->eventHistoryFilename);
175
+ }
176
+
177
+ /* Get Unique Store Number */
178
+ $store_number_list=array();
179
+ if (($handle = fopen($eventHistoryArchiveFilename, 'r')) !== FALSE) {
180
+ while (($rawRow = fgetcsv($handle, $maxRecord, $delimiter)) !== FALSE) {
181
+ $data = array();
182
+ $storeNumber = '';
183
+ switch ($this->captureValue(0,$rawRow)) {
184
+ case 'view':
185
+ $storeNumber = $this->captureValue(5,$rawRow);
186
+ break;
187
+ case 'purchase':
188
+ $storeNumber = $this->captureValue(5,$rawRow);
189
+ break;
190
+ case 'atc':
191
+ $storeNumber = $this->captureValue(4,$rawRow);
192
+ break;
193
+ }
194
+ if (!empty($storeNumber) && !in_array($storeNumber, $store_number_list)) {$store_number_list[]=$storeNumber;}
195
+ }
196
+ fclose($handle);
197
+ }
198
+ foreach ($store_number_list as $storeNumber) {
199
+ if ($storeNumber=='' || empty($storeNumber)) {
200
+ } else {
201
+
202
+ $springbotStoreID = Mage::helper('combine/harvest')->getSpringbotStoreId($storeNumber);
203
+
204
+ $logData = array();
205
+ if (($handle = fopen($eventHistoryArchiveFilename, 'r')) !== FALSE) {
206
+ $this->showMessage('Formatting'.$eventHistoryArchiveFilename);
207
+ $actionCount=0;
208
+ while (($rawRow = fgetcsv($handle, $maxRecord, $delimiter)) !== FALSE) {
209
+ $actionCount++;
210
+ $row = array();
211
+ $data = array();
212
+ foreach ($rawRow as $val) {
213
+ $row[]=preg_replace('/[^(\x20-\x7F)]*/','', $val);
214
+ }
215
+ $data['action'] = $this->captureValue(0,$row);
216
+ $data['datetime'] = $this->captureValue(1,$row);
217
+
218
+ switch ($data['action']) {
219
+ case 'view':
220
+ $currentURL = $this->captureValue(2,$row);
221
+ $currentIP = $this->captureValue(4,$row);
222
+ $data['page_url'] = $currentURL;
223
+ $data['sku'] = $this->captureValue(3,$row);
224
+ $data['sku_fulfillment'] = $this->captureValue(3,$row);
225
+ $data['visitor_ip'] = $currentIP;
226
+ $data['category_id'] = $this->captureValue(6,$row);
227
+ $eventStoreNumber = $this->captureValue(5,$row);
228
+ break;
229
+
230
+ case 'purchase':
231
+ $data['sku'] = $this->captureValue(2,$row);
232
+ $data['sku_fulfillment'] = $this->captureValue(3,$row);
233
+ $data['purchase_id'] = $this->captureValue(4,$row);
234
+ $data['category_id'] = $this->captureValue(6,$row);
235
+ $eventStoreNumber = $this->captureValue(5,$row);
236
+ break;
237
+
238
+ case 'atc':
239
+ $data['sku'] = $this->captureValue(2,$row);
240
+ $data['sku_fulfillment'] = $this->captureValue(2,$row);
241
+ $data['quote_id'] = $this->captureValue(3,$row);
242
+ $eventStoreNumber = $this->captureValue(4,$row);
243
+ $data['category_id'] = $this->captureValue(5,$row);
244
+ break;
245
+
246
+ default:
247
+ $data['sku'] = '';
248
+ $eventStoreNumber = '';
249
+
250
+ }
251
+ if ($eventStoreNumber==$storeNumber && !empty($data['sku'])) { array_push($logData, $data); }
252
+
253
+ }
254
+ $this->showMessage('Store->'.$storeNumber.' had '.$actionCount.' actions extracted from '.$eventHistoryArchiveFilename);
255
+ fclose($handle);
256
+ }
257
+ if (sizeof($logData)==self::ZERO) {
258
+ $this->showMessage('Empty '.$eventHistoryArchiveFilename);
259
+ } else {
260
+ $this->showMessage('Delivering '.$eventHistoryArchiveFilename);
261
+ $method = 'stores/'.$springbotStoreID.'/products/actions/create';
262
+ $result = Mage::getModel('combine/api')->call($method, json_encode($logData));
263
+
264
+ if (isset($result['status'])) {
265
+ if ($result['status']==self::SUCCESSFUL_RESPONSE) {
266
+ $error_msg='was successfully delivered';
267
+ } else {
268
+ $error_msg='delivery failed ->'.$result['status'];
269
+ }
270
+ } else {
271
+ $error_msg=' delivery failed (No status array) ->'.$result;
272
+ }
273
+ $this->showMessage('['.$method.'] '.$eventHistoryArchiveFilename.' '.$error_msg);
274
+ }
275
+ } // end foreach
276
+ }
277
+ return;
278
+ }
279
+
280
+ private function captureValue($ix,$rowArray)
281
+ {
282
+ if (isset($rowArray[$ix])) {
283
+ return $rowArray[$ix];
284
+ } else {
285
+ return '';
286
+ }
287
+ }
288
+
289
+ public function updatePackage($version)
290
+ {
291
+ $updater = new Springbot_Services_Cmd_Update();
292
+ $updater->setVersion($version);
293
+ $updater->run();
294
+ return true;
295
+ }
296
+
297
+ private function fileReplace($target,$source)
298
+ {
299
+ $openModeOutput = 'w';
300
+ $origSize = self::ZERO;
301
+ $newSize = self::ZERO;
302
+
303
+ $magentoRootDir = $this->rootAppPath;
304
+ if (substr($target,0,1) != '/') {
305
+ $qualifiedFilename = $magentoRootDir.'/'.$target;
306
+ } else {
307
+ $qualifiedFilename = $magentoRootDir.$target;
308
+ }
309
+
310
+ /* If file exists make a backup copy */
311
+
312
+ if (file_exists($qualifiedFilename)) {
313
+ $origSize=filesize($qualifiedFilename);
314
+ copy($qualifiedFilename, $qualifiedFilename.'.backup');
315
+ }
316
+
317
+ $fHandle = fopen($qualifiedFilename,$openModeOutput);
318
+ fwrite($fHandle, $source);
319
+ fclose($fHandle);
320
+ $newSize=filesize($qualifiedFilename);
321
+
322
+ $this->showMessage('File Updated->'.$qualifiedFilename.' Original Size:'.$origSize.'; New Size:'.$newSize);
323
+
324
+ return;
325
+ }
326
+
327
+ public function postItem($type, $id)
328
+ {
329
+ Springbot_Boss::internalCallback(
330
+ "post:$id",
331
+ array('i' => $id)
332
+ );
333
+ }
334
+
335
+ private function resumeHarvest()
336
+ {
337
+ Springbot_Boss::internalCallback('work:manager');
338
+ }
339
+
340
+ private function launchPartialHarvest($storeId, $type)
341
+ {
342
+ Mage::helper('combine/harvest')->truncateEngineLogs();
343
+ Springbot_Boss::scheduleJob(
344
+ 'cmd:harvest',
345
+ array(
346
+ 's' => $storeId,
347
+ 'c' => $type,
348
+ ),
349
+ Springbot_Services_Priority::HARVEST,
350
+ 'default',
351
+ $storeId
352
+ );
353
+ }
354
+
355
+ private function setAdrollFeature($storeId, $data, $enable)
356
+ {
357
+ $configPath = 'design/footer/absolute_footer';
358
+ $pixel_code = html_entity_decode($data['data']['pixel_script'], ENT_QUOTES);
359
+ $new_footer = '';
360
+ $existing=$this->RemoveAdRoll($this->get_config($configPath));
361
+
362
+ if ($enable) {
363
+ if (strlen($pixel_code)>self::ZERO) {
364
+ $new_footer = $this->InsertAdrollScript($existing,$pixel_code);
365
+ }
366
+ } else {
367
+ $new_footer = $existing;
368
+ }
369
+
370
+ // Only set adroll for current store!
371
+ $scope_id = Mage::app()->getStore()->getStoreId();
372
+ $this->set_config($configPath, $new_footer, 'stores', $storeId);
373
+ $this->showMessage($configPath.' is now ['.$new_footer.']');
374
+
375
+ return;
376
+ }
377
+
378
+ private function RemoveAdRoll($existing)
379
+ {
380
+ $footerLength = strlen($existing);
381
+ $bannerPrefix = "<!-- Springbot: Begin Adroll Script ";
382
+ $endScriptMarker = "<!-- Springbot: End Adroll Script -->";
383
+
384
+ $beginPointer = strpos($existing, $bannerPrefix);
385
+ $endPointer = strpos($existing, $endScriptMarker);
386
+
387
+ if ($endPointer>self::ZERO) {
388
+ return $this->EraseScript($existing,$beginPointer,$endPointer);
389
+ } else {
390
+ return $existing;
391
+ }
392
+ return;
393
+ }
394
+
395
+ private function EraseScript($scriptCode,$begLoc,$endLoc)
396
+ {
397
+ $strLEN = strlen($scriptCode);
398
+ $newScript = '';
399
+ for ($c=self::ZERO;$c<$strLEN;$c++) {
400
+ if ($c<$begLoc || $c>$endLoc) {
401
+ $newScript=$newScript.substr($scriptCode,$c,self::ZERO);
402
+ }
403
+ }
404
+ return $newScript;
405
+ }
406
+
407
+ private function InsertAdrollScript($currentFooterCode,$ad_pixelCode)
408
+ {
409
+ $now = date(self::DATE_FORMAT);
410
+
411
+ $newFooter = $currentFooterCode
412
+ ."<!-- Springbot: Begin Adroll Script ".$now.' -->'."\n"
413
+ .$ad_pixelCode
414
+ ."<!-- Springbot: End Adroll Script -->";
415
+
416
+ return $newFooter;
417
+ }
418
+
419
+ private function get_config($path)
420
+ {
421
+ return Mage::getStoreConfig($path);
422
+ }
423
+
424
+ private function set_config($path, $value, $scope = 'default', $scope_id = 0)
425
+ {
426
+ return Mage::getModel('core/config')->saveConfig($path, $value, $scope, $scope_id);
427
+ }
428
+
429
+ private function showMessage($msg,$abort=false,$ignoreMessage=true)
430
+ {
431
+ Springbot_Log::debug($msg);
432
+ if ($ignoreMessage==false) {
433
+ Springbot_Log::harvest($msg);
434
+ }
435
+ if ($abort) {
436
+ Springbot_Log::harvest($msg);
437
+ Springbot_Log::harvest('Process Abort requested');
438
+ exit;
439
+ }
440
+ return;
441
+ }
442
+
443
+ public function logTableData() {
444
+ $count = Mage::getModel('combine/cron_queue')->getCollection()->count();
445
+ Springbot_Log::remote('Cron queue table size: ' . $count);
446
+ }
447
+
448
+ }
app/code/community/Springbot/Log.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Log
4
+ {
5
+ const EMERG = 0; // Emergency: system is unusable
6
+ const ALERT = 1; // Alert: action must be taken immediately
7
+ const CRIT = 2; // Critical: critical conditions
8
+ const ERR = 3; // Error: error conditions
9
+ const WARN = 4; // Warning: warning conditions
10
+ const NOTICE = 5; // Notice: normal but significant condition
11
+ const INFO = 6; // Informational: informational messages
12
+ const DEBUG = 7; // Debug: debug messages
13
+
14
+ const LOGFILE = 'Springbot.log';
15
+ const ERRFILE = 'Springbot.err';
16
+ const HTTPFILE = 'Springbot-Http.log';
17
+
18
+ protected static $_logger;
19
+
20
+ public static function getFormat()
21
+ {
22
+ return Mage::getStoreConfig('springbot/debug/log_format');
23
+ }
24
+
25
+ public static function getExtras()
26
+ {
27
+ $caller = Springbot_Util_Caller::find(4);
28
+ if(self::getFormat() == 'expanded') {
29
+ return array(
30
+ 'className' => $caller->class,
31
+ 'method' => $caller->method,
32
+ 'callType' => $caller->call_type,
33
+ 'line' => $caller->line,
34
+ );
35
+ }
36
+ }
37
+
38
+ public static function logger()
39
+ {
40
+ if(!isset(self::$_logger)) {
41
+ self::$_logger = new Springbot_Util_Logger();
42
+ }
43
+ return self::$_logger;
44
+ }
45
+
46
+ public static function release($filename)
47
+ {
48
+ self::logger()->release($filename);
49
+ }
50
+
51
+ private static function _log($message, $level, $fmt = null, $file = self::LOGFILE)
52
+ {
53
+ if(self::_levelAllowed($level)) {
54
+ if(!$fmt) {
55
+ $fmt = self::getFormat();
56
+ }
57
+ self::logger()->log($message, $level, $file, $fmt, self::getExtras());
58
+ }
59
+ }
60
+
61
+ public static function debug($message)
62
+ {
63
+ self::_log($message, Zend_Log::DEBUG);
64
+ }
65
+
66
+ public static function harvest($message, $remote = false, $storeId = 1)
67
+ {
68
+ if(is_null($storeId)) {
69
+ $storeId = isset(self::$_currentStore) ? self::$_currentStore : 1;
70
+ }
71
+ self::_log($message, Zend_Log::CRIT, 'simple', self::LOGFILE);
72
+
73
+ if($remote) {
74
+ self::remote($message, $storeId);
75
+ }
76
+ }
77
+
78
+ public static function info($message)
79
+ {
80
+ self::_log($message, Zend_Log::INFO);
81
+ }
82
+
83
+ public static function error(Exception $e)
84
+ {
85
+ self::_log("\n" . $e->__toString(), Zend_Log::ERR, 'default', self::ERRFILE);
86
+ }
87
+
88
+ public static function http($message)
89
+ {
90
+ if(Mage::getStoreConfig('springbot/debug/log_http')) {
91
+ if(Mage::getStoreConfig('springbot/debug/pretty_print')) {
92
+ $message = Zend_Json::prettyPrint($message, array("indent" => " "));
93
+ }
94
+ // lowest possible level - we have another setting controlling this logging
95
+ self::_log($message, Zend_Log::CRIT, 'simple', 'Springbot-Http.log');
96
+ }
97
+ }
98
+
99
+ public static function remote($message, $id = 1, $priority = 5, $alert = false)
100
+ {
101
+ $id = (is_null($id)) ? 1 : $id;
102
+
103
+ $struct = new stdClass;
104
+
105
+ if($storeId = Mage::helper('combine/harvest')->getSpringbotStoreId($id)) {
106
+ $ar = array(
107
+ 'store_id' => $storeId,
108
+ 'event_time' => Mage::helper('combine')->formatDateTime(),
109
+ 'store_url' => Mage::helper('combine/harvest')->getStoreUrl($id),
110
+ 'remote_addr' => self::getRemoteAddress(),
111
+ 'priority' => $priority,
112
+ 'description' => $message,
113
+ );
114
+
115
+ if($alert) {
116
+ $ar['log_type'] = 'ALERT';
117
+ }
118
+
119
+ $struct->{$storeId} = (object) $ar;
120
+
121
+ $api = Mage::getModel('combine/api');
122
+ $payload = $api->wrap('logs', $struct);
123
+ $api->reinit()->call('logs', $payload);
124
+ }
125
+ }
126
+
127
+ public static function getRemoteAddress()
128
+ {
129
+ return isset($_SERVER['REMOTE_ADDR']) ? $remAddr=$_SERVER['REMOTE_ADDR'] : null;
130
+ }
131
+
132
+ private static function _levelAllowed($level)
133
+ {
134
+ return $level <= Mage::getStoreConfig('springbot/debug/log_level');
135
+ }
136
+
137
+ public static function printLine($remote = false)
138
+ {
139
+ Springbot_Log::harvest('--------------------------------------------------------------------------------', $remote);
140
+ }
141
+
142
+ }
app/code/community/Springbot/Services/Abstract.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Springbot_Services_Abstract extends Varien_Object
4
+ {
5
+ protected $_type = 'items';
6
+ protected $_harvester;
7
+ protected $_startTime;
8
+
9
+ protected function _construct()
10
+ {
11
+ $this->_startTime = microtime(true);
12
+ }
13
+
14
+ abstract public function run();
15
+
16
+ public function getData($key = '', $index = NULL)
17
+ {
18
+ $val = parent::getData($key);
19
+
20
+ if(!(isset($val) || is_array($val))) {
21
+ throw new Exception($this->_humanize($key) . ' required for harvest!');
22
+ } else {
23
+ return $val;
24
+ }
25
+ }
26
+
27
+ public function getHarvestId()
28
+ {
29
+ return parent::getData('harvest_id');
30
+ }
31
+
32
+ public function hasRange()
33
+ {
34
+ return isset($this->_data['start_id']) || isset($this->_data['stop_id']);
35
+ }
36
+
37
+ public function getStoreId()
38
+ {
39
+ if($storeId = parent::getData('store_id')) {
40
+ return $storeId;
41
+ } else {
42
+ return 0;
43
+ }
44
+ }
45
+
46
+ public function getStartId()
47
+ {
48
+ return parent::getData('start_id');
49
+ }
50
+
51
+ public function getStopId()
52
+ {
53
+ return parent::getData('stop_id');
54
+ }
55
+
56
+ public function getFailedStartId()
57
+ {
58
+ return parent::getData('failed_start_id');
59
+ }
60
+
61
+ public function getFailedStopId()
62
+ {
63
+ return parent::getData('failed_stop_id');
64
+ }
65
+
66
+ public function getIsResume()
67
+ {
68
+ return isset($this->_data['resume']);
69
+ }
70
+
71
+ public function getLastFailedPartition()
72
+ {
73
+ return isset($this->_data['failed_partition']);
74
+ }
75
+
76
+ public function getForce()
77
+ {
78
+ return isset($this->_data['force']) && $this->_data['force'] === true;
79
+ }
80
+
81
+ public function getProcessedCount()
82
+ {
83
+ return $this->getHarvester()->getProcessedCount();
84
+ }
85
+
86
+ public function getHarvesterName()
87
+ {
88
+ return $this->getHarvester()->getHarvesterName();
89
+ }
90
+
91
+ public function getHarvester()
92
+ {
93
+ return $this->_harvester;
94
+ }
95
+
96
+ public function getSegmentMin()
97
+ {
98
+ return $this->getHarvester()->getSegmentMin();
99
+ }
100
+
101
+ public function getSegmentMax()
102
+ {
103
+ return $this->getHarvester()->getSegmentMax();
104
+ }
105
+
106
+ public function getType()
107
+ {
108
+ return ucwords($this->_type);
109
+ }
110
+
111
+ public function getRuntime()
112
+ {
113
+ return number_format(microtime(true) - $this->_startTime, 3, '.', '');
114
+ }
115
+
116
+ protected function _humanize($var)
117
+ {
118
+ return ucfirst(preg_replace('/\_/', ' ', $var));
119
+ }
120
+
121
+ protected function _getStatus()
122
+ {
123
+ return Mage::getSingleton('combine/cron_manager_status');
124
+ }
125
+ }
app/code/community/Springbot/Services/Cmd/Forecast.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Cmd_Forecast extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ if($storeId = $this->getStoreId()) {
8
+ $harvestId = Mage::helper('combine/harvest')->initRemoteHarvest($storeId);
9
+ self::forecastStore($storeId, $harvestId);
10
+ }
11
+ else {
12
+ self::forecastAllStores();
13
+ }
14
+ }
15
+
16
+ public static function forecastAllStores() {
17
+ foreach(Mage::helper('combine/harvest')->getStoresToHarvest() as $store) {
18
+ $harvestId = Mage::helper('combine/harvest')->initRemoteHarvest($store->getStoreId());
19
+ self::forecastStore($store->getStoreId(), $harvestId);
20
+ }
21
+ }
22
+
23
+ public static function forecastStore($storeId, $harvestId)
24
+ {
25
+ foreach(Springbot_Services_Cmd_Harvest::getClasses() as $key) {
26
+ $keyUpper = ucwords($key);
27
+ $collection = call_user_func(array('Springbot_Services_Harvest_' . $keyUpper, 'getCollection'), $storeId);
28
+ Mage::helper('combine/harvest')->forecast($collection, $storeId, $keyUpper, $harvestId);
29
+ }
30
+ }
31
+
32
+ }
app/code/community/Springbot/Services/Cmd/Halt.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Cmd_Halt extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ if(isset($this->_data['halt_command'])) {
8
+ $out = Springbot_Boss::halt($this->getHaltCommand());
9
+ } else {
10
+ $out = Springbot_Boss::halt();
11
+ }
12
+ print $out . PHP_EOL;
13
+ }
14
+ }
app/code/community/Springbot/Services/Cmd/Harvest.php ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Services_Cmd_Harvest extends Springbot_Services_Abstract
3
+ {
4
+ const SEGMENT_SIZE = 2000;
5
+
6
+ protected $_harvestId;
7
+
8
+ // Defines order when all
9
+ protected static $_classes = array(
10
+ 'categories',
11
+ 'attributeSets',
12
+ 'customerAttributeSets',
13
+ 'products',
14
+ 'purchases',
15
+ 'customers',
16
+ 'guests',
17
+ 'subscribers',
18
+ //'carts',
19
+ );
20
+
21
+ protected function _init()
22
+ {
23
+ $service = new Springbot_Services_Store_Register;
24
+
25
+ // Init all stores upfront
26
+ foreach($this->getHelper()->getStoresToHarvest() as $store) {
27
+ $service->setStoreId($store->getStoreId())->run();
28
+ }
29
+
30
+ // Have to clear cache in parent thread after config set
31
+ Mage::getConfig()->cleanCache();
32
+ Springbot_Log::debug(Mage::getStoreConfig('springbot'));
33
+ }
34
+
35
+ public function run()
36
+ {
37
+ Springbot_Log::debug(__METHOD__);
38
+
39
+ if($this->getIsResume()) {
40
+ $this->_resumeHarvest();
41
+ }
42
+ else if($this->hasClass() && $this->hasRange()) {
43
+ $this->_harvest($this->getClass(), $this->getStoreId());
44
+ }
45
+ else if($this->hasClass()) {
46
+ $this->_segmentHarvest($this->getClass(), $this->getStoreId());
47
+ }
48
+ else {
49
+ $this->_fullHarvest();
50
+ }
51
+ }
52
+
53
+ protected function _fullHarvest()
54
+ {
55
+ if(!$this->getHelper()->isHarvestRunning()) {
56
+ $this->_init();
57
+ //Iterate all stores
58
+ foreach($this->getHelper()->getStoresToHarvest() as $store) {
59
+ $this->_harvestId = Mage::helper('combine/harvest')->initRemoteHarvest($store->getStoreId());
60
+ $this->_harvestStore($store, self::$_classes, $this->_harvestId);
61
+ }
62
+ }
63
+ }
64
+
65
+ protected function _resumeHarvest()
66
+ {
67
+ $harvestCursor = Mage::getStoreConfig('springbot/config/harvest_cursor');
68
+
69
+ if (!$harvestCursor) {
70
+ Springbot_Log::remote('Resume harvest command received, no valid harvest cursor found: Cursor value: ' . $harvestCursor);
71
+ }
72
+ else {
73
+ list($lastClassCompleted, $partition, $storeId) = explode('|', $harvestCursor);
74
+ foreach($this->getHelper()->getStoresToHarvest() as $store) {
75
+ $this->_harvestId = Mage::helper('combine/harvest')->initRemoteHarvest($store->getStoreId());
76
+ if ($store->getStoreId() == $storeId) {
77
+ // Only harvest classes for this store that have not been fully or partially harvested yet
78
+ $classesLeft = $this->_getClassesSubset($lastClassCompleted);
79
+ $this->_partialHarvestClass($store, $lastClassCompleted, $partition, $this->_harvestId);
80
+ $this->_harvestStore($store, $classesLeft, $this->_harvestId);
81
+ }
82
+ else if ($store->getStoreId() > $storeId) {
83
+ // Harvest has not begun for this store so harvest all classes
84
+ $this->_harvestStore($store, self::$_classes, $this->_harvestId);
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ protected function _partialHarvestClass($store, $class, $partition, $harvestId)
91
+ {
92
+ Springbot_Log::debug("Partial harvest started {$store->getStoreId()} | $lastClassCompleted | $lastFailedPartition | $harvestId");
93
+ Springbot_Boss::scheduleJob(
94
+ 'cmd:harvest',
95
+ array(
96
+ 's' => $store->getStoreId(),
97
+ 'c' => $class,
98
+ 'v' => $harvestId,
99
+ 'i' => $partition,
100
+ ),
101
+ Springbot_Services_Priority::CATEGORY,
102
+ 'default',
103
+ $store->getStoreId()
104
+ );
105
+ }
106
+
107
+ protected function _harvestStore($store, $classes, $harvestId) {
108
+ $this->_setActive($store->getStoreId());
109
+ $this->_logStoreHeader($store);
110
+
111
+ Springbot_Services_Cmd_Forecast::forecastStore($store->getStoreId(), $this->_harvestId);
112
+
113
+ foreach ($classes as $class) {
114
+ Springbot_Boss::scheduleJob(
115
+ 'cmd:harvest',
116
+ array(
117
+ 's' => $store->getStoreId(),
118
+ 'c' => $class,
119
+ 'v' => $harvestId,
120
+ ),
121
+ Springbot_Services_Priority::CATEGORY,
122
+ 'default',
123
+ $store->getStoreId()
124
+ );
125
+ Springbot_Boss::scheduleJob(
126
+ 'work:report',
127
+ array(
128
+ 's' => $store->getStoreId(),
129
+ 'c' => $class,
130
+ 'v' => $harvestId,
131
+ ),
132
+ Springbot_Services_Priority::CATEGORY,
133
+ 'default',
134
+ $store->getStoreId()
135
+ );
136
+ }
137
+ Springbot_Boss::scheduleJob(
138
+ 'store:finalize',
139
+ array('s' => $store->getStoreId()),
140
+ Springbot_Services_Priority::CATEGORY,
141
+ 'default',
142
+ $store->getStoreId()
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Harvests a class segment for a specific store. Possible flags are:
148
+ *
149
+ * -s store_id, required
150
+ * -i _start_:_stop_ will limit the collection to be partitioned - both sides are optional
151
+ * -v harvest_id
152
+ *
153
+ * This will forecast the collection to be harvested, and it will then be split up into
154
+ * smaller chunks, each of which will be scheduled.
155
+ *
156
+ * @param string $key
157
+ * @param int $storeId
158
+ */
159
+ protected function _harvest($key, $storeId)
160
+ {
161
+ $count = 0;
162
+ $keyUpper = ucwords($key);
163
+
164
+ Springbot_Log::harvest("Harvesting {$keyUpper}");
165
+
166
+ $collection = $this->_getCollection($keyUpper, $storeId);
167
+
168
+ $this->getHelper()->forecast($collection, $storeId, $keyUpper, $this->getHarvestId());
169
+
170
+ $scheduler = Mage::getModel('combine/cron_queue_batch');
171
+
172
+ foreach(Mage::helper('combine/harvest')->partitionCollection($collection) as $partition) {
173
+ $count++;
174
+ $scheduler->schedule(
175
+ "harvest:{$key}",
176
+ array(
177
+ 's' => $storeId,
178
+ 'i' => (string) $partition,
179
+ 'c' => $key,
180
+ 'v' => $this->getHarvestId(),
181
+ ),
182
+ Springbot_Services_Priority::PARTITION,
183
+ 'partition', // Partition queue
184
+ $storeId
185
+ );
186
+ //Mage::getModel('core/config')->saveConfig('springbot/config/harvest_cursor', $key . '|' . $partition->fromStart() . '|' . $storeId);
187
+ }
188
+ $scheduler->insert();
189
+
190
+ Springbot_Log::harvest("{$count} partitions created for {$keyUpper}");
191
+ }
192
+
193
+ protected function _segmentHarvest($key, $storeId)
194
+ {
195
+ $keyUpper = ucwords($key);
196
+ $collection = $this->_getCollection($keyUpper, $storeId);
197
+
198
+ Springbot_Log::harvest("Segmenting {$keyUpper}");
199
+ $scheduler = Mage::getModel('combine/cron_queue_batch');
200
+
201
+
202
+ $this->_reportHarvestStartTime($this->getHarvestId(), $storeId, $key);
203
+
204
+ $count = 0;
205
+ foreach(Mage::helper('combine/harvest')->partitionCollection($collection, self::SEGMENT_SIZE) as $partition) {
206
+ $count++;
207
+ $scheduler->schedule(
208
+ "cmd:harvest",
209
+ array(
210
+ 's' => $storeId,
211
+ 'c' => $key,
212
+ 'i' => (string) $partition,
213
+ 'v' => $this->getHarvestId(),
214
+ ),
215
+ Springbot_Services_Priority::SEGMENT,
216
+ 'default',
217
+ $storeId
218
+ );
219
+ }
220
+ $scheduler->insert();
221
+ Springbot_Log::harvest("{$count} segments created for {$key}");
222
+ }
223
+
224
+ private function _reportHarvestStartTime($harvestId, $storeId, $type)
225
+ {
226
+ $cronCount =Mage::getModel('combine/cron_count');
227
+
228
+ // Create the cron count row for the entity if it doesn't exist already.
229
+ $cronCount->increaseCount($storeId, $harvestId, $type, 0);
230
+
231
+ $started = $cronCount->getEntityStartTime($storeId, $harvestId, $type);
232
+ $params = array(
233
+ 'store_id' => $this->getHelper()->getSpringbotStoreId($storeId),
234
+ 'type' => $type,
235
+ 'started' => $started,
236
+ );
237
+ $api = Mage::getModel('combine/api');
238
+ $payload = $api->wrap('harvest_segments', array($params));
239
+ if(!is_null($harvestId)) {
240
+ return $api->put("harvests/{$harvestId}", $payload);
241
+ }
242
+ }
243
+
244
+ /**
245
+ * @return Varien_Db_Collection_Abstract
246
+ */
247
+ protected function _getCollection($type, $storeId)
248
+ {
249
+ Springbot_Log::debug("Building collection $type for partition => {$this->getPartition()}");
250
+ return call_user_func_array(
251
+ array('Springbot_Services_Harvest_' . $type, 'getCollection'),
252
+ array($storeId, $this->getPartition())
253
+ );
254
+ }
255
+
256
+ /**
257
+ * When resuming a harvest, get all class types that have not been fully or partially harvested yet
258
+ *
259
+ * @param $lastClassCompleted
260
+ * @return array
261
+ */
262
+ private function _getClassesSubset($lastClassCompleted) {
263
+ $classesLeft = array();
264
+ $foundLastClass = false;
265
+ foreach (self::$_classes as $class) {
266
+ if ($foundLastClass) {
267
+ $classesLeft[] = $class;
268
+ }
269
+ if ($class == $lastClassCompleted) {
270
+ $foundLastClass = true;
271
+ }
272
+ }
273
+ return $classesLeft;
274
+ }
275
+
276
+ public function getPartition()
277
+ {
278
+ return new Springbot_Util_Partition($this->getStartId(), $this->getStopId());
279
+ }
280
+
281
+ public function getHarvestId()
282
+ {
283
+ return isset($this->_harvestId) ? $this->_harvestId : $this->_data['harvest_id'];
284
+ }
285
+
286
+ public function getHelper()
287
+ {
288
+ return Mage::helper('combine/harvest');
289
+ }
290
+
291
+ protected function _setActive($storeId)
292
+ {
293
+ Springbot_Boss::setActive($storeId);
294
+ return $this;
295
+ }
296
+
297
+ protected function _getDate()
298
+ {
299
+ return date("Y-m-d H:i:s");
300
+ }
301
+
302
+ protected function _logStoreHeader($store)
303
+ {
304
+ $helper = Mage::helper('combine/store')->setStore($store);
305
+
306
+ Springbot_Log::printLine(true);
307
+ Springbot_Log::harvest("Harvesting Store {$store->getUrl()} => {$store->getId()}/{$helper->getSpringbotStoreId()}", true);
308
+ Springbot_Log::harvest("Harvest ID => {$this->_harvestId}\nGUID => {$helper->getGuid()}\nEmail => {$helper->getAccountEmail()}", true);
309
+ Springbot_Log::printLine(true);
310
+ }
311
+
312
+ public static function getClasses()
313
+ {
314
+ return self::$_classes;
315
+ }
316
+ }
app/code/community/Springbot/Services/Cmd/Healthcheck.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Cmd_Healthcheck extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ // Run checkin process
8
+ $harvestingManager = new Springbot_DataServices_HarvestingManager();
9
+ $harvestingManager->harvestHealthCheck($this->getStoreId());
10
+
11
+ // Inspect, rollover and delete logs
12
+ $rollover = new Springbot_Util_Log_Rollover();
13
+ $rollover->expireLogs();
14
+ $rollover->ensureLogSize();
15
+ $rollover->reset();
16
+
17
+ Springbot_Log::debug("Healthcheck job complete");
18
+ }
19
+ }
app/code/community/Springbot/Services/Cmd/Parse.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Cmd_Parse extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ print_r($this->_data);
8
+ }
9
+ }
app/code/community/Springbot/Services/Cmd/Update.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Cmd_Update extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ if(Mage::getStoreConfig('springbot/config/remote_update') || $this->getForce()) {
8
+ try {
9
+ Springbot_Log::info("Updating Springbot extension");
10
+
11
+ $connect = new Springbot_Services_Update_Connect;
12
+ $connect->setVersion($this->_getVersion());
13
+ $version = $connect->run();
14
+
15
+ Springbot_Log::info("Update to version $version.");
16
+
17
+ $downloader = new Springbot_Services_Update_Downloader($version);
18
+ $archivePath = $downloader->run();
19
+
20
+ Springbot_Log::info("Archive downloaded to $archivePath");
21
+
22
+ $package = new Springbot_Services_Update_Package($archivePath);
23
+ $package->unpack();
24
+
25
+ Springbot_Log::info("Archive extracted to {$package->getUnpackedPath()}");
26
+
27
+ $installer = new Springbot_Services_Update_Installer($package);
28
+ $installer->run();
29
+
30
+ Springbot_Log::info("Install was successful. Clearing cache.");
31
+
32
+ Mage::app()->cleanCache();
33
+ } catch (Exception $e) {
34
+ Springbot_Log::error($e);
35
+ die($e->getMessage() . PHP_EOL);
36
+ }
37
+ $msg = "Updated to version $version successfully!";
38
+ Springbot_Log::remote($msg);
39
+ echo $msg . PHP_EOL;
40
+ } else {
41
+ throw new Exception('Remote update not allowed by configuration! Please enable or use -f param.');
42
+ }
43
+ }
44
+
45
+ protected function _getVersion()
46
+ {
47
+ return isset($this->_data['version']) ? $this->_data['version'] : null;
48
+ }
49
+ }
app/code/community/Springbot/Services/Harvest.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Springbot_Services_Harvest extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ $mb = round(memory_get_peak_usage(true) / pow(1024, 2), 2);
8
+
9
+ $msg = "{$this->getHarvesterName()} block {$this->getSegmentMin()} : {$this->getSegmentMax()} posted [{$this->getProcessedCount()} overall]";
10
+ $msg .= " | " . $mb . ' MB';
11
+ $msg .= " | {$this->getRuntime()} sec";
12
+
13
+ Springbot_Log::harvest($msg);
14
+ $countObject = Mage::getModel('combine/cron_count');
15
+ $countObject->increaseCount($this->getStoreId(), $this->getHarvestId(), $this->getClass(), $this->getProcessedCount());
16
+
17
+ return $this->getProcessedCount();
18
+ }
19
+
20
+ public function getDataSource()
21
+ {
22
+ return Springbot_Boss::SOURCE_BULK_HARVEST;
23
+ }
24
+
25
+ public static function limitCollection($collection, Springbot_Util_Partition $partition, $id = 'entity_id')
26
+ {
27
+ if($partition->start) {
28
+ $collection->addFieldToFilter($id, array('gteq' => $partition->start));
29
+ }
30
+
31
+ if($partition->stop) {
32
+ $collection->addFieldToFilter($id, array('lteq' => $partition->stop));
33
+ }
34
+ return $collection;
35
+ }
36
+ }
app/code/community/Springbot/Services/Harvest/AttributeSets.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_AttributeSets extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'attributes';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('attribute_set_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('attribute_set_id', array('lteq' => $this->getStopId()));
14
+ }
15
+
16
+ $this->_harvester = Mage::getModel('combine/harvest_attributeSets')
17
+ ->setDataSource($this->getDataSource())
18
+ ->setStoreId($this->getStoreId())
19
+ ->setCollection($collection)
20
+ ->harvest();
21
+
22
+ return parent::run();
23
+ }
24
+
25
+ public static function getCollection()
26
+ {
27
+ return Mage::helper('combine/attributes')->getAttributeSets();
28
+ }
29
+ }
app/code/community/Springbot/Services/Harvest/Carts.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Carts extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'carts';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('entity_id', array('gt' => $this->getStartId()))
11
+ ->addFieldToFilter('entity_id', array('lteq' => $this->getStopId()));
12
+
13
+ $this->_harvester = Mage::getModel('combine/harvest_carts')
14
+ ->setDataSource($this->getDataSource())
15
+ ->setStoreId($this->getStoreId())
16
+ ->setCollection($collection)
17
+ ->harvest();
18
+
19
+ return parent::run();
20
+ }
21
+
22
+ public static function getCollection($storeId, $partition = null)
23
+ {
24
+ $collection = Mage::getModel('sales/quote')
25
+ ->getCollection()
26
+ ->addFieldToFilter('customer_email', array('notnull' => true))
27
+ ->addFieldToFilter('store_id', $storeId)
28
+ ->addFieldToFilter('is_active', 1);
29
+
30
+ if($partition) {
31
+ $collection = parent::limitCollection($collection, $partition);
32
+ }
33
+ return $collection;
34
+ }
35
+ }
app/code/community/Springbot/Services/Harvest/Categories.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Categories extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'categories';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('entity_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('entity_id', array('lteq' => $this->getStopId()));
14
+ }
15
+
16
+ $this->_harvester = Mage::getModel('combine/harvest_categories')
17
+ ->setCollection($collection)
18
+ ->setStoreId($this->getStoreId())
19
+ ->setDataSource($this->getDataSource())
20
+ ->harvest();
21
+
22
+ return parent::run();
23
+ }
24
+
25
+ public static function getCollection($storeId, $partition = null)
26
+ {
27
+ $rootCategory = Mage::app()->getStore($storeId)->getRootCategoryId();
28
+
29
+ $collection = Mage::getModel('catalog/category')
30
+ ->getCollection()
31
+ ->addAttributeToFilter(array(
32
+ array(
33
+ 'attribute' => 'entity_id',
34
+ 'eq' => $rootCategory
35
+ ),
36
+ array(
37
+ 'attribute' => 'path',
38
+ 'like' => "1/{$rootCategory}/%"
39
+ ),
40
+ ));
41
+
42
+ if($partition) {
43
+ $collection = parent::limitCollection($collection, $partition);
44
+ }
45
+ return $collection;
46
+ }
47
+ }
app/code/community/Springbot/Services/Harvest/CustomerAttributeSets.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_CustomerAttributeSets extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'attributes';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection();
10
+
11
+ $this->_harvester = Mage::getModel('combine/harvest_customerAttributeSets')
12
+ ->setDataSource($this->getDataSource())
13
+ ->setStoreId($this->getStoreId())
14
+ ->setCollection($collection)
15
+ ->harvest();
16
+
17
+ return parent::run();
18
+ }
19
+
20
+ public static function getCollection()
21
+ {
22
+ return Mage::helper('combine/attributes')->getCustomerAttributeSets();
23
+ }
24
+ }
app/code/community/Springbot/Services/Harvest/Customers.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Customers extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'customers';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('entity_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('entity_id', array('lteq' => $this->getStopId()));
14
+ }
15
+ $this->_harvester = Mage::getModel('combine/harvest_customers')
16
+ ->setCollection($collection)
17
+ ->setDataSource($this->getDataSource())
18
+ ->harvest();
19
+
20
+ return parent::run();
21
+ }
22
+
23
+ public static function getCollection($storeId, $partition = null)
24
+ {
25
+ $collection = Mage::getModel('customer/customer')
26
+ ->getCollection()
27
+ ->addFieldToFilter('store_id', $storeId);
28
+
29
+ if($partition) {
30
+ $collection = parent::limitCollection($collection, $partition);
31
+ }
32
+ return $collection;
33
+ }
34
+ }
app/code/community/Springbot/Services/Harvest/Guests.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Guests extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'guests';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('entity_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('entity_id', array('lteq' => $this->getStopId()));
14
+ }
15
+
16
+ if(method_exists($collection, 'groupByAttribute')) {
17
+ // Magento 1.3.*
18
+ $collection->groupByAttribute('customer_email');
19
+ } else if($collection->getSelect() instanceof Zend_Db_Select) {
20
+ // Deduplicate by customer email
21
+ try {
22
+ $collection->getSelect()->order('increment_id')->group('customer_email');
23
+ } catch (Exception $e) { }
24
+ }
25
+
26
+ $this->_harvester = Mage::getModel('combine/harvest_guests')
27
+ ->setCollection($collection)
28
+ ->setDataSource($this->getDataSource())
29
+ ->harvest();
30
+
31
+ return parent::run();
32
+ }
33
+
34
+ public static function getCollection($storeId, $partition = null)
35
+ {
36
+ $collection = Mage::getModel('sales/order')
37
+ ->getCollection()
38
+ ->addFieldToFilter('store_id', $storeId)
39
+ ->addFieldToFilter('customer_is_guest', true);
40
+
41
+ if($partition) {
42
+ $collection = parent::limitCollection($collection, $partition);
43
+ }
44
+ return $collection;
45
+ }
46
+ }
app/code/community/Springbot/Services/Harvest/Products.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Products extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'products';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('entity_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('entity_id', array('lteq' => $this->getStopId()));
14
+ }
15
+
16
+ $this->_harvester = Mage::getModel('combine/harvest_products')
17
+ ->setStoreId($this->getStoreId())
18
+ ->setDataSource($this->getDataSource())
19
+ ->setCollection($collection)
20
+ ->harvest();
21
+
22
+ return parent::run();
23
+ }
24
+
25
+ public static function getCollection($storeId, $partition = null)
26
+ {
27
+ $collection = Mage::getModel('catalog/product')
28
+ ->getCollection()
29
+ ->addStoreFilter($storeId);
30
+
31
+ if($partition) {
32
+ $collection = parent::limitCollection($collection, $partition);
33
+ }
34
+ return $collection;
35
+ }
36
+ }
app/code/community/Springbot/Services/Harvest/Purchases.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Purchases extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'purchases';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('entity_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('entity_id', array('lteq' => $this->getStopId()));
14
+ }
15
+
16
+ $this->_harvester = Mage::getModel('combine/harvest_purchases')
17
+ ->setCollection($collection)
18
+ ->setDataSource($this->getDataSource())
19
+ ->harvest();
20
+
21
+ return parent::run();
22
+ }
23
+
24
+ public static function getCollection($storeId, $partition = null)
25
+ {
26
+ $collection = Mage::getModel('sales/order')
27
+ ->getCollection()
28
+ ->addFieldToFilter('store_id', $storeId);
29
+
30
+ if($partition) {
31
+ $collection = parent::limitCollection($collection, $partition);
32
+ }
33
+ return $collection;
34
+ }
35
+ }
app/code/community/Springbot/Services/Harvest/Subscribers.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Harvest_Subscribers extends Springbot_Services_Harvest
4
+ {
5
+ protected $_type = 'subscribers';
6
+
7
+ public function run()
8
+ {
9
+ $collection = self::getCollection($this->getStoreId())
10
+ ->addFieldToFilter('subscriber_id', array('gt' => $this->getStartId()));
11
+ $stopId = $this->getStopId();
12
+ if ($stopId !== null) {
13
+ $collection->addFieldToFilter('subscriber_id', array('lteq' => $this->getStopId()));
14
+ }
15
+
16
+ $this->_harvester = Mage::getModel('combine/harvest_subscribers')
17
+ ->setCollection($collection)
18
+ ->setDataSource($this->getDataSource())
19
+ ->harvest();
20
+
21
+ return parent::run();
22
+ }
23
+
24
+ public static function getCollection($storeId, $partition = null)
25
+ {
26
+ $collection = Mage::getResourceSingleton('newsletter/subscriber_collection')
27
+ ->addFieldToFilter('store_id', $storeId);
28
+
29
+ if($partition) {
30
+ $collection = parent::limitCollection($collection, $partition, 'subscriber_id');
31
+ }
32
+ return $collection;
33
+ }
34
+ }
app/code/community/Springbot/Services/Log/Installer.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Log_Installer extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ $setupModel = Mage::getModel('Springbot_Combine_Model_Resource_Setup');
8
+
9
+ $setupModel->resendInstallLog();
10
+ }
11
+ }
app/code/community/Springbot/Services/Log/Purchase.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Log_Purchase extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ $orderId = $this->getEntityId();
8
+ Springbot_Log::debug("Log purchase action for order_id : $orderId");
9
+ $purchase = Mage::getModel('sales/order')->load($orderId);
10
+
11
+ if(!$purchase->getId()) {
12
+ // @TODO create remote error logger?
13
+ throw new Exception("Purchase record not available in database!");
14
+ }
15
+
16
+ foreach($purchase->getAllVisibleItems() as $item) {
17
+ $this->_logEvent('purchase', array(
18
+ $this->_getAccessibleSku($item),
19
+ $item->getSku(),
20
+ $purchase->getIncrementId(),
21
+ $purchase->getStoreId(),
22
+ Mage::helper('combine')->checkCategoryIdSanity($this->_getCategoryId(), $item->getProductId()),
23
+ ));
24
+ }
25
+ return;
26
+ }
27
+
28
+ protected function _logEvent($action, $content)
29
+ {
30
+ $eventDatetime = date(Springbot_Boss::DATE_FORMAT);
31
+ $logContent = array($action, $eventDatetime);
32
+ $fHandle = fopen(Springbot_Boss::getEventHistoryFilename(), 'a');
33
+
34
+ fputcsv($fHandle, array_merge($logContent, $content));
35
+ fclose ($fHandle);
36
+ }
37
+
38
+
39
+ protected function _getCategoryId()
40
+ {
41
+ if($this->hasCategoryId()) {
42
+ return $this->getCategoryId();
43
+ }
44
+ return null;
45
+ }
46
+
47
+ protected function _getAccessibleSku($item)
48
+ {
49
+ return Mage::helper('combine/parser')->getAccessibleSkuFromSalesItem($item);
50
+ }
51
+ }
app/code/community/Springbot/Services/Post.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Springbot_Services_Post extends Springbot_Services_Abstract
4
+ {
5
+
6
+ public function getDataSource()
7
+ {
8
+ return Springbot_Boss::SOURCE_OBSERVER;
9
+ }
10
+ }
app/code/community/Springbot/Services/Post/Attribute.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Attribute extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $attribute = $this->loadAttribute();
8
+ $harvester = $this->_getAttributeSetHarvester();
9
+ $ids = $this->getAllAttributeSetIds();
10
+
11
+ if(($count = count($ids)) > 0) {
12
+ Springbot_Log::debug("{$count} related attribute sets found, saving!");
13
+ foreach($ids as $setId) {
14
+ $set = $this->loadAttributeSet($setId);
15
+ foreach($this->_getStoreIds() as $id) {
16
+ $harvester->setStoreId($id);
17
+ $harvester->push($set);
18
+ }
19
+ }
20
+ } else {
21
+ Springbot_Log::debug("No related attribute sets found");
22
+ }
23
+ $harvester->postSegment();
24
+ }
25
+
26
+ protected function _getStoreIds()
27
+ {
28
+ $stores = Mage::helper('combine/harvest')->getStoresToHarvest();
29
+ $ids = array();
30
+ foreach($stores as $store) {
31
+ $ids[] = $store->getStoreId();
32
+ }
33
+ return $ids;
34
+ }
35
+
36
+ protected function _getAttributeSetHarvester()
37
+ {
38
+ return Mage::getModel('combine/harvest_attributeSets');
39
+ }
40
+
41
+ public function loadAttributeSet($setId)
42
+ {
43
+ return Mage::getModel('eav/entity_attribute_set')->load($setId);
44
+ }
45
+
46
+ public function getAllAttributeSetIds()
47
+ {
48
+ return Mage::helper('combine/attributes')->getAllSetsForAttribute($this->getEntityId());
49
+ }
50
+
51
+ public function loadAttribute()
52
+ {
53
+ return Mage::getModel('eav/entity_attribute')->load($this->getEntityId());
54
+ }
55
+ }
app/code/community/Springbot/Services/Post/AttributeSet.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_AttributeSet extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $harvester = Mage::getModel('combine/harvest_attributeSets');
8
+
9
+ foreach($this->_getStoreIds() as $id) {
10
+ $harvester->setStoreId($id);
11
+ $harvester->push(Mage::getModel('eav/entity_attribute_set')->load($this->getEntityId()));
12
+ }
13
+ $harvester->postSegment();
14
+ }
15
+
16
+ protected function _getStoreIds()
17
+ {
18
+ $stores = Mage::helper('combine/harvest')->getStoresToHarvest();
19
+ $ids = array();
20
+ foreach($stores as $store) {
21
+ $ids[] = $store->getStoreId();
22
+ }
23
+ return $ids;
24
+ }
25
+ }
app/code/community/Springbot/Services/Post/Cart.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Cart extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $quoteId = $this->getEntityId();
8
+
9
+ Springbot_Log::debug("Posting quote $quoteId");
10
+
11
+ $store = Mage::getModel('core/store')->load($this->getStoreId());
12
+
13
+ // For some reason you have to set the store to load
14
+ // a quote, why??? Varien knows...
15
+ $quote = Mage::getModel('sales/quote')->setStore($store)->load($quoteId);
16
+
17
+ $parser = Mage::getModel('combine/parser_quote', $quote);
18
+ $quoteJson = $parser->toJson();
19
+
20
+ Mage::helper('combine')->apiPostWrapped('carts', json_decode($quoteJson), true);
21
+ }
22
+ }
app/code/community/Springbot/Services/Post/Category.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Category extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $category = Mage::getModel('catalog/category')->load($this->getEntityId());
8
+ $harvester = Mage::getModel('combine/harvest_categories');
9
+
10
+ foreach(Mage::helper('combine/harvest')->mapStoreIds($category) as $mapped) {
11
+ $harvester->push($mapped);
12
+ }
13
+
14
+ $harvester->postSegment();
15
+ }
16
+ }
app/code/community/Springbot/Services/Post/Customer.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Customer extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $harvester = Mage::getModel('combine/harvest_customers')->setDelete($this->getDelete());
8
+ $harvester->push(Mage::getModel('customer/customer')->load($this->getStartId()));
9
+ $harvester->postSegment();
10
+ }
11
+ }
app/code/community/Springbot/Services/Post/Json.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Json extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $file = Mage::getModel('combine/file_io');
8
+ $filename = $this->getFilename();
9
+
10
+ if($file->exists($filename)) {
11
+ $quoteJson = $file->read($filename);
12
+ $file->delete();
13
+ }
14
+
15
+ Mage::helper('combine')->apiPostWrapped($this->getPostMethod(), json_decode($quoteJson));
16
+ }
17
+ }
app/code/community/Springbot/Services/Post/Jsonstring.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Jsonstring extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ Mage::helper('combine')->apiPostWrapped(parent::getData('method'), parent::getData('json'));
8
+ }
9
+ }
10
+
11
+
12
+
app/code/community/Springbot/Services/Post/Product.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Product extends Springbot_Services_Post
4
+ {
5
+ protected $_harvester;
6
+
7
+ public function run()
8
+ {
9
+ $this->_harvester = Mage::getModel('combine/harvest_products');
10
+
11
+ $this->_aggregateProduct($this->getEntityId());
12
+
13
+ $this->_harvester->postSegment();
14
+ }
15
+
16
+ protected function _aggregateProduct($entityId)
17
+ {
18
+ $product = Mage::getModel('catalog/product')->load($entityId);
19
+
20
+ foreach(Mage::helper('combine/harvest')->mapStoreIds($product) as $mapped) {
21
+ $this->_harvester->push($mapped);
22
+ }
23
+
24
+ if($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
25
+ Springbot_Log::debug('Executing configurable callback save');
26
+ foreach(Mage::helper('combine/parser')->getChildProductIds($product) as $childId) {
27
+ $this->_aggregateProduct($childId);
28
+ }
29
+ }
30
+ }
31
+ }
app/code/community/Springbot/Services/Post/Purchase.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Purchase extends Springbot_Services_Post
4
+ {
5
+ protected $_purchase;
6
+
7
+ public function run()
8
+ {
9
+ $orderId = $this->getEntityId();
10
+ Springbot_Log::debug('Executing Purchase Method (at '.date(Springbot_Boss::DATE_FORMAT).') Order Number->' . $orderId);
11
+
12
+ $this->_purchase = Mage::getModel('sales/order')->load($orderId);
13
+ $redirectIds = $this->_getRedirectIds();
14
+
15
+ if(count($redirectIds)) {
16
+ $this->_createRedirectForOrder($redirectIds[0]);
17
+
18
+ $this->_purchase->setRedirectMongoId($redirectIds[0])
19
+ ->setRedirectMongoIds($redirectIds);
20
+ }
21
+
22
+ $harvester = Mage::getModel('combine/harvest_purchases');
23
+
24
+ $harvester->push($this->_purchase);
25
+ $harvester->postSegment();
26
+
27
+ if($this->_purchase->getCustomerIsGuest()) {
28
+ Mage::getModel('combine/harvest_guests')
29
+ ->push($this->_purchase)
30
+ ->postSegment();
31
+ }
32
+ }
33
+
34
+ protected function _createRedirectForOrder($redirectId)
35
+ {
36
+ Springbot_Log::debug("Creating order entry for redirect_id: {$redirectId}");
37
+
38
+ if($redirectId) {
39
+ $redirect = Mage::getResourceModel('combine/redirect_collection')
40
+ ->loadByKey($this->_purchase->getCustomerEmail(), $redirectId);
41
+
42
+ if($redirect) {
43
+ $redirectOrder = Mage::getModel('combine/redirect_order');
44
+
45
+ $redirectOrder->setData(array(
46
+ 'redirect_entity_id' => $redirect->getId(),
47
+ 'order_id' => $this->_purchase->getId(),
48
+ ));
49
+
50
+ $redirectOrder->insertIgnore();
51
+ }
52
+ }
53
+ }
54
+
55
+ protected function _getRedirectIds()
56
+ {
57
+ return isset($this->_data['redirect_ids']) ? (array) $this->_data['redirect_ids'] : array();
58
+ }
59
+ }
app/code/community/Springbot/Services/Post/Subscriber.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Post_Subscriber extends Springbot_Services_Post
4
+ {
5
+ public function run()
6
+ {
7
+ $harvester = Mage::getModel('combine/harvest_subscribers')->setDelete($this->getDelete());
8
+ $harvester->push(Mage::getModel('newsletter/subscriber')->load($this->getStartId()));
9
+ $harvester->postSegment();
10
+ }
11
+ }
app/code/community/Springbot/Services/Priority.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Priority
4
+ {
5
+ const HARVEST = 0;
6
+ const PARTITION = 1;
7
+ const SEGMENT = 2;
8
+ const CATEGORY = 3;
9
+ const LISTENER = 5;
10
+ const FAILED = 8;
11
+ }
app/code/community/Springbot/Services/Registry.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Registry
4
+ {
5
+ public static function getInstance($method)
6
+ {
7
+ $classname = self::_constantize($method);
8
+ Springbot_Log::debug("Creating instance of $classname");
9
+ try {
10
+ return new $classname();
11
+ } catch (Exception $e) {
12
+ throw new Exception("$classname not found!");
13
+ }
14
+ }
15
+
16
+ public static function parseOpts($opts)
17
+ {
18
+ Springbot_Log::debug("Parsing args");
19
+
20
+ if(isset($opts['s'])) {
21
+ $args['store_id'] = $opts['s'];
22
+ }
23
+
24
+ if(isset($opts['i'])) {
25
+ list($start, $stop) = array_pad(explode(':', $opts['i']), 2, null);
26
+ $args['entity_id'] = $start;
27
+ $args['start_id'] = $start;
28
+ $args['stop_id'] = $stop;
29
+ }
30
+
31
+ if(isset($opts['h'])) {
32
+ $args['halt_command'] = $opts['h'];
33
+ }
34
+
35
+ if(isset($opts['c'])) {
36
+ $args['class'] = $opts['c'];
37
+ }
38
+
39
+ if(isset($opts['v'])) {
40
+ $args['version'] = $opts['v'];
41
+ $args['harvest_id'] = $opts['v'];
42
+ }
43
+
44
+ if(isset($opts['r'])) {
45
+ $args['redirect_ids'] = $opts['r'];
46
+ }
47
+
48
+ if(isset($opts['j'])) {
49
+ $args['json'] = $opts['j'];
50
+ }
51
+
52
+ if(isset($opts['n'])) {
53
+ $args['filename'] = $opts['n'];
54
+ }
55
+
56
+ if(isset($opts['m'])) {
57
+ $args['post_method'] = $opts['m'];
58
+ }
59
+
60
+ if(isset($opts['p'])) {
61
+ $args['pid'] = $opts['p'];
62
+ }
63
+
64
+ $args['delete'] = isset($opts['d']);
65
+ $args['force'] = isset($opts['f']);
66
+ $args['is_foreman'] = isset($opts['o']);
67
+
68
+ return $args;
69
+ }
70
+
71
+ /**
72
+ * Take a colon-separated string and turns it into a classname
73
+ * in the Springbot_Services_ namespace
74
+ *
75
+ * @param string $method
76
+ * @return string
77
+ */
78
+ private static function _constantize($method)
79
+ {
80
+ $mods = array_map('ucfirst', explode(':', $method));
81
+ return 'Springbot_Services_' . implode('_', $mods);
82
+ }
83
+ }
app/code/community/Springbot/Services/Store/Finalize.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Store_Finalize extends Springbot_Services_Abstract
4
+ {
5
+
6
+ public function run()
7
+ {
8
+
9
+ $storeId = $this->getStoreId();
10
+
11
+ if ($store = Mage::getModel('core/store')->load($storeId)) {
12
+ $helper = Mage::helper('combine/store')->setStore($store);
13
+
14
+ Springbot_Log::printLine();
15
+ Springbot_Log::harvest('Store level harvesting complete for Store->'. $storeId .'/'. $helper->getSpringbotStoreId(), true);
16
+ Springbot_Log::printLine();
17
+
18
+ $api = Mage::getModel('combine/api');
19
+ $api->call('sync_status/' . $helper->getSpringbotStoreId(), '{"status":"synced"}');
20
+
21
+ $countResource = Mage::getResourceModel('combine/cron_count');
22
+ $countResource->clearStoreCounts($storeId);
23
+
24
+ Mage::getModel('core/config')->saveConfig('springbot/config/harvest_cursor', '0');
25
+ }
26
+ }
27
+
28
+ }
app/code/community/Springbot/Services/Store/Register.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Store_Register extends Springbot_Services_Abstract
4
+ {
5
+ const API_CLASS = 'stores';
6
+
7
+ protected $_guid;
8
+
9
+ public function run()
10
+ {
11
+ $guid = $this->_getGuid();
12
+
13
+ $response = $this->_query($guid, $this->getStoreArray($guid));
14
+
15
+ if ($response['status'] == 'ok' && isset($response['stores'])) {
16
+ $springbotStoreId = array_search($guid, $response['stores']);
17
+ $id = $this->getStoreId();
18
+ $vars = array(
19
+ 'store_guid' => $guid,
20
+ 'store_id' => $springbotStoreId,
21
+ 'security_token' => $this->_getSecurityToken()
22
+ );
23
+ $this->commitVars($vars, $id);
24
+ Mage::getConfig()->cleanCache();
25
+ }
26
+ }
27
+
28
+ public function getStoreArray($guid)
29
+ {
30
+ $helper = $this->_getHelper();
31
+ $storeUrl = $helper->getStoreUrl($this->getStoreId());
32
+ $store = $this->_getStore();
33
+
34
+ $storeDetail = array(
35
+ 'guid' => $guid,
36
+ 'url' => $storeUrl,
37
+ 'name' => $store->getName(),
38
+ 'logo_src' => Mage::getStoreConfig('design/header/logo_src'),
39
+ 'logo_alt_tag' => Mage::getStoreConfig('design/header/logo_alt'),
40
+ 'json_data' => array(
41
+ 'web_id' => $store->getWebsiteId(),
42
+ 'store_id' => $this->getStoreId(),
43
+ 'store_name' => $store->getName(),
44
+ 'store_code' => $store->getCode(),
45
+ 'store_active' => $store->getIsActive(),
46
+ 'store_url' => $storeUrl,
47
+ 'media_url' => Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA),
48
+ 'store_mail_address' => $this->_getStoreAddress(),
49
+ 'store_custsrv_email' => Mage::getStoreConfig('trans_email/ident_support/email'),
50
+ 'store_statuses' => $this->_getStoreStatuses($this->getStoreId())
51
+ ),
52
+ );
53
+
54
+ return $storeDetail;
55
+ }
56
+
57
+ public function commitVars($vars)
58
+ {
59
+ foreach ($vars as $key => $val)
60
+ {
61
+ $configKey = $this->_makeConfigKey($key, $this->getStoreId());
62
+ Springbot_Log::harvest('Committing Config Var ['. $configKey .']->'.$val);
63
+ Mage::getConfig()->saveConfig($configKey, $val, 'default', 0);
64
+ }
65
+ }
66
+
67
+ protected function _getStoreStatuses($storeId) {
68
+ return Mage::getModel('sales/order')->getConfig()->getStatuses();
69
+ }
70
+
71
+ protected function _getSecurityToken()
72
+ {
73
+ return Mage::helper('combine')->requestSecurityToken();
74
+ }
75
+
76
+ protected function _query($guid, $storeMetaData)
77
+ {
78
+ return Mage::helper('combine')->apiPostWrapped(self::API_CLASS, array($guid => $storeMetaData));
79
+ }
80
+
81
+ protected function _getGuid()
82
+ {
83
+ return Mage::helper('combine')->getStoreGuid($this->getStoreId());
84
+ }
85
+
86
+ protected function _getStore()
87
+ {
88
+ return Mage::getModel('core/store')->load($this->getStoreId());
89
+ }
90
+
91
+ protected function _getStoreAddress()
92
+ {
93
+ return str_replace(array("\n","\r"),"|",Mage::getStoreConfig('general/store_information/address'));
94
+ }
95
+
96
+ protected function _getHelper()
97
+ {
98
+ return Mage::helper('combine/harvest');
99
+ }
100
+
101
+ protected function _makeConfigKey($dataClass, $storeId = '')
102
+ {
103
+ $cKey = 'springbot/config/'.$dataClass;
104
+ if ($storeId != '') {
105
+ $cKey = $cKey.'_'.$storeId;
106
+ }
107
+ return $cKey;
108
+ }
109
+ }
app/code/community/Springbot/Services/Update/Abstract.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Update_Abstract
4
+ {
5
+ protected $_version;
6
+
7
+ const CHANNELS_XML = "channels.xml";
8
+ const CHANNEL_XML = "channel.xml";
9
+ const PACKAGES_XML = "packages.xml";
10
+ const RELEASES_XML = "releases.xml";
11
+ const PACKAGE_XML = "package.xml";
12
+ const EXT = "tgz";
13
+
14
+ protected $_channelUrl = 'http://connect20.magentocommerce.com/community';
15
+ protected $_package = 'Springbot';
16
+
17
+ public function get($uri = '')
18
+ {
19
+ $url = "{$this->_channelUrl}/{$this->_package}/{$uri}";
20
+ return $this->_getClient($url)->request();
21
+ }
22
+
23
+ protected function _getClient($url)
24
+ {
25
+ $this->_client = new Zend_Http_Client($url);
26
+ return $this->_client;
27
+ }
28
+
29
+ public function parse($xml)
30
+ {
31
+ try {
32
+ $xml = simplexml_load_string($xml);
33
+ } catch (Exception $e) {
34
+ throw new Exception ('Releases not valid XML! Please check connection.');
35
+ }
36
+ return $xml;
37
+ }
38
+
39
+ public function getVersion()
40
+ {
41
+ return $this->_version;
42
+ }
43
+
44
+ public function setVersion($version)
45
+ {
46
+ $this->_version = $version;
47
+ return $this;
48
+ }
49
+ }
app/code/community/Springbot/Services/Update/Connect.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Update_Connect extends Springbot_Services_Update_Abstract
4
+ {
5
+ protected $_allowedStability;
6
+
7
+ public function run()
8
+ {
9
+ try {
10
+ $this->_releases = $this->getConnectReleases();
11
+ $toInstall = $this->getReleaseToInstall();
12
+ } catch(Exception $e) {
13
+ Springbot_Log::error($e);
14
+ exit;
15
+ }
16
+ return $toInstall->v;
17
+ }
18
+
19
+ public function getConnectReleases()
20
+ {
21
+ $response = $this->get(self::RELEASES_XML);
22
+ $code = $response->getStatus();
23
+
24
+ if($code == 200) {
25
+ $xml = $response->getBody();
26
+ $parsed = $this->parse($xml);
27
+ $this->_releases = $parsed->r;
28
+ }
29
+
30
+ if(!$this->_releases) {
31
+ throw new Exception ("Server returned with status of {$code} when fetching releases. Please check connection.");
32
+ }
33
+ return $this->_releases;
34
+ }
35
+
36
+ public function getReleaseToInstall()
37
+ {
38
+ $version = $this->getVersion();
39
+ return $this->getRelease($version);
40
+ }
41
+
42
+ public function getRelease($version = null)
43
+ {
44
+ $releases = $this->getSortedReleases();
45
+
46
+ if(is_null($version)) {
47
+ return $releases[0];
48
+ } else {
49
+ foreach($releases as $release) {
50
+ if($release->v == $version) {
51
+ return $release;
52
+ }
53
+ }
54
+ }
55
+ throw new Exception('Release number not found!');
56
+ }
57
+
58
+ public function getLatest()
59
+ {
60
+ $releases = $this->getSortedReleases();
61
+ return $releases[0];
62
+ }
63
+
64
+ public function getLatestVersion()
65
+ {
66
+ if(!isset($this->_latest)) {
67
+ $this->_latest = $this->getLatest();
68
+ }
69
+ return $this->_latest->v;
70
+ }
71
+
72
+ public function getReleases()
73
+ {
74
+ if(!isset($this->_releases)) {
75
+ $this->_releases = $this->getConnectReleases();
76
+ }
77
+ return $this->_releases;
78
+ }
79
+
80
+ public function getSortedReleases()
81
+ {
82
+ $releases = array();
83
+ foreach($this->getReleases() as $release) {
84
+ if($this->allowStability($release)) {
85
+ $releases[] = $release;
86
+ }
87
+ }
88
+
89
+ if(count($releases) < 1) {
90
+ throw new Exception ('No releases found!');
91
+ }
92
+
93
+ usort($releases, array($this, '_sortReleasesCallback'));
94
+ return array_reverse($releases);
95
+ }
96
+
97
+ public function allowStability($release)
98
+ {
99
+ if($this->_getAllowedStability() == $release->s) {
100
+ return true;
101
+ } else if ($release->s == 'stable') {
102
+ return true;
103
+ }
104
+ return false;
105
+ }
106
+
107
+ protected function _getAllowedStability()
108
+ {
109
+ if(!$this->_allowedStability) {
110
+ $this->_allowedStability = Mage::getStoreConfig('springbot/config/stability');
111
+ }
112
+ return $this->_allowedStability;
113
+ }
114
+
115
+ protected function _sortReleasesCallback($a, $b)
116
+ {
117
+ return version_compare($a->v,$b->v);
118
+ }
119
+ }
app/code/community/Springbot/Services/Update/Downloader.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Update_Downloader extends Springbot_Services_Update_Abstract
4
+ {
5
+ protected $_version;
6
+
7
+ public function __construct($version)
8
+ {
9
+ $this->_version = $version;
10
+ }
11
+
12
+ public function run()
13
+ {
14
+ $checksum = $this->getRemoteContents('checksum');
15
+ $archive = $this->getRemoteContents('archive');
16
+
17
+ if($checksum !== md5($archive)) {
18
+ throw new Exception('Remote archive does not match checksum!');
19
+ }
20
+
21
+ return $this->putFile($archive);
22
+ }
23
+
24
+ public function putFile($archive)
25
+ {
26
+ $file = Mage::getModel('combine/file_io');
27
+ $file->write($this->_getFilename(), $archive);
28
+ return $file->getFilename();
29
+ }
30
+
31
+ public function getRemoteContents($type)
32
+ {
33
+ $response = $this->get($this->{'_get' . ucfirst($type) . 'Uri'}());
34
+
35
+ if($response->getStatus() == 200) {
36
+ $body = $response->getBody();
37
+ } else {
38
+ throw new Exception("Could not get {$type}!");
39
+ }
40
+ return $body;
41
+ }
42
+
43
+ public function setVersion($version)
44
+ {
45
+ $this->_version = $version;
46
+ return $this;
47
+ }
48
+
49
+ protected function _getFilename()
50
+ {
51
+ return "{$this->_package}-{$this->_version}.tgz";
52
+ }
53
+
54
+ protected function _getArchiveUri()
55
+ {
56
+ return "{$this->_version}/{$this->_package}-{$this->_version}.tgz";
57
+ }
58
+
59
+ protected function _getChecksumUri()
60
+ {
61
+ return "{$this->_version}/checksum";
62
+ }
63
+ }
app/code/community/Springbot/Services/Update/Installer.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Update_Installer extends Springbot_Services_Update_Abstract
4
+ {
5
+ protected $_package;
6
+
7
+ const DIRMODE = 0755;
8
+ const FILEMODE = 0644;
9
+
10
+ public function __construct(Springbot_Services_Update_Package $package = null)
11
+ {
12
+ if(is_null($package)) {
13
+ throw new Exception('Package object required!');
14
+ }
15
+ $this->_package = $package;
16
+ }
17
+
18
+ public function run()
19
+ {
20
+ try {
21
+ foreach($this->getPackageContents() as $file) {
22
+ Springbot_Log::info("Put $file");
23
+ $this->putFile($file);
24
+ }
25
+ } catch (Exception $e) {
26
+ Springbot_Log::error($e);
27
+ }
28
+ $this->_package->cleanUp();
29
+ }
30
+
31
+ public function getPackageContents()
32
+ {
33
+ return $this->_package->getContents();
34
+ }
35
+
36
+ public function getInstallPath($file)
37
+ {
38
+ $realPath = realpath($file);
39
+ return empty($realPath) ? Mage::getBaseDir() . DS . $file : $realPath;
40
+ }
41
+
42
+ public function putFile($file)
43
+ {
44
+ $source = $this->_package->getTempFilePath($file);
45
+ $dir = dirname($this->getInstallPath($file));
46
+ $dest = $dir . DS . basename($file);
47
+ @mkdir($dir, self::DIRMODE, true);
48
+ if(is_file($source)) {
49
+ Springbot_Log::info("Copy $source to $dest");
50
+ @copy($source, $dest);
51
+ @chmod($dest, self::FILEMODE);
52
+ } else {
53
+ Springbot_Log::info("Creating directory $source");
54
+ @mkdir($source);
55
+ }
56
+ }
57
+ }
app/code/community/Springbot/Services/Update/Package.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Update_Package extends Springbot_Services_Update_Abstract
4
+ {
5
+ const PACKAGE_XML = 'package.xml';
6
+
7
+ protected $_archivePath;
8
+ protected $_unpackedPath;
9
+ protected $_contents;
10
+ protected $_targetMap = array(
11
+ "magelocal" => "./app/code/local",
12
+ "magecommunity" => "./app/code/community",
13
+ "magecore" => "./app/code/core",
14
+ "magedesign" => "./app/design",
15
+ "mageetc" => "./app/etc",
16
+ "magelib" => "./lib",
17
+ "magelocale" => "./app/locale",
18
+ "magemedia" => "./media",
19
+ "mageskin" => "./skin",
20
+ "mageweb" => ".",
21
+ "magetest" => "./tests",
22
+ "mage" => ".",
23
+ );
24
+
25
+ public function __construct($path = null)
26
+ {
27
+ if(!$path) {
28
+ throw new Exception('Path requried!');
29
+ }
30
+ $this->_archivePath = $path;
31
+ }
32
+
33
+ public function unpack()
34
+ {
35
+ try {
36
+ $this->_unpackedPath = $this->_readyPath();
37
+ $this->_cmdTar($this->getArchivePath(), $this->_unpackedPath);
38
+ } catch (Exception $e) {
39
+ Springbot_Log::error($e);
40
+ }
41
+ }
42
+
43
+ public function cleanUp()
44
+ {
45
+ @unlink($this->_archivePath);
46
+ $this->_rmDir($this->_unpackedPath);
47
+ }
48
+
49
+ public function getContents()
50
+ {
51
+ if(!isset($this->_contents)) {
52
+ $this->_prepareContents();
53
+ }
54
+ return $this->_contents;
55
+ }
56
+
57
+ public function getTempFilePath($file)
58
+ {
59
+ return realpath($this->getTempOutPath() . DS . $file);
60
+ }
61
+
62
+ public function getTempOutPath()
63
+ {
64
+ return Mage::getBaseDir('tmp') . DS . $this->getVersionNumber();
65
+ }
66
+
67
+ public function getVersionNumber()
68
+ {
69
+ return basename($this->_archivePath, '.' . self::EXT);
70
+ }
71
+
72
+ public function getArchivePath()
73
+ {
74
+ return $this->_archivePath;
75
+ }
76
+
77
+ public function getUnpackedPath()
78
+ {
79
+ return $this->_unpackedPath;
80
+ }
81
+
82
+ protected function _rmDir($dirPath)
83
+ {
84
+ foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) {
85
+ $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname());
86
+ }
87
+ rmdir($dirPath);
88
+ }
89
+
90
+ protected function _prepareContents()
91
+ {
92
+ $xml = $this->_getPackageXml();
93
+ if(!isset($xml->contents->target)) {
94
+ return $this->_contents;
95
+ }
96
+ foreach($xml->contents->target as $target) {
97
+ $targetUri = $this->_getTargetPath($target['name']);
98
+ $this->_getList($target, $targetUri);
99
+ }
100
+ return $this->_contents;
101
+ }
102
+
103
+ protected function _getTargetPath($name)
104
+ {
105
+ $name = (string) $name;
106
+ return isset($this->_targetMap[$name]) ? $this->_targetMap[$name] : '';
107
+ }
108
+
109
+ protected function _getList($parent, $path)
110
+ {
111
+ if (count($parent) == 0) {
112
+ $this->_contents[] = $path;
113
+ } else {
114
+ foreach($parent as $_content) {
115
+ $this->_getList($_content, ($path ? $path . DS : '') . $_content['name']);
116
+ }
117
+ }
118
+ }
119
+
120
+ protected function _readyPath()
121
+ {
122
+ $path = $this->getTempOutPath();
123
+ @mkdir($path, 0777, true);
124
+ if(!is_writable($path)) {
125
+ throw new Exception('Created extraction directory not writable!');
126
+ }
127
+ return $path;
128
+ }
129
+
130
+ protected function _cmdTar($file, $out)
131
+ {
132
+ Springbot_Boss::spawn("tar -zxf $file -C $out");
133
+ if($this->_empty($out)) {
134
+ throw new Exception('Tar empty!');
135
+ }
136
+ return $out;
137
+ }
138
+
139
+ protected function _empty($dir)
140
+ {
141
+ return !count(glob("$dir/*"));
142
+ }
143
+
144
+ protected function _getPackageXml()
145
+ {
146
+ return simplexml_load_file($this->getUnpackedPath() . DS . self::PACKAGE_XML);
147
+ }
148
+ }
app/code/community/Springbot/Services/Work/Cleanup.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Work_Cleanup extends Springbot_Services_Abstract
4
+ {
5
+ const UNLOCK_AFTER_X_HOURS = 24;
6
+
7
+ public function run()
8
+ {
9
+ $this->_unlockOrphanedRows();
10
+ $this->_unlockForgottenJobs(self::UNLOCK_AFTER_X_HOURS);
11
+ }
12
+
13
+ protected function _unlockOrphanedRows()
14
+ {
15
+ Springbot_Log::debug("Unlocking orphaned rows");
16
+ $queueDb = Mage::getResourceModel('combine/cron_queue');
17
+ $status = Mage::getModel('combine/cron_manager_status');
18
+ $pids = $status->getActiveWorkerPids();
19
+ $queueDb->unlockOrphanedRows($pids);
20
+ }
21
+
22
+ protected function _unlockForgottenJobs($hoursOld)
23
+ {
24
+ Springbot_Log::debug("Unlocking forgotten jobs");
25
+ $queueDb = Mage::getResourceModel('combine/cron_queue');
26
+ $queueDb->unlockOldRows($hoursOld);
27
+ }
28
+ }
app/code/community/Springbot/Services/Work/Manager.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Work_Manager extends Springbot_Services_Abstract
4
+ {
5
+ const WORKMANAGER_FILENAME = 'springbot-workmanager';
6
+ const WORKER_PREFIX = 'springbotworker-';
7
+ const SPAWN_WORKER_SECONDS = 2;
8
+ const WORKMANAGER_MAX_TIME = 50;
9
+
10
+ protected $_foremanQueued = false;
11
+
12
+ public function run()
13
+ {
14
+ Springbot_Log::debug("Starting work manager");
15
+ if (!$this->_managerRunning()) {
16
+
17
+ $filename = Mage::getBaseDir('tmp') . DS . self::WORKMANAGER_FILENAME;
18
+ file_put_contents($filename, getmypid());
19
+
20
+ if (file_exists($filename)) {
21
+ if (!$maxWorkers = Mage::getStoreConfig('springbot/advanced/worker_count')) {
22
+ $maxWorkers = 1;
23
+ }
24
+
25
+ $start = time();
26
+ do {
27
+ $currentWorkerCount = $this->_getWorkerCount();
28
+ if(!$this->_foremanRunning()) {
29
+ $this->_foremanQueued = true;
30
+ Springbot_Boss::internalCallback('work:runner', array('o' => true));
31
+ }
32
+ else if($currentWorkerCount < $maxWorkers) {
33
+ Springbot_Boss::internalCallback('work:runner');
34
+ }
35
+ sleep(self::SPAWN_WORKER_SECONDS);
36
+ $this->_verifyWorkersRunning();
37
+ $currentWorkerCount = $this->_getWorkerCount();
38
+ $elapsedTime = time() - $start;
39
+ } while (($elapsedTime < self::WORKMANAGER_MAX_TIME) && ($currentWorkerCount > 0) && $this->_hasJobs());
40
+ unlink($filename);
41
+ }
42
+
43
+ if($this->_hasJobs()) {
44
+ Springbot_Log::debug("Jobs still queued, restarting manager");
45
+ Springbot_Boss::startWorkManager();
46
+ } else {
47
+ Springbot_Log::debug("No more jobs found. Exiting. Manager will restart on next checkin.");
48
+ }
49
+
50
+ }
51
+ }
52
+
53
+ public function cleanup()
54
+ {
55
+ $this->_managerRunning();
56
+ $this->_verifyWorkersRunning();
57
+ }
58
+
59
+ public function hasWorkers()
60
+ {
61
+ return $this->_getWorkerCount() > 0 && !$this->_managerRunning();
62
+ }
63
+
64
+ private function _managerRunning()
65
+ {
66
+ $filename = Mage::getBaseDir('tmp') . DS . self::WORKMANAGER_FILENAME;
67
+ if (file_exists($filename)) {
68
+ $pid = file_get_contents($filename);
69
+ if (!file_exists("/proc/{$pid}")) {
70
+ unlink($filename);
71
+ return false;
72
+ }
73
+ else {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ }
79
+
80
+ private function _foremanRunning()
81
+ {
82
+ if($this->_foremanQueued) { return true; }
83
+
84
+ $files = @glob(Mage::getBaseDir('tmp') . DS . 'springbotworkerforeman*');
85
+ return count($files) > 0;
86
+ }
87
+
88
+ private function _verifyWorkersRunning()
89
+ {
90
+ foreach ($this->_getWorkerFiles() as $workerFile) {
91
+ list($frontName, $pid) = explode('-', basename($workerFile));
92
+ if (!file_exists("/proc/{$pid}")) {
93
+ Springbot_Log::debug("Procfile not found, removing work file for pid => $pid");
94
+ unlink($workerFile);
95
+ }
96
+ }
97
+ }
98
+
99
+ private function _getWorkerCount()
100
+ {
101
+ return count($this->_getWorkerFiles());
102
+ }
103
+
104
+ private function _getWorkerFiles()
105
+ {
106
+ return glob(Mage::getBaseDir('tmp') . DS . 'springbotworker*');
107
+ }
108
+
109
+ private function _hasJobs()
110
+ {
111
+ return Mage::getModel('combine/cron_queue')->getCollection()->hasJobs();
112
+ }
113
+ }
app/code/community/Springbot/Services/Work/Report.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Work_Report extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ $keyUpper = ucwords($this->getClass());
8
+ $countModel = Mage::getModel('combine/cron_count');
9
+ $count = $countModel->getProcessedCount($this->getStoreId(), $this->getHarvestId(), $this->getClass());
10
+ $springbotStoreId = Mage::helper('combine/harvest')->getSpringbotStoreId($this->getStoreId());
11
+ $countModel->setCompletedTime($this->getStoreId(), $this->getHarvestId(), $this->getClass());
12
+ $startTime = $countModel->getEntityStartTime($this->getStoreId(), $this->getHarvestId(), $this->getClass());
13
+ $completedTime = $countModel->getEntityCompletedTime($this->getStoreId(), $this->getHarvestId(), $this->getClass());
14
+ $params = array(
15
+ 'store_id' => $springbotStoreId,
16
+ 'type' => $keyUpper,
17
+ 'sent' => $count,
18
+ 'started' => $startTime,
19
+ 'completed' => $completedTime,
20
+ );
21
+
22
+ Mage::helper('combine/harvest')->reportHarvestCount($params, $this->getHarvestId());
23
+ Springbot_Log::remote("Harvested '{$count} {$keyUpper}s from store " . $this->getStoreId());
24
+ }
25
+
26
+ }
app/code/community/Springbot/Services/Work/Restart.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Work_Restart extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ $status = Mage::getModel('combine/cron_manager_status');
8
+
9
+ if($this->getForce()) {
10
+ $this->_getStatus()->removeWorkBlocker();
11
+ }
12
+
13
+ if($status->isActive()) {
14
+ $status->haltManager();
15
+ }
16
+ Springbot_Boss::startWorkManager();
17
+ }
18
+
19
+ }
app/code/community/Springbot/Services/Work/Runner.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Work_Runner extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ if ($this->getIsForeman()) {
8
+ $filename = Mage::getBaseDir('tmp') . DS . 'springbotworkerforeman-' . getmypid();
9
+ }
10
+ else {
11
+ $filename = Mage::getBaseDir('tmp') . DS . 'springbotworker-' . getmypid();
12
+ }
13
+
14
+ Springbot_Log::debug("Creating workerfile : $filename");
15
+
16
+ file_put_contents($filename, time());
17
+
18
+ if(file_exists($filename)) {
19
+ if($this->hasEntityId()) {
20
+ Mage::getModel('combine/cron_worker')->run($this->getIsForeman(), $this->getEntityId());
21
+ }
22
+ else {
23
+ Mage::getModel('combine/cron_worker')->run($this->getIsForeman());
24
+ }
25
+
26
+ Springbot_Log::debug('Worker ' . getmypid() . ' spinning down');
27
+
28
+ @unlink($filename);
29
+ }
30
+ }
31
+
32
+ }
app/code/community/Springbot/Services/Work/Stop.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Services_Work_Stop extends Springbot_Services_Abstract
4
+ {
5
+ public function run()
6
+ {
7
+ if($this->getForce() === true) {
8
+ $this->_getStatus()->issueWorkBlocker();
9
+ }
10
+
11
+ do{
12
+ if($pid = $this->getPid()) {
13
+ Springbot_Log::debug("Issuing kill command for manager pid: {$pid}");
14
+ Springbot_Boss::spawn('kill ' . $pid);
15
+ } else {
16
+ Springbot_Log::debug("No active manager found, skipping");
17
+ }
18
+
19
+ if($pids = $this->_getStatus()->getActiveWorkerPids()) {
20
+ foreach($pids as $pid) {
21
+ Springbot_Log::debug("Issuing kill command for worker pid: {$pid}");
22
+ Springbot_Boss::spawn('kill ' . $pid);
23
+ }
24
+ } else {
25
+ Springbot_Log::debug("No active workers found, skipping");
26
+ }
27
+
28
+ $this->_getManager()->cleanup();
29
+
30
+ sleep(2); // ensure that we don't get jobs spinning up
31
+ } while ($this->_getManager()->hasWorkers());
32
+ }
33
+
34
+ public function getPid()
35
+ {
36
+ if(isset($this->_data['pid'])) {
37
+ return parent;
38
+ } else {
39
+ return $this->_getStatus()->getPid();
40
+ }
41
+ }
42
+
43
+ protected function _getManager()
44
+ {
45
+ return new Springbot_Services_Work_Manager();
46
+ }
47
+ }
app/code/community/Springbot/Shadow/Helper/Listeners.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Shadow_Helper_Listeners extends Mage_Core_Helper_Abstract
4
+ {
5
+ public function getListenerIds()
6
+ {
7
+ return explode(',', Mage::getStoreConfig('springbot/config/email_selector'));
8
+ }
9
+
10
+ public function getListenerClasses()
11
+ {
12
+ return explode(',', Mage::getStoreConfig('springbot/config/email_selector_classes'));
13
+ }
14
+
15
+ public function getBaseUrlNoProtocol()
16
+ {
17
+ return preg_replace('/https?:/','', Mage::getBaseUrl());
18
+ }
19
+ }
20
+
21
+
app/code/community/Springbot/Shadow/Model/Listeners/Observer.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Visitor Shadow Event Listener
4
+ *
5
+ * @version v1.0.1 - 12/28/2012
6
+ *
7
+ * @category Magento Intergations
8
+ * @package springbot
9
+ * @author William Seitz
10
+ * @division SpringBot Integration Team
11
+ * @support magentosupport@springbot.com
12
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
13
+ *
14
+ */
15
+ class Springbot_Shadow_Model_Listeners_Observer {
16
+
17
+ const TOKEN_DELIMITER = '%7';
18
+ const COOKIE_NAME = 'springbot_redirect_queue';
19
+ const SB_TRACKABLES_COOKIE = '_sbtk';
20
+ const MAXIMUM_IDS_SAVED = 32;
21
+
22
+ /**
23
+ * Order of priority desc - "there can only be one!"
24
+ */
25
+ protected $_redirectIds = array(
26
+ 'sb',
27
+ 'redirect_mongo_id',
28
+ );
29
+
30
+ public function post($observer)
31
+ {
32
+ try {
33
+ Mage::helper('combine')->setLastCategoryId();
34
+ } catch (Exception $e) {
35
+ Mage::logException($e);
36
+ }
37
+ }
38
+
39
+ public function escort($observer)
40
+ {
41
+ if ($quoteId = Mage::app()->getRequest()->getParam('quote_id')) {
42
+ $suppliedSecurityHash = Mage::app()->getRequest()->getParam('sec_key');
43
+ Mage::helper('combine/cart')->setQuote($quoteId, $suppliedSecurityHash);
44
+ }
45
+
46
+ try {
47
+ // Set springbot redirect queue
48
+ if($param = $this->_getParam()) {
49
+ $this->_setSpringbotRedirectQueueCookie($param);
50
+ }
51
+
52
+ // Set sb trackables cookie
53
+ if($trackables = $this->getTrackables()) {
54
+ $this->_setSbTrackablesCookie($trackables);
55
+ }
56
+
57
+ $storeId = Mage::app()->getStore()->getStoreId();
58
+
59
+ $this->runHealthcheck($storeId);
60
+
61
+ $this->runQueueCleanup();
62
+
63
+ } catch (Exception $e) {
64
+ Mage::logException($e);
65
+ }
66
+ return;
67
+ }
68
+
69
+ public function runQueueCleanup()
70
+ {
71
+ // global scope, no need for store_id
72
+ // run every hour
73
+ if (Springbot_Shadow_Model_Timer::fire('cleanup', 0, 60)) {
74
+ Springbot_Boss::internalCallback('work:cleanup');
75
+ }
76
+ }
77
+
78
+ public function runHealthcheck($storeId)
79
+ {
80
+ if (Springbot_Shadow_Model_Timer::fire('healthcheck', $storeId)) {
81
+ $this->querySpringbot($storeId);
82
+ if (!Springbot_Boss::isCron()) {
83
+ Springbot_Boss::startWorkManager();
84
+ }
85
+ }
86
+ }
87
+
88
+ public function getTrackables()
89
+ {
90
+ $params = $this->_getParams();
91
+ $origParams = Mage::helper('combine/trackable')->getTrackables();
92
+ $sbParams = $origParams ? clone $origParams : new stdClass();
93
+
94
+ foreach($params as $param => $value) {
95
+ if(preg_match('/^sb_/', $param)) {
96
+ Springbot_Log::debug("Assigning $param from url with $value");
97
+ $sbParams->$param = $value;
98
+ }
99
+ }
100
+ $this->_ensureHttpReferer($sbParams);
101
+ return !Mage::helper('combine')->isEmpty($sbParams) && $sbParams != $origParams ? $sbParams : false;
102
+ }
103
+
104
+ protected function _getParam()
105
+ {
106
+ $params = Mage::app()->getRequest()->getParams();
107
+ foreach($this->_redirectIds as $id) {
108
+ if(isset($params[$id])) {
109
+ return $params[$id];
110
+ }
111
+ }
112
+ }
113
+
114
+ protected function _setSbTrackablesCookie($params)
115
+ {
116
+ if(!Mage::helper('combine')->isEmpty($params)) {
117
+ $encoded = base64_encode(json_encode($params));
118
+ $this->_setCookie(self::SB_TRACKABLES_COOKIE, $encoded);
119
+ }
120
+ }
121
+
122
+ protected function _ensureHttpReferer(&$params)
123
+ {
124
+ if(!isset($params->sb_referer_host) && !$this->_hasSbReferHost()) {
125
+ if(isset($_SERVER['HTTP_REFERER']) && isset($_SERVER['HTTP_HOST'])) {
126
+ $host = $_SERVER['HTTP_HOST'];
127
+ $parsed = parse_url($_SERVER['HTTP_REFERER']);
128
+ $referer = isset($parsed['host']) ? $parsed['host'] : null;
129
+
130
+ if($referer != $host) {
131
+ Springbot_Log::debug("refered by $referer");
132
+ if($host) {
133
+ $params->sb_referer_host = $referer;
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ protected function _hasSbReferHost()
141
+ {
142
+ $params = Mage::helper('combine/trackable')->getTrackables();
143
+ return isset($params->sb_referer_host);
144
+ }
145
+
146
+ protected function _setSpringbotRedirectQueueCookie($param)
147
+ {
148
+ $redirectQueue = Mage::getModel('core/cookie')->get(self::COOKIE_NAME);
149
+ $cookieValues = explode(self::TOKEN_DELIMITER,$redirectQueue);
150
+
151
+ if (count($cookieValues) >= self::MAXIMUM_IDS_SAVED) {
152
+ $oldestValue = array_shift($cookieValues);
153
+ }
154
+
155
+ if (end($cookieValues) != $param) {
156
+ $cookieValues[] = $param;
157
+ }
158
+
159
+ $redirectQueue = implode(self::TOKEN_DELIMITER, $cookieValues);
160
+
161
+ $this->_setCookie(self::COOKIE_NAME, $redirectQueue);
162
+ }
163
+
164
+ protected function _setCookie($name, $value)
165
+ {
166
+ Springbot_Log::debug("Saving cookie $name : $value");
167
+
168
+ Mage::getModel('core/cookie')->set(
169
+ $name,
170
+ $value,
171
+ strtotime('+365 days'),
172
+ '/', // path
173
+ null, // domain
174
+ null, // secure
175
+ false // httpOnly
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Query Springbot api healthcheck when running
181
+ * in standard / not cron mode.
182
+ */
183
+ private function querySpringbot($storeId)
184
+ {
185
+ Springbot_Boss::internalCallback('cmd:healthcheck', array('s' => $storeId));
186
+ }
187
+
188
+ protected function _getParams()
189
+ {
190
+ return Mage::app()->getRequest()->getParams();
191
+ }
192
+
193
+ protected function _isCron()
194
+ {
195
+ return Springbot_Boss::isCron();
196
+ }
197
+ }
app/code/community/Springbot/Shadow/Model/Timer.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Shadow_Model_Timer
4
+ {
5
+ const DEFAULT_POLLING_INTERVAL = 5; // minutes
6
+
7
+ protected $_task;
8
+ protected $_storeId;
9
+ protected $_interval;
10
+
11
+ public function __construct($task, $storeId, $interval)
12
+ {
13
+ $this->_task = $task;
14
+ $this->_storeId = $storeId;
15
+ $this->_interval = is_null($interval) ? $this->_getQueryInterval() : $interval;
16
+ }
17
+
18
+ public static function fire($task, $storeId, $interval = null)
19
+ {
20
+ $ins = new Springbot_Shadow_Model_Timer($task, $storeId, $interval);
21
+ if($ins->doRunTask()) {
22
+ Springbot_Log::debug("Firing $task for store_id: $storeId");
23
+ $ins->_setCache(time());
24
+ return true;
25
+ } else {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ public function doRunTask()
31
+ {
32
+ $intervalDiff = (time() - $this->_getCache()) / 60;
33
+ return $intervalDiff > $this->_interval;
34
+ }
35
+
36
+ protected function _getQueryInterval()
37
+ {
38
+ $interval = Mage::getStoreConfig('springbot/config/query_interval');
39
+ if(empty($interval) || !isset($interval)) {
40
+ $interval = self::DEFAULT_POLLING_INTERVAL;
41
+ }
42
+ return $interval;
43
+ }
44
+
45
+ protected function _getCache()
46
+ {
47
+ $fname = $this->_getFilename();
48
+ $lastFired = 0;
49
+
50
+ if(file_exists($fname)) {
51
+ if($fHandle = $this->_openCacheWithLock($fname,'r')) {
52
+ $lastFired = trim(fread($fHandle, 128));
53
+ fclose($fHandle);
54
+ }
55
+ }
56
+ return $lastFired;
57
+ }
58
+
59
+ protected function _setCache($value)
60
+ {
61
+ if ($fHandle = $this->_openCacheWithLock($this->_getFilename(),'w')) {
62
+ fwrite($fHandle, $value."\n");
63
+ fclose($fHandle);
64
+ }
65
+ return $this;
66
+ }
67
+
68
+ protected function _getFilename()
69
+ {
70
+ return Mage::getBaseDir('tmp') . DS . 'sb-cache-' . $this->_task . '-' . $this->_storeId . '.dat';
71
+ }
72
+
73
+ private function _openCacheWithLock($fileName, $mode = 'w')
74
+ {
75
+ return fopen($fileName, $mode);
76
+ }
77
+ }
app/code/community/Springbot/Shadow/controllers/IndexController.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Springbot_Shadow_IndexController extends Mage_Core_Controller_Front_Action
3
+ {
4
+ private $_acceptedParams = array('email');
5
+
6
+ public function indexAction()
7
+ {
8
+ if ($quote = Mage::getSingleton('checkout/session')->getQuote()) {
9
+ $params = $this->getRequest()->getParams();
10
+ foreach ($params as $paramName => $paramValue) {
11
+ if ($this->_isValidParam($paramName)) {
12
+ if ($paramName == 'email') {
13
+ $sessionQuoteExists = $quote->hasEntityId();
14
+ if ($quote = Mage::getModel('checkout/session')->getQuote()) {
15
+ // If there is no email address associated with the quote, check to see if one exists from our js listener
16
+ if (!$quote->getCustomerEmail()) {
17
+ $quote->setCustomerEmail($paramValue);
18
+ $quote->save();
19
+ }
20
+ }
21
+ if (!$sessionQuoteExists) {
22
+ Mage::getSingleton('checkout/session')->setQuoteId($quote->getId());
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+
29
+ $this->loadLayout()->getLayout()->getBlock('root')->setTemplate('shadow/emailupdate.phtml');
30
+ $this->_initLayoutMessages('core/session');
31
+ $this->renderLayout();
32
+ }
33
+
34
+ private function _isValidParam($paramName) {
35
+ return in_array($paramName, $this->_acceptedParams);
36
+ }
37
+
38
+ }
app/code/community/Springbot/Shadow/etc/config.xml ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * @category springbot
5
+ * @package springbot_Shadow
6
+ * @author springbot integration team
7
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+ */
9
+ -->
10
+ <config>
11
+ <frontend>
12
+
13
+ <models>
14
+ <Springbot_Shadow>
15
+ <class>Springbot_Shadow_Model</class>
16
+ </Springbot_Shadow>
17
+ </models>
18
+
19
+ <routers>
20
+ <shadow>
21
+ <use>standard</use>
22
+ <args>
23
+ <module>Springbot_Shadow</module>
24
+ <frontName>springbot_update</frontName>
25
+ </args>
26
+ </shadow>
27
+ </routers>
28
+
29
+
30
+ <layout>
31
+ <updates>
32
+ <shadow>
33
+ <file>shadow.xml</file>
34
+ </shadow>
35
+ </updates>
36
+ </layout>
37
+
38
+ <events>
39
+ <controller_action_predispatch>
40
+ <observers>
41
+ <springbot_shadow_observer>
42
+ <type>singleton</type>
43
+ <class>Springbot_Shadow_Model_Listeners_Observer</class>
44
+ <method>escort</method>
45
+ </springbot_shadow_observer>
46
+ </observers>
47
+ </controller_action_predispatch>
48
+ </events>
49
+ <events>
50
+ <controller_action_postdispatch>
51
+ <observers>
52
+ <springbot_shadow_observer>
53
+ <type>singleton</type>
54
+ <class>Springbot_Shadow_Model_Listeners_Observer</class>
55
+ <method>post</method>
56
+ </springbot_shadow_observer>
57
+ </observers>
58
+ </controller_action_postdispatch>
59
+ </events>
60
+
61
+
62
+ </frontend>
63
+
64
+ <global>
65
+ <helpers>
66
+ <shadow>
67
+ <class>Springbot_Shadow_Helper</class>
68
+ </shadow>
69
+ </helpers>
70
+ </global>
71
+
72
+
73
+
74
+ </config>
app/code/community/Springbot/Util/Caller.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Util_Caller
4
+ {
5
+ public $class;
6
+ public $line;
7
+ public $method;
8
+ public $call_type;
9
+
10
+ private static $_trace;
11
+
12
+ public function __construct($class, $method, $type, $line)
13
+ {
14
+ $this->class = $class;
15
+ $this->method = $method;
16
+ $this->call_type = $type;
17
+ $this->line = $line;
18
+ }
19
+
20
+ /**
21
+ * This weird little method returns an instance of this class
22
+ * populated with data retrived from the backtrace inspection.
23
+ *
24
+ * @param $depth int
25
+ * @return Springbot_Util_Caller
26
+ */
27
+ public static function find($depth = 1)
28
+ {
29
+ self::$_trace = debug_backtrace();
30
+
31
+ // Get the class that is asking for who awoke it
32
+ $caller = self::$_trace[$depth];
33
+ $called = self::_getCalledAt($depth);
34
+
35
+ return new Springbot_Util_Caller(
36
+ self::_safeGet($caller, 'class'),
37
+ self::_safeGet($caller, 'function'),
38
+ self::_safeGet($caller, 'type'),
39
+ self::_safeGet($called, 'line')
40
+ );
41
+ }
42
+
43
+ protected static function _safeGet($array, $key)
44
+ {
45
+ return isset($array[$key]) ? $array[$key] : null;
46
+ }
47
+
48
+ private static function _getCalledAt($depth)
49
+ {
50
+ return self::_safeGet(self::$_trace, $depth - 1);
51
+ }
52
+
53
+ private static function _getCaller($depth)
54
+ {
55
+ }
56
+ }
app/code/community/Springbot/Util/Categories.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Util_Categories
4
+ {
5
+ protected $_product;
6
+ protected $_paths = array();
7
+ protected $_pathsBuilt = false;
8
+
9
+ public function __construct($product)
10
+ {
11
+ $this->_product = $product;
12
+ }
13
+
14
+ public static function forProduct($product)
15
+ {
16
+ return new Springbot_Util_Categories($product);
17
+ }
18
+
19
+ public function getRoots()
20
+ {
21
+ $roots = array();
22
+ foreach($this->_getPaths() as $path) {
23
+ if(isset($path[2])) {
24
+ $roots[] = $path[2];
25
+ }
26
+ }
27
+ return array_values(array_unique($roots));
28
+ }
29
+
30
+ public function getAll()
31
+ {
32
+ $categories = array();
33
+ foreach($this->_getPaths() as $path) {
34
+ $categories = array_merge($path, $categories);
35
+ }
36
+ sort($categories);
37
+ return array_values(array_unique($categories));
38
+ }
39
+
40
+ protected function _getPaths()
41
+ {
42
+ if(!$this->_pathsBuilt) {
43
+ $_paths = $this->_product->getCategoryCollection()->getColumnValues('path');
44
+ foreach($_paths as $_path) {
45
+ $path = explode('/', $_path);
46
+ if(count($path) > 2) {
47
+ $this->_paths[] = $path;
48
+ }
49
+ }
50
+ $this->_pathsBuild = true;
51
+ }
52
+ return $this->_paths;
53
+ }
54
+ }
app/code/community/Springbot/Util/Log/Rollover.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Util_Log_Rollover
4
+ {
5
+ protected $_globbed = array();
6
+
7
+ public function reset()
8
+ {
9
+ $this->_globbed = array();
10
+ }
11
+
12
+ public function ensureLogSize()
13
+ {
14
+ Springbot_Log::debug('Ensure log size');
15
+
16
+ foreach($this->getLogFilenames() as $filename) {
17
+ if($this->sizeLimitReached($filename)) {
18
+ Springbot_Log::debug('File limit reached for ' . $filename);
19
+ $this->rolloverLog($filename);
20
+ }
21
+ }
22
+ }
23
+
24
+ public function rolloverLog($initFilename)
25
+ {
26
+ $filename = $this->_qualify($initFilename);
27
+ Springbot_Log::debug("Rolling log for $filename");
28
+ if(file_exists($filename)) {
29
+ Springbot_Log::debug('Rolling over ' . $filename);
30
+ $id = $this->_getNextLogId($filename);
31
+ rename($filename, $filename . '.' . $id);
32
+ Springbot_Log::release($initFilename);
33
+ }
34
+ }
35
+
36
+ public function expireLogs()
37
+ {
38
+ Springbot_Log::debug('Check expire time');
39
+
40
+ foreach($this->getLogFilenames() as $filename) {
41
+ foreach($this->getGlobbedFilenames($filename) as $file) {
42
+ $this->_expireFile($file);
43
+ }
44
+ }
45
+ }
46
+
47
+ public function getGlobbedFilenames($filename)
48
+ {
49
+ if(!isset($this->_globbed[$filename])) {
50
+ $filename = $this->_qualify($filename);
51
+ $pattern = $filename . '.*';
52
+ $this->_globbed[$filename] = glob($pattern);
53
+ Springbot_Log::debug("Checking glob for $pattern, returns " . count($this->_globbed[$filename]) . " results");
54
+ }
55
+ return $this->_globbed[$filename];
56
+ }
57
+
58
+ public function getLogFilenames()
59
+ {
60
+ return array(
61
+ Springbot_Log::LOGFILE,
62
+ Springbot_Log::ERRFILE,
63
+ Springbot_Log::HTTPFILE
64
+ );
65
+ }
66
+
67
+ public function sizeLimitReached($filename)
68
+ {
69
+ $filename = $this->_qualify($filename);
70
+ $fileLimit = Mage::getStoreConfig('springbot/debug/filesize_limit');
71
+
72
+ if(file_exists($filename)) {
73
+ return filesize($filename) > $fileLimit;
74
+ } else {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ protected function _getNextLogId($filename)
80
+ {
81
+ $ids = array(0);
82
+ foreach($this->getGlobbedFilenames($filename) as $file) {
83
+ $matches = array();
84
+ preg_match('/\d+$/', $file, $matches);
85
+ if(isset($matches[0])) {
86
+ $ids[] = $matches[0];
87
+ }
88
+ }
89
+ $id = max($ids) + 1;
90
+ Springbot_Log::debug("Next id: $id");
91
+ return $id;
92
+ }
93
+
94
+ protected function _qualify($filename)
95
+ {
96
+ return $this->_getLogDir() . DS . basename($filename);
97
+ }
98
+
99
+ protected function _getLogDir()
100
+ {
101
+ return Mage::getBaseDir('var') . DS . 'log';
102
+ }
103
+
104
+ protected function _expireFile($file)
105
+ {
106
+ if(file_exists($file)) {
107
+ $elapsedSinceModified = time() - filectime($file);
108
+ if($elapsedSinceModified > $this->_getExpireLimit()) {
109
+ Springbot_Log::debug("Removing $file due to expiration");
110
+ unlink($file);
111
+ }
112
+ }
113
+ }
114
+
115
+ private function _getExpireLimit()
116
+ {
117
+ // converting days into seconds for comparision to filectime
118
+ return Mage::getStoreConfig('springbot/debug/expire_time_days') * 24 * 60 * 60;
119
+ }
120
+ }
app/code/community/Springbot/Util/Logger.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Util_Logger
4
+ {
5
+ const SIMPLE_FORMAT = '%message%';
6
+ const DEFAULT_FORMAT = '%timestamp% %priorityName% (%priority%): %message%';
7
+ const EXPANDED_FORMAT = '%timestamp% %className%%callType%%method%(%line%) [%priorityName%] : %message%';
8
+
9
+ protected static $_loggers = array();
10
+
11
+ protected $_format;
12
+
13
+ public function log($message, $level, $file, $format = null, $extras = null)
14
+ {
15
+ switch ($format) {
16
+ case 'simple':
17
+ $this->_format = self::SIMPLE_FORMAT;
18
+ break;
19
+ case 'expanded':
20
+ $this->_format = self::EXPANDED_FORMAT;
21
+ break;
22
+ default:
23
+ $this->_format = self::DEFAULT_FORMAT;
24
+ }
25
+ $this->_log($message, $level, $file, $extras);
26
+ }
27
+
28
+ public function release($filename)
29
+ {
30
+ Springbot_Log::debug('Releasing log for ' . $filename);
31
+ unset(self::$_loggers[$filename]);
32
+ $this->_provisionLogFile($filename);
33
+ }
34
+
35
+ protected function _log($message, $level, $file, $extras = null)
36
+ {
37
+ try {
38
+ if (!isset(self::$_loggers[$file])) {
39
+ $this->_provisionLogFile($file);
40
+ }
41
+
42
+ if (is_array($message) || is_object($message)) {
43
+ $message = print_r($message, true);
44
+ }
45
+
46
+ self::$_loggers[$file]->log($message, $level, $extras);
47
+ }
48
+ catch (Exception $e) {
49
+ }
50
+ }
51
+
52
+ private function _provisionLogFile($file)
53
+ {
54
+ $logDir = Mage::getBaseDir('var') . DS . 'log';
55
+ $logFile = $logDir . DS . $file;
56
+
57
+ if (!is_dir($logDir)) {
58
+ mkdir($logDir);
59
+ chmod($logDir, 0777);
60
+ }
61
+
62
+ if (!file_exists($logFile)) {
63
+ file_put_contents($logFile, '');
64
+ chmod($logFile, 0777);
65
+ }
66
+
67
+ $format = $this->_format . PHP_EOL;
68
+ $formatter = new Zend_Log_Formatter_Simple($format);
69
+ $writer = new Zend_Log_Writer_Stream($logFile);
70
+ $writer->setFormatter($formatter);
71
+ self::$_loggers[$file] = new Zend_Log($writer);
72
+ }
73
+ }
app/code/community/Springbot/Util/Partition.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Springbot_Util_Partition
4
+ {
5
+ public $start;
6
+ public $stop;
7
+
8
+ public function __construct($start, $stop)
9
+ {
10
+ $this->start = $start;
11
+ $this->stop = $stop;
12
+ }
13
+
14
+ public function fromStart()
15
+ {
16
+ return $this->start . ':';
17
+ }
18
+
19
+ public function __toString()
20
+ {
21
+ return $this->start . ':' . $this->stop;
22
+ }
23
+ }
app/design/adminhtml/base/default/layout/bmbleb.xml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <bmbleb_index_index>
4
+ <reference name="head">
5
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
6
+ </reference>
7
+ </bmbleb_index_index>
8
+ <bmbleb_adminhtml_index_index>
9
+ <reference name="head">
10
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
11
+ </reference>
12
+ <reference name="content">
13
+ <block type="bmbleb/adminhtml_index" name="bmbleb.index"></block>
14
+ </reference>
15
+ <reference name="left">
16
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
17
+ </reference>
18
+ </bmbleb_adminhtml_index_index>
19
+ <bmbleb_adminhtml_index_auth>
20
+ <reference name="head">
21
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
22
+ </reference>
23
+ <reference name="content">
24
+ <block type="bmbleb/adminhtml_auth" name="bmbleb.auth">
25
+ <block type="bmbleb/adminhtml_bmbleb_login" as="login_form" name="bmbleb.auth.loginform" />
26
+ </block>
27
+ </reference>
28
+ <reference name="left">
29
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
30
+ </reference>
31
+ </bmbleb_adminhtml_index_auth>
32
+ <bmbleb_adminhtml_index_status>
33
+ <reference name="head">
34
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
35
+ </reference>
36
+ <reference name="content">
37
+ <block type="bmbleb/adminhtml_status" name="bmbleb.status"></block>
38
+ </reference>
39
+ <reference name="left">
40
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
41
+ </reference>
42
+ </bmbleb_adminhtml_index_status>
43
+ <bmbleb_adminhtml_connectedtospringbot>
44
+ <reference name="head">
45
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
46
+ </reference>
47
+ <reference name="content">
48
+ <block type="bmbleb/adminhtml_status" name="bmbleb.status"></block>
49
+ </reference>
50
+ <reference name="left">
51
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
52
+ </reference>
53
+ </bmbleb_adminhtml_connectedtospringbot>
54
+ <bmbleb_adminhtml_help_index>
55
+ <reference name="head">
56
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
57
+ </reference>
58
+ <reference name="content">
59
+ <block type="bmbleb/adminhtml_help" name="bmbleb.help" />
60
+ </reference>
61
+ <reference name="left">
62
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
63
+ </reference>
64
+ </bmbleb_adminhtml_help_index>
65
+ <bmbleb_account_index>
66
+ <reference name="head">
67
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
68
+ </reference>
69
+ </bmbleb_account_index>
70
+ <bmbleb_adminhtml_settings_index>
71
+ <reference name="head">
72
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
73
+ </reference>
74
+ <reference name="content">
75
+ <block type="bmbleb/adminhtml_settings" name="bmbleb.settings" />
76
+ </reference>
77
+ <reference name="left">
78
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
79
+ </reference>
80
+ </bmbleb_adminhtml_settings_index>
81
+ </layout>
app/design/adminhtml/default/default/layout/bmbleb.xml ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <bmbleb_index_index>
4
+ <reference name="head">
5
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
6
+ </reference>
7
+ </bmbleb_index_index>
8
+ <bmbleb_adminhtml_index_index>
9
+ <reference name="head">
10
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
11
+ </reference>
12
+ <reference name="content">
13
+ <block type="bmbleb/adminhtml_index" name="bmbleb.index"></block>
14
+ </reference>
15
+ <reference name="left">
16
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
17
+ </reference>
18
+ </bmbleb_adminhtml_index_index>
19
+ <bmbleb_adminhtml_index_auth>
20
+ <reference name="head">
21
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
22
+ </reference>
23
+ <reference name="content">
24
+ <block type="bmbleb/adminhtml_auth" name="bmbleb.auth">
25
+ <block type="bmbleb/adminhtml_bmbleb_login" as="login_form" name="bmbleb.auth.loginform" />
26
+ </block>
27
+ </reference>
28
+ <reference name="left">
29
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
30
+ </reference>
31
+ </bmbleb_adminhtml_index_auth>
32
+ <bmbleb_adminhtml_index_status>
33
+ <reference name="head">
34
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
35
+ </reference>
36
+ <reference name="content">
37
+ <block type="bmbleb/adminhtml_status" name="bmbleb.status"></block>
38
+ </reference>
39
+ <reference name="left">
40
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
41
+ </reference>
42
+ </bmbleb_adminhtml_index_status>
43
+ <bmbleb_adminhtml_connectedtospringbot>
44
+ <reference name="head">
45
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
46
+ </reference>
47
+ <reference name="content">
48
+ <block type="bmbleb/adminhtml_status" name="bmbleb.status"></block>
49
+ </reference>
50
+ <reference name="left">
51
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
52
+ </reference>
53
+ </bmbleb_adminhtml_connectedtospringbot>
54
+ <bmbleb_adminhtml_help_index>
55
+ <reference name="head">
56
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
57
+ </reference>
58
+ <reference name="content">
59
+ <block type="bmbleb/adminhtml_help" name="bmbleb.help" />
60
+ </reference>
61
+ <reference name="left">
62
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
63
+ </reference>
64
+ </bmbleb_adminhtml_help_index>
65
+ <bmbleb_adminhtml_jobs_index>
66
+ <reference name="head">
67
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
68
+ </reference>
69
+ <reference name="content">
70
+ <block type="bmbleb/adminhtml_jobs" template="bmbleb/jobs.phtml" name="bmbleb.jobs">
71
+ <block type="bmbleb/adminhtml_jobs_status" name="bmbleb.jobs.status"></block>
72
+ </block>
73
+ </reference>
74
+ <reference name="left">
75
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
76
+ </reference>
77
+ </bmbleb_adminhtml_jobs_index>
78
+ <bmbleb_adminhtml_logs_index>
79
+ <reference name="head">
80
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
81
+ </reference>
82
+ <reference name="content">
83
+ <block type="bmbleb/adminhtml_logs" name="bmbleb.logs"></block>
84
+ </reference>
85
+ <reference name="left">
86
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
87
+ </reference>
88
+ </bmbleb_adminhtml_logs_index>
89
+ <bmbleb_adminhtml_problems_index>
90
+ <reference name="head">
91
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
92
+ </reference>
93
+ <reference name="content">
94
+ <block type="bmbleb/adminhtml_problems" name="bmbleb.problems" />
95
+ </reference>
96
+ <reference name="left">
97
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
98
+ </reference>
99
+ </bmbleb_adminhtml_problems_index>
100
+ <bmbleb_account_index>
101
+ <reference name="head">
102
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
103
+ </reference>
104
+ </bmbleb_account_index>
105
+ <bmbleb_adminhtml_settings_index>
106
+ <reference name="head">
107
+ <action method="addCss"><stylesheet>bmbleb/bmbleb.css</stylesheet></action>
108
+ </reference>
109
+ <reference name="content">
110
+ <block type="bmbleb/adminhtml_settings" name="bmbleb.settings" />
111
+ </reference>
112
+ <reference name="left">
113
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
114
+ </reference>
115
+ </bmbleb_adminhtml_settings_index>
116
+ <bmbleb_logout_logout>
117
+ <reference name="left">
118
+ <block type="bmbleb/adminhtml_tabs" name="bmbleb.tabs"></block>
119
+ </reference>
120
+ <reference name="content">
121
+ <block type="bmbleb/adminhtml_logout" name="bmbleb.logout" template="bmbleb/logout.phtml" />
122
+ </reference>
123
+ </bmbleb_logout_logout>
124
+
125
+ <default>
126
+ <reference name="notifications">
127
+ <block type="bmbleb/adminhtml_notifications" name="bmbleb_notifications" template="bmbleb/notifications.phtml"/>
128
+ </reference>
129
+ </default>
130
+
131
+ </layout>
app/design/adminhtml/default/default/template/bmbleb/alert/finished.phtml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <div id="bmbleb-alerts-item" style="width:600px;">
2
+ <h2>All Done!</h2>
3
+ <div class="j-box-left">
4
+ <div class="upgrade-box pad12">
5
+ <p style="text-align:center;">You've responded to all of your open alerts.</p>
6
+ <a href="<?php echo $this->getUrl('bmbleb/index/index'); ?>" class="upg-btn jbtn">Back to the Dashboard</a>
7
+ </div>
8
+ </div>
9
+ </div>
app/design/adminhtml/default/default/template/bmbleb/alert/index.phtml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="j-content">
2
+ <h3 class="alert-head"><span class="head">Alerts</span></h3>
3
+
4
+ <?php
5
+ $stats = Mage::getModel('bmbleb/alert')->getStatusCounts();
6
+ $countTotal = intval($stats['total']);
7
+ $countNew = intval($stats['new']);
8
+ $countDone = intval($stats['completed']);
9
+
10
+ ?>
11
+
12
+ <div class="headline orng"><span><?php echo $countNew; ?> New Alert<?php if ($countNew != 1){ print 's'; } ?>!</span></div>
13
+
14
+ <div class="j-box-left">
15
+ <div class="upgrade-box" style="margin-bottom:20px;">
16
+ <?php if ($countNew > 0): ?>
17
+ <a class="upg-btn jbtn" href="<?php echo $this->getUrl('*/*/respond'); ?>">See your New Alerts</a>
18
+ <span>or <a href="<?php echo $this->getUrl('*/*/sync'); ?>">check for new ones now</a></span>
19
+ <?php else: ?>
20
+ <a class="upg-btn jbtn" href="<?php echo $this->getUrl('*/*/sync'); ?>">Check for New Alerts</a>
21
+ <?php endif; ?>
22
+ </div>
23
+
24
+ <?php if ($countTotal > 0) : ?>
25
+ <p><a href="<?php echo $this->getUrl('*/*/grid'); ?>">view all as a list</a></p>
26
+ <?php endif; ?>
27
+ </div>
28
+
29
+ <?php if ($countDone > 5) : ?>
30
+ <div class="headline"><span><?php echo $countDone; ?> completed so far!
31
+ <?php endif; ?>
32
+ </div>
app/design/adminhtml/default/default/template/bmbleb/alert/next/form.phtml ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Academic Free License (AFL 3.0)
8
+ * that is bundled with this package in the file LICENSE_AFL.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/afl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category design
22
+ * @package default_default
23
+ * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
25
+ */
26
+ ?>
27
+ <?php
28
+ $alert = Mage::registry('current_bmbleb_alert');
29
+ ?>
30
+
31
+ <div id="bmbleb-alerts-item" style="width:600px;">
32
+ <div class="pad12left">
33
+ <div class="to-do"> <!--<span class="ttl">Current Alert:</span>-->
34
+ <!--<?php if ($alert->getStatus() == 0) { ?><p><em>NEW ALERT!</em></p>-->
35
+ <!--<?php } else if ($alert->getStatus() == 1) { ?><p><em>previously skipped</em></p><?php } ?>-->
36
+ <h2><?php echo $alert->getBody(); ?></h2>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="w100per">
42
+
43
+ <div class="jleft-box">
44
+ <div class="pad12">
45
+ <div class="to-do"><?php echo $alert->getDescription(); ?></div>
46
+ <!--<div class="to-do"><span class="ttl">We suggest:</span> <?php echo $alert->getResponse(); ?></div>-->
47
+ <div class="clear"></div>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="clear"></div>
52
+
53
+ <div class="pad12">
54
+ <?php echo $this->getFormHtml();?>
55
+
56
+ <!--<p class="msg-row">Did you do this? Or would you like to skip </p>-->
57
+ <div class="w100per">
58
+ <a id="bmbleb-alert-complete" class="jbtn btn1" href="<?php print $this->getUrl('bmbleb/adminhtml_alert/complete', array('id' => $alert->getId())); ?>">Done</a> <a id="bmbleb-alert-skip" class="cancle" href="<?php print $this->getUrl('bmbleb/adminhtml_alert/skip', array('id' => $alert->getId())); ?>">Skip</a>
59
+ </div>
60
+ <div class="clear"></div>
61
+ </div>
62
+
63
+ </div>
64
+
65
+ <?php echo $this->getChildHtml('form_after');?>
app/design/adminhtml/default/default/template/bmbleb/auth.phtml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php /* @var $this Socketware_Bmbleb_Block_Adminhtml_Login */ ?>
2
+ <div class="form-wrap last">
3
+ <?php echo $this->getChildHtml('login_form'); ?>
4
+ </div>
5
+ </div>
app/design/adminhtml/default/default/template/bmbleb/dashboard_loggedout.phtml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="content-header-block">
2
+ <div class="bmb-logo"><a href="javascript:void(0);"><img src="<?php echo $this->getSkinUrl('bmbleb/images/bmb-ctl.png'); ?>" alt="bmbleb logo" /></a></div>
3
+ <div class="header-content">
4
+ <h2>Please login or register for a free bmbleb account</h2>
5
+ </div>
6
+ </div>
7
+ <style type="text/css">
8
+
9
+ div.form-wrap { width:48%; float:left; margin-top:20px; margin-right:20px; }
10
+ div.form-wrap.last { margin-right:0; }
11
+
12
+ div.form-wrap .form-list td.value input.input-text { width:90%; }
13
+ div.form-wrap .form-list td.label label { width:125px; padding-right:10px; }
14
+ /* more adjustments - move these all to css */
15
+
16
+ /* override some default boxes backgrounds */
17
+ .entry-edit .entry-edit-head { background:none; }
18
+ .entry-edit fieldset, .entry-edit .fieldset { background:none; border:none; }
19
+
20
+ div.form-wrap div.content-header { display:none; }
21
+ .form-wrap .content-footer { padding-left:80px; }
22
+ #login-register-wrapper .form-buttons { padding-left:0px; }
23
+ #login-register-wrapper .forgot-pwd { padding-left:80px; }
24
+
25
+ /*
26
+ ! working on table cell problem
27
+ this works in ffox and chrome but not IE 9 or IE compat
28
+ */
29
+ #login-register-wrapper td { display: table-row; }
30
+ #login-register-wrapper td.value input.input-text { margin-bottom:10px; }
31
+
32
+
33
+ </style>
34
+
app/design/adminhtml/default/default/template/bmbleb/help/index.phtml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="content-header">
2
+ <h3>Help</h3>
3
+ </div>
4
+
5
+ <div id="help-screen-wrap">
6
+ <div class="col-one">
7
+ <script type="text/javascript" charset="utf-8">
8
+ var is_ssl = ("https:" == document.location.protocol);
9
+ var asset_host = is_ssl ? "https://s3.amazonaws.com/getsatisfaction.com/" : "http://s3.amazonaws.com/getsatisfaction.com/";
10
+ document.write(unescape("%3Cscript src='" + asset_host + "javascripts/feedback-v2.js' type='text/javascript'%3E%3C/script%3E"));
11
+ </script>
12
+ <script type="text/javascript" charset="utf-8">
13
+ var feedback_widget_options = {};
14
+ feedback_widget_options.display = "inline";
15
+ feedback_widget_options.company = "bmbleb";
16
+ feedback_widget_options.style = "question";
17
+ var feedback_widget = new GSFN.feedback_widget(feedback_widget_options);
18
+ </script>
19
+ </div>
20
+ <div class="col-two">
21
+ <div id='gsfn_search_widget'>
22
+ <h3>Search</h3>
23
+ <div class='gsfn_content'>
24
+ <form accept-charset='utf-8' action='https://getsatisfaction.com/bmbleb' id='gsfn_search_form' method='get' onsubmit='gsfn_search(this); return false;'>
25
+ <div>
26
+ <input name='style' type='hidden' value='' />
27
+ <input name='limit' type='hidden' value='10' />
28
+ <input name='utm_medium' type='hidden' value='widget_search' />
29
+ <input name='utm_source' type='hidden' value='widget_bmbleb' />
30
+ <input name='callback' type='hidden' value='gsfnResultsCallback' />
31
+ <input name='format' type='hidden' value='widget' />
32
+ <label class='gsfn_label' for='gsfn_search_query'>Ask a question, share an idea, or report a problem.</label>
33
+ <input id='gsfn_search_query' maxlength='120' name='query' type='text' value='' />
34
+ <input id='continue' type='submit' value='Continue' />
35
+ </div>
36
+ </form>
37
+ <div id='gsfn_search_results' style='height: auto;'></div>
38
+ </div>
39
+ </div>
40
+ <script src="https://getsatisfaction.com/bmbleb/widgets/javascripts/3081adb659/widgets.js" type="text/javascript"></script>
41
+ </div>
42
+ </div>
43
+
44
+
app/design/adminhtml/default/default/template/bmbleb/index.phtml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="j-content">
2
+ <div class="j-box-left box2">
3
+ <a href="http://app.springbot.com/dashboard">
4
+ <img align=left src="<?php echo $this->getSkinUrl('bmbleb/images/plugin_dashboard_syncing.jpg'); ?>" alt="springbot logo" />&nbsp;</a>
5
+ <?php
6
+ try {
7
+ Springbot_Boss::launchHarvest();
8
+ } catch (Exception $e) {
9
+ Mage::logException($e);
10
+ }
11
+ ?>
12
+ </div>
13
+ </div>
14
+ <div class="tc-info">
15
+ <p class="tc-link"><a href="http://www.springbot.com/content/terms_of_use" target="_blank" title="Terms &amp; Conditions">View our Terms &amp; Conditions</a></p>
16
+ </div>
17
+
app/design/adminhtml/default/default/template/bmbleb/index/messages.phtml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $messages = $this->getSystemMessages(); ?>
2
+ <?php if (count($messages) > 0): ?>
3
+ <div class="upgrade-box">
4
+ <h4>Notice</h4>
5
+ <ul>
6
+ <?php foreach ($this->getSystemMessages() as $mess): ?>
7
+ <li><p><?php echo $mess; ?></p></li>
8
+ <?php endforeach; ?>
9
+ </ul>
10
+ </div>
11
+ <?php endif; ?>
app/design/adminhtml/default/default/template/bmbleb/index/terms.phtml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <div id="tc-content" class="tc-content">
2
+ <div class="tc-content-inner">
3
+ <p class="close-btn"><a href="#" title="Close window" onclick="Modalbox.hide(); return false;">Close</a></p>
4
+ <h3>springbot Terms &amp; Conditions</h3>
5
+ <?php
6
+ print nl2br(Mage::helper('bmbleb/GetTermsOfService')->getTermsOfService());
7
+ ?>
8
+ </div>
9
+ </div>
app/design/adminhtml/default/default/template/bmbleb/jobs.phtml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ ?>
3
+ <div class="content-header">
4
+ <table cellspacing="0">
5
+ <tr>
6
+ <td style="<?php echo $this->getHeaderWidth() ?>"><?php echo $this->getHeaderHtml() ?></td>
7
+ <td class="form-buttons"><?php echo $this->getButtonsHtml() ?></td>
8
+ </tr>
9
+ </table>
10
+ </div>
11
+ <div>
12
+ <?php echo $this->getChildHtml('bmbleb.jobs.status', true, true); ?>
13
+ </div>
14
+ <div>
15
+ <?php echo $this->getGridHtml(true, true); ?>
16
+ </div>
app/design/adminhtml/default/default/template/bmbleb/jobs/status.phtml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ ?>
3
+ <div class="box-left">
4
+ <div class="entry-edit">
5
+ <div class="entry-edit-head">
6
+ <h4><?php echo $this->__('Job Worker Status') ?></h4>
7
+ </div>
8
+ <div class="fieldset">
9
+ <?php $manager = $this->getManager() ?>
10
+ <div class="bmbleb-mgr-status">
11
+ <?php $buttonText = $this->getButtonLabelText() ?>
12
+ <label for="bmbleb-mgr-button" id="bmbleb-mgr-status"><?php echo $this->getLabelText() ?></label>
13
+ <button id="bmbleb-mgr-button" id="bmbleb-mgr-button" title="<?php echo $buttonText ?>" class="scalable" onclick="javascript:bmblebToggleMgr(); return false;" ><?php echo $buttonText ?></button>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ </div>
18
+ <script type='text/javascript'>
19
+ //<![CDATA[
20
+ function bmblebToggleMgr() {
21
+ new Ajax.Request('<?php echo $this->getAjaxToggleUrl() ?>', {
22
+ method: 'get',
23
+ onSuccess: function(transport){
24
+ if (res = transport.responseJSON){
25
+ document.getElementById('bmbleb-mgr-status').innerHTML = res.label_text;
26
+ var button = document.getElementById('bmbleb-mgr-button');
27
+ button.innerHTML = res.button_text;
28
+ button.title = res.button_text;
29
+ }
30
+ }
31
+ });
32
+ }
33
+ //]]>
34
+ </script>
app/design/adminhtml/default/default/template/bmbleb/login.phtml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* @var $this Socketware_Bmbleb_Block_Adminhtml_Login */ ?>
2
+ <div id="sb-login-register-wrapper">
3
+ <div class="content-header-block">
4
+ <div class="bmb-logo"><a href="www.springbot.com"><img align=left width=60 height=60 src="<?php echo $this->getSkinUrl('bmbleb/images/springbot-ctl.png'); ?>" alt="springbot logo" />&nbsp;</a>
5
+ <h2>Springbot Account Login</h2>
6
+ </div>
7
+ <div class="header-content">
8
+ <p>Our Marketing Magic uncovers hard-to-find social and demographic data about your customers.</p>
9
+ </div>
10
+ </div>
11
+
12
+ <div style="background:#F9F9F9 url(skin/adminhtml/base/default/bmbleb/images/registration-bg.png) no-repeat left top;">
13
+ <?php echo $this->getChildHtml('form'); ?>
14
+ </div>
15
+ </div>
app/design/adminhtml/default/default/template/bmbleb/logout.phtml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for Springbot_Bmbleb_Block_Adminhtml_Logout
4
+ */
5
+ ?>
6
+ <style>
7
+ .sb-alert {
8
+ margin: 8px 0 8px;
9
+ }
10
+ .sb-accept {
11
+ margin: 8px 0 8px;
12
+ }
13
+ </style>
14
+
15
+ <h2 class="sb-header">Unplugging Springbot Services</h2>
16
+
17
+ <div class="sb-alert">
18
+ <p>This will disconnect you from Springbot's services.</p>
19
+ <p>Your account will still be accessible through the Springbot web application, however you will need to re-enter your credentials here on in the Magento admin panel in order for the Springbots to get back to work harvesting your data.</p>
20
+ <p>Please click the button below to finish disconnecting.</p>
21
+ </div>
22
+
23
+ <div class="sb-accept">
24
+ <form action="<?php echo $this->getUrl('*/*/remoteDisconnect') ?>">
25
+ <input type="hidden" name="form_key" value="<?php echo $this->getFormKey() ?>" />
26
+ <p>
27
+ <button class="sb-button" type="submit" id="logout-button">Logout</button>
28
+ </p>
29
+ <p>
30
+ <span id="sb-checkbox">
31
+ <input type="checkbox" id="confirm-logout" required />
32
+ </span>
33
+ <label for="confirm-logout">I understand the risks but I wish to disconnect anyway.</label>
34
+ </p>
35
+ </form>
36
+ </div>
app/design/adminhtml/default/default/template/bmbleb/logs/index.phtml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <div class="entry-edit">
3
+ <div class="entry-edit-head">
4
+ <h4 class="icon-head head-edit-form fieldset-legend">Springbot Logs</h4>
5
+ <div class="form-buttons"></div>
6
+ </div>
7
+ <div class="fieldset fieldset-wide" id="base_fieldset">
8
+ <div class="hor-scroll">
9
+ <table cellspacing="0" class="form-list">
10
+ <tbody>
11
+ <tr>
12
+ <td class="label">
13
+ <label for="log1">Springbot.log</label>
14
+ </td>
15
+ <td class="value">
16
+ <textarea class="textarea monospace-textarea" id="springbot-log-textarea"><?php echo $this->getLogContent(Springbot_Log::LOGFILE) ?></textarea>
17
+ <a href="<?php echo $this->getUrl('bmbleb/adminhtml_logs/download/name/' . Springbot_Log::LOGFILE); ?>">Download Springbot.log</a>
18
+ </td>
19
+ </tr>
20
+ <tr>
21
+ <td class="label">
22
+ <label for="log1">Springbot-Http.log</label>
23
+ </td>
24
+ <td class="value">
25
+ <textarea class="textarea monospace-textarea" id="springbothttp-log-textarea"><?php echo $this->getLogContent(Springbot_Log::HTTPFILE) ?></textarea>
26
+ <a href="<?php echo $this->getUrl('bmbleb/adminhtml_logs/download/name/' . Springbot_Log::HTTPFILE); ?>">Download Springbot-Http.log</a>
27
+ </td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <script type="text/javascript">
36
+ var springbottextarea = document.getElementById('springbot-log-textarea');
37
+ springbottextarea.scrollTop = springbottextarea.scrollHeight;
38
+ var springbothttptextarea = document.getElementById('springbothttp-log-textarea');
39
+ springbothttptextarea.scrollTop = springbothttptextarea.scrollHeight;
40
+ </script>
app/design/adminhtml/default/default/template/bmbleb/notifications.phtml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php if ($message = $this->getMessage()) : ?>
2
+ <?php if ($message['type'] == 'success'): ?>
3
+ <div class="notification-global" style="background-color: #eff5ea; background-image: none"><?php echo $message['message'] ?></div>
4
+ <?php else: ?>
5
+ <div class="notification-global"><?php echo $message['message'] ?></div>
6
+ <?php endif ?>
7
+ <?php endif; ?>
app/design/adminhtml/default/default/template/bmbleb/problems/index.phtml ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ($solutions = $this->getSolutions()): ?>
2
+
3
+ <h1>Springbot encountered a problem</h1>
4
+ <p>
5
+ Don't worry! We can help with any problems you may have. Sometimes
6
+ <a href="<?php echo $this->getUrl('bmbleb/adminhtml_index/auth') ?>">re-logging into Springbot</a> will fix any
7
+ issues you may be having. If the problem persists please email our support team:
8
+ <a href="mailto:support@springbot.com">support@springbot.com</a>.
9
+ </p>
10
+
11
+ <div class="grid">
12
+ <table class="data" cellspacing="0">
13
+ <thead>
14
+ <tr class="headings">
15
+ <th class="no-link"><span class="nobr">Problem</span></th>
16
+ <th class="no-link"><span class="nobr">Possible solution</span></th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+
21
+
22
+ <?php foreach ($solutions as $problem): ?>
23
+
24
+ <tr>
25
+ <td class="a-left">
26
+ <strong><?php echo $problem['problem'] ?></strong>
27
+ </td>
28
+
29
+ <td class="a-left">
30
+ <?php echo $problem['solution'] ?>
31
+ </td>
32
+
33
+ </tr>
34
+
35
+ <?php endforeach ?>
36
+
37
+
38
+ </tbody>
39
+ </table>
40
+
41
+ </div>
42
+ <?php endif ?>
43
+
app/design/adminhtml/default/default/template/bmbleb/status.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="j-content">
2
+ <div class="j-box-left box2">
3
+ <a href="<?php echo 'http://app.springbot.com/dashboard'; ?>" target="_blank">
4
+ <img align=left src="<?php echo $this->getSkinUrl('bmbleb/images/plugin_dashboard_syncing.jpg'); ?>" alt="springbot logo" />
5
+ </a>
6
+ </div>
7
+ </div>
8
+ <div class="tc-info">
9
+ <p class="tc-link"><a href="http://www.springbot.com/content/terms_of_use" target="_blank" title="Terms &amp; Conditions">View our Terms &amp; Conditions</a></p>
10
+ <p style="float:right;">Springbot v<?php echo Springbot_DataServices_HarvestingManager::MAGENTO_PACKAGE_VERSION ?></p>
11
+ </div>
12
+
app/design/adminhtml/default/default/template/bmbleb/tabs.phtml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* @var $this Socketware_Bmbleb_Block_Adminhtml_Tabs */ ?>
2
+ <div class="left-logo">
3
+ <img alt="bmbleb logo" src="<?php echo $this->getSkinUrl('bmbleb/images/logo.png')?>" /><br/>
4
+ </div>
5
+
6
+ <ul class="tabs">
7
+ <li class="ico1"><a href="<?php echo $this->getUrl('bmbleb/adminhtml_index/status'); ?>" class="tab-item-link<?php if ($this->isActive('adminhtml_index')) { echo ' active'; } ?>"><span>Dashboard</span></a></li>
8
+ <?php if($this->useExtendedAdmin()): ?>
9
+ <li class="ico2"><a href="<?php echo $this->getUrl('bmbleb/adminhtml_jobs/index'); ?>" class="tab-item-link<?php if ($this->isActive('adminhtml_jobs')) { echo ' active'; } ?>"><span>Jobs</span></a></li>
10
+ <li class="ico3"><a href="<?php echo $this->getUrl('bmbleb/adminhtml_logs/index'); ?>" class="tab-item-link<?php if ($this->isActive('adminhtml_logs')) { echo ' active'; } ?>"><span>Logs</span></a></li>
11
+ <?php endif ?>
12
+ <li class="ico7"><a href="<?php echo $this->getUrl('bmbleb/adminhtml_help/index'); ?>" class="tab-item-link<?php if ($this->isActive('adminhtml_help')) { echo ' active'; } ?>"><span>Help</span></a></li>
13
+ </ul>
app/etc/modules/Springbot.xml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Springbot_Bmbleb>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Springbot_Bmbleb>
8
+ <Springbot_BoneCollector>
9
+ <active>true</active>
10
+ <codePool>community</codePool>
11
+ </Springbot_BoneCollector>
12
+ <Springbot_Combine>
13
+ <active>true</active>
14
+ <codePool>community</codePool>
15
+ </Springbot_Combine>
16
+ <Springbot_Shadow>
17
+ <active>true</active>
18
+ <codePool>community</codePool>
19
+ </Springbot_Shadow>
20
+ </modules>
21
+
22
+ <!--
23
+
24
+ <global>
25
+ <resources>
26
+ <Springbot_setup>
27
+ <setup>
28
+ <module>Springbot</module>
29
+ <class>Springbot_Model_Installers_Setup</class>
30
+ </setup>
31
+ <connection>
32
+ <use>core_setup</use>
33
+ </connection>
34
+ </Springbot_setup>
35
+ <Springbot_write>
36
+ <connection>
37
+ <use>core_write</use>
38
+ </connection>
39
+ </Springbot_write>
40
+ <Springbot_read>
41
+ <connection>
42
+ <use>core_read</use>
43
+ </connection>
44
+ </Springbot_read>
45
+ </resources>
46
+ </global>
47
+ -->
48
+ </config>
package.xml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Springbot</name>
4
+ <version>1.2.1.2</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Springbot helps merchants drive marketing actions based on insights from its &#x201C;big data&#x201D; analytics engine</summary>
10
+ <description>Springbot - Better Data. Better Actions. More Revenue.&#xD;
11
+ &#xD;
12
+ Do you need help deciding how to spend your next marketing dollar? Springbot can help. &#xD;
13
+ &#xD;
14
+ Know your customers better. &#xD;
15
+ Take better marketing actions. &#xD;
16
+ Drive more revenue. &#xD;
17
+ &#xD;
18
+ Springbot helps Magento merchants drive marketing actions based on insights from its &#x201C;big data&#x201D; analytics engine. &#xD;
19
+ - Analyze and augment consumer data and purchase behavior &#xD;
20
+ - Integrate online marketing channels, content and marketplaces &#xD;
21
+ - Prioritize future marketing actions based on the potential ROI &#xD;
22
+ &#xD;
23
+ Get to Know your Customers&#xD;
24
+ &#xD;
25
+ Springbot works magic by adding hard-to-find social and demographic data about your customers directly into Magento.&#xD;
26
+ &#xD;
27
+ age, gender, geo - gain an intuitive sense of your real market dynamics &#xD;
28
+ social media data - facebook, twitter, linkedin... find your customers online without googling all of them! &#xD;
29
+ home owner data - renting vs. owning and home value can drive purchase behavior &#xD;
30
+ &#xD;
31
+ plus employment data, income data and much more! &#xD;
32
+ &#xD;
33
+ Get Started Now with a Limited Time Free Data Offer&#xD;
34
+ &#xD;
35
+ It's completely free to sign up. We'll give you augmented data for up to 1000 of your customers so that you can experience the power of our awesome data service.&#xD;
36
+ &#xD;
37
+ Deep Magento Integration&#xD;
38
+ &#xD;
39
+ You have enough screens, graphs and forms to run your store. bmbleb enhances those screens... it doesn't replace them. Your extra customer data appears in your normal Magento customer screens. You can sort and search using it. And when you do want to dig deeper... Springbot has screens for you!&#xD;
40
+ &#xD;
41
+ Powerful Living Data Alerts&#xD;
42
+ &#xD;
43
+ Data isn't static. It changes constantly as we go about our lives. While you're busy watching over your store, Springbot watches over your data and alerts you to sales, marketing and relationship opportunities. Living Data Alerts tell you when your customers are ready to buy. Social media monitors tell you when customers are tweeting about topics relevant to your store.&#xD;
44
+ &#xD;
45
+ Living Data Alerts example: 378 pieces of data have been added to your customer database &#xD;
46
+ Social Media Alerts example: Ted Smith Tweeted about your company &#xD;
47
+ Proclivity to Buy Alerts example: Sally Avalon bought a house recently &#xD;
48
+ &#xD;
49
+ For support information, features and pricing and more details visit springbot.com</description>
50
+ <notes>Corrected logging for non-info class messages</notes>
51
+ <authors><author><name>Springbot </name><user>Springbot</user><email>bseitz@springbot.com</email></author></authors>
52
+ <date>2014-07-28</date>
53
+ <time>19:06:58</time>
54
+ <contents><target name="mageetc"><dir name="modules"><file name="Springbot.xml" hash="cf640afd5be296dd9736b152247703a8"/></dir></target><target name="magecommunity"><dir name="Springbot"><dir name="Bmbleb"><dir name="Block"><dir name="Adminhtml"><file name="Auth.php" hash="41b76e63e15d51f697a1378b9e904061"/><dir name="Bmbleb"><dir name="Login"><file name="Form.php" hash="4076e0de77169b9aadc793b53353ee6b"/></dir><file name="Login.php" hash="81a3dbbf4dd4f51599b6f7eb01bc8f1e"/><dir name="Register"><file name="Form.php" hash="f0936c29356624200541a4f67fd096ab"/></dir><file name="Register.php" hash="8c1dc7d214022497f9ac56313a449f66"/></dir><file name="Connected.php" hash="f5378fd914cc36264376a987b7ce96c8"/><file name="Help.php" hash="a756092bca1a4a5bd96f0f652a37de3c"/><dir name="Index"><file name="Messages.php" hash="2294dd16a061b1c35b8f7991c7f42d8c"/><file name="Terms.php" hash="ae2667e078aef09dbe35fe4d45783616"/></dir><file name="Index.php" hash="7c11054e36a4658564938ae8e71e00db"/><dir name="Jobs"><file name="Grid.php" hash="d027f3e93522ce932f0056714993ba95"/><file name="Status.php" hash="f1b197bf6fdc392bed93ff0734a54ad6"/></dir><file name="Jobs.php" hash="23d8bdfa6d0fbcabba2b17445effb248"/><file name="Login.php" hash="7d5022345a00b051422cd09915a9ddf9"/><file name="Logout.php" hash="5332e08d0d18b38f1175c1f0cb48f2f8"/><file name="Logs.php" hash="b6cd90251b4b1919af019f7cd93040de"/><file name="Notifications.php" hash="7ad224da80a68fa8334c1d26b9ad4907"/><file name="Problems.php" hash="9840cf49dc25237815ce4275264d4640"/><dir name="Settings"><dir name="Edit"><file name="Form.php" hash="429cc1dd30cf303591bb7a1bd26c30ab"/></dir></dir><file name="Settings.php" hash="3a59b0eb20b7359944c73c9005058739"/><file name="Status.php" hash="a7d1f735d1ed476672ec80fbcb2af16a"/><file name="Tabs.php" hash="8cc4c162985f479461ce2beda582c1c2"/></dir></dir><dir name="Helper"><file name="Account.php" hash="a7368f189cc2536aceec0e023d3d0aee"/><file name="Alerts.php" hash="172e06b4fa099a301b920d0e363c2005"/><file name="ApiCall.php" hash="9040a1002f1706a86d8d1af814314c8b"/><file name="ApiResponse.php" hash="7fd44a77bce45074bd97aaba4ddc135d"/><file name="BmblebProps.php" hash="32af63b204ead1ef06c5cc08c73cf34d"/><file name="ChangePassword.php" hash="8d4eec87843fc60c6642ed8214a8ea42"/><file name="Data.php" hash="62a4dc31da79d87f272c115ad131fe29"/><file name="ExternalLogging.php" hash="6eb1acae916965bb656869f447166e76"/><file name="GetTermsOfService.php" hash="af8ea4a9f2c506e356667273e3c4f8d1"/><file name="PluginStatus.php" hash="6034f9a89bb09ec995b4a07bbd7a5d59"/></dir><dir name="Model"><dir name="Alert"><file name="Priority.php" hash="3f6b3a1ae75007ec8dee2df93480ff85"/><file name="Status.php" hash="701abcb929f68362354b7a8e5085db58"/><file name="Type.php" hash="976018f681012f6b815a921e9c515e22"/></dir><file name="Alert.php" hash="760c3d8737b85c9af9bc6385acdf4ed0"/><file name="Bmbleb.php" hash="700d11c3006f2dcd2e80cd8bbbab15f9"/><file name="Bmblebprops.php" hash="8a87b5625f62cd3060d0ddec84b5d16c"/><dir name="Mysql4"><dir name="Bmbleb"><file name="Collection.php" hash="a0981b543c972b39ab2e59baec9806cc"/></dir></dir><file name="Status.php" hash="9409d26c7884be6b8075ba97dbf71f78"/><file name="Sync.php" hash="a2c4d9761c52055ba8dfbd7d9a33c2e7"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="HelpController.php" hash="087e7868dafe9dd2df89a642d405424a"/><file name="IndexController.php" hash="6b1059484b2d46453e0a429e2d4c8d71"/><file name="JobsController.php" hash="4e912a8fafd2f58235cb2299a83a128b"/><file name="LogsController.php" hash="27461bcdd3bba759b3e31604181bcc37"/><file name="ProblemsController.php" hash="a257b10a90bd3924d2396c1a7c272a38"/><file name="SettingsController.php" hash="64568fca522817ae77ef5015decd5a21"/></dir><file name="HelpController.php" hash="2df4608a957f151bf1d01dbde2113680"/><file name="IndexController.php" hash="ea5fa5e2b305f46b222cb2fc2a44f6a5"/><file name="LoginController.php" hash="e5e8516a27f7e61083182457017ec013"/><file name="LogoutController.php" hash="140c9d32f5557aa1169fd1b85cd5cc9f"/><file name="RegisterController.php" hash="15511b8a249557a11583446b6a2ee747"/></dir><dir name="etc"><file name="config.xml" hash="c64e84f64b6f46453605e181167287a1"/></dir></dir><dir name="BoneCollector"><dir name="Model"><file name="HarvestAbstract.php" hash="c033a6bfeed3a494b61df594c44facc4"/><dir name="HarvestAttribute"><file name="Observer.php" hash="780800606f3fa556962d3dbcd6afa7e4"/></dir><dir name="HarvestCaptureSKU"><file name="Observer.php" hash="5e9d22e60265a29ed742e161bc9b6b6e"/></dir><dir name="HarvestCart"><file name="Observer.php" hash="cea5b393881d7cb82092cbd7e7ca58ef"/></dir><dir name="HarvestCategory"><file name="Observer.php" hash="f6657dc3941f2ea20a0051357128e796"/></dir><dir name="HarvestCustomer"><file name="Observer.php" hash="036f6499bc1b029e6081fa61b7d72973"/></dir><dir name="HarvestProduct"><file name="Observer.php" hash="422c087f79ef3df48cc1f1a8afa34892"/></dir><dir name="HarvestPurchase"><file name="Observer.php" hash="6e52c4b1a5909cdae81cc9fc6968d6b6"/></dir><dir name="HarvestSubscriber"><file name="Observer.php" hash="d5c988abc6bc697fd8d1d9c78eb89de5"/></dir></dir><dir name="etc"><file name="config.xml" hash="1c8425c080e0ef9a957553695530bc4d"/></dir></dir><file name="Boss.php" hash="cbc7365b26f8f06b59a33767de92c545"/><dir name="Combine"><dir name="Helper"><file name="Attributes.php" hash="5fb06e7cc89bb710039a79fb16e2a998"/><file name="Cart.php" hash="07ce5f461ecded3b9b00ed5c30faa266"/><file name="Data.php" hash="5fedff48d6efc495aa824592eed9a0ab"/><file name="Harvest.php" hash="1a662a08a3f1ab865f1376a5bd238989"/><file name="Parser.php" hash="4b0f4023ce8099615db908736c73386b"/><file name="Redirect.php" hash="3b81406f5105040d17699ad1e0d7cd5d"/><file name="Store.php" hash="8db94d157d7e497e7612844da43a6c11"/><file name="Trackable.php" hash="8bc28aa708be61d4d0d1f79b3ad51a78"/></dir><dir name="Model"><file name="Abstract.php" hash="77bc1e853f03ca6a12ddd1dda12feda2"/><file name="Api.php" hash="73062689fd74716e639bb3145e98c8d9"/><dir name="Cron"><file name="Count.php" hash="06824a092141baf29222607a2dcb3d9c"/><dir name="Manager"><file name="Status.php" hash="d3e0e732a3512bb95214f33fa9f08457"/></dir><dir name="Queue"><dir name="Batch"><file name="Row.php" hash="f703a1bf9e14ef428ade448febc9d35b"/></dir><file name="Batch.php" hash="658522bd3e0eef3afa1e0107ff9f858a"/></dir><file name="Queue.php" hash="fa0d2bfd3fbb879442e0edd4ea7915a9"/><file name="Worker.php" hash="f83199c1491083b68eaea08467114d4f"/></dir><dir name="File"><file name="Io.php" hash="6d1f79eaf45897bf0525b0f3f3ac69d2"/><file name="Path.php" hash="24900b670c07fcdc4e54bae585f20002"/></dir><dir name="Harvest"><file name="Abstract.php" hash="76ac23999160edfdd86a2c6b5c8d1569"/><file name="AttributeSets.php" hash="2799ee325cd5bdcae597e482ef96551a"/><file name="Carts.php" hash="27e992f0f8cc4b274cd05efc7ce3a7f2"/><file name="Categories.php" hash="ab5bd7e91444fd7cb8bfeae376cd3ea7"/><file name="CustomerAttributeSets.php" hash="9be018cc3cbb0f495aec4f1c3a9013c8"/><file name="Customers.php" hash="f31427406c56f2dcde5a4f7d04a773c9"/><file name="Guests.php" hash="bcb1fbdfb7b5bf822934d300c423e17f"/><file name="Products.php" hash="2261f2c79b59e59977d050630d0fe92f"/><file name="Purchases.php" hash="585b1cf18e857850920abc7425830388"/><file name="Subscribers.php" hash="0c6f8ce380f036599141c9231562aaa9"/></dir><file name="Harvester.php" hash="b13a9b2dec995bc53335a5c35f67c035"/><dir name="Mysql4"><dir name="Cron"><file name="Count.php" hash="acbbb7ec28afbbe98101f5d114cb30b3"/><dir name="Queue"><file name="Collection.php" hash="b26806c9e7cefd052bb784f5a6ce814c"/></dir><file name="Queue.php" hash="4add10644bfc94b88ef5042b23c82ae7"/></dir><dir name="Redirect"><file name="Collection.php" hash="3bbe4f8729c603f8d8131154a0a117c1"/><dir name="Order"><file name="Collection.php" hash="88c0cfcce31b0eed8c035dee4e7e86df"/></dir><file name="Order.php" hash="852bea330edac3372ec5c168111301a8"/></dir><file name="Redirect.php" hash="842e4ba35c6b049c8eaa64704588ca76"/><file name="Setup.php" hash="3fdec335980846a4c3adbc6f4e3478eb"/><dir name="Trackable"><file name="Collection.php" hash="8799c5bf630d267b551cf9dba986cbb0"/></dir><file name="Trackable.php" hash="b38749697b641874b42dceae38ab4a30"/></dir><dir name="Parser"><file name="Abstract.php" hash="90a77c771599b3f6ee290586cc7c7d34"/><file name="AttributeSet.php" hash="fcb90beeebb921b349b28cadf354786c"/><file name="Category.php" hash="352b6ed7328c94524f4a7c3042949295"/><file name="Customer.php" hash="083edca18fbdf0c496e6ad22775f596e"/><file name="CustomerAttributeSet.php" hash="f926ec29d1ae837c0ae3c0a9257438d4"/><file name="Guest.php" hash="cbcbf7532c67c72d7e3b77022abc76c3"/><file name="Product.php" hash="2a0d3822877074b2258cd10f9d230cb1"/><dir name="Purchase"><file name="Item.php" hash="c7e563921e7876016a1197c2ec0c245b"/></dir><file name="Purchase.php" hash="4f161bc021af1851c92bb1c9c33a26ee"/><dir name="Quote"><file name="Item.php" hash="d7648174b642ae02cb225bb96b932286"/></dir><file name="Quote.php" hash="498cd2b6b39e4ca519d2b4652324cdd4"/><file name="Subscriber.php" hash="51e6e8e3152c2d94d52706582f1fa204"/></dir><file name="Parser.php" hash="72b273215802ee5244fd9e24c223beb8"/><dir name="Redirect"><file name="Order.php" hash="0c31dee69473751d95e5c52f5c60ea48"/></dir><file name="Redirect.php" hash="1248d0c43fb6f273ec48d8f2b91dcbdd"/><dir name="Resource"><file name="Abstract.php" hash="a50723654e084875378838b89ffb74ce"/><dir name="Cron"><dir name="Count"><file name="Collection.php" hash="c5cb4ab406c1d008c1bc22bb95b3ba28"/></dir><file name="Count.php" hash="6a356b5d92b509945c4567f479b9bfdd"/><dir name="Queue"><file name="Collection.php" hash="c182c6f6f7d29ee26ddf2900a8497cdb"/></dir><file name="Queue.php" hash="01d35c9491516f7f4ef7d9cccb7b6202"/></dir><dir name="Redirect"><file name="Collection.php" hash="0f9db83ade4c50c2f7bbe40deae1c065"/><dir name="Order"><file name="Collection.php" hash="162359ed9499b6f976f5c341fd0585c3"/></dir><file name="Order.php" hash="7ea4477380a5215dc0efe561ede359d9"/></dir><file name="Redirect.php" hash="d239af442388bb9fa80db81a7fc43711"/><file name="Setup.php" hash="46cc5ec83478021a1fe7251d73ee568e"/><dir name="Trackable"><file name="Collection.php" hash="6f060c3537b49710302e38e881885a69"/></dir><file name="Trackable.php" hash="764b0d21c492dd69b9f85ae3c647666e"/></dir><dir name="System"><dir name="Config"><dir name="Source"><file name="LogFormat.php" hash="828680dafe5a7042221900cb6d9dfa17"/><file name="LogLevel.php" hash="b86c793ca04205f045efd9ea42d02a10"/><file name="Stability.php" hash="830e5bc4e8ce9657221224dbaf99cee6"/><file name="UrlType.php" hash="28f9a5bc024afe5526685d429a751ad8"/></dir></dir></dir><file name="Trackable.php" hash="710d298645c0a809a1b70b936b0e4d09"/></dir><dir name="etc"><file name="adminhtml.xml" hash="794fc8a1d67ac3e6b5d71c707a0c7cad"/><file name="config.xml" hash="8faddfe50bd946db66efa5ed2c6941a1"/><file name="system.xml" hash="12f0f05be054854c29751767a3c8ef97"/></dir><dir name="sql"><dir name="combine_setup"><file name="mysql4-install-1.0.0.70.php" hash="607957cf80927e2575e455074fea17ec"/><file name="mysql4-upgrade-1.0.0.70-1.0.0.84.php" hash="e51deaff9e65f43483ab00573605329d"/><file name="mysql4-upgrade-1.0.0.84-1.0.0.88.php" hash="89bd8a585c0d351aae6838ace48f608d"/><file name="mysql4-upgrade-1.0.0.88-1.2.0.0.php" hash="09f98d701b5030ccc27d9ca1503212c4"/><file name="mysql4-upgrade-1.2.0.0-1.2.0.1.php" hash="01a7ef2466b9f676884db4d7a7c562a9"/><file name="mysql4-upgrade-1.2.0.1-1.2.1.0.php" hash="d8e9568d81d9efbb215d3a46e1d3212c"/></dir></dir></dir><dir name="DataServices"><file name="HarvestingManager.php" hash="1a6c1146e46df418da49138514549f0d"/></dir><file name="Log.php" hash="52878fa6de3392b58974e2c8a5fb2031"/><dir name="Services"><file name="Abstract.php" hash="f779d8f0a1eaaa20364ccbf400c9e0ae"/><dir name="Cmd"><file name="Forecast.php" hash="3ac2b917db8c05ac085f213640b7be14"/><file name="Halt.php" hash="af1a47f7e9940c29b9c8380fbc69a447"/><file name="Harvest.php" hash="18236a9b3c56c46e2a7981d0f0b1de55"/><file name="Healthcheck.php" hash="a51b3b7766afa1e6cca60fc3b206fb85"/><file name="Parse.php" hash="dbf89d3e1395511728063128703e3e97"/><file name="Update.php" hash="804da06229cc47eb850291847883c96d"/></dir><dir name="Harvest"><file name="AttributeSets.php" hash="c37c4c0642a06a27c6a28bee27e20b14"/><file name="Carts.php" hash="9496d5150a9b5e1282adb78d96f846fc"/><file name="Categories.php" hash="fc1b5b26d612d3232838c975c3e55646"/><file name="CustomerAttributeSets.php" hash="67368d6ba001310912d39df0bd9037ad"/><file name="Customers.php" hash="9ae9805bb89a7546ad7c126281ce5220"/><file name="Guests.php" hash="940bcfd10c656195953b77159a4ace10"/><file name="Products.php" hash="d249ec369bdaa38964906f9998d77d4b"/><file name="Purchases.php" hash="859c8af86ee44a896d94ac69decb851a"/><file name="Subscribers.php" hash="8fa28e316d7396ca79300813ea9bd7ca"/></dir><file name="Harvest.php" hash="1ed4efb9aa1f0ae30a3a4afb2cbf98f6"/><dir name="Log"><file name="Installer.php" hash="584f730627201a9e73f1d4101ad3fedd"/><file name="Purchase.php" hash="6060265974fe6b45768617f96ebba6b1"/></dir><dir name="Post"><file name="Attribute.php" hash="7e7a307180eb9b9cd7f5b9137e4bd647"/><file name="AttributeSet.php" hash="66968b3ea659253789bbf96ec828afcc"/><file name="Cart.php" hash="7465d7587762b82ffaefef6f27b40a58"/><file name="Category.php" hash="68be39b090f46f3f438ad2ed9f97f09f"/><file name="Customer.php" hash="ecce6d30d3458e0786d4c6335a4c02cc"/><file name="Json.php" hash="86a5f26aa5367d8c4c66d278e4c02546"/><file name="Jsonstring.php" hash="9dfb5761d1a7835bf35040a073fa8fc4"/><file name="Product.php" hash="787e33146cc39d5f5eecd2d3a4ea842b"/><file name="Purchase.php" hash="80480bf01d11b1068d481c6ed50c2e1f"/><file name="Subscriber.php" hash="4d18235c4c0d11a8650b3e60c098908f"/></dir><file name="Post.php" hash="df362d5839aa050daa7b23660603b16d"/><file name="Priority.php" hash="d8fc0b57442d8e2e2ff7ba789c3d1fc9"/><file name="Registry.php" hash="c4f17c52b0814c29b7ab93e911a2de28"/><dir name="Store"><file name="Finalize.php" hash="6f61c4cd23ef3cd815b7eee545ecaa23"/><file name="Register.php" hash="523100a10a3ac39fa444801551bdbf25"/></dir><dir name="Update"><file name="Abstract.php" hash="7b9a1d36b4486e250587820731b7fb7c"/><file name="Connect.php" hash="3e4366a42e563ec3406a0fcb9a5f77bf"/><file name="Downloader.php" hash="934ef5788acb45ce94e0a32c1d705df1"/><file name="Installer.php" hash="a09ec2e9f7bbf12c117c8950e46072b0"/><file name="Package.php" hash="b025fa1c27aed03454e7d45d09705b3a"/></dir><dir name="Work"><file name="Cleanup.php" hash="d0915a0cde6d9cd4287a2f9fc4bc0056"/><file name="Manager.php" hash="507608c3fc9d34170e7326cb075d3b71"/><file name="Report.php" hash="2f56d51277b75d30e043d2151abcfc79"/><file name="Restart.php" hash="43af836b8950852efe62996facf4ad87"/><file name="Runner.php" hash="a2929a0e03dc99ac0ad5702b623eeb56"/><file name="Stop.php" hash="5e51ecf333282ad5d8dd6d40243c5e8e"/></dir></dir><dir name="Shadow"><dir name="Helper"><file name="Listeners.php" hash="432794c308714015009014a2620fca16"/></dir><dir name="Model"><dir name="Listeners"><file name="Observer.php" hash="27dae36d9c35a5176aeb4ee2b054a5bc"/></dir><file name="Timer.php" hash="bfdaef5388bfc7faf48544c18cee6854"/></dir><dir name="controllers"><file name="IndexController.php" hash="7740556a1a409412075fd3d1ce3514c5"/></dir><dir name="etc"><file name="config.xml" hash="1e96afde7389da5900c76a410436042a"/></dir></dir><dir name="Util"><file name="Caller.php" hash="7674f6d3f6d0fc5e238c7e31e0516c52"/><file name="Categories.php" hash="24333b598d8caab3bc796e4bd0e34559"/><dir name="Log"><file name="Rollover.php" hash="47ecb75b56743147d77b33219ac657ac"/></dir><file name="Logger.php" hash="9e9bcd3272bd908d8962a31ae5f5f998"/><file name="Partition.php" hash="5a8e39c1b54f4b8d5bc8793ea5f729be"/></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="bmbleb.xml" hash="aa390e236576f79375b407262961043e"/></dir><dir name="template"><dir name="bmbleb"><dir name="alert"><file name="finished.phtml" hash="cc41edbaf3d492afbfe63efa2a17ac48"/><file name="index.phtml" hash="0308f889169436921999cdec62706676"/><dir name="next"><file name="form.phtml" hash="f6acfc69d606c397f717f97a331e7f2c"/></dir></dir><file name="auth.phtml" hash="2fc86d90ab53799e7309d3f88afff077"/><file name="dashboard_loggedout.phtml" hash="19281143b19a544d4e3072dc754ada2d"/><dir name="help"><file name="index.phtml" hash="e9d3f11c623c735c3e699e406ff9e0e7"/></dir><dir name="index"><file name="messages.phtml" hash="fcbbb47d2cc30c493ed2316a8b888f5d"/><file name="terms.phtml" hash="47772def9e3490041d9374dde594aa34"/></dir><file name="index.phtml" hash="86ad04e7c0f91dad8e2a08a3f5d70b64"/><dir name="jobs"><file name="status.phtml" hash="77f0b0ae7c3c6c42031675cfc959e270"/></dir><file name="jobs.phtml" hash="961ac83f56bf8703dbc433894da4933e"/><file name="login.phtml" hash="0a1a20dfaffe8646bb56323ab811d46a"/><file name="logout.phtml" hash="09b92790c5e124a01086d6929ed7e8de"/><dir name="logs"><file name="index.phtml" hash="17e773a761a24e292b09fe7da1bd7662"/></dir><file name="notifications.phtml" hash="45f8767a090a4f7a7e177151bbc43f4f"/><dir name="problems"><file name="index.phtml" hash="2027d07eed8848a4ed8e801d67072796"/></dir><file name="status.phtml" hash="4c6d8cd43e39184f35c715b785c8595a"/><file name="tabs.phtml" hash="778b686fb073a8aeb973db3bfb0302aa"/></dir></dir></dir></dir><dir name="base"><dir name="default"><dir name="layout"><file name="bmbleb.xml" hash="df1c52c006a507e5c9bc73e66adf8ae8"/></dir></dir></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="bmbleb"><file name="bmbleb.css" hash="8aea7d5d46e2a0d0ece11abca6ef5d3f"/><dir name="images"><file name="arrows_up-down-large.png" hash="72c27995e1ab1d182891dad0a4d1dae2"/><file name="bmb-ctl.png" hash="de59a694a82b8699560df5146b2e315f"/><file name="check.png" hash="126f33ed483549e79a16186b7499c190"/><file name="grn-bg.png" hash="f681a524e2b4561dbe94152a2d24d60b"/><file name="h3-bg.png" hash="b93df0b0bdba8e8f6e0a07cc31fcc180"/><file name="icon-alert.png" hash="ac2e70efdcebc3813222d0d3ee62a6d9"/><file name="icon-bmbleb.png" hash="fb5574b5e63ee33b84eee26b3d8ef8e3"/><file name="icon-insights.png" hash="725fd29fe1b705e358c9080408693d3d"/><file name="icon-status.png" hash="bd13429f23166a6d431739010ea1b2cd"/><file name="left-ico1.png" hash="7d188f5e6021569750756f58067f0a3b"/><file name="left-ico2.png" hash="d2f6379a73290a8ffa4cb3e19a809d25"/><file name="left-ico3.png" hash="73bc75f7a746e54a75f14eda7a28a6b9"/><file name="left-ico4.png" hash="1da2c26187fed26b6c61599682b2dc4b"/><file name="left-ico5.png" hash="ada61cb32805f2cb8e8dace46361613e"/><file name="left-ico6.png" hash="1e62822267f72589ffa0771352a002da"/><file name="left-ico7.png" hash="16118412d581f0c83ce45c44f62f25a1"/><file name="left-ico8.png" hash="c7de2fe523c892b432b575648cc05631"/><file name="left-ico_demographics.png" hash="3fe23a2dea68f6c65114f248a8bdaa5e"/><file name="login-icn-b.png" hash="64e72070f595e215ece79736ac77ee2f"/><file name="login-icn.png" hash="6142cc2fc8ee2d1c40bf3c8f9ac1fa85"/><file name="logo.png" hash="8fb783f7d68fca3914123f56b8c066a4"/><file name="orng-bg.png" hash="074a6912ca2a139df537e3d15b6bc9b2"/><file name="plugin_dashboard_syncing.jpg" hash="8511648541f6f1b96ff1c53dda3a439b"/><file name="register.png" hash="f73fe51cf7df27ab11089385fa50714e"/><file name="registration-bg-25.png" hash="9d2cf77619cc8fce3ae4d44b0aae30c1"/><file name="registration-bg-50.png" hash="99942fdc8c3f88b0d4b09f87c9e39045"/><file name="registration-bg.png" hash="96365b39495e56ffe491dd9930fe221d"/><file name="spinner.gif" hash="add667817f25bce331a213ab3cc9621f"/><file name="springbot-ctl.png" hash="de59a694a82b8699560df5146b2e315f"/><file name="submit-btn-bg.png" hash="d98aa287b7b73dad9f780b22cb53fbdb"/><file name="sync_icon.png" hash="cb12f2e8943c8e324e3456375f953c86"/><file name="white-check.png" hash="126f33ed483549e79a16186b7499c190"/></dir></dir></dir></dir></dir></target><target name="mageweb"><dir name="shell"><file name="springbot.php" hash="1d00d3ab7a7e161628d6a8a9d3d43836"/></dir></target></contents>
55
+ <compatible/>
56
+ <dependencies><required><php><min>5.0.0</min><max>5.5.10</max></php></required></dependencies>
57
+ </package>
shell/springbot.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * There are a few similarities to this and Mage_Shell_Abstract. Springbot still
4
+ * officially supports Magento 1.3.*, therefore, we needed to roll our own.
5
+ */
6
+ class Springbot_Shell
7
+ {
8
+ protected $_action;
9
+
10
+ protected $_type;
11
+
12
+ protected $_appCode = 'admin';
13
+
14
+ protected $_appType = 'store';
15
+
16
+ protected $_magentoRootDir;
17
+
18
+ protected $_args;
19
+
20
+ protected $_registry = array(
21
+ 'harvest' => array(
22
+ 'attributeSets' => 'Springbot_Services_Harvest_AttributeSets',
23
+ 'carts' => 'Springbot_Services_Harvest_Carts',
24
+ 'categories' => 'Springbot_Services_Harvest_Categories',
25
+ 'customerAttributeSets' => 'Springbot_Services_Harvest_CustomerAttributeSets',
26
+ 'customers' => 'Springbot_Services_Harvest_Customers',
27
+ 'guests' => 'Springbot_Services_Harvest_Guests',
28
+ 'products' => 'Springbot_Services_Harvest_Products',
29
+ 'purchases' => 'Springbot_Services_Harvest_Purchases',
30
+ 'subscribers' => 'Springbot_Services_Harvest_Subscribers',
31
+ ),
32
+ 'post' => array(
33
+ 'attribute' => 'Springbot_Services_Post_Attribute',
34
+ 'attributeSet' => 'Springbot_Services_Post_AttributeSet',
35
+ 'cart' => 'Springbot_Services_Post_Cart',
36
+ 'category' => 'Springbot_Services_Post_Category',
37
+ 'customer' => 'Springbot_Services_Post_Customer',
38
+ 'json' => 'Springbot_Services_Post_Json',
39
+ 'product' => 'Springbot_Services_Post_Product',
40
+ 'purchase' => 'Springbot_Services_Post_Purchase',
41
+ 'subscriber' => 'Springbot_Services_Post_Subscriber',
42
+ 'jsonstring' => 'Springbot_Services_Post_Jsonstring'
43
+ ),
44
+ 'cmd' => array(
45
+ 'halt' => 'Springbot_Services_Cmd_Halt',
46
+ 'harvest' => 'Springbot_Services_Cmd_Harvest',
47
+ 'healthcheck' => 'Springbot_Services_Cmd_Healthcheck',
48
+ 'parse' => 'Springbot_Services_Cmd_Parse',
49
+ 'update' => 'Springbot_Services_Cmd_Update',
50
+ 'forecast' => 'Springbot_Services_Cmd_Forecast',
51
+ ),
52
+ 'store' => array(
53
+ 'register' => 'Springbot_Services_Store_Register',
54
+ 'finalize' => 'Springbot_Services_Store_Finalize'
55
+ ),
56
+ 'log' => array(
57
+ 'purchase' => 'Springbot_Services_Log_Purchase',
58
+ 'installer' => 'Springbot_Services_Log_Installer',
59
+ ),
60
+ 'work' => array(
61
+ 'cleanup' => 'Springbot_Services_Work_Cleanup',
62
+ 'runner' => 'Springbot_Services_Work_Runner',
63
+ 'manager' => 'Springbot_Services_Work_Manager',
64
+ 'restart' => 'Springbot_Services_Work_Restart',
65
+ 'stop' => 'Springbot_Services_Work_Stop',
66
+ 'report' => 'Springbot_Services_Work_Report'
67
+ ),
68
+ );
69
+
70
+ public function __construct()
71
+ {
72
+ $this->_validate();
73
+
74
+ require_once $this->getApplicationPath() . $this->getMagePath();
75
+ Mage::app($this->_appCode, $this->_appType);
76
+
77
+ $this->_parseArgs();
78
+ }
79
+
80
+ public function run()
81
+ {
82
+ try{
83
+ Springbot_Log::debug("Running {$this->_action}:{$this->_type}");
84
+ $className = $this->_registry[$this->_action][$this->_type];
85
+ $class = new $className();
86
+ $ret = $class->setData($this->_args)->run();
87
+ echo $ret;
88
+ } catch (Exception $e) {
89
+ Springbot_Log::error($e);
90
+ echo $e->getMessage() . PHP_EOL;
91
+ exit(1);
92
+ }
93
+ }
94
+
95
+ public function getMagePath()
96
+ {
97
+ return 'app' . DIRECTORY_SEPARATOR . 'Mage.php';
98
+ }
99
+
100
+ public function getApplicationPath()
101
+ {
102
+ if(!isset($this->_magentoRootDir)) {
103
+ if(file_exists($this->getMagePath())) {
104
+ $this->_magentoRootDir = getcwd() . DIRECTORY_SEPARATOR;
105
+ } else {
106
+ for ($i = 0, $d = ''; !file_exists($d.$this->getMagePath()) && $i++ < 10; $d .= '../');
107
+ $this->_magentoRootDir = getcwd() . DIRECTORY_SEPARATOR . $d;
108
+ }
109
+
110
+ if(!file_exists($this->_magentoRootDir . $this->getMagePath())) {
111
+ throw new Exception("Cannot find Mage root path!");
112
+ }
113
+ }
114
+ return $this->_magentoRootDir;
115
+ }
116
+
117
+ protected function _parseArgs()
118
+ {
119
+ try {
120
+ $argv = $_SERVER['argv'];
121
+
122
+ $opts = getopt('s:i:h:c:o:dfrv:m:j:n:p:');
123
+
124
+ list($this->_action, $this->_type) = explode(':', end($argv));
125
+
126
+ $this->_args = Springbot_Services_Registry::parseOpts($opts);
127
+
128
+ if(!isset($this->_action) || !isset($this->_type)) {
129
+ throw new Exception;
130
+ }
131
+
132
+ if(!isset($this->_registry[$this->_action][$this->_type])) {
133
+ $msg = 'Provided action not registered!';
134
+ print $msg . PHP_EOL;
135
+ Springbot_Log::debug($msg);
136
+ exit(1);
137
+ }
138
+ } catch (Exception $e) {
139
+ Springbot_Log::error($e);
140
+ $this->_usage();
141
+ exit;
142
+ }
143
+ }
144
+
145
+ protected function _validate()
146
+ {
147
+ }
148
+
149
+ protected function _usage()
150
+ {
151
+ print <<<USAGE
152
+ Usage: \033[1mphp shell/springbot.php -s\033[0m \033[4mstore_id\033[0m \033[1m-i\033[0m \033[4mstart_id\033[0m:\033[4mend_id\033[0m \033[1maction:type\033[0m
153
+
154
+ USAGE;
155
+ }
156
+ }
157
+
158
+ $shell = new Springbot_Shell;
159
+
160
+ $shell->run();
skin/adminhtml/default/default/bmbleb/bmbleb.css ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* General Styles */
2
+ .clear {
3
+ clear: both;
4
+ }
5
+
6
+ #bmbleb-sync-now {
7
+ height:170px;
8
+ width:170px;
9
+ }
10
+
11
+ #bmbleb-sync-stats-left{width:170px; float: left; text-align:center;}
12
+ #bmbleb-sync-stats-right{width:600px; float: left; margin:32px 0 0 40px;}
13
+
14
+ table.bmbleb-stat-table {
15
+ font-family:Arial;
16
+ font-size: 14px;
17
+ font-style: normal;
18
+ font-weight: normal;
19
+ text-transform: capitalize;
20
+ letter-spacing: -1px;
21
+ line-height: 1.7em;
22
+ text-align:left;
23
+ border-collapse:collapse;
24
+ }
25
+
26
+ table.bmbleb-stat-table td {
27
+ padding:5px 0 5px 10px;
28
+ text-shadow:1px 1px 1px #fff;
29
+ }
30
+
31
+ table.bmbleb-stat-table tr{
32
+ border-bottom:1px solid #fff;
33
+ }
34
+
35
+ table.bmbleb-stat-table tr.bmbleb-table-last-row{
36
+ border-bottom:none;
37
+ }
38
+
39
+ table.bmbleb-stat-table tr td.cell1{
40
+ font-weight:bold;
41
+ color: #338007;
42
+ border-right:1px solid #c0c0c0;
43
+ width:75px
44
+ }
45
+ table.bmbleb-stat-table tr td.cell2{
46
+ width:325px
47
+ }
48
+
49
+ table.bmbleb-stat-table{
50
+ font-style: normal;
51
+ font-weight: normal;
52
+ letter-spacing: -1px;
53
+ line-height: 1.2em;
54
+ border-collapse:collapse;
55
+ text-align:center;
56
+ }
57
+
58
+ .bmbleb-stat-table thead th {
59
+ padding:20px 10px 20px 10px;
60
+ color:#fff;
61
+ background-color:#222;
62
+ border-right:1px solid #666;
63
+ -moz-box-shadow:0px -1px 4px #000;
64
+ -webkit-box-shadow:0px -1px 4px #000;
65
+ box-shadow:0px -1px 4px #000;
66
+ text-shadow:0px 0px 1px #fff;
67
+ text-shadow:1px 1px 1px #000;
68
+ }
69
+
70
+ .bmbleb-stat-table thead :nth-last-child(1){
71
+ border-right:none;
72
+ }
73
+
74
+ .bmbleb-stat-tablethead :first-child,
75
+ .bmbleb-stat-table tbody :nth-last-child(1){
76
+ border:none;
77
+ }
78
+
79
+ .bmbleb-stat-table tbody td{
80
+ padding:10px;
81
+ background-color:#f0f0f0;
82
+ border-right:1px solid #9e9e9e;
83
+ text-shadow:-1px 1px 1px #fff;
84
+ text-transform:uppercase;
85
+ color:#333;
86
+ }
87
+
88
+ .bmbleb-account-index col {
89
+ width:30%;
90
+ }
91
+
92
+ .bmbleb-mgr-status label {
93
+ padding-right: 8px;
94
+ display: inline-block;
95
+ width: 70%;
96
+ }
97
+
98
+ .bmbleb-mgr-status button {
99
+ float: right;
100
+ }
101
+
102
+ .bmbleb-mgr-status .status-alert-active {
103
+ font-weight: bold;
104
+ color: green;
105
+ }
106
+
107
+ .bmbleb-mgr-status .status-alert-inactive {
108
+ font-weight: bold;
109
+ color: red;
110
+ }
111
+
112
+ /*-----------style for new design-----*/
113
+
114
+ #login-register-wrapper {width:600px; margin:0px auto; font-family:Arial, Helvetica, sans-serif;}
115
+ #login-register-wrapper .bmb-logo {float:left; margin:0 25px 0 0;}
116
+ #login-register-wrapper .content-header-block {float:left; }
117
+ #login-register-wrapper .content-header-block h3{float:none; font-size:24px; padding-bottom:10px; color:#63A814; margin-bottom:5px; background:url(images/top-sept.png) no-repeat bottom left;}
118
+ #login-register-wrapper .header-content {float:left; width:auto;}
119
+ #login-register-wrapper .header-content h2 {font-size:18px; color:#000; margin:0px; padding:0px;}
120
+ #login-register-wrapper .header-content p {font-size:14px; color:#333333; padding:5px 0;}
121
+ #login-register-wrapper .form-wrap {margin:2px 3px 3px 2px; min-height:420px; background:#F9F9F9; border:1px solid #CCCCCC; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px; -o-border-radius:5px; -webkit-box-shadow: 0px 0px 2px 1px #EEEEEE; -moz-box-shadow: 0px 0px 2px 1px #EEEEEE; box-shadow: 0px 0px 2px 1px #EEEEEE;}
122
+ /* removed font-weight: bold from the line below this one for IE9 fix */
123
+ #login-register-wrapper .form-wrap .entry-edit-head {border-bottom:1px solid #CCCCCC; padding: 14px 10px; color:#63A814; font-size:18px; }
124
+ #login-register-wrapper .form-wrap .entry-edit-head h4 {color:#63A814; padding:0 0 0 50px; margin:0 0 0 15px; height:28px; line-height:28px; background:url(images/login-icn.png) no-repeat left center; display:block;}
125
+ #login-register-wrapper .form-wrap .fieldset {padding:20px 0px 0 80px;}
126
+ #login-register-wrapper .form-wrap .fieldset label {color:#000; font-size:14px; padding: 0 0 4px; display:block;}
127
+
128
+ #login-register-wrapper .form-wrap input.input-text {padding:5px 10px; height:25px; line-height:25px; border:1px solid #999999; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px; -o-border-radius:5px; width:300px; -webkit-box-shadow: 0 1px 2px 0 #666666 inset; -moz-box-shadow: 0 1px 2px 0 #666666 inset; box-shadow: 0 1px 2px 0 #666666 inset; font-size:14px; }
129
+ #login-register-wrapper button {background: url(images/submit-btn-bg.png) repeat-x scroll top left ; border:1px solid #265602; border-radius: 5px 5px 5px 5px; color: #FFFFFF; cursor: pointer; font-size: 14px; font-weight: bold; height: 34px; padding: 5px 15px; -webkit-box-shadow: 0px 1px 4px 0px #cccccc; -moz-box-shadow: 0px 1px 4px 0px #cccccc; box-shadow: 0px 1px 4px 0px #cccccc; font-size:16px; text-shadow: 1px 1px #333333;}
130
+ #login-register-wrapper button:hover {background-position: 0 -37px;}
131
+ /* formerly - removed entry-edit wrapping element
132
+ #login-register-wrapper .form-wrap .form-buttons {margin:15px 0 0 0; padding:0 0 0 5px;}
133
+ */
134
+ #login-register-wrapper .form-buttons {margin:15px 0 0 0; padding:0 0 0 5px;}
135
+ #login-register-wrapper .forgot-pwd {float:left; width:100%; margin:50px 0 0 0;}
136
+ #login-register-wrapper .forgot-pwd a {color:#63A814; text-decoration:underline; font-size:13px;}
137
+ #login-register-wrapper .forgot-pwd a:hover {text-decoration:none;}
138
+ #login-register-wrapper .form-wrap form#register_form .entry-edit-head h4 {background:url(images/register.png) no-repeat left center;}
139
+ #login-register-wrapper .form-wrap.last {background:#F9F9F9 url(images/registration-bg.png) no-repeat left top;}
140
+
141
+ /* New Login Form Hacks, Broke in > IE7 */
142
+ .middle #login-register-wrapper td { float:left; display:inline; padding:0; }
143
+ .middle #login-register-wrapper td.label { height:14px; margin:0; padding:0; }
144
+ .middle #login-register-wrapper td.value input.input-text { margin:0; }
145
+
146
+
147
+
148
+ /* CSS add by Jitendra On 14 March (14-03-2012) */
149
+ .left-logo {float:left; width:100%; height:104px;}
150
+ .left-logo img{float:left; margin-top:17px;}
151
+ ul.tabs {float:left; width:218px; clear:both;}
152
+ ul.tabs li a {background:#E7EFEF;}
153
+ ul.tabs li a:hover {background:#D8E6E6;}
154
+ ul.tabs li a span {padding-left:34px; background:url(images/left-ico1.png) no-repeat 5px 50%; }
155
+ ul.tabs li {border-bottom:dotted #636363 1px;}
156
+ ul.tabs li a.active {border:none; background-color:#fff; color:#67AB18;}
157
+ ul.tabs li.ico1 a span, ul.tabs li.ico1 a:hover span, ul.tabs li.ico1 a.active span {background:url(images/left-ico1.png) no-repeat 5px 50%;}
158
+ ul.tabs li.ico2 a span, ul.tabs li.ico2 a:hover span, ul.tabs li.ico2 a.active span {background:url(images/left-ico2.png) no-repeat 5px 50%;}
159
+ ul.tabs li.ico3 a span, ul.tabs li.ico3 a:hover span, ul.tabs li.ico3 a.active span {background:url(images/left-ico3.png) no-repeat 5px 50%;}
160
+ ul.tabs li.ico4 a span, ul.tabs li.ico4 a:hover span, ul.tabs li.ico4 a.active span {background:url(images/left-ico4.png) no-repeat 5px 50%;}
161
+ ul.tabs li.ico5 a span, ul.tabs li.ico5 a:hover span, ul.tabs li.ico5 a.active span {background:url(images/left-ico5.png) no-repeat 5px 50%;}
162
+ ul.tabs li.ico6 a span, ul.tabs li.ico6 a:hover span, ul.tabs li.ico6 a.active span {background:url(images/left-ico6.png) no-repeat 5px 50%;}
163
+ ul.tabs li.ico7 a span, ul.tabs li.ico7 a:hover span, ul.tabs li.ico7 a.active span {background:url(images/left-ico7.png) no-repeat 5px 50%;}
164
+ ul.tabs li.ico8 a span, ul.tabs li.ico8 a:hover span, ul.tabs li.ico8 a.active span {background:url(images/left-ico8.png) no-repeat 5px 50%;}
165
+ ul.tabs li.ico9 a span, ul.tabs li.ico9 a:hover span, ul.tabs li.ico9 a.active span {background:url(images/left-ico_demographics.png) no-repeat 5px 50%;}
166
+
167
+ .j-content {float:left; width:100%; font-size:14px; min-width:970px; color:#333; }
168
+
169
+ .j-content .j-box-left {float:left; min-width:310px; margin-right:20px; width:31%;}
170
+ .j-content .j-box-left h3, h3.alert-head {background:url(images/h3-bg.png) repeat-x bottom; height:72px; float:left; width:100%; margin-bottom:20px; line-height:72px; color:#333333; font-size:24px; font-weight:bold;}
171
+ .j-content .j-box-left h3 span.head, h3.alert-head span.head {float:left; padding-left:50px; background:url(images/icon-alert.png) no-repeat 4px 50%; height:68px; line-height:68px;}
172
+ .j-content .j-box-left h3 .counter {height:33px; line-height:33px; float:right; background:#FFF9E9; border:solid 1px #EEE2BE; padding:0 8px; color:#000; font-size:18px; font-weight:bold; border-radius:3px; -moz-border-radius:3px; -webkit-border-radius:3px; margin-top:16px;}
173
+ .j-content .j-box-left.box2 h3 span.head {background-image:url(images/icon-insights.png);}
174
+ .j-content .j-box-left.box2 h3 {margin-bottom:10px;}
175
+ .j-content .j-box-left.box3 h3 span.head {background-image:url(images/icon-status.png);}
176
+ .j-content .j-box-left p {color:#333; margin-bottom:20px;}
177
+
178
+
179
+ .timebox {float:left; width:100%; height:116px; position:relative; margin-top:12px;}
180
+ .timebox .watch-m {position:absolute; top:0; left:0; z-index:2;}
181
+ .timebox .timebox-bg {position:absolute; z-index:1; height:48px; width:100%; left:0; top:34px; background:#E1EDD1; border-radius:5px; box-shadow:0 0 3px #aaa inset;}
182
+ .jbtn {background: url(images/submit-btn-bg.png) repeat-x scroll top left ; border:1px solid #265602; border-radius: 5px; color: #FFFFFF !important; cursor: pointer; font-size: 14px; font-weight: bold; height: 34px; line-height:34px; padding: 0px;text-align:center; -webkit-box-shadow: 0px 1px 4px 0px #cccccc; -moz-box-shadow: 0px 1px 4px 0px #cccccc; box-shadow: 0px 1px 4px 0px #cccccc; font-size:16px; float:right; margin:6px 6px 0 0 ; text-decoration:none !important; text-shadow: 1px 1px #333333;}
183
+ .jbtn.btn1 {float:left; padding:0 12px;}
184
+ .jbtn:hover {background-position: 0 -37px;}
185
+ .cancle {float:left; margin:14px 0 0 10px; }
186
+ .timebox .timebox-bg .timebox-in.jbtn { width:160px; }
187
+
188
+ .chart-box-outer {width:310px; margin:0 auto;}
189
+ .chart-box {float:left; width:100%; text-align:center; position:relative;}
190
+
191
+ .box2-down {float:left; width:100%; text-align:center; margin-top:50px; line-height:24px;}
192
+ .j-content a {color:#63A814; text-decoration:underline;}
193
+ .j-content a:hover {text-decoration:none;}
194
+ .box3-up {float:left; width:100%;}
195
+ .box3-tbl td {border-bottom:solid 1px #ccc; padding:10px 0; color:#666;}
196
+ .box3-tbl td+td+td {font-weight:bold; color:#000;}
197
+ .box3-tbl tr:first-child td {padding-top:0;}
198
+
199
+ .j-box-left.box3 {margin-right:0;}
200
+ .box3-up .link {float:left; margin:10px 0;}
201
+ .upgrade-box {width:100%; background:#E1EDD1; border-radius:5px; box-shadow:0 0 3px #aaa inset; float:left; margin-top:10px;}
202
+ .upgrade-box a.upg-btn.jbtn { width:90%; margin:20px 0 20px 5%; float:left; }
203
+
204
+ .upgrade-box span {float:left; width:100%; text-align:center; padding-bottom:23px;}
205
+ .wrapper {min-width:1245px;}
206
+
207
+ .headline {float:left; width:100%; height:35px; background:url(images/grn-bg.png) no-repeat left; margin-bottom:15px;}
208
+ .headline.orng {background-image:url(images/orng-bg.png);}
209
+ .headline span {float:left; margin-left:12px; line-height:35px; font-weight:bold; }
210
+ .j-content h4 {float:left; width:100%; font-size:14px; font-weight:bold; color:#333; margin:0; padding:6px 0;}
211
+ .j-content h2 {float:left; width:100%; font-size:18px; color:#333; margin:0; padding:10px 0;}
212
+ .marl12 {float:left; margin-left:12px;}
213
+ .w100per {width:100%; float:left;}
214
+ .marb20 {margin-bottom:20px;}
215
+
216
+ .jleft-box {width:638px; float:left; background:#FFF9E9; border:solid 1px #EEE2BE; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; margin-bottom:10px; line-height:1;}
217
+ .jright-box {width:310px; float:right; background:#E1EDD1; box-shadow:0 0 3px #aaa inset; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; overflow:hidden; text-align:center; font-weight:bold; font-size:16px; line-height:1; margin-bottom:10px;}
218
+ .pad12 {padding:12px;}
219
+ .to-do {float:left; width:100%; padding-bottom:20px; font-size:16px;}
220
+ .to-do span.ttl {display:block; float:left; width:100%; color:#666; font-size:13px; margin-bottom:8px; font-weight:bold;}
221
+ .jleft-box .to-do {padding:12px 0; font-size:14px;}
222
+
223
+ .jright-box .timer-up {float:left; width:100%; padding:17px 0 10px 0;}
224
+ .jright-box .timer-mid {float:left; width:100%; font-size:48px; padding:0px 0 10px 0;}
225
+ .jright-box .timer-down {float:left; width:100%; background:url(images/check.png) no-repeat 41px 12px #63A814; height:43px; line-height:43px; color:#fff; }
226
+ .pad12left {padding-left:12px;}
227
+ .msg-row {color:#666; margin-bottom:15px;}
228
+
229
+ .entry-edit-form td.label1 {padding:5px 5px 0 5px;}
230
+ .entry-edit-form td.label2 {padding:0px 5px 5px 5px;}
231
+ .entry-edit-form .content-footer, .entry-edit-form .hor-scroll {float:left;}
232
+ .entry-edit-form .content-footer {*float:none;}
233
+
234
+ /* CSS jitendra ends */
235
+
236
+
237
+ /* Help Screen */
238
+ #help-screen-wrap { width:100%; }
239
+ #help-screen-wrap:after { content:'.'; clear:both; display:block; height:0; visibility:hidden; }
240
+ #help-screen-wrap .col-one { float:left; margin-left:30px; width:60%; }
241
+ #help-screen-wrap .col-two { float:left; margin-left:30px; width:30%; }
242
+
243
+ #help-screen-wrap label.gsfn_label { display:block; margin-bottom:20px; font-weight:bold; }
244
+ #help-screen-wrap input#gsfn_search_query {
245
+ border: 1px solid #999999;
246
+ border-radius: 5px 5px 5px 5px;
247
+ box-shadow: 0 1px 2px 0 #666666 inset;
248
+ font-size: 14px;
249
+ height: 25px;
250
+ line-height: 25px;
251
+ margin:0 0 20px;
252
+ padding: 5px 10px;
253
+ width: 300px;
254
+ }
255
+ #help-screen-wrap input#continue { clear:both; display:block; }
256
+
257
+ /* Terms & Conditions */
258
+ .tc-info { float:right; display:inline; padding-top:20px; }
259
+ .tc-link {}
260
+ .tc-link a {}
261
+
262
+ .tc-content { position:relative; margin-bottom:40px; }
263
+ .tc-content-inner {}
264
+ .tc-content { display:none; height:0; }
265
+ .tc-content h3 { color:#333; font-size:24px; font-weight:bold; line-height:72px; margin-bottom:20px; }
266
+ .tc-content hr { margin:30px 0; }
267
+ .tc-content .close-btn { font-size:12px; font-weight:bold; text-align:right; padding-top:10px; }
268
+ #MB_window .tc-content { display:block; height:auto; padding:30px; }
269
+ #MB_window { z-index:999; }
270
+ #MB_window,
271
+ #MB_frame { background-color:#fff!important; }
272
+ #MB_caption { text-shadow:none; }
273
+
274
+ /* IE7 Hack */
275
+ .main-col { z-index:999!important; }
276
+
277
+ /* extras for login/register form */
278
+ #login-register-wrapper div.form-wrap { width:48%; float:left; margin-top:20px; margin-right:20px; }
279
+ #login-register-wrapper div.form-wrap.last { margin-right:0; }
280
+
281
+ #login-register-wrapper div.form-wrap .form-list td.value input.input-text { width:90%; }
282
+ #login-register-wrapper div.form-wrap .form-list td.label label { width:100%; padding-right:10px; }
283
+
284
+ #login-register-wrapper .entry-edit .entry-edit-head { background:none; }
285
+ /*#login-register-wrapper .entry-edit fieldset, .entry-edit .fieldset { background:none; border:none; } */
286
+
287
+ #login-register-wrapper div.form-wrap div.content-header { display:none; }
288
+ #login-register-wrapper .form-wrap .content-footer { padding-left:80px; }
289
+ #login-register-wrapper .form-buttons { padding-left:0px; }
290
+ #login-register-wrapper .forgot-pwd { padding-left:80px; }
291
+
292
+ #login-register-wrapper td { display: table-row; }
293
+ #login-register-wrapper td.value input.input-text { margin-bottom:10px; }
294
+
295
+ /* demographics tab */
296
+ .demographics li { width:220px; min-height:150px; float:left; margin:10px 10px 0 0; background:#efefef; border:solid #636363 1px; padding:5px; }
297
+ .demographics li.last { clear:left; }
298
+ .demographics li .chart-box { height:250px; }
299
+
300
+ .demographics dt { clear:left; float:left; width:150px; text-align:left; margin-left:10px; font-weight:bold; }
301
+ .demographics dd { }
302
+ .demographics h3 { }
303
+ .demographics p { font-weight:normal; font-size:11px; margin-top:15px; }
304
+
305
+
306
+ table.age-makeup {}
307
+ table.age-makeup tr {}
308
+ table.age-makeup td { width:200px; text-align:center; padding:2px 0; color:#2F2F2F; }
309
+ table.age-makeup td.label { width:100px; }
310
+
311
+ table.age-makeup tr.age-group td { background-color:#efefef; border:1px solid #999; }
312
+ table.age-makeup tr.gender td { height:100px; }
313
+
314
+ table.age-makeup tr.gender-male td { vertical-align:bottom; padding-bottom:0; margin-bottom:0; }
315
+ table.age-makeup tr.gender-female td { vertical-align:top; padding-top:0; margin-top:0; }
316
+
317
+ table.age-makeup tr.gender td span { display:block; background:#E7EFEF; border:dotted #636363 1px; }
318
+ table.age-makeup tr.gender-male td span { border-bottom:none; }
319
+ table.age-makeup tr.gender-female td span { border-top:none; }
320
+
321
+ /* insights table */
322
+ .insights-col { width:50%; float:left; }
323
+ table.insights-compare th { text-align:center; padding:2px 5px; }
324
+ table.insights-compare td { text-align:right; width:75px; padding:2px 5px; }
325
+ table.insights-compare td.label { text-align:left; width:auto; }
326
+
327
+ .j-box-left table.insights-compare { width:100%; }
328
+ .j-box-left table.insights-compare td { width:145px; }
329
+ .j-box-left table.insights-compare td.label { width:auto; }
330
+
331
+ table.insights-compare .increase { color:green; padding-right:15px; background:url(images/arrows_up-down-large.png) no-repeat right 8px; }
332
+ table.insights-compare .decrease { color:red; padding-right:15px; background:url(images/arrows_up-down-large.png) no-repeat right -38px; }
333
+
334
+ /* faces grid */
335
+ .faces-grid li { position:relative; float:left; width:100px; height:100px; margin:1px; }
336
+ .faces-grid img.avatar { width:100px; height:100px; position:relative; z-index:1; }
337
+ .faces-grid li .name { display: block; position: absolute; width: 92px; background: #333; color: white; z-index: 99; text-decoration: none; bottom: 0px; overflow:none; font-size:10px; padding:0 4px; }
338
+
339
+ .monospace-textarea { font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; }
skin/adminhtml/default/default/bmbleb/images/arrows_up-down-large.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/bmb-ctl.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/check.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/grn-bg.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/h3-bg.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/icon-alert.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/icon-bmbleb.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/icon-insights.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/icon-status.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico1.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico2.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico3.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico4.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico5.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico6.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico7.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico8.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/left-ico_demographics.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/login-icn-b.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/login-icn.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/logo.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/orng-bg.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/plugin_dashboard_syncing.jpg ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/register.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/registration-bg-25.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/registration-bg-50.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/registration-bg.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/spinner.gif ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/springbot-ctl.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/submit-btn-bg.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/sync_icon.png ADDED
Binary file
skin/adminhtml/default/default/bmbleb/images/white-check.png ADDED
Binary file