SearchSpring_Manager - Version 1.1.0

Version Notes

Bringing Live Indexing up to stable state

Download this release

Release Info

Developer SearchSpring Development Team
Extension SearchSpring_Manager
Version 1.1.0
Comparing to
See all releases


Version 1.1.0

Files changed (83) hide show
  1. app/code/community/SearchSpring/ErrorCodes.php +46 -0
  2. app/code/community/SearchSpring/Manager/Block/Layer/Filter/SearchSpringCategory.php +37 -0
  3. app/code/community/SearchSpring/Manager/Block/Layer/Filter/SearchSpringList.php +41 -0
  4. app/code/community/SearchSpring/Manager/Block/Layer/View.php +186 -0
  5. app/code/community/SearchSpring/Manager/Builder/OperationBuilder.php +196 -0
  6. app/code/community/SearchSpring/Manager/Collection.php +60 -0
  7. app/code/community/SearchSpring/Manager/Entity/IndexingRequestBody.php +91 -0
  8. app/code/community/SearchSpring/Manager/Entity/OperationsCollection.php +17 -0
  9. app/code/community/SearchSpring/Manager/Entity/RecordsCollection.php +142 -0
  10. app/code/community/SearchSpring/Manager/Entity/RequestBody.php +16 -0
  11. app/code/community/SearchSpring/Manager/Entity/RequestParams.php +81 -0
  12. app/code/community/SearchSpring/Manager/Entity/SearchCollection.php +138 -0
  13. app/code/community/SearchSpring/Manager/Entity/SearchRequestBody.php +131 -0
  14. app/code/community/SearchSpring/Manager/Entity/SearchResult.php +69 -0
  15. app/code/community/SearchSpring/Manager/Entity/SearchResult/Result.php +37 -0
  16. app/code/community/SearchSpring/Manager/Factory/ApiFactory.php +70 -0
  17. app/code/community/SearchSpring/Manager/Factory/GeneratorFactory.php +222 -0
  18. app/code/community/SearchSpring/Manager/Factory/IndexingRequestBodyFactory.php +38 -0
  19. app/code/community/SearchSpring/Manager/Factory/PricingFactory.php +53 -0
  20. app/code/community/SearchSpring/Manager/Factory/SearchRequestBodyFactory.php +22 -0
  21. app/code/community/SearchSpring/Manager/Generator/ProductGenerator.php +68 -0
  22. app/code/community/SearchSpring/Manager/Handler/ApiErrorHandler.php +90 -0
  23. app/code/community/SearchSpring/Manager/Helper/Data.php +117 -0
  24. app/code/community/SearchSpring/Manager/Iterator.php +21 -0
  25. app/code/community/SearchSpring/Manager/Model/Catalog/Category/Attribute/EnableSearchSpring/Backend.php +26 -0
  26. app/code/community/SearchSpring/Manager/Model/Catalog/Category/Attribute/EnableSearchSpring/Source.php +18 -0
  27. app/code/community/SearchSpring/Manager/Model/Layer.php +65 -0
  28. app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpring.php +131 -0
  29. app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpringCategory.php +109 -0
  30. app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpringCategoryItem.php +29 -0
  31. app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpringItem.php +126 -0
  32. app/code/community/SearchSpring/Manager/Model/Observer/CategorySaveObserver.php +202 -0
  33. app/code/community/SearchSpring/Manager/Model/Observer/LiveIndexer.php +26 -0
  34. app/code/community/SearchSpring/Manager/Model/Observer/ProductSaveObserver.php +186 -0
  35. app/code/community/SearchSpring/Manager/Operation/Product.php +121 -0
  36. app/code/community/SearchSpring/Manager/Operation/Product/SetCategories.php +125 -0
  37. app/code/community/SearchSpring/Manager/Operation/Product/SetCoreFields.php +169 -0
  38. app/code/community/SearchSpring/Manager/Operation/Product/SetFields.php +78 -0
  39. app/code/community/SearchSpring/Manager/Operation/Product/SetId.php +30 -0
  40. app/code/community/SearchSpring/Manager/Operation/Product/SetImages.php +90 -0
  41. app/code/community/SearchSpring/Manager/Operation/Product/SetOptions.php +47 -0
  42. app/code/community/SearchSpring/Manager/Operation/Product/SetPricing.php +79 -0
  43. app/code/community/SearchSpring/Manager/Operation/Product/SetRatings.php +56 -0
  44. app/code/community/SearchSpring/Manager/Operation/ProductOperation.php +41 -0
  45. app/code/community/SearchSpring/Manager/Provider/ProductCollection/FeedProvider.php +99 -0
  46. app/code/community/SearchSpring/Manager/Provider/ProductCollection/ProductProvider.php +101 -0
  47. app/code/community/SearchSpring/Manager/Provider/ProductCollectionProvider.php +28 -0
  48. app/code/community/SearchSpring/Manager/Request/JSON.php +56 -0
  49. app/code/community/SearchSpring/Manager/Service/SearchSpring/ApiAdapter.php +77 -0
  50. app/code/community/SearchSpring/Manager/Service/SearchSpring/IndexingApiAdapter.php +64 -0
  51. app/code/community/SearchSpring/Manager/Service/SearchSpring/SearchApiAdapter.php +72 -0
  52. app/code/community/SearchSpring/Manager/Strategy/Pricing/BundleStrategy.php +69 -0
  53. app/code/community/SearchSpring/Manager/Strategy/Pricing/ConfigurableStrategy.php +29 -0
  54. app/code/community/SearchSpring/Manager/Strategy/Pricing/GroupedStrategy.php +47 -0
  55. app/code/community/SearchSpring/Manager/Strategy/Pricing/SimpleStrategy.php +29 -0
  56. app/code/community/SearchSpring/Manager/Strategy/Pricing/Strategy.php +190 -0
  57. app/code/community/SearchSpring/Manager/Strategy/PricingStrategy.php +49 -0
  58. app/code/community/SearchSpring/Manager/String/Sanitizer.php +46 -0
  59. app/code/community/SearchSpring/Manager/Transformer/ProductCollectionToRecordCollectionTransformer.php +91 -0
  60. app/code/community/SearchSpring/Manager/Validator/ProductValidator.php +132 -0
  61. app/code/community/SearchSpring/Manager/VarienObject/Data.php +94 -0
  62. app/code/community/SearchSpring/Manager/Writer/Product/FileWriter.php +145 -0
  63. app/code/community/SearchSpring/Manager/Writer/Product/Params/FileWriterParams.php +155 -0
  64. app/code/community/SearchSpring/Manager/Writer/Product/Params/ResponseWriterParams.php +95 -0
  65. app/code/community/SearchSpring/Manager/Writer/Product/ResponseWriter.php +49 -0
  66. app/code/community/SearchSpring/Manager/Writer/ProductWriter.php +33 -0
  67. app/code/community/SearchSpring/Manager/controllers/GenerateController.php +174 -0
  68. app/code/community/SearchSpring/Manager/etc/adminhtml.xml +26 -0
  69. app/code/community/SearchSpring/Manager/etc/composer.json +15 -0
  70. app/code/community/SearchSpring/Manager/etc/config.xml +138 -0
  71. app/code/community/SearchSpring/Manager/etc/readme.md +55 -0
  72. app/code/community/SearchSpring/Manager/etc/system.xml +154 -0
  73. app/design/frontend/base/default/layout/searchspring_manager.xml +41 -0
  74. app/design/frontend/base/default/template/searchspring_manager/layer/category_filter.phtml +29 -0
  75. app/design/frontend/base/default/template/searchspring_manager/layer/filter.phtml +20 -0
  76. app/design/frontend/base/default/template/searchspring_manager/layer/state.phtml +29 -0
  77. app/design/frontend/base/default/template/searchspring_manager/layer/view.phtml +32 -0
  78. app/design/frontend/base/default/template/searchspring_manager/product/list.phtml +37 -0
  79. app/design/frontend/base/default/template/searchspring_manager/product/list/toolbar.phtml +94 -0
  80. app/etc/modules/SearchSpring_SearchSpringManager.xml +12 -0
  81. app/locale/en_US/template/email/searchspring_api_error.html +1 -0
  82. package.xml +2 -0
  83. skin/frontend/base/default/searchspring/css/styles.css +10 -0
app/code/community/SearchSpring/ErrorCodes.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ErrorCodes.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_ErrorCodes
10
+ *
11
+ * Hold a list of all the error codes that can be returned from the API
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_ErrorCodes
16
+ {
17
+ /**
18
+ * If we can not resolve the product type for a product
19
+ */
20
+ const PRODUCT_TYPE_NOT_FOUND = 1;
21
+
22
+ /**
23
+ * Could not write to the directory
24
+ */
25
+ const DIR_NOT_WRITABLE = 2;
26
+
27
+ /**
28
+ * Could not create feed for unkown reason
29
+ */
30
+ const FEED_WRITE_ERROR = 3;
31
+
32
+ /**
33
+ * Filename is not passed into feed generation
34
+ */
35
+ const FILENAME_NOT_SET = 4;
36
+
37
+ /**
38
+ * Type parameter not found
39
+ */
40
+ const TYPE_NOT_SET = 5;
41
+
42
+ /**
43
+ * Ids parameter not found
44
+ */
45
+ const IDS_NOT_SET = 6;
46
+ }
app/code/community/SearchSpring/Manager/Block/Layer/Filter/SearchSpringCategory.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Block_Layer_Filter_SearchSpringCategory extends SearchSpring_Manager_Block_Layer_Filter_SearchSpringList
4
+ {
5
+
6
+ protected $_filterModelName = 'searchspring_manager/layer_filter_searchSpringCategory';
7
+ protected $_searchSpringFacet;
8
+
9
+ /**
10
+ * Return the name of the facet
11
+ *
12
+ * @return string
13
+ */
14
+ public function getName() {
15
+ return $this->__('Category');
16
+ }
17
+
18
+ public function showFilter() {
19
+ return true;
20
+ }
21
+
22
+ /**
23
+ * Passthrough Parent Categories - from filter model
24
+ *
25
+ * @return array
26
+ */
27
+ public function getParents() {
28
+ if (!$this->hasData('parent_categories')) {
29
+ $parents = $this->_filter->getParents();
30
+ if (!is_array($parents))
31
+ $parents = array();
32
+ $this->setData('parent_categories', $parents);
33
+ }
34
+ return $this->getData('parent_categories');
35
+ }
36
+
37
+ }
app/code/community/SearchSpring/Manager/Block/Layer/Filter/SearchSpringList.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Block_Layer_Filter_SearchSpringList extends Mage_Catalog_Block_Layer_Filter_Abstract
4
+ {
5
+
6
+ protected $_filterModelName = 'searchspring_manager/layer_filter_searchSpring';
7
+ protected $_searchSpringFacet;
8
+
9
+ /**
10
+ * Set the SearchSpring facet response
11
+ */
12
+ public function setSearchSpringFacet($facet) {
13
+ $this->_searchSpringFacet = $facet;
14
+ return $this;
15
+ }
16
+
17
+ protected function _prepareFilter()
18
+ {
19
+ parent::_prepareFilter();
20
+ $this->_filter->setSearchSpringFacet($this->_searchSpringFacet);
21
+ return $this;
22
+ }
23
+
24
+ /**
25
+ * Return the filter model
26
+ *
27
+ * @return Mage_Catalog_Model_Layer_Filter_Attribute
28
+ */
29
+ public function getFilter() {
30
+ return $this->_filter;
31
+ }
32
+
33
+ /**
34
+ * Return whether or not to display the filter
35
+ *
36
+ * @return bool
37
+ */
38
+ public function showFilter() {
39
+ return $this->getItemsCount() > 0;
40
+ }
41
+ }
app/code/community/SearchSpring/Manager/Block/Layer/View.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Block_Layer_View extends Mage_Catalog_Block_Layer_View {
4
+
5
+ protected $_defaultToolbarBlock = 'catalog/product_list_toolbar';
6
+
7
+ /**
8
+ * Internal constructor
9
+ */
10
+ protected function _construct()
11
+ {
12
+ parent::_construct();
13
+ if ($this->isSearchSpringEnabled()) {
14
+ // Register our version of the layer model for the benifit of the product_list block
15
+ Mage::register('current_layer', $this->getLayer(), true);
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Apply filters from a request to a collection
21
+ *
22
+ * TODO - When we start to have more layer filter
23
+ * types, we'll want to move this logic into the
24
+ * apply functions of each filter model.
25
+ *
26
+ * @param Zend_Controller_Request_Abstract $request
27
+ * @param $collection
28
+ */
29
+ protected function _applyFilters(Zend_Controller_Request_Abstract $request, $collection) {
30
+ $params = $request->getParams();
31
+ foreach($params as $param => $values) {
32
+ if(strpos($param, 'filter_') === 0) {
33
+
34
+ // convert non-arrays into arrays
35
+ if(!is_array($values)) {
36
+ $values = array($values);
37
+ }
38
+
39
+ $field = substr($param, 7);
40
+
41
+ foreach($values as $value) {
42
+ if(strpos($value, 'RANGE:') === 0) {
43
+ list($low, $high) = explode(' TO ', substr($value, 6));
44
+ $collection->addFilter($field . '.low', $low);
45
+ $collection->addFilter($field . '.high', $high);
46
+ } else {
47
+ $collection->addFilter($field, $value);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Prepare child blocks
56
+ *
57
+ * @return Mage_Catalog_Block_Layer_View
58
+ */
59
+ protected function _prepareLayout()
60
+ {
61
+ if ($this->isSearchSpringEnabled()) {
62
+ $head = Mage::app()->getLayout()->getBlock('head');
63
+ $head->addItem('skin_css', 'searchspring/css/styles.css');
64
+
65
+ // Load toolbar to apply pagination/sorting to collection
66
+ $toolbarBlock = $this->getLayout()->createBlock($this->_defaultToolbarBlock, microtime());
67
+ $collection = $this->getLayer()->getProductCollection();
68
+ $toolbarBlock->setCollection($collection);
69
+
70
+ $this->_applyFilters($this->getRequest(), $collection);
71
+
72
+ $stateBlock = $this->getLayout()->createBlock($this->_stateBlockName, 'ss_layer_view_state')
73
+ ->setLayer($this->getLayer());
74
+
75
+ $stateBlock->setTemplate('searchspring_manager/layer/state.phtml');
76
+
77
+ $this->setChild('layer_state', $stateBlock);
78
+
79
+ $filterableAttributes = $this->_getFilterableAttributes();
80
+
81
+ foreach ($filterableAttributes as $attribute) {
82
+ if ($attribute->field == 'category_ids') {
83
+ $blockType = 'searchspring_manager/layer_filter_searchSpringCategory';
84
+ $template = 'searchspring_manager/layer/category_filter.phtml';
85
+ } else {
86
+ $blockType = 'searchspring_manager/layer_filter_searchSpringList';
87
+ $template = 'searchspring_manager/layer/filter.phtml';
88
+ }
89
+
90
+ $blocks[$attribute->field] = $this->getLayout()->createBlock($blockType)
91
+ ->setLayer($this->getLayer())
92
+ ->setSearchSpringFacet($attribute)
93
+ ->init()
94
+ ->setTemplate($template);
95
+
96
+ $this->setChild($attribute->field . '_filter', $blocks[$attribute->field]);
97
+ }
98
+
99
+
100
+ foreach($collection->getFilterSummary() as $summaryFilter) {
101
+ // Skip Category
102
+ if($summaryFilter->field == 'category_ids') {
103
+ continue;
104
+ }
105
+
106
+ $filter = $blocks[$summaryFilter->field]->getFilter();
107
+ $this->getLayer()->getState()->addFilter($this->_createFilterItem($filter, $summaryFilter->filterValue, $summaryFilter->value));
108
+ }
109
+
110
+ return $this;
111
+ } else {
112
+ return parent::_prepareLayout();
113
+ }
114
+ }
115
+
116
+ protected function _getEmptyCategoryAttribute() {
117
+ $attribute = new stdClass();
118
+ $attribute->field = 'category_ids';
119
+ $attribute->label = 'category_ids';
120
+ $attribute->type = 'list';
121
+ $attribute->collapse = 0;
122
+ $attribute->facet_active = 1;
123
+ $attribute->values = array();
124
+ return $attribute;
125
+ }
126
+
127
+ /**
128
+ * Create filter item object
129
+ *
130
+ * @param SearchSpring_Manager_Model_Layer_Filter_SearchSpring $filter
131
+ * @param string $label
132
+ * @param mixed $value
133
+ * @param int $count
134
+ * @return Mage_Catalog_Model_Layer_Filter_Item
135
+ */
136
+ protected function _createFilterItem($filter, $label, $value, $count=0)
137
+ {
138
+ $item = Mage::getModel('searchspring_manager/layer_filter_searchSpringItem')
139
+ ->setFilter($filter)
140
+ ->setLabel($label)
141
+ ->setValue($value)
142
+ ->setCount($count);
143
+
144
+ return $item;
145
+ }
146
+
147
+ /**
148
+ * Get all layer filters
149
+ *
150
+ * @return array
151
+ */
152
+ public function getFilters()
153
+ {
154
+ if ($this->isSearchSpringEnabled()) {
155
+ $filters = array();
156
+ if ($categoryFilter = $this->_getCategoryFilter()) {
157
+ $filters[] = $categoryFilter;
158
+ }
159
+
160
+ $filterableAttributes = $this->_getFilterableAttributes();
161
+ foreach ($filterableAttributes as $attribute) {
162
+ $filters[] = $this->getChild($attribute->field . '_filter');
163
+ }
164
+
165
+ return $filters;
166
+ } else {
167
+ return parent::getFilters();
168
+ }
169
+ }
170
+
171
+ public function isSearchSpringEnabled()
172
+ {
173
+ return $this->getLayer()->isSearchSpringEnabled();
174
+ }
175
+
176
+ /**
177
+ * Get layer object
178
+ *
179
+ * @return Mage_Catalog_Model_Layer
180
+ */
181
+ public function getLayer()
182
+ {
183
+ return Mage::getSingleton('searchspring_manager/layer');
184
+ }
185
+
186
+ }
app/code/community/SearchSpring/Manager/Builder/OperationBuilder.php ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * OperationBuilder.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Builder_OperationBuilder
10
+ *
11
+ * Builder for product operations
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Builder_OperationBuilder
16
+ {
17
+ /**
18
+ * Service to sanitize string data
19
+ *
20
+ * @var SearchSpring_Manager_String_Sanitizer $sanitizer
21
+ */
22
+ private $sanitizer;
23
+
24
+ /**
25
+ * Api records collection
26
+ *
27
+ * @var SearchSpring_Manager_Entity_RecordsCollection $records
28
+ */
29
+ private $records;
30
+
31
+ /**
32
+ * The operation class name
33
+ *
34
+ * @var string $className
35
+ */
36
+ private $className;
37
+
38
+ /**
39
+ * Magento product
40
+ *
41
+ * @var Mage_Catalog_Model_Product $product
42
+ */
43
+ private $product;
44
+
45
+ /**
46
+ * The class prefix
47
+ *
48
+ * Use this to prepend to the class name when the object is built
49
+ *
50
+ * @var string $classPrefix
51
+ */
52
+ private $classPrefix = '';
53
+
54
+ /**
55
+ * Operation parameters
56
+ *
57
+ * Used for adding additional data to the operation
58
+ *
59
+ * @var array $parameters
60
+ */
61
+ private $parameters;
62
+
63
+ /**
64
+ * Constructor
65
+ *
66
+ * @param SearchSpring_Manager_String_Sanitizer $sanitizer
67
+ * @param SearchSpring_Manager_Entity_RecordsCollection $records
68
+ * @param null $className
69
+ * @param Mage_Catalog_Model_Product $product
70
+ * @param string $classPrefix
71
+ */
72
+ public function __construct(
73
+ SearchSpring_Manager_String_Sanitizer $sanitizer = null,
74
+ SearchSpring_Manager_Entity_RecordsCollection $records = null,
75
+ $className = null,
76
+ Mage_Catalog_Model_Product $product = null,
77
+ $classPrefix = ''
78
+ ) {
79
+ $this->sanitizer = $sanitizer;
80
+ $this->records = $records;
81
+ $this->className = $className;
82
+ $this->product = $product;
83
+ $this->classPrefix = $classPrefix;
84
+ }
85
+
86
+ /**
87
+ * Set the string sanitizer
88
+ *
89
+ * @param SearchSpring_Manager_String_Sanitizer $sanitizer
90
+ *
91
+ * @return $this
92
+ */
93
+ public function setSanitizer(SearchSpring_Manager_String_Sanitizer $sanitizer)
94
+ {
95
+ $this->sanitizer = $sanitizer;
96
+
97
+ return $this;
98
+ }
99
+
100
+ /**
101
+ * Set the records collection
102
+ *
103
+ * @param SearchSpring_Manager_Entity_RecordsCollection $records
104
+ *
105
+ * @return $this
106
+ */
107
+ public function setRecords(SearchSpring_Manager_Entity_RecordsCollection $records)
108
+ {
109
+ $this->records = $records;
110
+
111
+ return $this;
112
+ }
113
+
114
+ /**
115
+ * Set the class prefix
116
+ *
117
+ * @param $classPrefix
118
+ *
119
+ * @return $this
120
+ */
121
+ public function setClassPrefix($classPrefix)
122
+ {
123
+ $this->classPrefix = (string)$classPrefix;
124
+
125
+ return $this;
126
+ }
127
+
128
+ /**
129
+ * Set the class name
130
+ *
131
+ * @param string $className
132
+ *
133
+ * @return $this
134
+ */
135
+ public function setClassName($className)
136
+ {
137
+ $this->className = $className;
138
+
139
+ return $this;
140
+ }
141
+
142
+ /**
143
+ * Set additional data to operation
144
+ *
145
+ * @param array $parameters
146
+ *
147
+ * @return $this
148
+ */
149
+ public function setParameters(array $parameters)
150
+ {
151
+ $this->parameters = $parameters;
152
+
153
+ return $this;
154
+ }
155
+
156
+ /**
157
+ * Make a new operation instance
158
+ *
159
+ * @param null $className
160
+ * @param array $parameters
161
+ * @param bool $useClassPrefix
162
+ *
163
+ * @throws InvalidArgumentException If className is not a valid type
164
+ * @throws UnexpectedValueException If a member variable is not set
165
+ *
166
+ * @return SearchSpring_Manager_Operation_Product
167
+ */
168
+ public function build($className = null, array $parameters = array(), $useClassPrefix = true)
169
+ {
170
+ if ($className !== null) {
171
+ $this->className = $className;
172
+ }
173
+
174
+ if (null === $this->sanitizer) {
175
+ throw new UnexpectedValueException('Sanitizer must not be null');
176
+ }
177
+
178
+ if (null === $this->records) {
179
+ throw new UnexpectedValueException('Records collection must not be null');
180
+ }
181
+
182
+ if (null === $this->className) {
183
+ throw new UnexpectedValueException('Class name must not be null');
184
+ }
185
+
186
+ $class = ($useClassPrefix) ? $this->classPrefix . $this->className : $this->className;
187
+ $operation = new $class($this->sanitizer, $this->records, $parameters);
188
+
189
+ $class = 'SearchSpring_Manager_Operation_Product';
190
+ if (!$operation instanceof $class) {
191
+ throw new InvalidArgumentException('$class must be an instance of SearchSpring_Manager_Operation_Product');
192
+ }
193
+
194
+ return $operation;
195
+ }
196
+ }
app/code/community/SearchSpring/Manager/Collection.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File Collection.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Interface SearchSpring_Manager_Collection
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ interface SearchSpring_Manager_Collection
14
+ {
15
+ /**
16
+ * Get value of key from the current index
17
+ *
18
+ * @param string $key
19
+ *
20
+ * @return mixed
21
+ */
22
+ public function get($key);
23
+
24
+ /**
25
+ * Set data to key at current index
26
+ *
27
+ * @param string $key
28
+ * @param mixed $element
29
+ *
30
+ * @return mixed
31
+ */
32
+ public function set($key, $element);
33
+
34
+ /**
35
+ * Add data to key at current index
36
+ *
37
+ * Will not replace existing data at key
38
+ *
39
+ * @param string $key
40
+ * @param $element
41
+ *
42
+ * @return mixed
43
+ */
44
+ public function add($key, $element);
45
+
46
+ /**
47
+ * Unset key from current index
48
+ *
49
+ * @param string $key
50
+ * @return mixed
51
+ */
52
+ public function remove($key);
53
+
54
+ /**
55
+ * Return an array of data
56
+ *
57
+ * @return array
58
+ */
59
+ public function toArray();
60
+ }
app/code/community/SearchSpring/Manager/Entity/IndexingRequestBody.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File IndexingRequestBody.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_IndexingRequestBody
10
+ *
11
+ * The class models a SearchSpring API request body which needs a feed id and an array of records.
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_IndexingRequestBody extends SearchSpring_Manager_Entity_RequestBody
16
+ {
17
+ /**#@+
18
+ * Request types
19
+ */
20
+ const TYPE_PRODUCT = 'product';
21
+ const TYPE_CATEGORY = 'category';
22
+ /**#@-*/
23
+
24
+ /**
25
+ * An array of allowable types
26
+ *
27
+ * @var array $allowableTypes
28
+ */
29
+ public static $allowableTypes = array(
30
+ self::TYPE_PRODUCT,
31
+ self::TYPE_CATEGORY,
32
+ );
33
+
34
+ /**
35
+ * The type of request
36
+ *
37
+ * @var string $type
38
+ */
39
+ private $type;
40
+
41
+ /**
42
+ * An array of category/product ids
43
+ *
44
+ * @var array $ids
45
+ */
46
+ private $ids;
47
+
48
+ /**
49
+ * If we should delete these ids
50
+ *
51
+ * @var bool $shouldDelete
52
+ */
53
+ private $shouldDelete;
54
+
55
+ /**
56
+ * Constructor
57
+ *
58
+ * @param string $type
59
+ * @param array $ids
60
+ * @param $shouldDelete
61
+ */
62
+ public function __construct($type, array $ids, $shouldDelete)
63
+ {
64
+ $this->type = $type;
65
+ $this->ids = $ids;
66
+ $this->shouldDelete = $shouldDelete;
67
+
68
+ $this->feedId = Mage::helper('searchspring_manager')->getApiFeedId();
69
+ if (null === $this->feedId) {
70
+ throw new UnexpectedValueException('SearchSpring: Feed ID must be set');
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Returns a value that's allowed to be given to json_encode
76
+ *
77
+ * @return array
78
+ */
79
+ public function jsonSerialize()
80
+ {
81
+ $body = array(
82
+ 'type' => $this->type,
83
+ 'ids' => $this->ids,
84
+ 'delete' => $this->shouldDelete,
85
+ 'feedId' => $this->feedId,
86
+ 'generateUrl' => Mage::getUrl('searchspring/generate/index',array('_secure'=>true))
87
+ );
88
+
89
+ return $body;
90
+ }
91
+ }
app/code/community/SearchSpring/Manager/Entity/OperationsCollection.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * OperationsCollection.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_OperationsCollection
10
+ *
11
+ * Simply extending \ArrayObject in order to provide more functionality down the road if need be
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_OperationsCollection extends ArrayObject
16
+ {
17
+ }
app/code/community/SearchSpring/Manager/Entity/RecordsCollection.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File RecordsCollection.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_RecordsCollection
10
+ *
11
+ * Manages a 2d array of field data. The Iterator methods will reference the first dimension of the array. The
12
+ * Collection methods will use the current index and act on the second dimension of the array.
13
+ *
14
+ * @author Nate Brunette <nate@b7interactive.com>
15
+ */
16
+ class SearchSpring_Manager_Entity_RecordsCollection
17
+ implements SearchSpring_Manager_Collection, SearchSpring_Manager_Iterator
18
+ {
19
+ /**
20
+ * The array of data
21
+ *
22
+ * @var array $fields
23
+ */
24
+ private $fields = array();
25
+
26
+ /**
27
+ * The current record index
28
+ *
29
+ * @var int $index
30
+ */
31
+ private $index = 0;
32
+
33
+ /**
34
+ * {@inheritdoc}
35
+ */
36
+ public function current()
37
+ {
38
+ if (!isset($this->fields[$this->key()])) {
39
+ throw new OutOfBoundsException('Index not found');
40
+ }
41
+
42
+ return $this->fields[$this->key()];
43
+ }
44
+
45
+ /**
46
+ * {@inheritdoc}
47
+ */
48
+ public function next()
49
+ {
50
+ ++$this->index;
51
+
52
+ return null;
53
+ }
54
+
55
+ /**
56
+ * {@inheritdoc}
57
+ */
58
+ public function key()
59
+ {
60
+ return $this->index;
61
+ }
62
+
63
+ /**
64
+ * {@inheritdoc}
65
+ */
66
+ public function valid()
67
+ {
68
+ return isset($this->fields[$this->key()]);
69
+ }
70
+
71
+ /**
72
+ * {@inheritdoc}
73
+ */
74
+ public function rewind()
75
+ {
76
+ $this->index = 0;
77
+
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * {@inheritdoc}
83
+ */
84
+ public function end()
85
+ {
86
+ end($this->fields);
87
+
88
+ return null;
89
+ }
90
+
91
+ /**
92
+ * {@inheritdoc}
93
+ */
94
+ public function get($key)
95
+ {
96
+ if (!isset($this->fields[$this->key()][$key])) {
97
+ throw new OutOfBoundsException('Key not found');
98
+ }
99
+
100
+ return $this->fields[$this->key()][$key];
101
+ }
102
+
103
+ /**
104
+ * {@inheritdoc}
105
+ */
106
+ public function set($key, $element)
107
+ {
108
+ $this->fields[$this->key()][$key] = $element;
109
+
110
+ return $this;
111
+ }
112
+
113
+ /**
114
+ * {@inheritdoc}
115
+ */
116
+ public function add($key, $element)
117
+ {
118
+ $this->fields[$this->key()][$key][] = $element;
119
+
120
+ return $this;
121
+ }
122
+
123
+ /**
124
+ * {@inheritdoc}
125
+ */
126
+ public function remove($key)
127
+ {
128
+ if (!isset($this->fields[$this->key()][$key])) {
129
+ throw new OutOfBoundsException('Key not found');
130
+ }
131
+
132
+ unset($this->fields[$this->key()][$key]);
133
+ }
134
+
135
+ /**
136
+ * {@inheritdoc}
137
+ */
138
+ public function toArray()
139
+ {
140
+ return $this->fields;
141
+ }
142
+ }
app/code/community/SearchSpring/Manager/Entity/RequestBody.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File IndexingRequestBody.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_RequestBody
10
+ *
11
+ * @author James Bathgate <james@b7interactive.com>
12
+ */
13
+ class SearchSpring_Manager_Entity_RequestBody
14
+ {
15
+
16
+ }
app/code/community/SearchSpring/Manager/Entity/RequestParams.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * RequestParams.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_RequestParams
10
+ *
11
+ * Store request parameters
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_RequestParams
16
+ {
17
+ /**
18
+ * How many records to limit
19
+ *
20
+ * @var int $count
21
+ */
22
+ private $count;
23
+
24
+ /**
25
+ * Limit offest
26
+ *
27
+ * @var int $offset
28
+ */
29
+ private $offset;
30
+
31
+ /**
32
+ * The store id
33
+ *
34
+ * @var string $store
35
+ */
36
+ private $store;
37
+
38
+ /**
39
+ * Constructor
40
+ *
41
+ * @param int $count
42
+ * @param int $offset
43
+ * @param string $store
44
+ */
45
+ public function __construct($count, $offset, $store)
46
+ {
47
+ $this->count = $count;
48
+ $this->offset = $offset;
49
+ $this->store = $store;
50
+ }
51
+
52
+ /**
53
+ * Get the offset
54
+ *
55
+ * @return int
56
+ */
57
+ public function getOffset()
58
+ {
59
+ return $this->offset;
60
+ }
61
+
62
+ /**
63
+ * Get the store id
64
+ *
65
+ * @return string
66
+ */
67
+ public function getStore()
68
+ {
69
+ return $this->store;
70
+ }
71
+
72
+ /**
73
+ * Get the limit count
74
+ *
75
+ * @return int
76
+ */
77
+ public function getCount()
78
+ {
79
+ return $this->count;
80
+ }
81
+ }
app/code/community/SearchSpring/Manager/Entity/SearchCollection.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File SearchRequest.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_SearchCollection
10
+ *
11
+ * The class models a SearchSpring SearchCollection
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_SearchCollection extends Mage_Catalog_Model_Resource_Collection_Abstract
16
+ {
17
+ protected $_api;
18
+ protected $_requestBody;
19
+
20
+ protected $_results;
21
+
22
+ protected $_loaded = false;
23
+
24
+ const SORT_ORDER_ASC = 'asc';
25
+ const SORT_ORDER_DESC = 'desc';
26
+
27
+ /**
28
+ * @param SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter $api
29
+ * @param SearchSpring_Manager_Entity_SearchRequestBody $requestBody
30
+ */
31
+
32
+ public function __construct(
33
+ SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter $api,
34
+ SearchSpring_Manager_Entity_SearchRequestBody $requestBody
35
+ ) {
36
+ $this->_api = $api;
37
+ $this->_requestBody = $requestBody;
38
+ }
39
+
40
+ /*
41
+ * $type will be ignored since it's handled by the SMC settings
42
+ */
43
+ public function addFilter($field, $value, $type='and') {
44
+ $this->_requestBody->addFilter($field, $value);
45
+ return $this;
46
+ }
47
+
48
+ public function addBackgroundFilter($field, $value) {
49
+ $this->_requestBody->addBackgroundFilter($field, $value);
50
+ return $this;
51
+ }
52
+
53
+ public function addFilterOnly($field, $values) {
54
+ foreach($values as $value) {
55
+ $this->_requestBody->addFilterOnly($field, $value);
56
+ }
57
+ }
58
+
59
+ public function setCurPage($page) {
60
+ $this->_requestBody->setPage($page);
61
+ return $this;
62
+ }
63
+
64
+ public function getCurPage($offset=0) {
65
+ return $this->_requestBody->getPage()+$offset;
66
+ }
67
+
68
+ public function setPageSize($size) {
69
+ $this->_requestBody->setResultsPerPage($size);
70
+ return $this;
71
+ }
72
+
73
+ public function getPageSize() {
74
+ return $this->_requestBody->getResultsPerPage();
75
+ }
76
+
77
+ public function setOrder($field, $dir = self::SORT_ORDER_ASC) {
78
+ $this->_requestBody->setSort($field, $dir);
79
+ return $this;
80
+ }
81
+
82
+ public function load($printQuery = false, $logQuery = false) {
83
+ if(!$this->_loaded) {
84
+ $this->_results = new SearchSpring_Manager_Entity_SearchResult(
85
+ $this->_api->search($this->_requestBody)
86
+ );
87
+
88
+ $this->_loaded = true;
89
+ }
90
+
91
+ return $this;
92
+ }
93
+
94
+ public function count() {
95
+ return $this->_results->getNumResults();
96
+ }
97
+
98
+ public function getSize() {
99
+ return $this->_results->getNumResults();
100
+ }
101
+
102
+ public function getLastPageNumber() {
103
+ return ceil($this->_results->getNumResults() / $this->_results->getPerPage());
104
+ }
105
+
106
+ public function getResults() {
107
+ if(!$this->_loaded) {
108
+ $this->load();
109
+ }
110
+
111
+ return $this->_results;
112
+ }
113
+
114
+ public function getMerchandisingContent() {
115
+ return $this->_results->getMerchandising();
116
+ }
117
+
118
+ public function getSorting() {
119
+ return $this->_results->getSorting();
120
+ }
121
+
122
+ public function getFilterSummary() {
123
+ return $this->_results->getFilterSummary();
124
+ }
125
+
126
+
127
+ /**
128
+ * SearchSpring automatically includes counts, so we do nothing.
129
+ *
130
+ * @param Mage_Eav_Model_Entity_Collection_Abstract $categoryCollection
131
+ * @return Mage_Catalog_Model_Resource_Product_Collection
132
+ */
133
+ public function addCountToCategories($categoryCollection)
134
+ {
135
+ return $this;
136
+ }
137
+
138
+ }
app/code/community/SearchSpring/Manager/Entity/SearchRequestBody.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File IndexingRequestBody.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_SearchRequestBody
10
+ *
11
+ * The class models a SearchSpring Search API request body
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_SearchRequestBody extends SearchSpring_Manager_Entity_RequestBody
16
+ {
17
+
18
+ protected $_siteId;
19
+
20
+ protected $_filters = array();
21
+ protected $_backgroundFilters = array();
22
+
23
+ protected $_filterOnly = array();
24
+
25
+ protected $_page = 1;
26
+ protected $_resultsPerPage = 30;
27
+
28
+ protected $_sortField;
29
+ protected $_sortDir = 'asc';
30
+
31
+ /**
32
+ * Constructor
33
+ */
34
+ public function __construct()
35
+ {
36
+ $this->_siteId = Mage::helper('searchspring_manager')->getApiSiteId();
37
+
38
+ if (null === $this->_siteId) {
39
+ throw new UnexpectedValueException('SearchSpring: Site ID must be set');
40
+ }
41
+ }
42
+
43
+ public function addFilter($field, $value, $append = false) {
44
+ if(!isset($this->_filters[$field])) {
45
+ $this->_filters[$field] = $value;
46
+ } else {
47
+ if(!is_array($this->_filters[$field])) {
48
+ $this->_filters[$field] = array($this->_filters[$field]);
49
+ }
50
+
51
+ $this->_filters[$field][] = $value;
52
+ }
53
+ }
54
+
55
+ public function addBackgroundFilter($field, $value, $append = false) {
56
+ if(!isset($this->_backgroundFilters[$field])) {
57
+ $this->_backgroundFilters[$field] = $value;
58
+ } else {
59
+ if(!is_array($this->_backgroundFilters[$field])) {
60
+ $this->_backgroundFilters[$field] = array($this->_backgroundFilters[$field]);
61
+ }
62
+
63
+ $this->_backgroundFilters[$field][] = $value;
64
+ }
65
+ }
66
+
67
+ public function addFilterOnly($field, $value) {
68
+ if(!isset($this->_filterOnly[$field])) {
69
+ $this->_filterOnly[$field] = $value;
70
+ } else {
71
+ if(!is_array($this->_filterOnly[$field])) {
72
+ $this->_filterOnly[$field] = array($this->_filterOnly[$field]);
73
+ }
74
+
75
+ $this->_filterOnly[$field][] = $value;
76
+ }
77
+ }
78
+
79
+ public function setPage($page) {
80
+ $this->_page = $page;
81
+ }
82
+
83
+ public function getPage() {
84
+ return $this->_page;
85
+ }
86
+
87
+ public function setResultsPerPage($rpp) {
88
+ $this->_resultsPerPage = $rpp;
89
+ }
90
+
91
+ public function getResultsPerPage() {
92
+ return $this->_resultsPerPage;
93
+ }
94
+
95
+ public function setSort($field, $dir) {
96
+ $this->_sortField = $field;
97
+ $this->_sortDir = $dir;
98
+ }
99
+
100
+ public function __toString() {
101
+ $parameters = array(
102
+ 'siteId' => $this->_siteId,
103
+ 'page' => $this->_page,
104
+ 'resultsPerPage' => $this->_resultsPerPage
105
+ );
106
+
107
+ foreach($this->_filters as $field => $value) {
108
+ $parameters['filter.' . $field] = $value;
109
+ }
110
+
111
+ foreach($this->_backgroundFilters as $field => $value) {
112
+ $parameters['bgfilter.' . $field] = $value;
113
+ }
114
+
115
+ foreach($this->_filterOnly as $field => $value) {
116
+ $parameters['filter.' . $field . '.only'] = $value;
117
+ }
118
+
119
+ if(!is_null($this->_sortField)) {
120
+ $parameters['sort.' . $this->_sortField] = $this->_sortDir;
121
+ }
122
+
123
+ $url = http_build_query($parameters);
124
+
125
+ // remove [] from multi-valued filters
126
+ $url = preg_replace('/\%5B\d+\%5D/', '', $url);
127
+
128
+ return $url;
129
+ }
130
+
131
+ }
app/code/community/SearchSpring/Manager/Entity/SearchResult.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File SearchResult.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_SearchResult
10
+ *
11
+ * The class models a SearchSpring Search API result body
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_SearchResult
16
+ {
17
+
18
+ protected $_results = array();
19
+
20
+ protected $_pagination;
21
+
22
+ /**
23
+ * Constructor
24
+ *
25
+ * @param mixed JSON decoded SearchSpring search result
26
+ */
27
+ public function __construct($results)
28
+ {
29
+ // print "<pre>"; var_dump($results);print "</pre>";exit;
30
+ $this->_pagination = $results->pagination;
31
+ $this->_results = $results->results;
32
+ $this->_facets = $results->facets;
33
+ $this->_merchandising = $results->merchandising;
34
+ $this->_sorting = $results->sorting;
35
+ $this->_filterSummary = $results->filterSummary;
36
+ }
37
+
38
+ public function getResults() {
39
+ return $this->_results;
40
+ }
41
+
42
+ public function getFacets() {
43
+ return $this->_facets;
44
+ }
45
+
46
+ public function getNumResults() {
47
+ return $this->_pagination->totalResults;
48
+ }
49
+
50
+ public function getPerPage() {
51
+ return $this->_pagination->perPage;
52
+ }
53
+
54
+ public function getMerchandisingContent() {
55
+ if(isset($this->_merchandising->content)) {
56
+ return $this->_merchandising->content;
57
+ } else {
58
+ return new stdClass();
59
+ }
60
+ }
61
+
62
+ public function getSorting() {
63
+ return $this->_sorting->options;
64
+ }
65
+
66
+ public function getFilterSummary() {
67
+ return $this->_filterSummary;
68
+ }
69
+ }
app/code/community/SearchSpring/Manager/Entity/SearchResult/Result.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File Result.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Entity_SearchResult_Result
10
+ *
11
+ * The class implements product functions for a SearchSpring Result
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Entity_SearchResult_Result
16
+ {
17
+
18
+ protected $_result;
19
+ /**
20
+ * Constructor
21
+ *
22
+ * @param stdClass $result
23
+ */
24
+ public function __construct($result)
25
+ {
26
+ $this->_result = $result;
27
+ }
28
+
29
+ public function getProductUrl() {
30
+ return $this->_result->url;
31
+ }
32
+
33
+ public function getData($field) {
34
+ $data = $this->_result->$field;
35
+ var_dump($data);
36
+ }
37
+ }
app/code/community/SearchSpring/Manager/Factory/ApiFactory.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ApiFactory.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Factory_ApiFactory
10
+ *
11
+ * Create a SearchSpring api request object
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Factory_ApiFactory
16
+ {
17
+ /**
18
+ * Make the object
19
+ *
20
+ * Pulls in config values from the Magento configuration. Uses the Varien curl adapter.
21
+ *
22
+ * @param $type string The type of API to build (index or search)
23
+ *
24
+ * @return SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter
25
+ *
26
+ * @throws UnexpectedValueException
27
+ */
28
+ public function make($type)
29
+ {
30
+ $baseUrl = Mage::helper('searchspring_manager')->getApiBaseUrl();
31
+ $siteId = Mage::helper('searchspring_manager')->getApiSiteId();
32
+ $secretKey = Mage::helper('searchspring_manager')->getApiSecretKey();
33
+
34
+ if (null === $baseUrl) {
35
+ throw new UnexpectedValueException('SearchSpring: Base URL must be set');
36
+ }
37
+
38
+ if (null === $siteId) {
39
+ throw new UnexpectedValueException('SearchSpring: Site ID must be set');
40
+ }
41
+
42
+ if (null === $secretKey) {
43
+ throw new UnexpectedValueException('SearchSpring: Secret key must be set');
44
+ }
45
+
46
+ $apiErrorHandler = new SearchSpring_Manager_Handler_ApiErrorHandler();
47
+
48
+ if($type == 'index') {
49
+ $curl = new Varien_Http_Adapter_Curl();
50
+ $curl->setConfig(array('timeout' => 15, 'userpwd' => $siteId . ':' . $secretKey));
51
+
52
+ $api = new SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter(
53
+ $apiErrorHandler,
54
+ $curl,
55
+ $baseUrl
56
+ );
57
+ } else {
58
+ $curl = new Varien_Http_Adapter_Curl();
59
+ $curl->setConfig(array('timeout' => 15));
60
+
61
+ $api = new SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter(
62
+ $apiErrorHandler,
63
+ $curl,
64
+ $baseUrl
65
+ );
66
+ }
67
+
68
+ return $api;
69
+ }
70
+ }
app/code/community/SearchSpring/Manager/Factory/GeneratorFactory.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GeneratorFactory.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Factory_GeneratorFactory
10
+ *
11
+ * Creates a generator object
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Factory_GeneratorFactory
16
+ {
17
+ /**#@+
18
+ * Generator types
19
+ */
20
+ const TYPE_FEED = 'feed';
21
+ const TYPE_PRODUCT = 'product';
22
+ const TYPE_CATEGORY = 'category';
23
+ /**#@-*/
24
+
25
+ /**
26
+ * Create a generator
27
+ *
28
+ * @param string $type Generator type
29
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams Request parameters
30
+ * @param array $params Additional parameters
31
+ *
32
+ * @throws OutOfBoundsException If a key is not found
33
+ * @throws InvalidArgumentException If the type is not found
34
+ *
35
+ * @return SearchSpring_Manager_Generator_ProductGenerator
36
+ *
37
+ */
38
+ public function make($type, SearchSpring_Manager_Entity_RequestParams $requestParams, array $params = array())
39
+ {
40
+ $operationsBuilder = new SearchSpring_Manager_Builder_OperationBuilder();
41
+ $operationsCollection = new SearchSpring_Manager_Entity_OperationsCollection();
42
+ $productRecords = new SearchSpring_Manager_Entity_RecordsCollection();
43
+
44
+ // setup default operations
45
+ $this->createOperations($operationsBuilder, $operationsCollection, $productRecords);
46
+
47
+ $transformer = new SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer(
48
+ $productRecords,
49
+ $operationsCollection
50
+ );
51
+
52
+ switch($type) {
53
+ case self::TYPE_FEED:
54
+ if (!isset($params['filename'])) {
55
+ throw new OutOfBoundsException('Key "filename" not found in array');
56
+ }
57
+
58
+ $filename = $params['filename'];
59
+
60
+ $generator = $this->makeFeedGenerator($transformer, $requestParams, $filename);
61
+
62
+ break;
63
+ case self::TYPE_PRODUCT:
64
+ if (!isset($params['ids'])) {
65
+ throw new OutOfBoundsException('Key "ids" not found in array');
66
+ }
67
+
68
+ $ids = $params['ids'];
69
+
70
+ $generator = $this->makeProductGenerator($transformer, $ids, $requestParams);
71
+
72
+ break;
73
+ case self::TYPE_CATEGORY:
74
+ if (!isset($params['ids'])) {
75
+ throw new OutOfBoundsException('Key "ids" not found in array');
76
+ }
77
+
78
+ $ids = $params['ids'];
79
+
80
+ if (!is_array($ids)) {
81
+ throw new InvalidArgumentException('Ids must be an array');
82
+ }
83
+
84
+ $validator = new SearchSpring_Manager_Validator_ProductValidator();
85
+
86
+ // get product ids for each category
87
+ $productIds = array();
88
+ foreach ($ids as $id) {
89
+ /** @var Mage_Catalog_Model_Category $category */
90
+ $category = Mage::getModel('catalog/category')->load($id);
91
+
92
+ // Check for non-existent category
93
+ if(!is_null($category->getId())) {
94
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $products */
95
+ $products = Mage::getModel('catalog/product')
96
+ ->getCollection()
97
+ ->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id = entity_id', null, 'left')
98
+ ->addAttributeToSelect('*')
99
+ ->addAttributeToFilter('category_id', array('in' => $id));
100
+ foreach($products as $product) {
101
+ if($validator->isValid($product)) {
102
+ $productIds[] = $product->getId();
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ // remove duplicates
109
+ $productIds = array_unique($productIds);
110
+
111
+ // make a product generator based on product ids
112
+ $generator = $this->makeProductGenerator($transformer, $productIds, $requestParams);
113
+
114
+ break;
115
+ default:
116
+ throw new InvalidArgumentException('Type not found.');
117
+ }
118
+
119
+ return $generator;
120
+ }
121
+
122
+ /**
123
+ * Create default operations
124
+ *
125
+ * @param SearchSpring_Manager_Builder_OperationBuilder $operationsBuilder
126
+ * @param SearchSpring_Manager_Entity_OperationsCollection $operationsCollection
127
+ * @param SearchSpring_Manager_Entity_RecordsCollection $productRecords
128
+ */
129
+ private function createOperations(
130
+ SearchSpring_Manager_Builder_OperationBuilder $operationsBuilder,
131
+ SearchSpring_Manager_Entity_OperationsCollection $operationsCollection,
132
+ SearchSpring_Manager_Entity_RecordsCollection $productRecords
133
+ ) {
134
+ $operationsBuilder->setSanitizer(new SearchSpring_Manager_String_Sanitizer())
135
+ ->setRecords($productRecords)
136
+ ->setClassPrefix(SearchSpring_Manager_Operation_Product::OPERATION_CLASS_PREFIX);
137
+
138
+ $operationsCollection->append($operationsBuilder->build('SetFields'));
139
+ $operationsCollection->append($operationsBuilder->build('SetCoreFields'));
140
+ $operationsCollection->append($operationsBuilder->build('SetImages'));
141
+ $operationsCollection->append($operationsBuilder->build('SetOptions'));
142
+ $operationsCollection->append($operationsBuilder->build('SetCategories'));
143
+
144
+ // add pricing factory and if we should display zero priced products as additional data
145
+ $operationsCollection->append($operationsBuilder->build('SetPricing',
146
+ array(
147
+ 'pricingFactory' => new SearchSpring_Manager_Factory_PricingFactory(),
148
+ 'displayZeroPrice' => (int)Mage::helper('searchspring_manager')->isZeroPriceIndexingEnabled(),
149
+ )
150
+ )
151
+ );
152
+
153
+ $operationsCollection->append($operationsBuilder->build('SetRatings'));
154
+
155
+ // dispatch event allowing additional operations to be added before loop starts
156
+ $operationsBuilder->setClassPrefix(null);
157
+ Mage::dispatchEvent('searchspring_add_operations', array(
158
+ 'operations' => $operationsCollection,
159
+ 'builder' => $operationsBuilder,
160
+ )
161
+ );
162
+ }
163
+
164
+ /**
165
+ * Make a feed generator
166
+ *
167
+ * Uses a feed collection provider and file writer
168
+ *
169
+ * @param SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer $transformer
170
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams
171
+ * @param string $filename
172
+ *
173
+ * @return SearchSpring_Manager_Generator_ProductGenerator
174
+ */
175
+ private function makeFeedGenerator(
176
+ SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer $transformer,
177
+ SearchSpring_Manager_Entity_RequestParams $requestParams,
178
+ $filename
179
+ ) {
180
+ $collectionProvider = new SearchSpring_Manager_Provider_ProductCollection_FeedProvider($requestParams);
181
+
182
+ $xmlWriter = new XMLWriter();
183
+ $writerParams = new SearchSpring_Manager_Writer_Product_Params_FileWriterParams(
184
+ $requestParams,
185
+ $collectionProvider->getCollectionCount(),
186
+ $filename
187
+ );
188
+ $writer = new SearchSpring_Manager_Writer_Product_FileWriter($xmlWriter, $writerParams);
189
+ $generator = new SearchSpring_Manager_Generator_ProductGenerator($collectionProvider, $writer, $transformer);
190
+
191
+ return $generator;
192
+ }
193
+
194
+ /**
195
+ * Make a product generator
196
+ *
197
+ * Uses a product type product collection provider and response writer
198
+ *
199
+ * @param SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer $transformer
200
+ * @param array $ids
201
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams
202
+ *
203
+ * @return SearchSpring_Manager_Generator_ProductGenerator
204
+ */
205
+ private function makeProductGenerator(
206
+ SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer $transformer,
207
+ array $ids,
208
+ SearchSpring_Manager_Entity_RequestParams $requestParams
209
+ ) {
210
+ $collectionProvider = new SearchSpring_Manager_Provider_ProductCollection_ProductProvider($ids, $requestParams);
211
+ $writerParams = new SearchSpring_Manager_Writer_Product_Params_ResponseWriterParams(
212
+ $requestParams,
213
+ $collectionProvider->getCollectionCount()
214
+ );
215
+ $writer = new SearchSpring_Manager_Writer_Product_ResponseWriter($writerParams);
216
+ $generator = new SearchSpring_Manager_Generator_ProductGenerator($collectionProvider, $writer, $transformer);
217
+
218
+ return $generator;
219
+
220
+
221
+ }
222
+ }
app/code/community/SearchSpring/Manager/Factory/IndexingRequestBodyFactory.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * IndexingRequestBodyFactory.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Factory_IndexingRequestBodyFactory
10
+ *
11
+ * Create a request body
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Factory_IndexingRequestBodyFactory
16
+ {
17
+ /**
18
+ * Make a request body
19
+ *
20
+ * @param $type
21
+ * @param array $ids
22
+ * @param bool $shouldDelete
23
+ *
24
+ * @throws InvalidArgumentException
25
+ * @return SearchSpring_Manager_Entity_IndexingRequestBody
26
+ *
27
+ */
28
+ public function make($type, array $ids, $shouldDelete = false)
29
+ {
30
+ if (!in_array($type, SearchSpring_Manager_Entity_IndexingRequestBody::$allowableTypes)) {
31
+ throw new InvalidArgumentException('Type must be one of allowable types');
32
+ }
33
+
34
+ $requestBody = new SearchSpring_Manager_Entity_IndexingRequestBody($type, $ids, $shouldDelete);
35
+
36
+ return $requestBody;
37
+ }
38
+ }
app/code/community/SearchSpring/Manager/Factory/PricingFactory.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File PricingFactory.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Factory_PricingFactory
10
+ *
11
+ * Creates a pricing strategy.
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Factory_PricingFactory
16
+ {
17
+ /**
18
+ * Creates a pricing strategy based on product type
19
+ *
20
+ * Throws an exception if it can't properly resolve the type.
21
+ *
22
+ * @param Mage_Catalog_Model_Product $product
23
+ *
24
+ * @return SearchSpring_Manager_Strategy_Pricing_Strategy
25
+ * @throws UnexpectedValueException
26
+ */
27
+ static public function make(Mage_Catalog_Model_Product $product)
28
+ {
29
+ $productType = $product->getTypeId();
30
+
31
+ switch($productType) {
32
+ case Mage_Catalog_Model_Product_Type::TYPE_SIMPLE:
33
+ $strategy = new SearchSpring_Manager_Strategy_Pricing_SimpleStrategy($product);
34
+
35
+ break;
36
+ case Mage_Catalog_Model_Product_Type::TYPE_GROUPED:
37
+ $strategy = new SearchSpring_Manager_Strategy_Pricing_GroupedStrategy($product);
38
+
39
+ break;
40
+ case Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE:
41
+ $strategy = new SearchSpring_Manager_Strategy_Pricing_ConfigurableStrategy($product);
42
+ break;
43
+ case Mage_Catalog_Model_Product_Type::TYPE_BUNDLE:
44
+ $strategy = new SearchSpring_Manager_Strategy_Pricing_BundleStrategy($product);
45
+
46
+ break;
47
+ default:
48
+ throw new UnexpectedValueException('Could not resolve product type');
49
+ }
50
+
51
+ return $strategy;
52
+ }
53
+ }
app/code/community/SearchSpring/Manager/Factory/SearchRequestBodyFactory.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * IndexingRequestBodyFactory.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Factory_SearchRequestBodyFactory
10
+ *
11
+ * Create a request body
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Factory_SearchRequestBodyFactory
16
+ {
17
+ public function make()
18
+ {
19
+ $requestBody = new SearchSpring_Manager_Entity_SearchRequestBody();
20
+ return $requestBody;
21
+ }
22
+ }
app/code/community/SearchSpring/Manager/Generator/ProductGenerator.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ProductGenerator.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Generator_ProductGenerator
10
+ *
11
+ * Handles generation of product records
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Generator_ProductGenerator
16
+ {
17
+ /**
18
+ * Object that handles writing a record collection
19
+ *
20
+ * @var SearchSpring_Manager_Writer_ProductWriter $writer
21
+ */
22
+ private $writer;
23
+
24
+ /**
25
+ * Transforms product collection to record collection
26
+ *
27
+ * @var SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer
28
+ */
29
+ private $transformer;
30
+
31
+ /**
32
+ * Provides a collection
33
+ *
34
+ * @var SearchSpring_Manager_Provider_ProductCollectionProvider
35
+ */
36
+ private $collectionProvider;
37
+
38
+ /**
39
+ * Constructor
40
+ *
41
+ * @param SearchSpring_Manager_Provider_ProductCollectionProvider $collectionProvider
42
+ * @param SearchSpring_Manager_Writer_ProductWriter $writer
43
+ * @param SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer $transformer
44
+ */
45
+ public function __construct(
46
+ SearchSpring_Manager_Provider_ProductCollectionProvider $collectionProvider,
47
+ SearchSpring_Manager_Writer_ProductWriter $writer,
48
+ SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer $transformer
49
+ ) {
50
+ $this->collectionProvider = $collectionProvider;
51
+ $this->writer = $writer;
52
+ $this->transformer = $transformer;
53
+ }
54
+
55
+ /**
56
+ * Gets a product collection, transform to record collection, and then write the records collection
57
+ *
58
+ * @return string The response message
59
+ */
60
+ public function generate()
61
+ {
62
+ $productCollection = $this->collectionProvider->getCollection();
63
+ $recordCollection = $this->transformer->transform($productCollection);
64
+ $resultMessage = $this->writer->write($recordCollection);
65
+
66
+ return $resultMessage;
67
+ }
68
+ }
app/code/community/SearchSpring/Manager/Handler/ApiErrorHandler.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File ApiErrorHandler.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Handler_ApiErrorHandler
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ class SearchSpring_Manager_Handler_ApiErrorHandler
14
+ {
15
+ /**
16
+ * The amount of times we'll try to update before giving up
17
+ */
18
+ const RETRY_LIMIT = 3;
19
+
20
+ /**
21
+ * Email to send API failures
22
+ */
23
+ const FAILURE_EMAIL = 'dev-null+alert-email@b7interactive.com';
24
+
25
+ /**
26
+ * The number of times we've tried to update SearchSpring
27
+ *
28
+ * @var int
29
+ */
30
+ private $attempts = 0;
31
+
32
+ /**
33
+ * Check if we should continue trying based on the response code and number of attempts
34
+ *
35
+ * @param string $response
36
+ *
37
+ * @return bool
38
+ */
39
+ public function shouldRetry($response)
40
+ {
41
+ $responseCode = Zend_Http_Response::extractCode($response);
42
+
43
+ if (200 === $responseCode) {
44
+ return false;
45
+ }
46
+
47
+ ++$this->attempts;
48
+
49
+ if ($this->attempts < self::RETRY_LIMIT) {
50
+ return true;
51
+ }
52
+
53
+ $this->notifyUser();
54
+ $this->email($response);
55
+
56
+ return false;
57
+ }
58
+
59
+ /**
60
+ * Put a message in the flashbag that there was an issue
61
+ */
62
+ private function notifyUser()
63
+ {
64
+ /** @var Mage_Core_Model_Session $session */
65
+ $session = Mage::getSingleton('core/session');
66
+ $session->addNotice('There was a problem while trying to update SearchSpring. This issue will be investigated.');
67
+ }
68
+
69
+ /**
70
+ * Send an email that something failed
71
+ *
72
+ * @param string $response
73
+ *
74
+ * @return bool
75
+ */
76
+ private function email($response)
77
+ {
78
+ $templateVars = array('response' => $response);
79
+
80
+ /** @var Mage_Core_Model_Email_Template $template */
81
+ $template = Mage::getModel('core/email_template');
82
+ $emailTemplate = $template->loadDefault('api_error');
83
+ $emailTemplate->setData('sender_name', 'System')->setData('sender_email', 'no-reply@b7interactive.com');
84
+ $emailTemplate->setTemplateType('html')->setTemplateSubject('SearchSpring Magento Live Indexing Error');
85
+
86
+ $emailTemplate->send(self::FAILURE_EMAIL, 'SearchSpring', $templateVars);
87
+
88
+ return true;
89
+ }
90
+ }
app/code/community/SearchSpring/Manager/Helper/Data.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File Data.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Helper_Data
10
+ *
11
+ * You should need to put anything in this class, but Magento needs to to function.
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
16
+ {
17
+
18
+ const XML_PATH_GLOBAL_LIVE_INDEXING_ENABLE_FL = 'ssmanager/ssmanager_general/live_indexing';
19
+ const XML_PATH_GLOBAL_CATEGORY_ENABLE_FL = 'ssmanager/ssmanager_catalog/enable_categories';
20
+
21
+ const XML_PATH_INDEX_ZERO_PRICE = 'ssmanager/ssmanager_general/index_zero_price';
22
+
23
+ const XML_PATH_API_FEED_ID = 'ssmanager/ssmanager_api/feed_id';
24
+ const XML_PATH_API_SITE_ID = 'ssmanager/ssmanager_api/site_id';
25
+ const XML_PATH_API_BASE_URL = 'ssmanager/ssmanager_api/base_url';
26
+ const XML_PATH_API_SECRET_KEY = 'ssmanager/ssmanager_api/secret_key';
27
+
28
+ const XML_PATH_GENERATE_CACHE_IMAGES = 'ssmanager/ssmanager_images/generate_cache_images';
29
+ const XML_PATH_IMAGE_WIDTH = 'ssmanager/ssmanager_images/image_width';
30
+ const XML_PATH_IMAGE_HEIGHT = 'ssmanager/ssmanager_images/image_height';
31
+
32
+ const XML_PATH_GENERATE_SWATCH_IMAGES = 'ssmanager/ssmanager_images/generate_swatch_images';
33
+ const XML_PATH_SWATCH_WIDTH = 'ssmanager/ssmanager_images/swatch_width';
34
+ const XML_PATH_SWATCH_HEIGHT = 'ssmanager/ssmanager_images/swatch_height';
35
+
36
+ public function isLiveIndexingEnabled()
37
+ {
38
+ return Mage::getStoreConfigFlag(self::XML_PATH_GLOBAL_LIVE_INDEXING_ENABLE_FL);
39
+ }
40
+
41
+ public function isCategorySearchEnabled()
42
+ {
43
+ return Mage::getStoreConfigFlag(self::XML_PATH_GLOBAL_CATEGORY_ENABLE_FL);
44
+ }
45
+
46
+ public function isZeroPriceIndexingEnabled()
47
+ {
48
+ return Mage::getStoreConfigFlag(self::XML_PATH_INDEX_ZERO_PRICE);
49
+ }
50
+
51
+ public function getApiFeedId()
52
+ {
53
+ return Mage::getStoreConfig(self::XML_PATH_API_FEED_ID);
54
+ }
55
+
56
+ public function getApiSiteId()
57
+ {
58
+ return Mage::getStoreConfig(self::XML_PATH_API_SITE_ID);
59
+ }
60
+
61
+ public function getApiBaseUrl()
62
+ {
63
+ return Mage::getStoreConfig(self::XML_PATH_API_BASE_URL);
64
+ }
65
+
66
+ public function getApiSecretKey()
67
+ {
68
+ return Mage::getStoreConfig(self::XML_PATH_API_SECRET_KEY);
69
+ }
70
+
71
+ public function isCacheImagesEnabled()
72
+ {
73
+ return Mage::getStoreConfig(self::XML_PATH_GENERATE_CACHE_IMAGES);
74
+ }
75
+
76
+ public function getImageHeight()
77
+ {
78
+ return Mage::getStoreConfig(self::XML_PATH_IMAGE_HEIGHT);
79
+ }
80
+
81
+ public function getImageWidth()
82
+ {
83
+ return Mage::getStoreConfig(self::XML_PATH_IMAGE_WIDTH);
84
+ }
85
+
86
+ public function isSwatchImagesEnabled()
87
+ {
88
+ return Mage::getStoreConfig(self::XML_PATH_GENERATE_SWATCH_IMAGES);
89
+ }
90
+
91
+ public function getSwatchHeight()
92
+ {
93
+ return Mage::getStoreConfig(self::XML_PATH_SWATCH_HEIGHT);
94
+ }
95
+
96
+ public function getSwatchWidth()
97
+ {
98
+ return Mage::getStoreConfig(self::XML_PATH_SWATCH_WIDTH);
99
+ }
100
+
101
+ /**
102
+ * Intended for layout action parameter helper calls
103
+ *
104
+ * If the current layer category is enabled, return
105
+ * new template; if not return the blocks existing template
106
+ */
107
+ public function getBlockTemplateIfCategoryEnabled($block, $newTemplate)
108
+ {
109
+ $layer = Mage::getSingleton('searchspring_manager/layer');
110
+ if ($layer->isSearchSpringEnabled()) {
111
+ return $newTemplate;
112
+ }
113
+ return Mage::app()->getLayout()->getBlock($block)->getTemplate();
114
+ }
115
+
116
+
117
+ }
app/code/community/SearchSpring/Manager/Iterator.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File Iterator.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Interface SearchSpring_Manager_Iterator
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ interface SearchSpring_Manager_Iterator extends Iterator
14
+ {
15
+ /**
16
+ * Move the index to the last element
17
+ *
18
+ * @return null
19
+ */
20
+ public function end();
21
+ }
app/code/community/SearchSpring/Manager/Model/Catalog/Category/Attribute/EnableSearchSpring/Backend.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Model_Catalog_Category_Attribute_EnableSearchSpring_Backend extends Mage_Eav_Model_Entity_Attribute_Backend_Abstract
4
+ {
5
+
6
+ public function afterLoad($object)
7
+ {
8
+ parent::afterLoad($object);
9
+ $attrCode = $this->getAttribute()->getAttributeCode();
10
+ $rawValue = $object->getData($attrCode);
11
+ switch ($rawValue) {
12
+ case SearchSpring_Manager_Model_Catalog_Category_Attribute_EnableSearchSpring_Source::VALUE_ENABLED:
13
+ $boolValue = true;
14
+ break;
15
+ case SearchSpring_Manager_Model_Catalog_Category_Attribute_EnableSearchSpring_Source::VALUE_DISABLED:
16
+ $boolValue = false;
17
+ break;
18
+ default:
19
+ $boolValue = Mage::helper('searchspring_manager')->isCategorySearchEnabled();
20
+ break;
21
+ }
22
+ $object->setData('is_search_spring_enabled', $boolValue);
23
+ return $this;
24
+ }
25
+
26
+ }
app/code/community/SearchSpring/Manager/Model/Catalog/Category/Attribute/EnableSearchSpring/Source.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class SearchSpring_Manager_Model_Catalog_Category_Attribute_EnableSearchSpring_Source extends Mage_Eav_Model_Entity_Attribute_Source_Abstract {
3
+
4
+ const VALUE_CONFIG = 0;
5
+ const VALUE_DISABLED = 1;
6
+ const VALUE_ENABLED = 2;
7
+
8
+ public function getAllOptions($withEmpty = true)
9
+ {
10
+ $options = array(
11
+ array('label' => 'Use Store SearchSpring Config', 'value' => self::VALUE_CONFIG),
12
+ array('label' => 'Disabled', 'value' => self::VALUE_DISABLED),
13
+ array('label' => 'Enabled', 'value' => self::VALUE_ENABLED)
14
+ );
15
+
16
+ return $options;
17
+ }
18
+ }
app/code/community/SearchSpring/Manager/Model/Layer.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Layer.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Model_Layer
10
+ *
11
+ * Overrides existing Layer object
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Model_Layer extends Mage_Catalog_Model_Layer
16
+ {
17
+
18
+ /**
19
+ * Retrieve current layer search collection
20
+ */
21
+ public function getProductCollection()
22
+ {
23
+ if (!$this->isSearchSpringEnabled()) {
24
+ return parent::getProductCollection();
25
+ }
26
+
27
+ if (isset($this->_productCollections[$this->getCurrentCategory()->getId()])) {
28
+ $collection = $this->_productCollections[$this->getCurrentCategory()->getId()];
29
+ } else {
30
+ $apiFactory = new SearchSpring_Manager_Factory_ApiFactory();
31
+ $api = $apiFactory->make('search');
32
+
33
+ $searchRequestBodyFactory = new SearchSpring_Manager_Factory_SearchRequestBodyFactory();
34
+ $searchRequestBody = $searchRequestBodyFactory->make();
35
+
36
+ $collection = new SearchSpring_Manager_Entity_SearchCollection($api, $searchRequestBody);
37
+ $collection->addFilter('category_ids', $this->getCurrentCategory()->getId());
38
+ $collection->addFilterOnly('category_ids', explode(',', $this->getCurrentCategory()->getChildren()));
39
+
40
+ $this->_productCollections[$this->getCurrentCategory()->getId()] = $collection;
41
+ }
42
+
43
+ return $collection;
44
+ }
45
+
46
+ /**
47
+ * Returns whether SearchSpring is enabled for the current category or not
48
+ *
49
+ * @return bool
50
+ */
51
+ public function isSearchSpringEnabled()
52
+ {
53
+ return $this->getCurrentCategory()->getIsSearchSpringEnabled();
54
+ }
55
+
56
+ public function getFilterableAttributes()
57
+ {
58
+ if($this->isSearchSpringEnabled()) {
59
+ return $this->getProductCollection()->getResults()->getFacets();
60
+ } else {
61
+ return parent::getFilterableAttributes();
62
+ }
63
+ }
64
+
65
+ }
app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpring.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Model_Layer_Filter_SearchSpring extends Mage_Catalog_Model_Layer_Filter_Abstract
4
+ {
5
+
6
+ protected $_searchSpringFacet;
7
+ protected $_currentValue;
8
+
9
+ protected $_itemModelName = 'searchspring_manager/layer_filter_searchSpringItem';
10
+
11
+ /**
12
+ * Apply attribute option to State
13
+ *
14
+ * @param Zend_Controller_Request_Abstract $request
15
+ * @param Varien_Object $filterBlock
16
+ * @return Mage_Catalog_Model_Layer_Filter_Attribute
17
+ */
18
+ public function apply(Zend_Controller_Request_Abstract $request, $filterBlock)
19
+ {
20
+ return $this;
21
+ }
22
+
23
+ /**
24
+ * Returns the multiple mode of the SearchSpring Facet
25
+ */
26
+ public function getMultipleMode() {
27
+
28
+ return isset($this->_searchSpringFacet->multiple)?$this->_searchSpringFacet->multiple:'single';
29
+ }
30
+
31
+ /**
32
+ * Set searchspring facet data
33
+ *
34
+ * @param $facet
35
+ * @return $this
36
+ */
37
+ public function setSearchSpringFacet($facet) {
38
+ $this->_searchSpringFacet = $facet;
39
+ $this->_requestVar = 'filter_' . $facet->field;
40
+ return $this;
41
+ }
42
+
43
+ /**
44
+ * Initialize filter items
45
+ *
46
+ * @return Mage_Catalog_Model_Layer_Filter_Abstract
47
+ */
48
+ protected function _initItems()
49
+ {
50
+ $data = $this->_getItemsData();
51
+ $items = array();
52
+ foreach ($data as $itemData) {
53
+ $items[] = $this->_createItem(
54
+ $itemData['label'],
55
+ $itemData['value'],
56
+ $itemData['count']
57
+ );
58
+ }
59
+ $this->_items = $items;
60
+
61
+
62
+ return $this;
63
+ }
64
+
65
+ /**
66
+ * Create filter item object
67
+ *
68
+ * @param string $label
69
+ * @param mixed $value
70
+ * @param int $count
71
+ * @return Mage_Catalog_Model_Layer_Filter_Item
72
+ */
73
+ protected function _createItem($label, $value, $count=0)
74
+ {
75
+ $item = Mage::getModel($this->_itemModelName)
76
+ ->setFilter($this)
77
+ ->setLabel($label)
78
+ ->setValue($value)
79
+ ->setCount($count);
80
+
81
+ return $item;
82
+ }
83
+
84
+ public function getCurrentValue() {
85
+ if(!isset($this->_currentValue)) {
86
+ $this->_currentValue = Mage::app()->getRequest()->getParam($this->getRequestVar());
87
+ }
88
+ return $this->_currentValue;
89
+ }
90
+
91
+ /**
92
+ * Get data array for building attribute filter items
93
+ *
94
+ * @return array
95
+ */
96
+ protected function _getItemsData()
97
+ {
98
+ $items = array();
99
+
100
+ foreach($this->_searchSpringFacet->values as $value) {
101
+ $item = array(
102
+ 'label' => $value->label,
103
+ 'count' => $value->count,
104
+ 'type' => $value->type
105
+ );
106
+
107
+ if($value->type == 'value') {
108
+ $item['value'] = $value->value;
109
+ } else if($value->type == 'range') {
110
+ $value = 'RANGE:' . $value->low . ' TO ' . $value->high;
111
+ /* 'low' => $value->low,
112
+ 'high' => $value->high
113
+ );*/
114
+ $item['value'] = $value;
115
+ }
116
+ $items[] = $item;
117
+ }
118
+
119
+ return $items;
120
+ }
121
+
122
+ /**
123
+ * Return the name of the facet
124
+ *
125
+ * @return string
126
+ */
127
+ public function getName() {
128
+ return $this->_searchSpringFacet->label;
129
+ }
130
+
131
+ }
app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpringCategory.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Model_Layer_Filter_SearchSpringCategory extends SearchSpring_Manager_Model_Layer_Filter_SearchSpring
4
+ {
5
+
6
+ protected $_itemModelName = 'searchspring_manager/layer_filter_searchSpringCategoryItem';
7
+
8
+ protected $_childCategories = array();
9
+
10
+ public function getCurrentValue() {
11
+ if(!isset($this->_currentValue)) {
12
+ $this->_currentValue = Mage::app()->getRequest()->getParam($this->getRequestVar());
13
+ }
14
+
15
+ if(is_null($this->_currentValue)) {
16
+ $this->_currentValue = $this->getLayer()->getCurrentCategory()->getId();
17
+ }
18
+
19
+ return $this->_currentValue;
20
+ }
21
+
22
+ public function getCurrentCategory() {
23
+ if(!isset($this->_currentCategory)) {
24
+ $categoryId = $this->getCurrentValue();
25
+ $this->_currentCategory = Mage::getModel('catalog/category')->load($categoryId);
26
+ }
27
+
28
+ return $this->_currentCategory;
29
+ }
30
+
31
+ /**
32
+ * Get data array for building attribute filter items
33
+ *
34
+ * @return array
35
+ */
36
+ protected function _getItemsData()
37
+ {
38
+ $childCategories = $this->_getChildCategories();
39
+
40
+ $items = array();
41
+
42
+ foreach($this->_searchSpringFacet->values as $value) {
43
+
44
+ if(!isset($this->_childCategories[$value->value])) {
45
+ continue;
46
+ }
47
+
48
+ $category = $this->_childCategories[$value->value];
49
+
50
+ $item = array(
51
+ 'label' => $category->getName(),
52
+ 'count' => $value->count,
53
+ 'type' => $value->type,
54
+ 'value' => $value->value
55
+ );
56
+
57
+ $items[] = $item;
58
+ }
59
+
60
+ return $items;
61
+ }
62
+
63
+ public function getChildCategory($id) {
64
+ if(empty($this->_childCategories)) {
65
+ $this->_getChildCategories();
66
+ }
67
+
68
+ return $this->_childCategories[$id];
69
+ }
70
+
71
+ protected function _getChildCategories() {
72
+ if(empty($this->_childCategories)) {
73
+ $category = $this->getCurrentCategory();
74
+ $children = $category->getChildrenCategories();
75
+
76
+ foreach ($children as $child) {
77
+ $this->_childCategories[$child->getId()] = $child;
78
+ }
79
+ }
80
+
81
+ return $this->_childCategories;
82
+
83
+ }
84
+
85
+ public function getParents() {
86
+ $pathIds = array_reverse(explode(',', $this->getCurrentCategory()->getPathInStore()));
87
+ $categories = Mage::getResourceModel('catalog/category_collection')
88
+ ->setStore(Mage::app()->getStore())
89
+ ->addAttributeToSelect('name')
90
+ ->addAttributeToSelect('url_key')
91
+ ->addFieldToFilter('entity_id', array('in' => $pathIds))
92
+ ->addFieldToFilter('is_active', 1)
93
+ ->setOrder('level')
94
+ ->load()
95
+ ->getItems();
96
+
97
+ return $categories;
98
+ }
99
+
100
+ /**
101
+ * Return the name of the facet
102
+ *
103
+ * @return string
104
+ */
105
+ public function getName() {
106
+ return $this->__('Category');
107
+ }
108
+
109
+ }
app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpringCategoryItem.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Model_Layer_Filter_SearchSpringCategoryItem extends SearchSpring_Manager_Model_Layer_Filter_SearchSpringItem
4
+ {
5
+
6
+ /**
7
+ * Get filter item url
8
+ *
9
+ * @return string
10
+ */
11
+ public function getUrl()
12
+ {
13
+
14
+ $query = array(
15
+ Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
16
+ );
17
+
18
+ $url = Mage::getUrl('',
19
+ array(
20
+ '_current' => true,
21
+ '_direct' => Mage::getModel('core/url_rewrite')->loadByIdPath('category/' . $this->getValue())->getRequestPath(),
22
+ '_query' => $query
23
+ )
24
+ );
25
+
26
+ return $url;
27
+ }
28
+
29
+ }
app/code/community/SearchSpring/Manager/Model/Layer/Filter/SearchSpringItem.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SearchSpring_Manager_Model_Layer_Filter_SearchSpringItem extends Mage_Catalog_Model_Layer_Filter_Item
4
+ {
5
+
6
+ /**
7
+ * Get filter item url
8
+ *
9
+ * @return string
10
+ */
11
+ public function getUrl()
12
+ {
13
+ $value = $this->_getUrlValue();
14
+
15
+ $query = array(
16
+ $this->getFilter()->getRequestVar() => $value,
17
+ Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
18
+ );
19
+
20
+ $url = Mage::getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query));
21
+
22
+ return $url;
23
+ }
24
+
25
+ /**
26
+ * Get url for remove item from filter
27
+ *
28
+ * @return string
29
+ */
30
+ public function getRemoveUrl()
31
+ {
32
+ // Get URL knows if the current value is already selected and will remove it as part of the URL.
33
+ return $this->getUrl();
34
+ }
35
+
36
+ /**
37
+ * Returns true if the current value item is active
38
+ */
39
+ public function isActive() {
40
+ $currentValue = $this->getFilter()->getCurrentValue();
41
+ $itemValue = $this->getValue();
42
+
43
+ return $this->_containsValue($currentValue, $itemValue);
44
+ }
45
+
46
+ /**
47
+ * Get the value to set for the URL if the current item is selected.
48
+ */
49
+ protected function _getUrlValue() {
50
+ $currentValue = $this->getFilter()->getCurrentValue();
51
+ $itemValue = $this->getValue();
52
+
53
+ if($this->_containsValue($currentValue, $itemValue)) {
54
+ return $this->_removeValue($currentValue, $itemValue);
55
+ } else {
56
+ return $this->_addValue($currentValue, $itemValue);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Return the current value
62
+ */
63
+ public function getValue() {
64
+ $value = parent::getValue();
65
+ if(isset($value->rangeLow)) {
66
+ $value = 'RANGE:' . $value->rangeLow . ' TO ' . $value->rangeHigh;
67
+ }
68
+ return $value;
69
+ }
70
+
71
+ /**
72
+ * Check if itemValue is contained in the currentValue
73
+ *
74
+ * @param $currentValue
75
+ * @param $itemValue
76
+ * @return bool
77
+ */
78
+ protected function _containsValue($currentValue, $itemValue) {
79
+ if(is_null($currentValue)) {
80
+ return false;
81
+ } else if(!is_array($currentValue)) {
82
+ return $currentValue == $itemValue;
83
+ } else {
84
+ return in_array($itemValue, $currentValue);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Remove itemValue from the currentValue
90
+ *
91
+ * @param $currentValue
92
+ * @param $itemValue
93
+ * @return mixed
94
+ */
95
+ protected function _removeValue($currentValue, $itemValue) {
96
+ if(!is_array($currentValue)) {
97
+ return null;
98
+ } else {
99
+ unset($currentValue[array_search($itemValue, $currentValue)]);
100
+ return $currentValue;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Add itemValue to the currentValue
106
+ *
107
+ * @param $currentValue
108
+ * @param $itemValue
109
+ * @return mixed
110
+ */
111
+ protected function _addValue($currentValue, $itemValue) {
112
+ if ($this->getFilter()->getMultipleMode() == 'multiple-intersect' || $this->getFilter()->getMultipleMode() == 'multiple-union') {
113
+ if (is_null($currentValue)) {
114
+ $currentValue = $itemValue;
115
+ } else if (!is_array($currentValue)) {
116
+ $currentValue = array($currentValue, $itemValue);
117
+ } else {
118
+ $currentValue[] = $itemValue;
119
+ }
120
+ } else {
121
+ $currentValue = $itemValue;
122
+ }
123
+ return $currentValue;
124
+ }
125
+
126
+ }
app/code/community/SearchSpring/Manager/Model/Observer/CategorySaveObserver.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CategorySaveObserver.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Model_Observer_CategorySaveObserver
10
+ *
11
+ * On a category change, trigger one of these methods
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpring_Manager_Model_Observer_LiveIndexer
16
+ {
17
+ /**
18
+ * Performs operations on a Varien_Object
19
+ *
20
+ * @var SearchSpring_Manager_VarienObject_Data $varienObjectData
21
+ */
22
+ private $varienObjectData;
23
+
24
+ /**
25
+ * Api adapter for connecting to SearchSpring api
26
+ *
27
+ * @var SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter $api
28
+ */
29
+ private $api;
30
+
31
+ /**
32
+ * Create a request body
33
+ *
34
+ * @var SearchSpring_Manager_Factory_IndexingRequestBodyFactory $requestBodyFactory
35
+ */
36
+ private $requestBodyFactory;
37
+
38
+ /**
39
+ * Constructor
40
+ */
41
+ public function __construct()
42
+ {
43
+ $apiFactory = new SearchSpring_Manager_Factory_ApiFactory();
44
+ $this->api = $apiFactory->make('index');
45
+ $this->requestBodyFactory = new SearchSpring_Manager_Factory_IndexingRequestBodyFactory();
46
+ $this->varienObjectData = new SearchSpring_Manager_VarienObject_Data();
47
+ }
48
+
49
+ /**
50
+ * After a category is saved
51
+ *
52
+ * We only push out an update to products and sub-products if the category name or status has changed
53
+ *
54
+ * @todo implement status change
55
+ *
56
+ * @param Varien_Event_Observer $productEvent
57
+ *
58
+ * @return bool
59
+ */
60
+ public function afterSaveUpdateProductCategory(Varien_Event_Observer $productEvent)
61
+ {
62
+ if (!$this->isEnabled()) {
63
+ return true;
64
+ }
65
+
66
+ /** @var Mage_Catalog_Model_Category $category */
67
+ $category = $productEvent->getData('category');
68
+
69
+ // if this is a new category return because we already sent the ids
70
+ if (true === $this->varienObjectData->isNew($category)) {
71
+ return true;
72
+ }
73
+
74
+ $updates = $this->varienObjectData->findUpdatedData($category);
75
+
76
+ // if category name is not changed, this will not affect the product categories
77
+ if (!isset($updates['name']) && !isset($updates['is_active'])) {
78
+ return true;
79
+ }
80
+
81
+ $requestBody = $this->requestBodyFactory->make(
82
+ SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_CATEGORY,
83
+ $category->getAllChildren(true)
84
+ );
85
+ $this->api->pushIds($requestBody);
86
+
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * After category products have changed
92
+ *
93
+ * This is triggered when products are checked or unchecked for a category. Only affects products of category.
94
+ *
95
+ * @param Varien_Event_Observer $productEvent
96
+ *
97
+ * @return bool
98
+ */
99
+ public function afterProductChangeUpdateProductCategory(Varien_Event_Observer $productEvent)
100
+ {
101
+ if (!$this->isEnabled()) {
102
+ return true;
103
+ }
104
+
105
+ $validator = new SearchSpring_Manager_Validator_ProductValidator();
106
+
107
+ $productIds = $productEvent->getData('product_ids');
108
+ foreach($productIds as $k => $productId) {
109
+ $product = Mage::getModel('catalog/product')->load($productId);
110
+ if(!$validator->isValid($product)) {
111
+ unset($productIds[$k]);
112
+ }
113
+ }
114
+
115
+ $requestBody = $this->requestBodyFactory->make(
116
+ SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_PRODUCT,
117
+ $productIds
118
+ );
119
+
120
+ $this->api->pushIds($requestBody);
121
+
122
+ return true;
123
+ }
124
+
125
+ /**
126
+ * After a category is moved
127
+ *
128
+ * Updates products and sub-products. Only update if path is changed. This should be double checked, but path
129
+ * should not be changed if the category is just reordered.
130
+ *
131
+ * @param Varien_Event_Observer $productEvent
132
+ *
133
+ * @return bool
134
+ */
135
+ public function afterMoveUpdateProductCategory(Varien_Event_Observer $productEvent)
136
+ {
137
+ if (!$this->isEnabled()) {
138
+ return true;
139
+ }
140
+
141
+ $category = $productEvent->getData('category');
142
+ $updates = $this->varienObjectData->findUpdatedData($category);
143
+
144
+ if (!isset($updates['path'])) {
145
+ return true;
146
+ }
147
+
148
+ $requestBody = $this->requestBodyFactory->make(
149
+ SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_CATEGORY,
150
+ $category->getAllChildren(true)
151
+ );
152
+ $this->api->pushIds($requestBody);
153
+
154
+ return true;
155
+ }
156
+
157
+ /**
158
+ * After a category is deleted
159
+ *
160
+ * Remove the category from products and sub-products.
161
+ *
162
+ * @param Varien_Event_Observer $productEvent
163
+ * @return bool
164
+ */
165
+ public function afterDeleteUpdateProductCategory(Varien_Event_Observer $productEvent)
166
+ {
167
+ if (!$this->isEnabled()) {
168
+ return true;
169
+ }
170
+
171
+ /** @var Mage_Catalog_Model_Category $category */
172
+ $category = $productEvent->getData('category');
173
+ $requestBody = $this->requestBodyFactory->make(
174
+ SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_PRODUCT,
175
+ $this->getCategoryProductIds($category)
176
+ );
177
+ $this->api->pushIds($requestBody);
178
+
179
+ return true;
180
+ }
181
+
182
+ /**
183
+ * Get product ids for category and subcategories
184
+ *
185
+ * @param Mage_Catalog_Model_Category $category
186
+ *
187
+ * @return array
188
+ */
189
+ private function getCategoryProductIds(Mage_Catalog_Model_Category $category)
190
+ {
191
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $collection */
192
+ $collection = mage::getModel('catalog/product')->getCollection()
193
+ ->addAttributeToSelect('*')
194
+ ->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id=entity_id', null, 'left')
195
+ ->addAttributeToFilter('category_id', array('in' => $category->getAllChildren(true)));
196
+ $collection->getSelect()->group('e.entity_id');
197
+ $productIds = $collection->load()->getAllIds();
198
+
199
+ return $productIds;
200
+ }
201
+
202
+ }
app/code/community/SearchSpring/Manager/Model/Observer/LiveIndexer.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * LiveIndexer.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Model_Observer_LiveIndexer
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ abstract class SearchSpring_Manager_Model_Observer_LiveIndexer
14
+ {
15
+
16
+ /**
17
+ * Checks if live indexing is enabled
18
+ *
19
+ * @return bool
20
+ */
21
+ protected function isEnabled()
22
+ {
23
+ return Mage::helper('searchspring_manager')->isLiveIndexingEnabled();
24
+ }
25
+
26
+ }
app/code/community/SearchSpring/Manager/Model/Observer/ProductSaveObserver.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File ProductSaveObserver.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Model_Observer_ProductSaveObserver
10
+ *
11
+ * Listens for product save events and pushes ids to api
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Model_Observer_ProductSaveObserver extends SearchSpring_Manager_Model_Observer_LiveIndexer
16
+ {
17
+ /**
18
+ * Create an api adapter for SearchSpring
19
+ *
20
+ * @var SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter $api
21
+ */
22
+ private $api;
23
+
24
+ /**
25
+ * Creates a request body
26
+ *
27
+ * @var SearchSpring_Manager_Factory_IndexingRequestBodyFactory
28
+ */
29
+ private $requestBodyFactory;
30
+
31
+ /**
32
+ * List of parentIds for the updated product.
33
+ * @var array
34
+ */
35
+ protected $_productIds = array();
36
+
37
+ /**
38
+ * Constructor
39
+ *
40
+ * We need to do some setup in here because there's no way to inject dependencies
41
+ */
42
+ public function __construct()
43
+ {
44
+ $apiFactory = new SearchSpring_Manager_Factory_ApiFactory();
45
+ $this->api = $apiFactory->make('index');
46
+ $this->requestBodyFactory = new SearchSpring_Manager_Factory_IndexingRequestBodyFactory();
47
+ }
48
+
49
+ /**
50
+ * After a product is saved, push that product to the SearchSpring API
51
+ *
52
+ * @param Varien_Event_Observer $productEvent The product event data
53
+ *
54
+ * @return bool
55
+ */
56
+ public function afterSavePushProduct(Varien_Event_Observer $productEvent)
57
+ {
58
+ if (!$this->isEnabled()) {
59
+ return true;
60
+ }
61
+
62
+ $product = $productEvent->getData('product');
63
+ $this->_productIds = $this->_getProductIds($product);
64
+
65
+ $this->_handleProductChange();
66
+ return true;
67
+ }
68
+
69
+ /**
70
+ * Before a product is deleted grab all IDs
71
+ *
72
+ * @param Varien_Event_Observer $productEvent The product event data
73
+ *
74
+ * @return bool
75
+ */
76
+ public function beforeDeletePushProduct(Varien_Event_Observer $productEvent) {
77
+ $product = $productEvent->getData('product');
78
+ $this->_productIds = $this->_getProductIds($product);
79
+ return true;
80
+ }
81
+
82
+ /**
83
+ * After a product is deleted, push that product delete to the SearchSpring API
84
+ *
85
+ * @param Varien_Event_Observer $productEvent The product event data
86
+ *
87
+ * @return bool
88
+ */
89
+ public function afterDeletePushProduct(Varien_Event_Observer $productEvent)
90
+ {
91
+ if (!$this->isEnabled()) {
92
+ return true;
93
+ }
94
+
95
+ $this->_handleProductChange();
96
+ return true;
97
+ }
98
+
99
+ protected function _handleProductChange() {
100
+ $productIds = $this->_getActions();
101
+
102
+ // create the request body with product ids
103
+ if(!empty($productIds['delete'])) {
104
+ $requestBody = $this->requestBodyFactory->make(
105
+ SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_PRODUCT,
106
+ $productIds['delete'],
107
+ true
108
+ );
109
+ }
110
+
111
+ // create the request body with product ids
112
+ if(!empty($productIds['update'])) {
113
+ $requestBody = $this->requestBodyFactory->make(
114
+ SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_PRODUCT,
115
+ $productIds['update']
116
+ );
117
+ }
118
+
119
+ // send ids to api
120
+ $this->api->pushIds($requestBody);
121
+
122
+ return true;
123
+ }
124
+
125
+ protected function _getProductIds($product) {
126
+ $productIds = array();
127
+ // If the product is a simple product we may need to check its parent(s)
128
+ if($product->getTypeId() == "simple"){
129
+ // Check for configurable parent
130
+ $productIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
131
+
132
+ // If there are no configurable parents check grouped
133
+ if(empty($productIds)) {
134
+ $productIds = Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($product->getId());
135
+ }
136
+ }
137
+
138
+ $productIds[] = $product->getId();
139
+
140
+ return $productIds;
141
+ }
142
+
143
+
144
+ /**
145
+ * Get list of update/deletes for product and parents.
146
+ *
147
+ * @param Mage_Catalog_Model_Product $product The product being updated
148
+ *
149
+ * @return array
150
+ */
151
+ protected function _getActions() {
152
+ $productActions = array();
153
+
154
+ $validator = new SearchSpring_Manager_Validator_ProductValidator();
155
+
156
+ // Figure out deletes
157
+ foreach($this->_productIds as $productId) {
158
+ $product = Mage::getModel('catalog/product')->load($productId);
159
+
160
+ $isValid = $validator->isValid(
161
+ $product
162
+ );
163
+
164
+ if(!$isValid) {
165
+ $productActions['delete'][] = $productId;
166
+ } else {
167
+ $pricingStrategy = SearchSpring_Manager_Factory_PricingFactory::make($product);
168
+ $pricingStrategy->calculatePrices();
169
+
170
+ // Only check should delete if the product is otherwise valid
171
+ $shouldDelete = $validator->shouldDelete(
172
+ $product,
173
+ $pricingStrategy
174
+ );
175
+
176
+ if($shouldDelete) {
177
+ $productActions['delete'][] = $productId;
178
+ } else {
179
+ $productActions['update'][] = $productId;
180
+ }
181
+ }
182
+ }
183
+
184
+ return $productActions;
185
+ }
186
+ }
app/code/community/SearchSpring/Manager/Operation/Product.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Product.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product
10
+ *
11
+ * Abstract class for all product operations
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ abstract class SearchSpring_Manager_Operation_Product implements SearchSpring_Manager_Operation_ProductOperation
16
+ {
17
+ /**
18
+ * SearchSpring operation class prefix
19
+ */
20
+ const OPERATION_CLASS_PREFIX = 'SearchSpring_Manager_Operation_Product_';
21
+
22
+ /**
23
+ * Service to sanitize string data
24
+ *
25
+ * @var SearchSpring_Manager_String_Sanitizer
26
+ */
27
+ private $sanitizer;
28
+
29
+ /**
30
+ * Records collection
31
+ *
32
+ * @var SearchSpring_Manager_Entity_RecordsCollection $records
33
+ */
34
+ private $records;
35
+
36
+ /**
37
+ * An array of additional data
38
+ *
39
+ * @var array $parameters
40
+ */
41
+ private $parameters;
42
+
43
+ /**
44
+ * Constructor
45
+ *
46
+ * @param SearchSpring_Manager_String_Sanitizer $sanitizer
47
+ * @param SearchSpring_Manager_Entity_RecordsCollection $records
48
+ * @param array $parameters
49
+ */
50
+ public function __construct(
51
+ SearchSpring_Manager_String_Sanitizer $sanitizer,
52
+ SearchSpring_Manager_Entity_RecordsCollection $records,
53
+ array $parameters = array()
54
+ ) {
55
+ $this->sanitizer = $sanitizer;
56
+ $this->records = $records;
57
+ $this->parameters = $parameters;
58
+ }
59
+
60
+ /**
61
+ * Overridable method to determine if an operation is valid for the product
62
+ *
63
+ * Will allow operations to cancel adding a product record. Defaults to true.
64
+ *
65
+ * @param Mage_Catalog_Model_Product $product
66
+ *
67
+ * @return bool
68
+ */
69
+ public function isValid(Mage_Catalog_Model_Product $product)
70
+ {
71
+ return true;
72
+ }
73
+
74
+ /**
75
+ * Get records collection
76
+ *
77
+ * @return SearchSpring_Manager_Entity_RecordsCollection
78
+ */
79
+ protected final function getRecords()
80
+ {
81
+ return $this->records;
82
+ }
83
+
84
+ /**
85
+ * {@inheritdoc}
86
+ */
87
+ public final function setRecords(SearchSpring_Manager_Entity_RecordsCollection $records)
88
+ {
89
+ $this->records = $records;
90
+
91
+ return $this;
92
+ }
93
+
94
+ /**
95
+ * Get the sanitizer
96
+ *
97
+ * @return SearchSpring_Manager_String_Sanitizer
98
+ */
99
+ protected final function getSanitizer()
100
+ {
101
+ return $this->sanitizer;
102
+ }
103
+
104
+ /**
105
+ * Get a parameter from array
106
+ *
107
+ * @param string $key
108
+ *
109
+ * @return mixed
110
+ *
111
+ * @throws InvalidArgumentException If the key does not exist
112
+ */
113
+ protected final function getParameter($key)
114
+ {
115
+ if (!isset($this->parameters[$key])) {
116
+ throw new InvalidArgumentException('Key does not exist');
117
+ }
118
+
119
+ return $this->parameters[$key];
120
+ }
121
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetCategories.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetCategories.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetCategories
10
+ *
11
+ * Set category data to the feed
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetCategories extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**#@+
18
+ * Feed Constants
19
+ */
20
+ const FEED_CATEGORY_HIERARCHY = 'category_hierarchy';
21
+ const FEED_CATEGORY_NAME = 'category_name';
22
+ const FEED_CATEGORY_IDS = 'category_ids';
23
+ /**#@-*/
24
+
25
+ /**
26
+ * Category names we should skip
27
+ *
28
+ * @var array
29
+ */
30
+ private $skipCategories = array('Root Catalog', 'Default Category', null);
31
+
32
+ /**
33
+ * Sets category data to feed
34
+ * - category_hierarchy
35
+ * - category_name
36
+ *
37
+ * @param Mage_Catalog_Model_Product $product
38
+ *
39
+ * @return $this
40
+ */
41
+ public function perform(Mage_Catalog_Model_Product $product)
42
+ {
43
+ $categoryHierarchies = array();
44
+ $categoryNames = array();
45
+ $categoryIds = array();
46
+
47
+
48
+ /** @var Mage_Catalog_Model_Category $category */
49
+ foreach($product->getCategoryIds() as $categoryId) {
50
+ // load category data
51
+ $category = Mage::getModel('catalog/category')->load($categoryId);
52
+
53
+ if (!$category->getData('is_active')) {
54
+ continue;
55
+ }
56
+
57
+ $categoryName = $category->getData('name');
58
+ if (in_array($categoryName, $this->skipCategories)) {
59
+ continue;
60
+ }
61
+
62
+ $categoryPath = $category->getPathIds();
63
+
64
+ $categoryIds = array_merge($categoryIds, $categoryPath);
65
+
66
+ $hierarchies = $this->buildCategoryHierarchy($categoryPath);
67
+
68
+ foreach ($hierarchies as $value) {
69
+ $categoryHierarchies[] = $value;
70
+ }
71
+
72
+ $categoryNames[] = $categoryName;
73
+ }
74
+
75
+ $categoryHierarchies = array_unique($categoryHierarchies);
76
+
77
+ if (empty($categoryHierarchies)) {
78
+ $this->getRecords()->set(self::FEED_CATEGORY_HIERARCHY, $categoryHierarchies);
79
+ }
80
+
81
+ // we need to do an additional loop here because array_unique preserves keys
82
+ // this causes an issue when converting to json as it will create an object if keys aren't sequential
83
+ foreach ($categoryHierarchies as $hierarchy) {
84
+ $this->getRecords()->add(self::FEED_CATEGORY_HIERARCHY, $hierarchy);
85
+ }
86
+
87
+ $this->getRecords()->set(self::FEED_CATEGORY_IDS, array_values(array_unique($categoryIds)));
88
+
89
+ $this->getRecords()->set(self::FEED_CATEGORY_NAME, $categoryNames);
90
+
91
+ return $this;
92
+ }
93
+
94
+ /**
95
+ * Build the category hierarchy based on path
96
+ *
97
+ * Will return an array with all levels
98
+ *
99
+ * Example:
100
+ * Level 1
101
+ * Level 1 | Level 2
102
+ * Level 1 | Level 2 | Level 3
103
+ *
104
+ * @param array $categoryPath
105
+ *
106
+ * @return array
107
+ */
108
+ private function buildCategoryHierarchy(array $categoryPath)
109
+ {
110
+ $hierarchy = array();
111
+ $currentHierarchy = array();
112
+ foreach ($categoryPath as $categoryId) {
113
+ $category = Mage::getModel('catalog/category')->load($categoryId);
114
+
115
+ $categoryName = $category->getData('name');
116
+ if (in_array($categoryName, $this->skipCategories)) {
117
+ continue;
118
+ }
119
+ $currentHierarchy[] = $categoryName;
120
+ $hierarchy[] = implode('/', $currentHierarchy);
121
+ }
122
+
123
+ return $hierarchy;
124
+ }
125
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetCoreFields.php ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetCoreFields.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetCoreFields
10
+ *
11
+ * Set SearchSpring core fields to the feed
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetCoreFields extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**#@+
18
+ * Feed constants
19
+ */
20
+ const FEED_SKU = 'sku';
21
+ const FEED_PRODUCT_TYPE = 'product_type';
22
+ const FEED_DESCRIPTION = 'description';
23
+ const FEED_SHORT_DESCRIPTION = 'short_description';
24
+ const FEED_QUANTITY = 'quantity';
25
+ const FEED_IN_STOCK = 'in_stock';
26
+ const FEED_WEIGHT = 'weight';
27
+ const FEED_MANUFACTURER = 'manufacturer';
28
+ const FEED_URL = 'url';
29
+ const FEED_NAME = 'name';
30
+ const FEED_CHILD_QUANTITY = 'child_quantity';
31
+ const FEED_CHILD_SKU = 'child_sku';
32
+ const FEED_CHILD_NAME = 'child_name';
33
+ /**#@-*/
34
+
35
+
36
+
37
+ /**
38
+ * Add SearchSpring core fields to the feed
39
+ * - sku
40
+ * - product_type
41
+ * - quantity
42
+ * - in_stock
43
+ * - weight
44
+ * - manufacturer
45
+ * - url
46
+ * - url
47
+ * - image_url
48
+ * - thumbnail_url
49
+ * - cached_thumbnail_url
50
+ * - name
51
+ * - description
52
+ * - short_description
53
+ * - child_quantity
54
+ * - child_name
55
+ * - child_sku
56
+ *
57
+ * @param Mage_Catalog_Model_Product $product
58
+ *
59
+ * @return $this
60
+ */
61
+ public function perform(Mage_Catalog_Model_Product $product)
62
+ {
63
+ $webBaseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
64
+
65
+ $this->getRecords()->set(self::FEED_SKU, $product->getSku());
66
+ $this->getRecords()->set(self::FEED_PRODUCT_TYPE, $product->getData('type_id'));
67
+ $this->getRecords()->set(self::FEED_QUANTITY, number_format($this->getQuantity($product)));
68
+ $this->getRecords()->set(self::FEED_IN_STOCK, $product->isInStock());
69
+ $this->getRecords()->set(self::FEED_WEIGHT, number_format((double)$product->getWeight(), 2));
70
+ $this->getRecords()->set(self::FEED_MANUFACTURER, $product->getAttributeText('manufacturer'));
71
+ $this->getRecords()->set(self::FEED_URL, $webBaseUrl . $product->getUrlPath());
72
+
73
+ $productName = $this->getSanitizer()->removeNewlinesAndStripTags($product->getName());
74
+ $description = $this->getSanitizer()->sanitizeForRequest($product->getData('description'));
75
+ $shortDescription = $this->getSanitizer()->sanitizeForRequest($product->getData('short_description'));
76
+ $this->getRecords()->set(self::FEED_NAME, $productName);
77
+ $this->getRecords()->set(self::FEED_DESCRIPTION, $description);
78
+ $this->getRecords()->set(self::FEED_SHORT_DESCRIPTION, $shortDescription);
79
+
80
+ $this->setChildQuantity($product);
81
+
82
+ return $this;
83
+ }
84
+
85
+ /**
86
+ * Helper method to get the quantity from a product
87
+ *
88
+ * @param Mage_Catalog_Model_Product $product
89
+ * @return int
90
+ */
91
+ private function getQuantity(Mage_Catalog_Model_Product $product)
92
+ {
93
+ $quantity = $product->getData('stock_item')->getData('qty');
94
+
95
+ return (int)$quantity;
96
+ }
97
+
98
+ /**
99
+ * If the product is composite, find the child quantities and set that to the field
100
+ */
101
+ private function setChildQuantity(Mage_Catalog_Model_Product $product)
102
+ {
103
+ // default to normal quantity
104
+ $childQuantity = $this->getRecords()->get(self::FEED_QUANTITY);
105
+
106
+ // find the child quantity if it exists
107
+ switch ($product->getTypeId()) {
108
+ case Mage_Catalog_Model_Product_Type::TYPE_GROUPED:
109
+ /** @var Mage_Catalog_Model_Product_Type_Grouped $typeInstance */
110
+ $typeInstance = $product->getTypeInstance(true);
111
+ $associated = $typeInstance->getAssociatedProducts($product);
112
+ $childQuantity = $this->getQuantityForChildren($associated);
113
+
114
+ break;
115
+ case Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE:
116
+ /** @var Mage_Catalog_Model_Product_Type_Configurable $typeInstance */
117
+ $typeInstance = $product->getTypeInstance(true);
118
+
119
+ /** @var Mage_Catalog_Model_Product_Type_Configurable_Attribute $attribute */
120
+ foreach ($typeInstance->getConfigurableAttributesAsArray($product) as $attribute) {
121
+ foreach ($attribute['values'] as $value) {
122
+ $this->getRecords()->add($attribute['attribute_code'], $value['label']);
123
+ }
124
+ }
125
+
126
+ $children = $typeInstance->getUsedProducts(null, $product);
127
+ $childQuantity = $this->getQuantityForChildren($children);
128
+
129
+ break;
130
+ case Mage_Catalog_Model_Product_Type::TYPE_BUNDLE:
131
+ /** @var Mage_Bundle_Model_Product_Type $typeInstance */
132
+ $typeInstance = $product->getTypeInstance(true);
133
+ $optionsIds = $typeInstance->getOptionsIds($product);
134
+ $selections = $typeInstance->getSelectionsCollection($optionsIds, $product);
135
+ $bundleOptions = $typeInstance->getOptionsByIds($optionsIds, $product);
136
+ $bundleOptions->appendSelections($selections);
137
+
138
+ /** @var Mage_Bundle_Model_Option $bundleOption */
139
+ foreach ($bundleOptions as $bundleOption) {
140
+ $childQuantity += $this->getQuantityForChildren($bundleOption->getData('selections'));
141
+ }
142
+
143
+ break;
144
+ }
145
+
146
+ $this->getRecords()->set(self::FEED_CHILD_QUANTITY, $childQuantity);
147
+ }
148
+
149
+ /**
150
+ * Loop over an array of products to set field data and calculate total child quantity
151
+ *
152
+ * @param array $products
153
+ * @return int
154
+ */
155
+ private function getQuantityForChildren(array $products)
156
+ {
157
+ $quantity = 0;
158
+
159
+ /** @var Mage_Catalog_Model_Product $product */
160
+ foreach ($products as $product) {
161
+ $this->getRecords()->add(self::FEED_CHILD_SKU, $product->getSku());
162
+ $this->getRecords()->add(self::FEED_CHILD_NAME, $product->getName());
163
+
164
+ $quantity += $this->getQuantity($product);
165
+ }
166
+
167
+ return $quantity;
168
+ }
169
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetFields.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetFields.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetFields
10
+ *
11
+ * Set product attributes to the field
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetFields extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**
18
+ * Add magento product attributes to the feed
19
+ *
20
+ * @param Mage_Catalog_Model_Product $product
21
+ * @return $this
22
+ */
23
+ public function perform(Mage_Catalog_Model_Product $product)
24
+ {
25
+ /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
26
+ foreach($product->getAttributes() as $key => $attribute) {
27
+ $this->getRecords()->set($key, $this->getAttributeValue($product, $attribute));
28
+ }
29
+
30
+ return $this;
31
+ }
32
+
33
+ /**
34
+ * If product is not enabled or visible, set invalid
35
+ *
36
+ * @param Mage_Catalog_Model_Product $product
37
+ *
38
+ * @return bool
39
+ */
40
+ public function isValid(Mage_Catalog_Model_Product $product)
41
+ {
42
+ // product must be enabled
43
+ if ((int)$product->getData('status') !== Mage_Catalog_Model_Product_Status::STATUS_ENABLED) {
44
+ return false;
45
+ }
46
+
47
+ // product must be visible in catalog and search
48
+ if ((int)$product->getData('visibility') !== Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) {
49
+ return false;
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Get the attribute value from the attribute object
57
+ *
58
+ * @param Mage_Catalog_Model_Product $product
59
+ * @param Mage_Eav_Model_Entity_Attribute $attribute
60
+ *
61
+ * @return array|string|null
62
+ */
63
+ private function getAttributeValue(Mage_Catalog_Model_Product $product, Mage_Eav_Model_Entity_Attribute $attribute)
64
+ {
65
+ $attributeValue = $attribute->getFrontend()->getValue($product);
66
+ $returnValue = null;
67
+
68
+ if (is_array($attributeValue)) {
69
+ foreach ($attributeValue as $v) {
70
+ $returnValue[] = json_encode($v);
71
+ }
72
+ } else {
73
+ $returnValue = $this->getSanitizer()->sanitizeForRequest($attributeValue);
74
+ }
75
+
76
+ return $returnValue;
77
+ }
78
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetId.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetId.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetId
10
+ *
11
+ * Set the product id to the feed
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetId extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**
18
+ * Set product id to the feed
19
+ *
20
+ * @param Mage_Catalog_Model_Product $product
21
+ *
22
+ * @return $this
23
+ */
24
+ public function perform(Mage_Catalog_Model_Product $product)
25
+ {
26
+ $this->getRecords()->set($product->getIdFieldName(), $product->getId());
27
+
28
+ return $this;
29
+ }
30
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetImages.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetImages.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetImages
10
+ *
11
+ * Add product images to the feed
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetImages extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ const DEFAULT_IMAGE_WIDTH = 200;
18
+ const DEFAULT_IMAGE_HEIGHT = 200;
19
+
20
+ const FEED_IMAGE_URL = 'image_url';
21
+ const FEED_THUMBNAIL_URL = 'thumbnail_url';
22
+ const FEED_CACHED_THUMBNAIL_URL = 'cached_thumbnail_url';
23
+
24
+ /**
25
+ * Url path for product images
26
+ */
27
+ const PREFIX_MEDIA_PRODUCT = 'catalog/product';
28
+
29
+ protected $_mediaBaseUrl;
30
+
31
+ protected $_imageHeight;
32
+ protected $_imageWidth;
33
+
34
+
35
+ public function __construct(
36
+ SearchSpring_Manager_String_Sanitizer $sanitizer,
37
+ SearchSpring_Manager_Entity_RecordsCollection $records,
38
+ array $parameters = array()
39
+ ) {
40
+ parent::__construct($sanitizer, $records, $parameters);
41
+
42
+ if(Mage::helper('searchspring_manager')->isCacheImagesEnabled()) {
43
+ $this->_imageWidth = Mage::helper('searchspring_manager')->getImageWidth();
44
+ $this->_imageHeight = Mage::helper('searchspring_manager')->getImageHeight();
45
+
46
+ if (empty($this->_imageWidth)) {
47
+ if (!empty($this->_imageHeight)) {
48
+ $this->_imageWidth = $this->_imageHeight;
49
+ } else {
50
+ $this->_imageWidth = self::DEFAULT_IMAGE_WIDTH;
51
+ $this->_imageHeight = self::DEFAULT_IMAGE_HEIGHT;
52
+ }
53
+ } else if (empty($this->_imageHeight)) {
54
+ if (!empty($this->_imageWidth)) {
55
+ $this->_imageHeight = $this->_imageWidth;
56
+ } else {
57
+ $this->_imageHeight = self::DEFAULT_IMAGE_HEIGHT;
58
+ }
59
+ }
60
+
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Set magento product options to the feed
66
+ *
67
+ * @param Mage_Catalog_Model_Product $product
68
+ *
69
+ * @return $this
70
+ */
71
+ public function perform(Mage_Catalog_Model_Product $product)
72
+ {
73
+ $mediaBaseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA);
74
+
75
+ $this->getRecords()->set(self::FEED_IMAGE_URL, $mediaBaseUrl . self::PREFIX_MEDIA_PRODUCT . $product->getData('image'));
76
+ $this->getRecords()->set(self::FEED_THUMBNAIL_URL, $mediaBaseUrl . self::PREFIX_MEDIA_PRODUCT . $product->getData('thumbnail'));
77
+
78
+ /** @var Mage_Catalog_Helper_Image $imageHelper */
79
+ $imageHelper = Mage::helper('catalog/image');
80
+
81
+ if(Mage::helper('searchspring_manager')->isCacheImagesEnabled()) {
82
+ $this->getRecords()->set(
83
+ self::FEED_CACHED_THUMBNAIL_URL,
84
+ (string) $imageHelper->init($product, 'image')->resize($this->_imageWidth, $this->_imageHeight)
85
+ );
86
+ }
87
+
88
+ return $this;
89
+ }
90
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetOptions.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetOptions.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetOptions
10
+ *
11
+ * Set product options to the feed
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetOptions extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**
18
+ * Set magento product options to the feed
19
+ *
20
+ * @param Mage_Catalog_Model_Product $product
21
+ *
22
+ * @return $this
23
+ */
24
+ public function perform(Mage_Catalog_Model_Product $product)
25
+ {
26
+ /** @var Mage_Catalog_Model_Product_Option $option */
27
+ foreach ($product->getOptions() as $option) {
28
+ $optionType = $option->getType();
29
+
30
+ if ($optionType !== Mage_Catalog_Model_Product_Option::OPTION_TYPE_DROP_DOWN) {
31
+ continue;
32
+ }
33
+
34
+ $title = strtolower(trim($option->getData('title')));
35
+ $title = preg_replace('/_+/', '_', preg_replace('/[^a-z0-9_]+/i', '_', $title));
36
+ $key = 'option_' . $title;
37
+
38
+ /** @var Mage_Catalog_Model_Product_Option_Value $optionValue */
39
+ foreach ($option->getValues() as $optionValue) {
40
+ $value = $this->getSanitizer()->sanitizeForRequest($optionValue->getData('title'));
41
+ $this->getRecords()->add($key, $value);
42
+ }
43
+ }
44
+
45
+ return $this;
46
+ }
47
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetPricing.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetPricing.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetPricing
10
+ *
11
+ * Set pricing data to the feed
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetPricing extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**
18
+ * Set the pricing data
19
+ *
20
+ * @param Mage_Catalog_Model_Product $product
21
+ *
22
+ * @return $this
23
+ * @throws UnexpectedValueException If pricing is not the right type
24
+ */
25
+ public function perform(Mage_Catalog_Model_Product $product)
26
+ {
27
+ /** @var SearchSpring_Manager_Factory_PricingFactory $pricingFactory */
28
+ $pricingFactory = $this->getParameter('pricingFactory');
29
+
30
+ // this will throw an exception if the product is not the right type
31
+ // that's OK because if it does, we did something wrong
32
+ $pricingStrategy = $pricingFactory->make($product);
33
+
34
+ $pricingStrategy->calculatePrices();
35
+
36
+ // prices should have been calculated earlier to determine if products should be deleted
37
+ $this->getRecords()->set('price', $pricingStrategy->getSalePrice());
38
+ $this->getRecords()->set('regular_price', $pricingStrategy->getTierPrice());
39
+ $this->getRecords()->set('normal_price', $pricingStrategy->getNormalPrice());
40
+
41
+ return $this;
42
+ }
43
+
44
+ /**
45
+ * If price is zero, set invalid
46
+ *
47
+ * @param Mage_Catalog_Model_Product $product
48
+ *
49
+ * @return bool
50
+ */
51
+ public function isValid(Mage_Catalog_Model_Product $product)
52
+ {
53
+ /** @var SearchSpring_Manager_Factory_PricingFactory $pricingFactory */
54
+ $pricingFactory = $this->getParameter('pricingFactory');
55
+ $displayZeroPrice = $this->getParameter('displayZeroPrice');
56
+
57
+ try {
58
+ $pricingStrategy = $pricingFactory->make($product);
59
+ } catch (UnexpectedValueException $e) {
60
+ // product is not a type we're currently handling
61
+ return false;
62
+ }
63
+
64
+ // we must run this before we can check prices
65
+ $pricingStrategy->calculatePrices();
66
+
67
+ $productHasZeroPrice = (0 == $pricingStrategy->getNormalPrice()
68
+ && 0 == $pricingStrategy->getTierPrice()
69
+ && 0 == $pricingStrategy->getSalePrice()
70
+ );
71
+
72
+ // if we shouldn't show 0 prices and the product price is 0
73
+ if ($productHasZeroPrice && !$displayZeroPrice) {
74
+ return false;
75
+ }
76
+
77
+ return true;
78
+ }
79
+ }
app/code/community/SearchSpring/Manager/Operation/Product/SetRatings.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SetRatings.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Operation_Product_SetRatings
10
+ *
11
+ * Set rating data to the feed
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Operation_Product_SetRatings extends SearchSpring_Manager_Operation_Product
16
+ {
17
+ /**#@+
18
+ * Feed Constants
19
+ */
20
+ const FEED_RATING_PERCENTAGE = 'rating_percentage';
21
+ const FEED_RATING_STAR = 'rating_star';
22
+ const FEED_RATING_COUNT = 'rating_count';
23
+ /**#@-*/
24
+
25
+ /**
26
+ * Set rating data to the feed
27
+ * - rating_percentage
28
+ * - rating_star
29
+ * - rating_count
30
+ *
31
+ * @param Mage_Catalog_Model_Product $product
32
+ *
33
+ * @return $this
34
+ */
35
+ public function perform(Mage_Catalog_Model_Product $product)
36
+ {
37
+ $votes = Mage::getModel('rating/rating_option_vote')
38
+ ->getResourceCollection()
39
+ ->setEntityPkFilter($product->getId())
40
+ ->load();
41
+
42
+ $ratings = array();
43
+ foreach ($votes as $vote) {
44
+ $ratings[] = $vote['percent'];
45
+ }
46
+
47
+ $ratingCount = count($votes);
48
+
49
+ $ratingPercentage = (0 === $ratingCount) ? 0 : array_sum($ratings) / $ratingCount;
50
+ $this->getRecords()->set(self::FEED_RATING_PERCENTAGE, $ratingPercentage);
51
+ $this->getRecords()->set(self::FEED_RATING_STAR, round($ratingPercentage / 20));
52
+ $this->getRecords()->set(self::FEED_RATING_COUNT, $ratingCount);
53
+
54
+ return $this;
55
+ }
56
+ }
app/code/community/SearchSpring/Manager/Operation/ProductOperation.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Operation.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Interface SearchSpring_Manager_Visitor_Product_Operation
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ interface SearchSpring_Manager_Operation_ProductOperation
14
+ {
15
+ /**
16
+ * Perform an operation
17
+ *
18
+ * @param Mage_Catalog_Model_Product $product
19
+ *
20
+ * @return self
21
+ */
22
+ public function perform(Mage_Catalog_Model_Product $product);
23
+
24
+ /**
25
+ * Checks validity of operation
26
+ *
27
+ * @param Mage_Catalog_Model_Product $product
28
+ *
29
+ * @return mixed
30
+ */
31
+ public function isValid(Mage_Catalog_Model_Product $product);
32
+
33
+ /**
34
+ * Set records collection to operation
35
+ *
36
+ * @param SearchSpring_Manager_Entity_RecordsCollection $records
37
+ *
38
+ * @return mixed
39
+ */
40
+ public function setRecords(SearchSpring_Manager_Entity_RecordsCollection $records);
41
+ }
app/code/community/SearchSpring/Manager/Provider/ProductCollection/FeedProvider.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * FeedProvider.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Provider_ProductCollection_FeedProvider
10
+ *
11
+ * Provides a ProductCollection for feeds
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Provider_ProductCollection_FeedProvider
16
+ implements SearchSpring_Manager_Provider_ProductCollectionProvider
17
+ {
18
+ /**
19
+ * A ProductCollection
20
+ *
21
+ * @var Mage_Catalog_Model_Resource_Product_Collection $collection
22
+ */
23
+ private $collection;
24
+
25
+ /**
26
+ * The size of the collection
27
+ *
28
+ * @var int $collectionCount
29
+ */
30
+ private $collectionCount;
31
+
32
+ /**
33
+ * Request parameters
34
+ *
35
+ * Used to get the store for filter
36
+ *
37
+ * @var SearchSpring_Manager_Entity_RequestParams
38
+ */
39
+ private $requestParams;
40
+
41
+ /**
42
+ * Constructor
43
+ *
44
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams
45
+ */
46
+ public function __construct(SearchSpring_Manager_Entity_RequestParams $requestParams)
47
+ {
48
+ $this->requestParams = $requestParams;
49
+ }
50
+
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ public function getCollection()
55
+ {
56
+ if (null !== $this->collection) {
57
+ return $this->collection;
58
+ }
59
+
60
+ $showOos = Mage::helper('cataloginventory')->isShowOutOfStock();
61
+
62
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $collection */
63
+ $collection = Mage::getModel('catalog/product')->getCollection();
64
+ $collection->addStoreFilter(Mage::app()->getStore($this->requestParams->getStore())->getId());
65
+ $collection->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
66
+ $collection->addAttributeToFilter('type_id', array('in' => SearchSpring_Manager_Validator_ProductValidator::$allowableTypes));
67
+ $collection->addAttributeToFilter('visibility', Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH);
68
+
69
+
70
+ if (!$showOos) {
71
+ Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
72
+ }
73
+
74
+ $collection->getSelect()->limit($this->requestParams->getCount(), $this->requestParams->getOffset());
75
+ $this->collection = $collection;
76
+
77
+ return $this->collection;
78
+ }
79
+
80
+ /**
81
+ * {@inheritdoc}
82
+ */
83
+ public function getCollectionCount()
84
+ {
85
+ if (null !== $this->collectionCount) {
86
+ return $this->collectionCount;
87
+ }
88
+
89
+ // if collection isn't set, get it first
90
+ if (null === $this->collection) {
91
+ $this->getCollection();
92
+ }
93
+
94
+ // set collection count before limit is applied
95
+ $this->collectionCount = $this->collection->getSize();
96
+
97
+ return $this->collectionCount;
98
+ }
99
+ }
app/code/community/SearchSpring/Manager/Provider/ProductCollection/ProductProvider.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ProductProvider.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Provider_ProductCollection_ProductProvider
10
+ *
11
+ * Provides a ProductCollection for product types
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Provider_ProductCollection_ProductProvider
16
+ implements SearchSpring_Manager_Provider_ProductCollectionProvider
17
+ {
18
+ /**
19
+ * A ProductCollection
20
+ *
21
+ * @var Mage_Catalog_Model_Resource_Product_Collection $collection
22
+ */
23
+ private $collection;
24
+
25
+ /**
26
+ * The size of the collection
27
+ *
28
+ * @var int $collectionCount
29
+ */
30
+ private $collectionCount;
31
+
32
+ /**
33
+ * An array of product ids
34
+ *
35
+ * @var array $ids
36
+ */
37
+ private $ids;
38
+
39
+ /**
40
+ * Request parameters
41
+ *
42
+ * Needed to get current count/offset values
43
+ *
44
+ * @var SearchSpring_Manager_Entity_RequestParams
45
+ */
46
+ private $requestParams;
47
+
48
+ /**
49
+ * Constructor
50
+ *
51
+ * @param array $ids
52
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams
53
+ */
54
+ public function __construct(array $ids, SearchSpring_Manager_Entity_RequestParams $requestParams = null)
55
+ {
56
+ $this->ids = $ids;
57
+ $this->requestParams = $requestParams;
58
+ }
59
+
60
+ /**
61
+ * {@inheritdoc}
62
+ */
63
+ public function getCollection()
64
+ {
65
+ if (null !== $this->collection) {
66
+ return $this->collection;
67
+ }
68
+
69
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $collection */
70
+ $collection = Mage::getModel('catalog/product')
71
+ ->getCollection()
72
+ ->addAttributeToFilter('entity_id', array('in' => $this->ids))
73
+ ->addAttributeToFilter('visibility', Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH);
74
+
75
+ if (null !== $this->requestParams) {
76
+ $collection->getSelect()->limit($this->requestParams->getCount(), $this->requestParams->getOffset());
77
+ }
78
+
79
+ $this->collection = $collection;
80
+
81
+ return $this->collection;
82
+ }
83
+
84
+ /**
85
+ * {@inheritdoc}
86
+ */
87
+ public function getCollectionCount()
88
+ {
89
+ if (null !== $this->collectionCount) {
90
+ return $this->collectionCount;
91
+ }
92
+
93
+ if (null === $this->collection) {
94
+ $this->getCollection();
95
+ }
96
+
97
+ $this->collectionCount = $this->collection->getSize();
98
+
99
+ return $this->collectionCount;
100
+ }
101
+ }
app/code/community/SearchSpring/Manager/Provider/ProductCollectionProvider.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ProductCollectionProvider.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Interface SearchSpring_Manager_Provider_ProductCollectionProvider
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ interface SearchSpring_Manager_Provider_ProductCollectionProvider
14
+ {
15
+ /**
16
+ * Gets the ProductCollection
17
+ *
18
+ * @return Mage_Catalog_Model_Resource_Product_Collection
19
+ */
20
+ public function getCollection();
21
+
22
+ /**
23
+ * Get the collection count
24
+ *
25
+ * @return int
26
+ */
27
+ public function getCollectionCount();
28
+ }
app/code/community/SearchSpring/Manager/Request/JSON.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * JSON.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Request_JSON
10
+ *
11
+ * Wrapper for Request object that allows you to easily access JSON POST Data.
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Request_JSON
16
+ {
17
+
18
+ /**
19
+ * Request parameters parsed from JSON
20
+ *
21
+ * @var object $request
22
+ */
23
+ private $params;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * @param Mage_Core_Controller_Request_Http $request
29
+ */
30
+ public function __construct(Mage_Core_Controller_Request_Http $request) {
31
+ $rawBody = $request->getRawBody();
32
+
33
+ if(!empty($rawBody)) {
34
+ $this->params = json_decode($rawBody, true);
35
+
36
+ if(null === $this->params) {
37
+ throw new Exception('Invalid JSON in Request');
38
+ }
39
+ } else {
40
+ $this->params = array();
41
+ }
42
+
43
+
44
+ }
45
+
46
+ /**
47
+ * Returns parameter from JSON request
48
+ *
49
+ * @param string $parameter
50
+ *
51
+ * @return mixed
52
+ */
53
+ public function getParam($parameter, $default=NULL) {
54
+ return isset($this->params[$parameter])?$this->params[$parameter]:$default;
55
+ }
56
+ }
app/code/community/SearchSpring/Manager/Service/SearchSpring/ApiAdapter.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File ApiAdapter.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Service_SearchSpring_ApiAdapter
10
+ *
11
+ * Adapter for the SearchSpring API
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Service_SearchSpring_ApiAdapter
16
+ {
17
+
18
+ /**
19
+ * Format for create the url endpoint
20
+ *
21
+ * [baseurl]/[method]
22
+ */
23
+ const URL_FORMAT = '%s/%s';
24
+
25
+ /**
26
+ * The curl adapter
27
+ *
28
+ * @var Varien_Http_Adapter_Curl $curl
29
+ */
30
+ protected $curl;
31
+
32
+ /**
33
+ * An API error handler if a request fails
34
+ *
35
+ * @var SearchSpring_Manager_Handler_ApiErrorHandler
36
+ */
37
+ protected $errorHandler;
38
+
39
+ /**
40
+ * The base url for the endpoint
41
+ *
42
+ * @var string $baseUrl
43
+ */
44
+ protected $baseUrl;
45
+
46
+ /**
47
+ * Constructor
48
+ *
49
+ * @param SearchSpring_Manager_Handler_ApiErrorHandler $errorHandler
50
+ * @param Varien_Http_Adapter_Curl $curl
51
+ * @param string $baseUrl
52
+ */
53
+ public function __construct(
54
+ SearchSpring_Manager_Handler_ApiErrorHandler $errorHandler,
55
+ Varien_Http_Adapter_Curl $curl,
56
+ $baseUrl
57
+ ) {
58
+ $this->errorHandler = $errorHandler;
59
+ $this->curl = $curl;
60
+ $this->baseUrl = $baseUrl;
61
+ }
62
+
63
+
64
+ /**
65
+ * Build the url based on expected format
66
+ *
67
+ * @param string $method
68
+ *
69
+ * @return string
70
+ */
71
+ protected function buildUrl($method)
72
+ {
73
+ $url = sprintf(self::URL_FORMAT, $this->baseUrl, $method);
74
+
75
+ return $url;
76
+ }
77
+ }
app/code/community/SearchSpring/Manager/Service/SearchSpring/IndexingApiAdapter.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File IndexingApiAdapter.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter
10
+ *
11
+ * Adapter for the SearchSpring Indexing API
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter extends SearchSpring_Manager_Service_SearchSpring_ApiAdapter
16
+ {
17
+ const API_INDEX = 'api/index/magento/generate';
18
+
19
+ /**
20
+ * Push ids to api
21
+ *
22
+ * @param SearchSpring_Manager_Entity_IndexingRequestBody $requestBody
23
+ */
24
+ public function pushIds(SearchSpring_Manager_Entity_IndexingRequestBody $requestBody)
25
+ {
26
+ $this->call(self::API_INDEX, $requestBody);
27
+ }
28
+
29
+ /**
30
+ * Generic method that accepts any api endpoint path
31
+ *
32
+ * @param string $method
33
+ * @param SearchSpring_Manager_Entity_IndexingRequestBody $requestBody
34
+ */
35
+ public function call($method, SearchSpring_Manager_Entity_IndexingRequestBody $requestBody)
36
+ {
37
+ $url = $this->buildUrl($method);
38
+ $this->request(Zend_Http_Client::POST, $url, json_encode($requestBody->jsonSerialize()));
39
+ }
40
+
41
+ /**
42
+ * Helper method to make an api request
43
+ *
44
+ * @param string $httpMethod
45
+ * @param string $url
46
+ * @param string $body
47
+ *
48
+ * @return int
49
+ */
50
+ private function request($httpMethod, $url, $body)
51
+ {
52
+ $this->curl->write($httpMethod, $url, Zend_Http_Client::HTTP_1, array(), $body);
53
+ $response = $this->curl->read();
54
+ $responseCode = Zend_Http_Response::extractCode($response);
55
+ $this->curl->close();
56
+
57
+ if ($this->errorHandler->shouldRetry($response)) {
58
+ $this->request($httpMethod, $url, $body);
59
+ }
60
+
61
+ return $responseCode;
62
+ }
63
+
64
+ }
app/code/community/SearchSpring/Manager/Service/SearchSpring/SearchApiAdapter.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File SearchApiAdapter.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter
10
+ *
11
+ * Adapter for the SearchSpring Indexing API
12
+ *
13
+ * @author James Bathgate <james@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter extends SearchSpring_Manager_Service_SearchSpring_ApiAdapter
16
+ {
17
+
18
+ const API_SEARCH = 'api/search/search.json';
19
+
20
+ /**
21
+ * Push ids to api
22
+ *
23
+ * @param SearchSpring_Manager_Entity_SearchRequestBody $requestBody
24
+ */
25
+ public function search(SearchSpring_Manager_Entity_SearchRequestBody $requestBody)
26
+ {
27
+ $response = $this->call(self::API_SEARCH, $requestBody);
28
+
29
+ if(null === ($result = json_decode($response))) {
30
+ throw new Exception('Unable to parse SearchSpring search response JSON');
31
+ }
32
+
33
+ return $result;
34
+ }
35
+
36
+ /**
37
+ * Generic method that accepts any api endpoint path
38
+ *
39
+ * @param string $method
40
+ * @param SearchSpring_Manager_Entity_SearchRequestBody $requestBody
41
+ */
42
+ public function call($method, SearchSpring_Manager_Entity_SearchRequestBody $requestBody)
43
+ {
44
+ $url = $this->buildUrl($method);
45
+ return $this->request(Zend_Http_Client::GET, $url, (string)$requestBody);
46
+ }
47
+
48
+ /**
49
+ * Helper method to make an api request
50
+ *
51
+ * @param string $httpMethod
52
+ * @param string $url
53
+ * @param string $body
54
+ *
55
+ * @return int
56
+ */
57
+ private function request($httpMethod, $url, $parameters)
58
+ {
59
+ $this->curl->write($httpMethod, $url . '?' . $parameters, Zend_Http_Client::HTTP_1, array());
60
+ $response = $this->curl->read();
61
+
62
+ $responseCode = Zend_Http_Response::extractCode($response);
63
+ $responseBody = Zend_Http_Response::extractBody($response);
64
+
65
+ if ($this->errorHandler->shouldRetry($response)) {
66
+ return $this->request($httpMethod, $url, $parameters);
67
+ }
68
+
69
+ return $responseBody;
70
+ }
71
+
72
+ }
app/code/community/SearchSpring/Manager/Strategy/Pricing/BundleStrategy.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File BundleStrategy.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Strategy_Pricing_BundleStrategy
10
+ *
11
+ * Calculate prices for bundled products
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Strategy_Pricing_BundleStrategy extends SearchSpring_Manager_Strategy_Pricing_Strategy
16
+ {
17
+ /**
18
+ * {@inheritdoc}
19
+ */
20
+ public function calculatePrices()
21
+ {
22
+ /** @var Mage_Bundle_Model_Product_Type $typeInstance */
23
+ $typeInstance = $this->getProduct()->getTypeInstance(true);
24
+
25
+ $totalRegularPrice = 0;
26
+ $totalTierPrice = 0;
27
+ $totalSalePrice = 0;
28
+
29
+ // set up bundle options
30
+ $optionsIds = $typeInstance->getOptionsIds($this->getProduct());
31
+ $selections = $typeInstance->getSelectionsCollection($optionsIds, $this->getProduct());
32
+ $bundleOptions = $typeInstance->getOptionsByIds($optionsIds, $this->getProduct());
33
+ $bundleOptions->appendSelections($selections);
34
+
35
+ /** @var Mage_Bundle_Model_Option $bundleOption */
36
+ foreach ($bundleOptions as $bundleOption) {
37
+ // if it's not required, it doesn't count as part of the minimal price
38
+ if (!$bundleOption->getRequired()) {
39
+ continue;
40
+ }
41
+
42
+ $regularPrices = array();
43
+ $tierPrices = array();
44
+ $salePrices = array();
45
+
46
+ /** @var Mage_Catalog_Model_Product $product */
47
+ foreach ($bundleOption->getData('selections') as $product) {
48
+ $regularPrice = (double)$product->getPrice();
49
+ $tierPrice = (double)$this->getLowestTierPrice($product);
50
+ $salePrice = (double)$product->getFinalPrice();
51
+
52
+ $regularPrices[] = $regularPrice;
53
+ $tierPrices[] = $tierPrice;
54
+ $salePrices[] = $salePrice;
55
+ }
56
+
57
+ $totalRegularPrice += $this->findMinPrice($regularPrices);
58
+ $totalTierPrice += $this->findMinPrice($tierPrices);
59
+ $totalSalePrice += $this->findMinPrice($salePrices);
60
+ }
61
+
62
+ $this->setNormalPrice($totalRegularPrice);
63
+ $this->setTierPrice($totalTierPrice);
64
+ $this->setSalePrice($totalSalePrice);
65
+
66
+ return $this;
67
+ }
68
+
69
+ }
app/code/community/SearchSpring/Manager/Strategy/Pricing/ConfigurableStrategy.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File ConfigurableStrategy.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Strategy_Pricing_ConfigurableStrategy
10
+ *
11
+ * Calculate prices for configurable products
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Strategy_Pricing_ConfigurableStrategy extends SearchSpring_Manager_Strategy_Pricing_Strategy
16
+ {
17
+ /**
18
+ * {@inheritdoc}
19
+ */
20
+ public function calculatePrices()
21
+ {
22
+ $product = $this->getProduct();
23
+ $this->setNormalPrice($product->getPrice());
24
+ $this->setTierPrice($this->getLowestTierPrice($product));
25
+ $this->setSalePrice($product->getFinalPrice());
26
+
27
+ return $this;
28
+ }
29
+ }
app/code/community/SearchSpring/Manager/Strategy/Pricing/GroupedStrategy.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File GroupedStrategy.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Strategy_Pricing_GroupedStrategy
10
+ *
11
+ * Calculate prices for grouped products
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Strategy_Pricing_GroupedStrategy extends SearchSpring_Manager_Strategy_Pricing_Strategy
16
+ {
17
+ /**
18
+ * {@inheritdoc}
19
+ */
20
+ public function calculatePrices()
21
+ {
22
+ /** @var Mage_Catalog_Model_Product_Type_Grouped $typeInstance */
23
+ $typeInstance = $this->getProduct()->getTypeInstance(true);
24
+ $associated = $typeInstance->getAssociatedProducts($this->getProduct());
25
+
26
+ $regularPrices = array();
27
+ $tierPrices = array();
28
+ $salePrices = array();
29
+
30
+ /** @var Mage_Catalog_Model_Product $product */
31
+ foreach($associated as $product) {
32
+ $regularPrice = (double)$product->getPrice();
33
+ $tierPrice = (double)$this->getLowestTierPrice($product);
34
+ $salePrice = (double)$product->getFinalPrice();
35
+
36
+ $regularPrices[] = $regularPrice;
37
+ $tierPrices[] = $tierPrice;
38
+ $salePrices[] = $salePrice;
39
+ }
40
+
41
+ $this->setNormalPrice($this->findMinPrice($regularPrices));
42
+ $this->setTierPrice($this->findMinPrice($tierPrices));
43
+ $this->setSalePrice($this->findMinPrice($salePrices));
44
+
45
+ return $this;
46
+ }
47
+ }
app/code/community/SearchSpring/Manager/Strategy/Pricing/SimpleStrategy.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File SimpleStrategy.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Strategy_Pricing_SimpleStrategy
10
+ *
11
+ * Calculate prices for grouped products
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Strategy_Pricing_SimpleStrategy extends SearchSpring_Manager_Strategy_Pricing_Strategy
16
+ {
17
+ /**
18
+ * {@inheritdoc}
19
+ */
20
+ public function calculatePrices()
21
+ {
22
+ $product = $this->getProduct();
23
+ $this->setNormalPrice($product->getPrice());
24
+ $this->setTierPrice($this->getLowestTierPrice($product));
25
+ $this->setSalePrice($product->getFinalPrice());
26
+
27
+ return $this;
28
+ }
29
+ }
app/code/community/SearchSpring/Manager/Strategy/Pricing/Strategy.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File Strategy.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Strategy_Pricing_Strategy
10
+ *
11
+ * Abstract class for pricing strategies. Holds getters for price data.
12
+ *
13
+ * @todo This should probably be a separate class that just holds the data rather than an abstract with subclasses.
14
+ *
15
+ * @author Nate Brunette <nate@b7interactive.com>
16
+ */
17
+ abstract class SearchSpring_Manager_Strategy_Pricing_Strategy implements SearchSpring_Manager_Strategy_PricingStrategy
18
+ {
19
+ /**
20
+ * Magento product
21
+ *
22
+ * @var Mage_Catalog_Model_Product $product
23
+ */
24
+ private $product;
25
+
26
+ /**
27
+ * The regular price of the product
28
+ *
29
+ * @var double $regularPrice
30
+ */
31
+ private $regularPrice;
32
+
33
+ /**
34
+ * The price of the product including tier prices
35
+ *
36
+ * @var double $tierPrice
37
+ */
38
+ private $tierPrice;
39
+
40
+ /**
41
+ * The price of the product from getFinalPrice()
42
+ *
43
+ * @var double $salePrice
44
+ */
45
+ private $salePrice;
46
+
47
+ /**
48
+ * {@inheritdoc}
49
+ */
50
+ public function __construct(Mage_Catalog_Model_Product $product)
51
+ {
52
+ $this->product = $product;
53
+ }
54
+
55
+ /**
56
+ * Get the product
57
+ *
58
+ * @return Mage_Catalog_Model_Product
59
+ */
60
+ protected function getProduct()
61
+ {
62
+ return $this->product;
63
+ }
64
+
65
+ /**
66
+ * {@inheritdoc}
67
+ */
68
+ public function getNormalPrice()
69
+ {
70
+ $price = $this->formatPrice($this->regularPrice);
71
+
72
+ return $price;
73
+ }
74
+
75
+ /**
76
+ * Set the regular price
77
+ *
78
+ * @param double $price
79
+ */
80
+ protected function setNormalPrice($price)
81
+ {
82
+ $this->regularPrice = $price;
83
+ }
84
+
85
+ /**
86
+ * {@inheritdoc}
87
+ */
88
+ public function getTierPrice()
89
+ {
90
+ $minPrice = min($this->regularPrice, $this->tierPrice);
91
+ $price = $this->formatPrice($minPrice);
92
+
93
+ return $price;
94
+ }
95
+
96
+ /**
97
+ * Set the tier price
98
+ *
99
+ * @param double $price
100
+ */
101
+ protected function setTierPrice($price)
102
+ {
103
+ $this->tierPrice = $price;
104
+ }
105
+
106
+ /**
107
+ * {@inheritdoc}
108
+ */
109
+ public function getSalePrice()
110
+ {
111
+ $minPrice = min($this->regularPrice, $this->tierPrice, $this->salePrice);
112
+ $price = $this->formatPrice($minPrice);
113
+
114
+ return $price;
115
+ }
116
+
117
+ /**
118
+ * Set the sale price
119
+ *
120
+ * @param double $price
121
+ */
122
+ protected function setSalePrice($price)
123
+ {
124
+ $this->salePrice = $price;
125
+ }
126
+
127
+ /**
128
+ * Find the minimum price in array
129
+ *
130
+ * Returns 0 if array if empty
131
+ *
132
+ * @param array $prices
133
+ *
134
+ * @return int
135
+ */
136
+ protected function findMinPrice(array $prices)
137
+ {
138
+ if (empty($prices)) {
139
+ return 0;
140
+ }
141
+
142
+ return min($prices);
143
+ }
144
+
145
+ /**
146
+ * Find the lowest tier price for product
147
+ *
148
+ * @param Mage_Catalog_Model_Product $product
149
+ *
150
+ * @return double
151
+ */
152
+ protected function getLowestTierPrice(Mage_Catalog_Model_Product $product)
153
+ {
154
+ /** @var array $tierPrices */
155
+ $tierPrices = $product->getTierPrice();
156
+
157
+ if (empty($tierPrices)) {
158
+ $price = (double)$product->getPrice();
159
+
160
+ return $price;
161
+ }
162
+
163
+ $prices = array();
164
+ foreach ($tierPrices as $tierPrice) {
165
+ if ($tierPrice == 0) {
166
+ continue;
167
+ }
168
+
169
+ $prices[] = (double)$tierPrice['price'];
170
+ }
171
+
172
+ $lowestPrice = min($prices);
173
+
174
+ return $lowestPrice;
175
+ }
176
+
177
+ /**
178
+ * Format number to two decimals
179
+ *
180
+ * @param double $price
181
+ * @return double
182
+ */
183
+ private function formatPrice($price)
184
+ {
185
+ $price = (double)$price;
186
+ $price = (double)number_format($price, 2);
187
+
188
+ return $price;
189
+ }
190
+ }
app/code/community/SearchSpring/Manager/Strategy/PricingStrategy.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File PricingStrategy.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Interface SearchSpring_Manager_Strategy_PricingStrategy
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ interface SearchSpring_Manager_Strategy_PricingStrategy
14
+ {
15
+ /**
16
+ * Constructor
17
+ *
18
+ * @param Mage_Catalog_Model_Product $product
19
+ */
20
+ public function __construct(Mage_Catalog_Model_Product $product);
21
+
22
+ /**
23
+ * Calculate lowest prices
24
+ *
25
+ * @return self
26
+ */
27
+ public function calculatePrices();
28
+
29
+ /**
30
+ * Return the regular price
31
+ *
32
+ * @return double
33
+ */
34
+ public function getNormalPrice();
35
+
36
+ /**
37
+ * Return the lowest between regular price and tier price
38
+ *
39
+ * @return double
40
+ */
41
+ public function getTierPrice();
42
+
43
+ /**
44
+ * Return the lowest price between all kinds of prices
45
+ *
46
+ * @return double
47
+ */
48
+ public function getSalePrice();
49
+ }
app/code/community/SearchSpring/Manager/String/Sanitizer.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Sanitizer.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_String_Sanitizer
10
+ *
11
+ * Service to sanitize string data
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_String_Sanitizer
16
+ {
17
+ /**
18
+ * Helper method to remove unwanted characters
19
+ *
20
+ * @param string $value
21
+ * @return string|null
22
+ */
23
+ public function sanitizeForRequest($value)
24
+ {
25
+ $value = preg_replace('/[^(\x20-\x7F)\x0A]*/','', $value);
26
+
27
+ return $value;
28
+ }
29
+
30
+ /**
31
+ * Strip newline and tab characters
32
+ *
33
+ * @param string $value
34
+ *
35
+ * @return string|null
36
+ */
37
+ public function removeNewlinesAndStripTags($value)
38
+ {
39
+ $value = strip_tags($value);
40
+ $value = str_replace("\n", "", $value);
41
+ $value = str_replace("\r", "", $value);
42
+ $value = str_replace("\t", "", $value);
43
+
44
+ return $value;
45
+ }
46
+ }
app/code/community/SearchSpring/Manager/Transformer/ProductCollectionToRecordCollectionTransformer.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ProductCollectionToRecordCollectionTransformer.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer
10
+ *
11
+ * Transform a Magento product collection to our records collection. Only performs a 1-way transform
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransformer
16
+ {
17
+ /**
18
+ * @var SearchSpring_Manager_Entity_RecordsCollection
19
+ */
20
+ private $recordsCollection;
21
+
22
+ /**
23
+ * @var SearchSpring_Manager_Entity_OperationsCollection
24
+ */
25
+ private $operationsCollection;
26
+
27
+ /**
28
+ * Constructor
29
+ *
30
+ * @param SearchSpring_Manager_Entity_RecordsCollection $recordsCollection
31
+ * @param SearchSpring_Manager_Entity_OperationsCollection $operationsCollection
32
+ */
33
+ public function __construct(
34
+ SearchSpring_Manager_Entity_RecordsCollection $recordsCollection,
35
+ SearchSpring_Manager_Entity_OperationsCollection $operationsCollection
36
+ ) {
37
+ $this->recordsCollection = $recordsCollection;
38
+ $this->operationsCollection = $operationsCollection;
39
+ }
40
+
41
+ /**
42
+ * Transforms product collection to records collection
43
+ *
44
+ * @param Mage_Catalog_Model_Resource_Product_Collection $productCollection
45
+ *
46
+ * @return SearchSpring_Manager_Entity_RecordsCollection
47
+ */
48
+ public function transform(Mage_Catalog_Model_Resource_Product_Collection $productCollection)
49
+ {
50
+ /** @var Mage_Catalog_Model_Product $product */
51
+ foreach ($productCollection as $product) {
52
+ // load product data
53
+ $product->load($product->getId());
54
+
55
+ // default to valid
56
+ $productValid = true;
57
+
58
+ /** @var SearchSpring_Manager_Operation_Product $operation */
59
+ foreach ($this->operationsCollection as $operation) {
60
+ // check if any operation validation invalidates this product
61
+ if (false === $operation->isValid($product)) {
62
+ $productValid = false;
63
+ }
64
+ }
65
+
66
+ // only set id if product is invalid and continue to next product
67
+ if (false === $productValid) {
68
+ $operation = new SearchSpring_Manager_Operation_Product_SetId(
69
+ new SearchSpring_Manager_String_Sanitizer(),
70
+ $this->recordsCollection
71
+ );
72
+
73
+ $operation->perform($product);
74
+
75
+ // increment record
76
+ $this->recordsCollection->next();
77
+
78
+ continue;
79
+ }
80
+
81
+ foreach ($this->operationsCollection as $operation) {
82
+ $operation->perform($product);
83
+ }
84
+
85
+ // increment record
86
+ $this->recordsCollection->next();
87
+ }
88
+
89
+ return $this->recordsCollection;
90
+ }
91
+ }
app/code/community/SearchSpring/Manager/Validator/ProductValidator.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File ProductValidator.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Validator_ProductValidator
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ class SearchSpring_Manager_Validator_ProductValidator implements Zend_Validate_Interface
14
+ {
15
+ /**
16
+ * An array of allowable product types for validation
17
+ *
18
+ * @var array $allowableTypes
19
+ */
20
+ public static $allowableTypes = array(
21
+ Mage_Catalog_Model_Product_Type::TYPE_SIMPLE,
22
+ Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE,
23
+ Mage_Catalog_Model_Product_Type::TYPE_GROUPED,
24
+ Mage_Catalog_Model_Product_Type::TYPE_BUNDLE,
25
+ );
26
+
27
+ /**
28
+ * An array of messages that details why the validator failed
29
+ *
30
+ * @var array $messages
31
+ */
32
+ private $messages = array();
33
+
34
+ /**
35
+ * Determines if we should delete this product from the SearchSpring index
36
+ *
37
+ * @param Mage_Catalog_Model_Product $product
38
+ * @param SearchSpring_Manager_Strategy_PricingStrategy $pricing
39
+ *
40
+ * @return bool
41
+ * @throws UnexpectedValueException
42
+ */
43
+ public function shouldDelete(
44
+ Mage_Catalog_Model_Product $product,
45
+ SearchSpring_Manager_Strategy_PricingStrategy $pricing
46
+ ) {
47
+ $status = (int)$product->getStatus();
48
+ $origStatus = (int)$product->getOrigData('status');
49
+ $visibility = (int)$product->getData('visibility');
50
+ $origVisibility = (int)$product->getOrigData('visibility');
51
+
52
+ // if product became disabled
53
+ if (
54
+ $status === Mage_Catalog_Model_Product_Status::STATUS_DISABLED
55
+ && $origStatus === Mage_Catalog_Model_Product_Status::STATUS_ENABLED
56
+ ) {
57
+ return true;
58
+ }
59
+
60
+ // if product became not visible
61
+ if (
62
+ $visibility !== Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH
63
+ && $origVisibility === Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH
64
+ ) {
65
+ return true;
66
+ }
67
+
68
+ // if product is out of stock
69
+ $displayOos = Mage::helper('cataloginventory')->isShowOutOfStock();
70
+ if (!$product->isInStock() && !$displayOos) {
71
+ return true;
72
+ }
73
+
74
+ if (null === $pricing->getNormalPrice() || null === $pricing->getTierPrice() || null === $pricing->getSalePrice()) {
75
+ throw new UnexpectedValueException('Pricing calculations must be run before we can check if product should be deleted');
76
+ }
77
+
78
+ // if product has a zero price
79
+ $displayZeroPrice = Mage::helper('searchspring_manager')->isZeroPriceIndexingEnabled();
80
+ $productHaZeroPrice = 0 == $pricing->getNormalPrice() && 0 == $pricing->getTierPrice() && 0 == $pricing->getSalePrice();
81
+ if ($productHaZeroPrice && !$displayZeroPrice) {
82
+ return true;
83
+ }
84
+
85
+ return false;
86
+ }
87
+
88
+ /**
89
+ * {@inheritdoc}
90
+ */
91
+ public function isValid($product)
92
+ {
93
+ $this->messages = array();
94
+
95
+ /** @var Mage_Catalog_Model_Product $product */
96
+ if (!$product instanceof Mage_Catalog_Model_Product) {
97
+ $this->messages[] = 'Validator expected a Mage_Catalog_Model_Product.';
98
+
99
+ return false;
100
+ }
101
+
102
+ // product must be enabled
103
+ if ((int)$product->getData('status') !== Mage_Catalog_Model_Product_Status::STATUS_ENABLED) {
104
+ $this->messages[] = 'Product is not enabled';
105
+ }
106
+
107
+ // product must be an allowed type
108
+ if (!in_array($product->getData('type_id'), self::$allowableTypes)) {
109
+ $this->messages[] = 'Product is not an allowable type.';
110
+ }
111
+
112
+ // product must be visible in catalog and search
113
+ if ((int)$product->getData('visibility') !== Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH) {
114
+ $this->messages[] = 'Product must be visible in catalog and search';
115
+ }
116
+
117
+ // if we have errors, return false
118
+ if (!empty($this->messages)) {
119
+ return false;
120
+ }
121
+
122
+ return true;
123
+ }
124
+
125
+ /**
126
+ * {@inheritdoc}
127
+ */
128
+ public function getMessages()
129
+ {
130
+ return $this->messages;
131
+ }
132
+ }
app/code/community/SearchSpring/Manager/VarienObject/Data.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Data.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_VarienObject_Data
10
+ *
11
+ * Performs operations on Varien_Objects
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_VarienObject_Data
16
+ {
17
+ /**
18
+ * Finds updated data on object
19
+ *
20
+ * @param Varien_Object $object
21
+ *
22
+ * @return array
23
+ */
24
+ public function findUpdatedData(Varien_Object $object)
25
+ {
26
+ $originalData = $object->getOrigData();
27
+ $data = $object->getData();
28
+
29
+ $updated = $this->arrayDiffAssocRecursive($data, $originalData);
30
+
31
+ return $updated;
32
+ }
33
+
34
+ /**
35
+ * Determines if the object is new
36
+ *
37
+ * @param Varien_Object $object
38
+ *
39
+ * @return bool
40
+ */
41
+ public function isNew(Varien_Object $object)
42
+ {
43
+ $isNew = null === $object->getOrigData();
44
+
45
+ return $isNew;
46
+ }
47
+
48
+ /**
49
+ * Finds array_diff_assoc recursively
50
+ *
51
+ * Will serialize each array field before performing array_diff_assoc(). This will resolve any errors that occur
52
+ * when running array_diff_assoc() and values that are not strings. It will then unserialize each field in the
53
+ * difference array returned from array_diff_assoc().
54
+ *
55
+ * @param array $changed
56
+ * @param array $original
57
+ * @return array
58
+ */
59
+ private function arrayDiffAssocRecursive(array $changed, array $original)
60
+ {
61
+ $changed = $this->arrayValuesToString($changed);
62
+ $original = $this->arrayValuesToString($original);
63
+
64
+ $difference = array_map(
65
+ 'unserialize',
66
+ array_diff_assoc(
67
+ array_map('serialize', $changed),
68
+ array_map('serialize', $original)
69
+ )
70
+ );
71
+
72
+ return $difference;
73
+ }
74
+
75
+ /**
76
+ * Casts scalar array values to strings
77
+ *
78
+ * @param array $array
79
+ *
80
+ * @return array
81
+ */
82
+ private function arrayValuesToString(array $array)
83
+ {
84
+ foreach ($array as $key => $value) {
85
+ if (!is_scalar($value)) {
86
+ continue;
87
+ }
88
+
89
+ $array[$key] = (string) $value;
90
+ }
91
+
92
+ return $array;
93
+ }
94
+ }
app/code/community/SearchSpring/Manager/Writer/Product/FileWriter.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ProductWriterXml.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Writer_Product_FileWriter
10
+ *
11
+ * Responsible for writing product data to xml
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Writer_Product_FileWriter implements SearchSpring_Manager_Writer_ProductWriter
16
+ {
17
+ /**
18
+ * Format for continue response
19
+ */
20
+ const MESSAGE_FORMAT_CONTINUE = '%s|%d';
21
+
22
+ /**
23
+ * XmlWriter object
24
+ *
25
+ * @var XMLWriter $xmlWriter
26
+ */
27
+ private $xmlWriter;
28
+
29
+ /**
30
+ * Holds values the writer needs
31
+ *
32
+ * @var SearchSpring_Manager_Writer_Product_FileWriter $writerParams
33
+ */
34
+ private $writerParams;
35
+
36
+ /**
37
+ * Constructor
38
+ *
39
+ * @param XMLWriter $xmlWriter
40
+ * @param SearchSpring_Manager_Writer_Product_Params_FileWriterParams $writerParams
41
+ */
42
+ public function __construct(
43
+ XMLWriter $xmlWriter,
44
+ SearchSpring_Manager_Writer_Product_Params_FileWriterParams $writerParams
45
+ ) {
46
+ $this->xmlWriter = $xmlWriter;
47
+ $this->writerParams = $writerParams;
48
+ }
49
+
50
+ /**
51
+ * Transform ProductRecords to xml and write to file
52
+ *
53
+ * @param SearchSpring_Manager_Entity_RecordsCollection $records
54
+ *
55
+ * @return null
56
+ * @throws UnexpectedValueException
57
+ */
58
+ public function write(SearchSpring_Manager_Entity_RecordsCollection $records)
59
+ {
60
+ $this->startWriting();
61
+
62
+ foreach ($records as $record) {
63
+ if (!is_array($record)) {
64
+ throw new UnexpectedValueException('Record in unknown format');
65
+ }
66
+ $this->xmlWriter->startElement('Product');
67
+
68
+ foreach ($record as $key => $value) {
69
+ if (!is_array($value)) {
70
+ $this->xmlWriter->writeElement($key, $value);
71
+
72
+ continue;
73
+ }
74
+
75
+ // if it's an array, we want to add all elements to the same node with the same key
76
+ foreach ($value as $v) {
77
+ $this->xmlWriter->writeElement($key, $v);
78
+ }
79
+ }
80
+
81
+ $this->xmlWriter->endElement();
82
+ }
83
+
84
+ $this->endWriting();
85
+
86
+ // get the response string
87
+ $response = $this->getResponse();
88
+
89
+ return $response;
90
+ }
91
+
92
+ /**
93
+ * Set up stream and setup document if it's the first write
94
+ */
95
+ private function startWriting()
96
+ {
97
+ $this->xmlWriter->openMemory();
98
+
99
+ if (!$this->writerParams->isFirst()) {
100
+ return;
101
+ }
102
+
103
+ if (is_file($this->writerParams->getTempFilename())) {
104
+ unlink($this->writerParams->getTempFilename());
105
+ }
106
+
107
+ $this->xmlWriter->startDocument('1.0', 'UTF-8');
108
+ $this->xmlWriter->startElement('Products');
109
+ }
110
+
111
+ /**
112
+ * Write to file from memory and cleanup document if it's the last write
113
+ */
114
+ private function endWriting()
115
+ {
116
+ if ($this->writerParams->isLast()) {
117
+ $this->xmlWriter->writeRaw('</Products>');
118
+ }
119
+
120
+ file_put_contents($this->writerParams->getTempFilename(), $this->xmlWriter->flush(), FILE_APPEND);
121
+
122
+ if ($this->writerParams->isLast()) {
123
+ rename($this->writerParams->getTempFilename(), $this->writerParams->getFilename());
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Get the response string
129
+ *
130
+ * @return string
131
+ */
132
+ private function getResponse()
133
+ {
134
+ $response = self::REGEN_COMPLETE;
135
+ if (!$this->writerParams->isLast()) {
136
+ $response = sprintf(
137
+ self::MESSAGE_FORMAT_CONTINUE,
138
+ self::REGEN_CONTINUE,
139
+ $this->writerParams->getRequestParams()->getCount() + $this->writerParams->getRequestParams()->getOffset()
140
+ );
141
+ }
142
+
143
+ return $response;
144
+ }
145
+ }
app/code/community/SearchSpring/Manager/Writer/Product/Params/FileWriterParams.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WriterParams.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Writer_Product_Params_FileWriterParams
10
+ *
11
+ * A parameter object that holds values needed for writing files
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Writer_Product_Params_FileWriterParams
16
+ {
17
+ /**
18
+ * Filename pattern
19
+ */
20
+ const FILENAME_FINAL_PATTERN = 'searchspring_%s.xml';
21
+
22
+ /**
23
+ * Temporary filename pattern
24
+ */
25
+ const FILENAME_TEMP_PATTERN = 'searchspring_%s.%s.tmp.xml';
26
+
27
+ /**
28
+ * Total number of products in Magento
29
+ *
30
+ * @var int $totalProducts
31
+ */
32
+ private $totalProducts;
33
+
34
+ /**
35
+ * The unique filename to avoid collisions during generation
36
+ *
37
+ * @var string $uniqueFilename
38
+ */
39
+ private $uniqueFilename;
40
+
41
+ /**
42
+ * Whether or not we're writing for the first time
43
+ *
44
+ * @var bool $isFirst
45
+ */
46
+ private $isFirst;
47
+
48
+ /**
49
+ * Whether or not we're writing the last set of records
50
+ *
51
+ * @var bool $isLast
52
+ */
53
+ private $isLast;
54
+
55
+ /**
56
+ * The temporary filename
57
+ *
58
+ * @var string $tempFilename
59
+ */
60
+ private $tempFilename;
61
+
62
+ /**
63
+ * The filename
64
+ *
65
+ * @var string $filename
66
+ */
67
+ private $filename;
68
+
69
+ /**
70
+ * @var SearchSpring_Manager_Entity_RequestParams
71
+ */
72
+ private $requestParams;
73
+
74
+ /**
75
+ * Constructor
76
+ *
77
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams
78
+ * @param int $totalProducts
79
+ * @param string $uniqueFilename
80
+ */
81
+ public function __construct(SearchSpring_Manager_Entity_RequestParams $requestParams, $totalProducts, $uniqueFilename)
82
+ {
83
+ $this->requestParams = $requestParams;
84
+ $this->totalProducts = $totalProducts;
85
+ $this->uniqueFilename = $uniqueFilename;
86
+ }
87
+
88
+ /**
89
+ * Get the request parameters object
90
+ *
91
+ * @return SearchSpring_Manager_Entity_RequestParams
92
+ */
93
+ public function getRequestParams()
94
+ {
95
+ return $this->requestParams;
96
+ }
97
+
98
+ /**
99
+ * Calculates if this is the first write
100
+ *
101
+ * @return bool
102
+ */
103
+ public function isFirst()
104
+ {
105
+ if (null === $this->isFirst) {
106
+ $this->isFirst = (0 === $this->requestParams->getOffset());
107
+ }
108
+
109
+ return $this->isFirst;
110
+ }
111
+
112
+ /**
113
+ * Calculates if this is the last write
114
+ *
115
+ * @return bool
116
+ */
117
+ public function isLast()
118
+ {
119
+ if (null === $this->isLast) {
120
+ $this->isLast = ($this->totalProducts <= ($this->requestParams->getCount() + $this->requestParams->getOffset()));
121
+ }
122
+
123
+ return $this->isLast;
124
+ }
125
+
126
+ /**
127
+ * Get temporary filename
128
+ *
129
+ * @return string
130
+ */
131
+ public function getTempFilename()
132
+ {
133
+ if (null === $this->tempFilename) {
134
+ $this->tempFilename = Mage::getBaseDir()
135
+ . DS
136
+ . sprintf(self::FILENAME_TEMP_PATTERN, $this->requestParams->getStore(), $this->uniqueFilename);
137
+ }
138
+
139
+ return $this->tempFilename;
140
+ }
141
+
142
+ /**
143
+ * Get filename
144
+ *
145
+ * @return string
146
+ */
147
+ public function getFilename()
148
+ {
149
+ if (null === $this->filename) {
150
+ $this->filename = Mage::getBaseDir() . DS . sprintf(self::FILENAME_FINAL_PATTERN, $this->requestParams->getStore());
151
+ }
152
+
153
+ return $this->filename;
154
+ }
155
+ }
app/code/community/SearchSpring/Manager/Writer/Product/Params/ResponseWriterParams.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WriterParams.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Writer_Provider_WriterParams
10
+ *
11
+ * Store parameters for response writer
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Writer_Product_Params_ResponseWriterParams
16
+ {
17
+
18
+ /**
19
+ * Total number of products in Magento
20
+ *
21
+ * @var int $totalProducts
22
+ */
23
+ private $totalProducts;
24
+
25
+ /**
26
+ * Whether or not we're writing for the first time
27
+ *
28
+ * @var bool $isFirst
29
+ */
30
+ private $isFirst;
31
+
32
+ /**
33
+ * Whether or not we're writing the last set of records
34
+ *
35
+ * @var bool $isLast
36
+ */
37
+ private $isLast;
38
+
39
+ /**
40
+ * @var SearchSpring_Manager_Entity_RequestParams
41
+ */
42
+ private $requestParams;
43
+
44
+ /**
45
+ * Constructor
46
+ *
47
+ * @param SearchSpring_Manager_Entity_RequestParams $requestParams
48
+ * @param int $totalProducts
49
+ */
50
+ public function __construct(SearchSpring_Manager_Entity_RequestParams $requestParams, $totalProducts)
51
+ {
52
+ $this->requestParams = $requestParams;
53
+ $this->totalProducts = $totalProducts;
54
+ }
55
+
56
+ public function getRequestParams()
57
+ {
58
+ return $this->requestParams;
59
+ }
60
+
61
+ /**
62
+ * Calculates if this is the first write
63
+ *
64
+ * @return bool
65
+ */
66
+ public function isFirst()
67
+ {
68
+ if (null === $this->isFirst) {
69
+ $this->isFirst = (0 === $this->requestParams->getOffset());
70
+ }
71
+
72
+ return $this->isFirst;
73
+ }
74
+
75
+ /**
76
+ * Calculates if this is the last write
77
+ *
78
+ * @return bool
79
+ */
80
+ public function isLast()
81
+ {
82
+ $totalCount = $this->requestParams->getCount() + $this->requestParams->getOffset();
83
+
84
+ // if we defaulted to 0, we're only doing one iteration
85
+ if (0 === $totalCount) {
86
+ return true;
87
+ }
88
+
89
+ if (null === $this->isLast) {
90
+ $this->isLast = ($this->totalProducts <= $totalCount);
91
+ }
92
+
93
+ return $this->isLast;
94
+ }
95
+ }
app/code/community/SearchSpring/Manager/Writer/Product/ResponseWriter.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ResponseWriter.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_Writer_Product_ResponseWriter
10
+ *
11
+ * Write a collection as a response
12
+ *
13
+ * @author Nate Brunette <nate@b7interactive.com>
14
+ */
15
+ class SearchSpring_Manager_Writer_Product_ResponseWriter implements SearchSpring_Manager_Writer_ProductWriter
16
+ {
17
+ /**
18
+ * Parameters for writing responses
19
+ *
20
+ * @var SearchSpring_Manager_Writer_Product_Params_ResponseWriterParams
21
+ */
22
+ private $params;
23
+
24
+ /**
25
+ * Constructor
26
+ *
27
+ * @param SearchSpring_Manager_Writer_Product_Params_ResponseWriterParams $params
28
+ */
29
+ public function __construct(SearchSpring_Manager_Writer_Product_Params_ResponseWriterParams $params)
30
+ {
31
+ $this->params = $params;
32
+ }
33
+
34
+ /**
35
+ * Write a response and return that as the message
36
+ *
37
+ * @param SearchSpring_Manager_Entity_RecordsCollection $recordsCollection
38
+ * @return array
39
+ */
40
+ public function write(SearchSpring_Manager_Entity_RecordsCollection $recordsCollection)
41
+ {
42
+ $response = array('status' => self::REGEN_COMPLETE, 'products' => $recordsCollection->toArray());
43
+ if (!$this->params->isLast()) {
44
+ $response['status'] = self::REGEN_CONTINUE;
45
+ }
46
+
47
+ return $response;
48
+ }
49
+ }
app/code/community/SearchSpring/Manager/Writer/ProductWriter.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ProductWriter.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Interface SearchSpring_Manager_Writer_ProductWriter
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ interface SearchSpring_Manager_Writer_ProductWriter
14
+ {
15
+ /**
16
+ * Continue response
17
+ */
18
+ const REGEN_CONTINUE = 'continue';
19
+
20
+ /**
21
+ * Complete response
22
+ */
23
+ const REGEN_COMPLETE = 'complete';
24
+
25
+ /**
26
+ * Write a collection
27
+ *
28
+ * @param SearchSpring_Manager_Entity_RecordsCollection $recordsCollection
29
+ *
30
+ * @return string The response message
31
+ */
32
+ public function write(SearchSpring_Manager_Entity_RecordsCollection $recordsCollection);
33
+ }
app/code/community/SearchSpring/Manager/controllers/GenerateController.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * GenerateController.php
4
+ *
5
+ * @copyright B7 Interactive, LLC. All Rights Reserved.
6
+ */
7
+
8
+ /**
9
+ * Class SearchSpring_Manager_GenerateController
10
+ *
11
+ * @author Nate Brunette <nate@b7interactive.com>
12
+ */
13
+ class SearchSpring_Manager_GenerateController extends Mage_Core_Controller_Front_Action
14
+ {
15
+ /**
16
+ * Default starting point if no parameter is set
17
+ */
18
+ const OFFSET_DEFAULT = 0;
19
+
20
+ /**
21
+ * Default product limit if no parameter is set
22
+ */
23
+ const COUNT_DEFAULT = 100;
24
+
25
+ /**
26
+ * Default store if no parameter is set
27
+ */
28
+ const STORE_DEFAULT = 'default';
29
+
30
+ /**
31
+ * Default action for updating based on product/category id
32
+ *
33
+ * Parameters:
34
+ * type (required): The type of id passed in. Can be 'product' or 'category'
35
+ * ids (required): An array of ids
36
+ */
37
+ public function indexAction()
38
+ {
39
+ $request = new SearchSpring_Manager_Request_JSON($this->getRequest());
40
+
41
+ $type = $request->getParam('type');
42
+ $ids = $request->getParam('ids');
43
+
44
+ if (null === $type) {
45
+ $this->setJsonResponse(
46
+ array(
47
+ 'status' => 'error',
48
+ 'errorCode' => SearchSpring_ErrorCodes::TYPE_NOT_SET,
49
+ 'message' => 'Type must be specified'
50
+ ),
51
+ 400
52
+ );
53
+
54
+ return;
55
+ }
56
+
57
+ if (null === $ids) {
58
+ $this->setJsonResponse(
59
+ array(
60
+ 'status' => 'error',
61
+ 'errorCode' => SearchSpring_ErrorCodes::IDS_NOT_SET,
62
+ 'message' => 'Ids must be specified'
63
+ ),
64
+ 400
65
+ );
66
+
67
+ return;
68
+ }
69
+
70
+ $requestParams = new SearchSpring_Manager_Entity_RequestParams(
71
+ (int)$request->getParam('size', null),
72
+ (int)$request->getParam('start', null),
73
+ $request->getParam('store', self::STORE_DEFAULT)
74
+ );
75
+
76
+ $params = array('ids' => $ids);
77
+
78
+ $generatorFactory = new SearchSpring_Manager_Factory_GeneratorFactory();
79
+ $generator = $generatorFactory->make($type, $requestParams, $params);
80
+ $message = $generator->generate();
81
+
82
+ $this->setJsonResponse($message);
83
+ }
84
+
85
+
86
+ /**
87
+ * Generate an xml feed of all products
88
+ *
89
+ * Parameters:
90
+ * filename (required): A unique filename when creating temporary files.
91
+ * start (optional): The starting point for fetching products. Defaults to 0.
92
+ * count (optional): The number of products to fetch. Defaults to 100.
93
+ * store (optional): The store name as a string. Defaults to 'default'
94
+ */
95
+ public function feedAction()
96
+ {
97
+ // check file is writable first
98
+ if (!is_writable(Mage::getBaseDir())) {
99
+ $this->setJsonResponse(
100
+ array(
101
+ 'status' => 'error',
102
+ 'errorCode' => SearchSpring_ErrorCodes::DIR_NOT_WRITABLE,
103
+ 'message' => 'Magento base directory is not writable'
104
+ ),
105
+ 500
106
+ );
107
+
108
+ return;
109
+ }
110
+
111
+ $uniqueFilename = $this->getRequest()->getParam('filename');
112
+
113
+ if (null === $uniqueFilename) {
114
+ $this->setJsonResponse(
115
+ array(
116
+ 'status' => 'error',
117
+ 'errorCode' => SearchSpring_ErrorCodes::FILENAME_NOT_SET,
118
+ 'message' => 'Unique filename must be passed in'
119
+ ),
120
+ 400
121
+ );
122
+
123
+ return;
124
+ }
125
+
126
+ $requestParams = new SearchSpring_Manager_Entity_RequestParams(
127
+ (int)$this->getRequest()->getParam('count', self::COUNT_DEFAULT),
128
+ (int)$this->getRequest()->getParam('start', self::OFFSET_DEFAULT),
129
+ $this->getRequest()->getParam('store', self::STORE_DEFAULT)
130
+ );
131
+
132
+ $params = array('filename' => $uniqueFilename);
133
+
134
+ $generatorFactory = new SearchSpring_Manager_Factory_GeneratorFactory();
135
+ $generator = $generatorFactory->make(
136
+ SearchSpring_Manager_Factory_GeneratorFactory::TYPE_FEED,
137
+ $requestParams,
138
+ $params
139
+ );
140
+ $message = $generator->generate();
141
+
142
+ $this->setTextResponse($message);
143
+
144
+ return;
145
+ }
146
+
147
+ /**
148
+ * Set appropriate response variables for a json response
149
+ *
150
+ * @param array $message The message that should be sent back
151
+ * @param int $responseCode The Http response code
152
+ */
153
+ private function setJsonResponse(array $message, $responseCode = 200)
154
+ {
155
+ $this->getResponse()->setHeader('Content-type', 'application/json');
156
+ $this->getResponse()->setHttpResponseCode($responseCode);
157
+
158
+ $responseBody = json_encode($message);
159
+ $this->getResponse()->setBody($responseBody);
160
+ }
161
+
162
+ /**
163
+ * Set a text based response
164
+ *
165
+ * @param string $message
166
+ * @param int $responseCode
167
+ */
168
+ private function setTextResponse($message, $responseCode = 200)
169
+ {
170
+ $this->getResponse()->setHeader('Content-type', 'text/plain');
171
+ $this->getResponse()->setHttpResponseCode($responseCode);
172
+ $this->getResponse()->setBody($message);
173
+ }
174
+ }
app/code/community/SearchSpring/Manager/etc/adminhtml.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <all>
6
+ <title>Allow Everything</title>
7
+ </all>
8
+ <admin>
9
+ <children>
10
+ <system>
11
+ <children>
12
+ <config>
13
+ <children>
14
+ <ssmanager translate="title">
15
+ <title>An Example Section</title>
16
+ <sort_order>100</sort_order>
17
+ </ssmanager>
18
+ </children>
19
+ </config>
20
+ </children>
21
+ </system>
22
+ </children>
23
+ </admin>
24
+ </resources>
25
+ </acl>
26
+ </config>
app/code/community/SearchSpring/Manager/etc/composer.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "SearchSpring/Manager",
3
+ "license": "OSL-3.0",
4
+ "type": "magento-module",
5
+ "description": "Add your description here",
6
+ "require": {
7
+ "magento-hackathon/magento-composer-installer": "*"
8
+ },
9
+ "authors": [
10
+ {
11
+ "name": "Nate Brunette",
12
+ "email": "nate@b7interactive.com"
13
+ }
14
+ ]
15
+ }
app/code/community/SearchSpring/Manager/etc/config.xml ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <SearchSpring_Manager>
5
+ <version>1.1.0</version>
6
+ </SearchSpring_Manager>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <searchspring_manager>
11
+ <class>SearchSpring_Manager_Model</class>
12
+ </searchspring_manager>
13
+ </models>
14
+ <blocks>
15
+ <searchspring_manager>
16
+ <class>SearchSpring_Manager_Block</class>
17
+ </searchspring_manager>
18
+ <!-- Add back in for Category Page Management -->
19
+ <!-- <catalog> -->
20
+ <!-- <rewrite> -->
21
+ <!-- <layer_view>SearchSpring_Manager_Block_Layer_View</layer_view> -->
22
+ <!-- </rewrite> -->
23
+ <!-- </catalog> -->
24
+ </blocks>
25
+ <helpers>
26
+ <searchspring_manager>
27
+ <class>SearchSpring_Manager_Helper</class>
28
+ </searchspring_manager>
29
+ </helpers>
30
+ <events>
31
+ <catalog_product_save_after>
32
+ <observers>
33
+ <searchspring_manager_after_save_push_product>
34
+ <class>searchspring_manager/observer_productSaveObserver</class>
35
+ <method>afterSavePushProduct</method>
36
+ </searchspring_manager_after_save_push_product>
37
+ </observers>
38
+ </catalog_product_save_after>
39
+ <catalog_product_delete_before>
40
+ <observers>
41
+ <searchspring_manager_before_delete_push_product>
42
+ <class>searchspring_manager/observer_productSaveObserver</class>
43
+ <method>beforeDeletePushProduct</method>
44
+ </searchspring_manager_before_delete_push_product>
45
+ </observers>
46
+ </catalog_product_delete_before>
47
+ <catalog_product_delete_after_done>
48
+ <observers>
49
+ <searchspring_manager_after_delete_push_product>
50
+ <class>searchspring_manager/observer_productSaveObserver</class>
51
+ <method>afterDeletePushProduct</method>
52
+ </searchspring_manager_after_delete_push_product>
53
+ </observers>
54
+ </catalog_product_delete_after_done>
55
+ <catalog_category_save_after>
56
+ <observers>
57
+ <searchspring_manager_after_save_update_product_category>
58
+ <class>searchspring_manager/observer_categorySaveObserver</class>
59
+ <method>afterSaveUpdateProductCategory</method>
60
+ </searchspring_manager_after_save_update_product_category>
61
+ </observers>
62
+ </catalog_category_save_after>
63
+ <category_move>
64
+ <observers>
65
+ <searchspring_manager_after_move_update_product_category>
66
+ <class>searchspring_manager/observer_categorySaveObserver</class>
67
+ <method>afterMoveUpdateProductCategory</method>
68
+ </searchspring_manager_after_move_update_product_category>
69
+ </observers>
70
+ </category_move>
71
+ <catalog_category_change_products>
72
+ <observers>
73
+ <searchspring_manager_after_product_change_update_product_category>
74
+ <class>searchspring_manager/observer_categorySaveObserver</class>
75
+ <method>afterProductChangeUpdateProductCategory</method>
76
+ </searchspring_manager_after_product_change_update_product_category>
77
+ </observers>
78
+ </catalog_category_change_products>
79
+ <catalog_controller_category_delete>
80
+ <observers>
81
+ <searchspring_manager_after_delete_update_product_category>
82
+ <class>searchspring_manager/observer_categorySaveObserver</class>
83
+ <method>afterDeleteUpdateProductCategory</method>
84
+ </searchspring_manager_after_delete_update_product_category>
85
+ </observers>
86
+ </catalog_controller_category_delete>
87
+ </events>
88
+ <template>
89
+ <email>
90
+ <api_error module="searchspring_manager">
91
+ <label>API Error Template</label>
92
+ <file>searchspring_api_error.html</file>
93
+ <type>html</type>
94
+ </api_error>
95
+ </email>
96
+ </template>
97
+ <resources>
98
+ <searchspring_manager>
99
+ <setup>
100
+ <module>SearchSpring_Manager</module>
101
+ <class>Mage_Catalog_Model_Resource_Setup</class>
102
+ </setup>
103
+ <connection>
104
+ <use>core_setup</use>
105
+ </connection>
106
+ </searchspring_manager>
107
+ </resources>
108
+ </global>
109
+ <frontend>
110
+ <routers>
111
+ <searchspring>
112
+ <use>standard</use>
113
+ <args>
114
+ <module>SearchSpring_Manager</module>
115
+ <frontName>searchspring</frontName>
116
+ </args>
117
+ </searchspring>
118
+ </routers>
119
+ <!-- Add back in for Category Page Management -->
120
+ <!-- <layout> -->
121
+ <!-- <updates> -->
122
+ <!-- <searchspring_manager module="SearchSpring_Manager"> -->
123
+ <!-- <file>searchspring_manager.xml</file> -->
124
+ <!-- </searchspring_manager> -->
125
+ <!-- </updates> -->
126
+ <!-- </layout> -->
127
+ </frontend>
128
+ <default>
129
+ <ssmanager>
130
+ <ssmanager_general>
131
+ <live_indexing>0</live_indexing>
132
+ </ssmanager_general>
133
+ <ssmanager_catalog>
134
+ <enable_categories>0</enable_categories>
135
+ </ssmanager_catalog>
136
+ </ssmanager>
137
+ </default>
138
+ </config>
app/code/community/SearchSpring/Manager/etc/readme.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SearchSpring SearchSpringManager Extension
2
+ =====================
3
+ Add your description here
4
+ Facts
5
+ -----
6
+ - version: 0.4.0
7
+ - extension key: SearchSpring_SearchSpringManager
8
+ - [extension on Magento Connect](http://www.magentocommerce.com/magento-connect/searchspring)-searchspringmanager-1234.html)
9
+ - Magento Connect 1.0 extension key: magento-community/SearchSpring_SearchSpringManager
10
+ - Magento Connect 2.0 extension key: http://connect20.magentocommerce.com/community/SearchSpring_SearchSpringManager
11
+ - [extension on GitHub](https://github.com/searchspring/SearchSpring_SearchSpringManager
12
+ - [direct download link](http://connect.magentocommerce.com/community/get/SearchSpring_SearchSpringManager-1.0.0.tgz)
13
+
14
+ Description
15
+ -----------
16
+ This paragraph describes what the extension does.
17
+
18
+ Requirements
19
+ ------------
20
+ - PHP >= 5.2.0
21
+ - Mage_Core
22
+ - ...
23
+
24
+ Compatibility
25
+ -------------
26
+ - Magento >= 1.7
27
+
28
+ Installation Instructions
29
+ -------------------------
30
+ 1. Install the extension via Magento Connect with the key shown above or copy all the files into your document root.
31
+ 2. Clear the cache, logout from the admin panel and then login again.
32
+ 3. Configure and activate the extension under System - Configuration - Company - Example Extension.
33
+ 4. ...
34
+
35
+ Uninstallation
36
+ --------------
37
+ 1. Remove all extension files from your Magento installation
38
+ 2. ...
39
+
40
+ Support
41
+ -------
42
+ If you have any issues with this extension, open an issue on [GitHub](https://github.com/searchspring/SearchSpring_SearchSpringManager/issues).
43
+
44
+ Contribution
45
+ ------------
46
+ Any contribution is highly appreciated. The best way to contribute code is to open a [pull request on GitHub](https://help.github.com/articles/using-pull-requests).
47
+
48
+ Developers
49
+ ---------
50
+ Nate Brunette
51
+ James Bathgate
52
+
53
+ Copyright
54
+ ---------
55
+ (c) 2014 SearchSpring
app/code/community/SearchSpring/Manager/etc/system.xml ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <searchspring translate="label" module="searchspring_manager">
5
+ <label>SearchSpring</label>
6
+ <sort_order>500</sort_order>
7
+ </searchspring>
8
+ </tabs>
9
+ <sections>
10
+ <ssmanager translate="label">
11
+ <label>Manager Settings</label>
12
+ <tab>searchspring</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>100</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <ssmanager_general translate="label" module="searchspring_manager">
20
+ <label>Indexing Configuration</label>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>100</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>1</show_in_website>
25
+ <show_in_store>1</show_in_store>
26
+ <fields>
27
+ <live_indexing translate="label">
28
+ <label>Live Indexing</label>
29
+ <comment>If live indexing is enabled, on product/category saves, SearchSpring will get an update of changes. Only enable this if Magento cache is updated on save.</comment>
30
+ <frontend_type>select</frontend_type>
31
+ <source_model>adminhtml/system_config_source_yesno</source_model>
32
+ <sort_order>5</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ <show_in_website>1</show_in_website>
35
+ <show_in_store>1</show_in_store>
36
+ </live_indexing>
37
+ <index_zero_price translate="label">
38
+ <label>Display products with 0 price</label>
39
+ <frontend_type>select</frontend_type>
40
+ <source_model>adminhtml/system_config_source_yesno</source_model>
41
+ <sort_order>10</sort_order>
42
+ <show_in_default>1</show_in_default>
43
+ <show_in_website>1</show_in_website>
44
+ <show_in_store>1</show_in_store>
45
+ </index_zero_price>
46
+ </fields>
47
+ </ssmanager_general>
48
+ <ssmanager_images translate="label" module="searchspring_manager">
49
+ <label>Image Settings</label>
50
+ <frontend_type>text</frontend_type>
51
+ <sort_order>100</sort_order>
52
+ <show_in_default>1</show_in_default>
53
+ <show_in_website>1</show_in_website>
54
+ <show_in_store>1</show_in_store>
55
+ <fields>
56
+ <generate_cache_images translate="label">
57
+ <label>Generate Cache Images</label>
58
+ <comment>If you use cache images and clear your image cache you will need to re-index SearchSpring to re-populate your product images.</comment>
59
+ <frontend_type>select</frontend_type>
60
+ <source_model>adminhtml/system_config_source_yesno</source_model>
61
+ <sort_order>10</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <show_in_website>1</show_in_website>
64
+ <show_in_store>1</show_in_store>
65
+ </generate_cache_images>
66
+ <image_width translate="label">
67
+ <label>Image Width</label>
68
+ <comment>Image width in pixels for generated cache images (Default: 200)</comment>
69
+ <frontend_type>text</frontend_type>
70
+ <sort_order>20</sort_order>
71
+ <show_in_default>1</show_in_default>
72
+ <show_in_website>1</show_in_website>
73
+ <show_in_store>1</show_in_store>
74
+ </image_width>
75
+ <image_height translate="label">
76
+ <label>Image Height</label>
77
+ <comment>Image height in pixels for generated cache images (Default: 200)</comment>
78
+ <frontend_type>text</frontend_type>
79
+ <sort_order>30</sort_order>
80
+ <show_in_default>1</show_in_default>
81
+ <show_in_website>1</show_in_website>
82
+ <show_in_store>1</show_in_store>
83
+ </image_height>
84
+ </fields>
85
+ </ssmanager_images>
86
+ <ssmanager_catalog translate="label" module="searchspring_manager">
87
+ <label>Catalog Configuration</label>
88
+ <frontend_type>text</frontend_type>
89
+ <sort_order>200</sort_order>
90
+ <show_in_default>1</show_in_default>
91
+ <show_in_website>1</show_in_website>
92
+ <show_in_store>1</show_in_store>
93
+ <fields>
94
+ <enable_categories translate="label">
95
+ <label>Category Display</label>
96
+ <comment>If category display is enabled SearchSpring will power your category pages.</comment>
97
+ <frontend_type>select</frontend_type>
98
+ <source_model>adminhtml/system_config_source_yesno</source_model>
99
+ <sort_order>10</sort_order>
100
+ <!-- Turn these back on for Category Page Management -->
101
+ <show_in_default>0</show_in_default>
102
+ <show_in_website>0</show_in_website>
103
+ <show_in_store>0</show_in_store>
104
+ </enable_categories>
105
+ </fields>
106
+ </ssmanager_catalog>
107
+ <ssmanager_api translate="label" module="searchspring_manager">
108
+ <label>SearchSpring Connection Settings</label>
109
+ <frontend_type>select</frontend_type>
110
+ <sort_order>300</sort_order>
111
+ <show_in_default>1</show_in_default>
112
+ <show_in_website>1</show_in_website>
113
+ <show_in_store>1</show_in_store>
114
+ <fields>
115
+ <base_url translate="label">
116
+ <label>Base URL</label>
117
+ <comment>https://example.com</comment>
118
+ <frontend_type>text</frontend_type>
119
+ <sort_order>15</sort_order>
120
+ <show_in_default>1</show_in_default>
121
+ <show_in_website>1</show_in_website>
122
+ <show_in_store>1</show_in_store>
123
+ </base_url>
124
+ <site_id translate="label">
125
+ <label>Site ID</label>
126
+ <comment>You can find this in the upper right corner of the SearchSpring Management Console</comment>
127
+ <frontend_type>text</frontend_type>
128
+ <sort_order>20</sort_order>
129
+ <show_in_default>1</show_in_default>
130
+ <show_in_website>1</show_in_website>
131
+ <show_in_store>1</show_in_store>
132
+ </site_id>
133
+ <feed_id translate="label">
134
+ <label>Feed ID</label>
135
+ <frontend_type>text</frontend_type>
136
+ <sort_order>30</sort_order>
137
+ <show_in_default>1</show_in_default>
138
+ <show_in_website>1</show_in_website>
139
+ <show_in_store>1</show_in_store>
140
+ </feed_id>
141
+ <secret_key translate="label">
142
+ <label>Secret Key</label>
143
+ <frontend_type>text</frontend_type>
144
+ <sort_order>40</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <show_in_store>1</show_in_store>
148
+ </secret_key>
149
+ </fields>
150
+ </ssmanager_api>
151
+ </groups>
152
+ </ssmanager>
153
+ </sections>
154
+ </config>
app/design/frontend/base/default/layout/searchspring_manager.xml ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+
4
+ <catalog_category_layered>
5
+ <update handle="_searchspring_layered_navigation_override" />
6
+ </catalog_category_layered>
7
+
8
+ <_searchspring_layered_navigation_override>
9
+
10
+ <!-- Custom Templates -->
11
+
12
+ <reference name="catalog.leftnav">
13
+ <action method="setTemplate">
14
+ <template helper="searchspring_manager/getBlockTemplateIfCategoryEnabled">
15
+ <blockName>catalog.leftnav</blockName>
16
+ <newTemplate>searchspring_manager/layer/view.phtml</newTemplate>
17
+ </template>
18
+ </action>
19
+ </reference>
20
+
21
+ <reference name="product_list">
22
+ <action method="setTemplate">
23
+ <template helper="searchspring_manager/getBlockTemplateIfCategoryEnabled">
24
+ <blockName>product_list</blockName>
25
+ <newTemplate>searchspring_manager/product/list.phtml</newTemplate>
26
+ </template>
27
+ </action>
28
+ </reference>
29
+
30
+ <reference name="product_list_toolbar">
31
+ <action method="setTemplate">
32
+ <template helper="searchspring_manager/getBlockTemplateIfCategoryEnabled">
33
+ <blockName>product_list_toolbar</blockName>
34
+ <newTemplate>searchspring_manager/product/list/toolbar.phtml</newTemplate>
35
+ </template>
36
+ </action>
37
+ </reference>
38
+
39
+ </_searchspring_layered_navigation_override>
40
+
41
+ </layout>
app/design/frontend/base/default/template/searchspring_manager/layer/category_filter.phtml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for category filter items block
4
+ *
5
+ * @see SearchSpring_Manager_Block_Layer_Filter_SearchSpringCategory
6
+ */
7
+ ?>
8
+
9
+ <ol>
10
+ <?php //TODO: Maybe we should make these some sort of category parent object instead of using raw category objects -James ?>
11
+ <?php $_parents = $this->getParents() ?>
12
+ <?php foreach($_parents as $_id => $_parent): ?>
13
+ <li class="ss-category_breadcrumb <?php echo ($_id == $this->getCurrentValue()) ? "ss-current_category":"" ?>">
14
+ <a href="<?php echo $this->urlEscape($_parent->getUrl()) ?>">
15
+ <?php echo ($_id == $this->getCurrentValue()) ? "":"&laquo;" ?> <?php echo $_parent->getName() ?>
16
+ </a>
17
+ </li>
18
+ <?php endforeach; ?>
19
+ <?php foreach ($this->getItems() as $_item): ?>
20
+ <li <?php echo $_item->isActive()?'active':'' ?>">
21
+ <a href="<?php echo $this->urlEscape($_item->getUrl()) ?>">
22
+ <?php echo $_item->getLabel() ?>
23
+ <?php if ($this->shouldDisplayProductCount()): ?>
24
+ <span class="count">(<?php echo $_item->getCount() ?>)</span>
25
+ <?php endif; ?>
26
+ </a>
27
+ </li>
28
+ <?php endforeach ?>
29
+ </ol>
app/design/frontend/base/default/template/searchspring_manager/layer/filter.phtml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for filter items block
4
+ *
5
+ * @see SearchSpring_Manager_Block_Layer_Filter_SearchSpringList
6
+ */
7
+ ?>
8
+
9
+ <ol>
10
+ <?php foreach ($this->getItems() as $_item): ?>
11
+ <li <?php echo $_item->isActive()?'class="active"':'' ?>>
12
+ <a rel="nofollow" href="<?php echo $this->urlEscape($_item->getUrl()) ?>">
13
+ <?php echo $_item->getLabel() ?>
14
+ <?php if ($this->shouldDisplayProductCount()): ?>
15
+ <span class="count">(<?php echo $_item->getCount() ?>)</span>
16
+ <?php endif; ?>
17
+ </a>
18
+ </li>
19
+ <?php endforeach ?>
20
+ </ol>
app/design/frontend/base/default/template/searchspring_manager/layer/state.phtml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Category layered navigation state
4
+ *
5
+ * @see Mage_Catalog_Block_Layer_State
6
+ */
7
+ ?>
8
+ <?php $_filters = $this->getActiveFilters() ?>
9
+ <?php if(!empty($_filters)): ?>
10
+ <div class="currently">
11
+ <p class="block-subtitle"><?php echo $this->__('Currently Shopping by:') ?></p>
12
+ <ol>
13
+ <?php foreach ($_filters as $_filter): ?>
14
+ <li>
15
+ <span class="label"><?php echo $this->__($_filter->getName()) ?>:</span> <span class="value"><?php echo $this->stripTags($_filter->getLabel()) ?></span>
16
+ <?php
17
+ $clearLinkUrl = $_filter->getClearLinkUrl();
18
+ if ($clearLinkUrl):
19
+ ?>
20
+ <a class="btn-previous" href="<?php echo $_filter->getRemoveUrl() ?>" title="<?php echo $this->__('Previous') ?>"><?php echo $this->__('Previous') ?></a>
21
+ <a class="btn-remove" title="<?php echo $this->escapeHtml($_filter->getFilter()->getClearLinkText()) ?>" href="<?php echo $clearLinkUrl ?>"><?php echo $this->escapeHtml($_filter->getFilter()->getClearLinkText()) ?></a>
22
+ <?php else: ?>
23
+ <a class="btn-remove" href="<?php echo $_filter->getRemoveUrl() ?>" title="<?php echo $this->__('Remove This Item') ?>"><?php echo $this->__('Remove This Item') ?></a>
24
+ <?php endif; ?>
25
+ </li>
26
+ <?php endforeach; ?>
27
+ </ol>
28
+ </div>
29
+ <?php endif; ?>
app/design/frontend/base/default/template/searchspring_manager/layer/view.phtml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Category layered navigation
4
+ *
5
+ * @see Mage_Catalog_Block_Layer_View
6
+ */
7
+ ?>
8
+ <?php if($this->canShowBlock()): ?>
9
+ <div class="block block-layered-nav">
10
+ <div class="block-title">
11
+ <strong><span><?php echo $this->__('Shop By') ?></span></strong>
12
+ </div>
13
+ <div class="block-content">
14
+ <?php echo $this->getStateHtml() ?>
15
+ <?php if ($this->getLayer()->getState()->getFilters()): ?>
16
+ <div class="actions"><a href="<?php echo $this->getClearUrl() ?>"><?php echo $this->__('Clear All') ?></a></div>
17
+ <?php endif; ?>
18
+ <?php if($this->canShowOptions()): ?>
19
+ <dl id="narrow-by-list">
20
+ <?php $_filters = $this->getFilters() ?>
21
+ <?php foreach ($_filters as $_filter): ?>
22
+ <?php if($_filter->showFilter()): ?>
23
+ <dt><?php echo $this->__($_filter->getName()) ?></dt>
24
+ <dd><?php echo $_filter->getHtml() ?></dd>
25
+ <?php endif; ?>
26
+ <?php endforeach; ?>
27
+ </dl>
28
+ <script type="text/javascript">decorateDataList('narrow-by-list')</script>
29
+ <?php endif; ?>
30
+ </div>
31
+ </div>
32
+ <?php endif; ?>
app/design/frontend/base/default/template/searchspring_manager/product/list.phtml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $_productCollection=$this->getLoadedProductCollection();
3
+ $_results = $_productCollection->getResults();
4
+ $_merchandisingContent = $_results->getMerchandisingContent();
5
+ ?>
6
+ <?php if(!$_productCollection->count()): ?>
7
+ <p class="note-msg"><?php echo $this->__('There are no products matching the selection.') ?></p>
8
+ <?php else: ?>
9
+ <div class="category-products">
10
+ <?php echo $this->getToolbarHtml() ?>
11
+
12
+ <div class="merchandising" id="searchspring-merch_header"><?php echo isset($_merchandisingContent->header)?$_merchandisingContent->header:'' ?></div>
13
+ <div class="merchandising" id="searchspring-merch_banner"><?php echo isset($_merchandisingContent->banner)?$_merchandisingContent->banner:'' ?></div>
14
+
15
+ <?php // List mode ?>
16
+ <?php if($this->getMode()!='grid'): ?>
17
+ <ol class="products-list" id="products-list">
18
+ <?php echo $_results->getResults(); ?>
19
+ </ol>
20
+ <script type="text/javascript">decorateList('products-list', 'none-recursive')</script>
21
+
22
+ <?php else: ?>
23
+
24
+ <?php // Grid Mode ?>
25
+ <ul class="products-grid">
26
+ <?php echo $_results->getResults(); ?>
27
+ </ul>
28
+ <script type="text/javascript">decorateGeneric($$('ul.products-grid'), ['odd','even','first','last'])</script>
29
+ <?php endif; ?>
30
+
31
+ <div class="merchandising" id="searchspring-merch_footer"><?php echo isset($_merchandisingContent->footer)?$_merchandisingContent->footer:'' ?></div>
32
+
33
+ <div class="toolbar-bottom">
34
+ <?php echo $this->getToolbarHtml() ?>
35
+ </div>
36
+ </div>
37
+ <?php endif; ?>
app/design/frontend/base/default/template/searchspring_manager/product/list/toolbar.phtml ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 rwd_default
23
+ * @copyright Copyright (c) 2014 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
+ /**
29
+ * Product list toolbar
30
+ *
31
+ * @see Mage_Catalog_Block_Product_List_Toolbar
32
+ */
33
+ ?>
34
+
35
+ <?php if($this->getCollection()->getSize()): ?>
36
+ <div class="toolbar">
37
+ <?php if( $this->isExpanded() ): ?>
38
+ <div class="sorter">
39
+ <?php if( $this->isEnabledViewSwitcher() ): ?>
40
+ <p class="view-mode">
41
+ <?php $_modes = $this->getModes(); ?>
42
+ <?php if($_modes && count($_modes)>1): ?>
43
+ <label><?php echo $this->__('View as') ?></label>
44
+ <?php foreach ($this->getModes() as $_code=>$_label): ?>
45
+ <?php if($this->isModeActive($_code)): ?>
46
+ <strong title="<?php echo $_label ?>" class="<?php echo strtolower($_code); ?>"><?php echo $_label ?></strong>
47
+ <?php else: ?>
48
+ <a href="<?php echo $this->getModeUrl($_code) ?>" title="<?php echo $_label ?>" class="<?php echo strtolower($_code); ?>"><?php echo $_label ?></a>
49
+ <?php endif; ?>
50
+ <?php endforeach; ?>
51
+ <?php endif; ?>
52
+ </p>
53
+ <?php endif; ?>
54
+ <div class="sort-by">
55
+ <label><?php echo $this->__('Sort By') ?></label>
56
+ <select onchange="setLocation(this.value)" title="<?php echo $this->__('Sort By') ?>">
57
+ <?php foreach($this->getCollection()->getSorting() as $_sort): ?>
58
+ <option value="<?php echo $this->getOrderUrl($_sort->field, $_sort->direction) ?>"<?php if(isset($_sort->active) && $_sort->active): ?> selected="selected"<?php endif; ?>>
59
+ <?php echo $this->__($_sort->label) ?>
60
+ </option>
61
+ <?php endforeach; ?>
62
+ </select>
63
+ </div>
64
+ </div>
65
+ <?php endif; ?>
66
+ <div class="pager">
67
+ <div class="count-container">
68
+ <?php if($this->getLastPageNum()>1): ?>
69
+ <p class="amount amount--has-pages">
70
+ <?php echo $this->__('%s-%s of %s', $this->getFirstNum(), $this->getLastNum(), $this->getTotalNum()) ?>
71
+ </p>
72
+ <?php else: ?>
73
+ <p class="amount amount--no-pages">
74
+ <strong><?php echo $this->__('%s Item(s)', $this->getTotalNum()) ?></strong>
75
+ </p>
76
+ <?php endif; ?>
77
+
78
+ <div class="limiter">
79
+ <label><?php echo $this->__('Show') ?></label>
80
+ <select onchange="setLocation(this.value)" title="<?php echo $this->__('Results per page'); ?>">
81
+ <?php foreach ($this->getAvailableLimit() as $_key=>$_limit): ?>
82
+ <option value="<?php echo $this->getLimitUrl($_key) ?>"<?php if($this->isLimitCurrent($_key)): ?> selected="selected"<?php endif ?>>
83
+ <?php echo $_limit ?>
84
+ </option>
85
+ <?php endforeach; ?>
86
+ </select>
87
+ </div>
88
+ </div>
89
+
90
+ <?php echo $this->getPagerHtml() ?>
91
+
92
+ </div>
93
+ </div>
94
+ <?php endif ?>
app/etc/modules/SearchSpring_SearchSpringManager.xml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <SearchSpring_Manager>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ <depends>
8
+ <Mage_Catalog/>
9
+ </depends>
10
+ </SearchSpring_Manager>
11
+ </modules>
12
+ </config>
app/locale/en_US/template/email/searchspring_api_error.html ADDED
@@ -0,0 +1 @@
 
1
+ {{var response}}
package.xml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?xml version="1.0"?>
2
+ <package><name>SearchSpring_Manager</name><version>1.1.0</version><stability>stable</stability><license>SearchSpring Terms of Service</license><channel>community</channel><extends></extends><summary>Connects your Magento store to SearchSpring's subscription-based site search and category navigation services.</summary><description>SearchSpring improves the default Magento site search. Providing merchants with flexible, learning technology that enhances site search functionality, optimizes product results in real-time, allows for custom navigation, automates product SEO and provides fully configurable merchandising. Shoppers will find your products quickly and easily, generating repeat visits and increased sales!</description><notes>Bringing Live Indexing up to stable state</notes><authors><author><name>SearchSpring Development Team</name><user>searchspring</user><email>info@searchspring.com</email></author></authors><date>2014-09-30</date><time>11:46:04</time><compatible></compatible><dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies><contents><target name="mage"><dir name="app"><dir name="code"><dir name="community"><dir name="SearchSpring"><file name="ErrorCodes.php" hash="0cd381b82de093b811a2b6b38b5977f0"/><dir name="Manager"><file name="Collection.php" hash="0ebd621892bccd5d59ed3edcafada89b"/><file name="Iterator.php" hash="6f68effbe5452c1a6abd97cbf4b528ee"/><dir name="Block"><dir name="Layer"><file name="View.php" hash="ae69c24e04b03048cc2cd9b7cc436603"/><dir name="Filter"><file name="SearchSpringCategory.php" hash="39fc479e15275db637c227e596d040f5"/><file name="SearchSpringList.php" hash="743bb9cb690c9df3d74e9de2c206d020"/></dir></dir></dir><dir name="Builder"><file name="OperationBuilder.php" hash="d85ad0a561091d34b3b98efd95646962"/></dir><dir name="controllers"><file name="GenerateController.php" hash="895d4dc9abc957bd6fbe177df313b0e9"/></dir><dir name="Entity"><file name="IndexingRequestBody.php" hash="673c2bbc190f59c43204f7c155baa912"/><file name="OperationsCollection.php" hash="be19c06bb1be447bd57601e67ac87557"/><file name="RecordsCollection.php" hash="845b1967928f9a2d7fd33cecdfd517a2"/><file name="RequestBody.php" hash="4b6d7fd23f57527e9d53dd938d69a88d"/><file name="RequestParams.php" hash="f4778075264cd441c87e8de220fdd2a9"/><file name="SearchCollection.php" hash="e64543d4315c16d7a0717da8ec081e09"/><file name="SearchRequestBody.php" hash="bdfd9a2244ce1f9a7db8daca6e613176"/><file name="SearchResult.php" hash="863a720b8a837bd7e56e8a7fa781a1a1"/><dir name="SearchResult"><file name="Result.php" hash="ea4e36fe95e4c412af8d525a4628db5d"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="879a64f061de25cbd1292f4ead6c5b15"/><file name="composer.json" hash="6a36df77eee995788585f7a8a535fa27"/><file name="config.xml" hash="dd8330fdccb265d7f17b6f5c8340d2b2"/><file name="readme.md" hash="30d24e1aae89696090dcf22203802ad2"/><file name="system.xml" hash="89909040b5ecbf08dc4eaee9ff702411"/></dir><dir name="Factory"><file name="ApiFactory.php" hash="3532daf1450984a876c952f33ae76c82"/><file name="GeneratorFactory.php" hash="0920740294f9e8502519e2a4adb75ffc"/><file name="IndexingRequestBodyFactory.php" hash="10e0717f6d938a06a9cfc68f6ac1ceaf"/><file name="PricingFactory.php" hash="bbba11efb7a2fd6ebf8c47d896c365a2"/><file name="SearchRequestBodyFactory.php" hash="4eb409f25d480ca11b08f206f7f4facc"/></dir><dir name="Generator"><file name="ProductGenerator.php" hash="9459fddaba8a85cd300b1063c3f3e76e"/></dir><dir name="Handler"><file name="ApiErrorHandler.php" hash="bcce8cf5cd9f8b2f2107f004338728fd"/></dir><dir name="Helper"><file name="Data.php" hash="3a1cecacc1b62dc0285c8bb49cc426ba"/></dir><dir name="Model"><file name="Layer.php" hash="54afafde0cff2fcf2de2240bbb6cdb20"/><dir name="Catalog"><dir name="Category"><dir name="Attribute"><dir name="EnableSearchSpring"><file name="Backend.php" hash="12d9799f273951ca22717df93088e6bb"/><file name="Source.php" hash="03fad199c925e07fffd5e42f63cbb1bb"/></dir></dir></dir></dir><dir name="Layer"><dir name="Filter"><file name="SearchSpring.php" hash="128a0a569e7d7a4e19b74bc947a4c01a"/><file name="SearchSpringCategory.php" hash="6654bdad88ca43917dbe072b6dfb21f8"/><file name="SearchSpringCategoryItem.php" hash="2dc143031706d2c6a7f4481ef7434bf4"/><file name="SearchSpringItem.php" hash="459fae0c48c411a58ae885d56124b1f3"/></dir></dir><dir name="Observer"><file name="CategorySaveObserver.php" hash="9df9bb087d1718d699e46f9bb47fcdde"/><file name="LiveIndexer.php" hash="f1a68b949648a16cde42df0eb92cc806"/><file name="ProductSaveObserver.php" hash="5739f9536f160e51b111b43bb06c035a"/></dir></dir><dir name="Operation"><file name="Product.php" hash="d1b3114f1463205340604be47ce9e001"/><file name="ProductOperation.php" hash="5e6d97d2aab321d2204b0cc2a774ff9b"/><dir name="Product"><file name="SetCategories.php" hash="9d064f046551e1e12870013aad0ffcaa"/><file name="SetCoreFields.php" hash="7776df7b6c63f3e18c079944a63558d8"/><file name="SetFields.php" hash="a9eaaac10e7ca9ae1719b09c0cacf6a6"/><file name="SetId.php" hash="792f08d85b399d57afca103e9d0eabd1"/><file name="SetImages.php" hash="964cd029a1a7bbffb21039bffb5ee68f"/><file name="SetOptions.php" hash="3db65aee252c0eb6a5c65eac1b641bfb"/><file name="SetPricing.php" hash="a2bb94df706c1c62c5b1cbe26d8afdf6"/><file name="SetRatings.php" hash="d04ad0e2f5e1820b4b8b94880a83aef7"/></dir></dir><dir name="Provider"><file name="ProductCollectionProvider.php" hash="e639e1667ed0f2bc59f2272ad36d3107"/><dir name="ProductCollection"><file name="FeedProvider.php" hash="79f2700320c0f48342f63075bb82ff95"/><file name="ProductProvider.php" hash="bdb5b820343e060bb7cf2bea807a7607"/></dir></dir><dir name="Request"><file name="JSON.php" hash="d88633c88c49d97abf5e92c7c71fe904"/></dir><dir name="Service"><dir name="SearchSpring"><file name="ApiAdapter.php" hash="819f9d17cf64603b360c66ad1e12ddf0"/><file name="IndexingApiAdapter.php" hash="a5d324c30d82d335f745ec601b5db243"/><file name="SearchApiAdapter.php" hash="4de843465c9eea8646402bcbd289aef9"/></dir></dir><dir name="Strategy"><file name="PricingStrategy.php" hash="ee021066a76db7fc2710e329daed1a4b"/><dir name="Pricing"><file name="BundleStrategy.php" hash="df2aee59e4a6ff19acf13bbc5f9ad5b9"/><file name="ConfigurableStrategy.php" hash="403e8298c4799ba2b7701255db86a941"/><file name="GroupedStrategy.php" hash="383c482098f502d6cde5dd9ed5cf9e13"/><file name="SimpleStrategy.php" hash="a1b9e7bae38c2def28a7b0a9e6fd456e"/><file name="Strategy.php" hash="78ffa36dd4f52d725178e3cbe716bc24"/></dir></dir><dir name="String"><file name="Sanitizer.php" hash="e89fa5f86714621ea554aea9aa3f796c"/></dir><dir name="Transformer"><file name="ProductCollectionToRecordCollectionTransformer.php" hash="92b10f9ffc3999193d2733ec0dabf276"/></dir><dir name="Validator"><file name="ProductValidator.php" hash="6af19c3c4b696a5e28d8d61aa73cabf4"/></dir><dir name="VarienObject"><file name="Data.php" hash="44f184dabbf0db1958d1f922af92f6a8"/></dir><dir name="Writer"><file name="ProductWriter.php" hash="c7b218d215e68b5e3076ef654a41b141"/><dir name="Product"><file name="FileWriter.php" hash="b296be56f236635afc20e817e6277c80"/><file name="ResponseWriter.php" hash="c1af0cc22a9e823ec07175e2d8e34c9f"/><dir name="Params"><file name="FileWriterParams.php" hash="56cd0166b08f8df5750ad5c98484603a"/><file name="ResponseWriterParams.php" hash="bf66753a5952a78c0049f69ccaa6e74d"/></dir></dir></dir></dir></dir></dir></dir><dir name="design"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="searchspring_manager.xml" hash="32227434f5770ddfd93de42d7feeff71"/></dir><dir name="template"><dir name="searchspring_manager"><dir name="layer"><file name="category_filter.phtml" hash="a9dd8c95a1d63b514cc8e9c5939168c5"/><file name="filter.phtml" hash="79888aac8f72d3597f2980d471237d64"/><file name="state.phtml" hash="168546af0f48a42c31c5a5d66b1de1c3"/><file name="view.phtml" hash="509776f71bc643839ade8d7e025764cb"/></dir><dir name="product"><file name="list.phtml" hash="9849b76e4fefac8ce181b428b5c6a148"/><dir name="list"><file name="toolbar.phtml" hash="aa2235708f7e861c63ba3e3e33ec041f"/></dir></dir></dir></dir></dir></dir></dir></dir><dir name="etc"><dir name="modules"><file name="SearchSpring_SearchSpringManager.xml" hash="613ec81afc8d858f60dee96d91719c09"/></dir></dir><dir name="locale"><dir name="en_US"><dir name="template"><dir name="email"><file name="searchspring_api_error.html" hash="20bc8d923f96ca22dd2fe8cd876c49fe"/></dir></dir></dir></dir></dir><dir name="skin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="searchspring"><dir name="css"><file name="styles.css" hash="95f1ec4a442f0e2239f9a2db9e6e517c"/></dir></dir></dir></dir></dir></dir></target></contents></package>
skin/frontend/base/default/searchspring/css/styles.css ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ /** Add Styling Override Here **/
2
+
3
+ .block-layered-nav dl dd ol > li.active > a {
4
+ font-weight: bold;
5
+ }
6
+
7
+ .ss-category_breadcrumb.ss-current_category {
8
+ font-weight: bold;
9
+ color: #2288bb;
10
+ }