Swiftype_Swiftype - Version 1.0.0

Version Notes

Changelog:

* Bumped to 1.0.0 version in anticipation of Magento Connect initial release.

Download this release

Release Info

Developer Swiftype
Extension Swiftype_Swiftype
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/community/Swiftype/Swiftype/Block/Ajax/Suggest.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Block_Ajax_Suggest
4
+ extends Mage_Core_Block_Template_Facade
5
+ {
6
+
7
+ }
app/code/community/Swiftype/Swiftype/Block/Autocomplete.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Block_Autocomplete
4
+ extends Mage_CatalogSearch_Block_Autocomplete
5
+ {
6
+ final public function getSuggestData()
7
+ {
8
+ $helper = Mage::helper('swiftype');
9
+ /* @var $helper Swiftype_Swiftype_Helper_Data */
10
+
11
+ if ($helper->getUseSwiftype()) {
12
+ $counter = 0;
13
+ $data = array();
14
+
15
+ foreach ($helper->getSuggestedProducts() as $product) {
16
+ /* @var $product Mage_Catalog_Model_Product */
17
+ $data[] = array(
18
+ 'title' => $product->getName(),
19
+ 'row_class' => (++$counter)%2?'odd':'even',
20
+ 'num_of_results' => ''
21
+ );
22
+ }
23
+
24
+ return $data;
25
+ } else {
26
+ return parent::getSuggestData();
27
+ }
28
+ }
29
+ }
app/code/community/Swiftype/Swiftype/Helper/Data.php ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Helper_Data
4
+ extends Mage_Core_Helper_Abstract
5
+ {
6
+ const CATALOG_SEARCH_ENGINE_SWIFTYPE = 'swiftype/fulltext_engine';
7
+ const CATALOG_SEARCH_ENGINE_DEFAULT = 'catalogsearch/fulltext_engine';
8
+
9
+ const API_URL = 'https://api.swiftype.com/api/v1';
10
+
11
+ const DOCUMENT_TYPE = 'product';
12
+ const DOCUMENT_FIELD_TYPE_STRING = 'string';
13
+ const DOCUMENT_FIELD_TYPE_TEXT = 'text';
14
+ const DOCUMENT_FIELD_TYPE_ENUM = 'enum';
15
+ const DOCUMENT_FIELD_TYPE_INTEGER = 'integer';
16
+ const DOCUMENT_FIELD_TYPE_FLOAT = 'float';
17
+ const DOCUMENT_FIELD_TYPE_DATE = 'date';
18
+ const DOCUMENT_FIELD_TYPE_LOCATION = 'location';
19
+
20
+
21
+ final public function handleException(Exception $exception, $throw = false, $log = true)
22
+ {
23
+ if ($log) {
24
+ $this->getSwiftypeLogger($exception->getMessage());
25
+ }
26
+
27
+ if (Mage::getIsDeveloperMode() || $throw) {
28
+ throw $exception;
29
+ }
30
+ }
31
+
32
+
33
+ final public function onClickAutoselect($productId, $queryText)
34
+ {
35
+ $client = new Zend_Http_Client(self::API_URL . '/public/analytics/pas');
36
+ $client->setParameterGet('prefix', $queryText);
37
+ $client->setParameterGet('engine_key', $this->getEngineSlug());
38
+ $client->setParameterGet('entry_id', $productId);
39
+ $client->setParameterGet('document_type_id', 'product');
40
+ $client->request(Zend_Http_Client::GET);
41
+ }
42
+ /**
43
+ * Autocomplete click tracking.
44
+ *
45
+ * This function is automatically added to clicks coming from autocomplete
46
+ * product clicks.
47
+ *
48
+ */
49
+ final public function getOnClickAutoselect(Mage_Catalog_Model_Product $product)
50
+ {
51
+ if ($onClickAutoselectUrl = $this->_getOnClickAutoselectUrl($product)) {
52
+ $onClickAutoselect = "new Ajax.Request('$onClickAutoselectUrl', {method: 'get', asynchronous: false})";
53
+ }
54
+ return isset($onClickAutoselect) ? $onClickAutoselect : '';
55
+ }
56
+
57
+ final protected function _getOnClickAutoselectUrl(Mage_Catalog_Model_Product $product)
58
+ {
59
+ if ($queryText = Mage::helper('catalogsearch')->getQueryText()) {
60
+ $onClickAutoselectUrl = Mage::getUrl('swiftype/analytics/onclickautoselect', array(
61
+ 'id' => $product->getId(),
62
+ 'q' => $queryText
63
+ ));
64
+ }
65
+ return isset($onClickAutoselectUrl) ? $onClickAutoselectUrl : null;
66
+ }
67
+
68
+ /**
69
+ * Search result click tracking.
70
+ *
71
+ * If you want to use it in search result pages, you need to
72
+ * include this in the HTML elements you want to track clicks on.
73
+ *
74
+ * In practice, this will be product links in the catalog/product/list.phtml
75
+ * file that your Package/Theme uses. To locate this file, turn on Template
76
+ * Hints in the System Configuration, and perform a test search. It will
77
+ * highlight the location of catalog/product/list.phtml
78
+ *
79
+ * @param Mage_Catalog_Model_Product $product
80
+ * @return string Contents of onClick if page is search result, empty string otherwise.
81
+ */
82
+ final public function getOnClick(Mage_Catalog_Model_Product $product)
83
+ {
84
+ $onClick = '';
85
+
86
+ if ($onClickUrl = $this->_getOnClickUrl($product)) {
87
+ $onClick = "new Ajax.Request('$onClickUrl', {method: 'get', asynchronous: false})";
88
+ }
89
+
90
+ return $onClick;
91
+ }
92
+
93
+ /**
94
+ * Get URL for Click Tracking
95
+ *
96
+ * @param Mage_Catalog_Model_Product $product
97
+ * @return string|null onClick URL if page is search result, null otherwise.
98
+ */
99
+ final protected function _getOnClickUrl(Mage_Catalog_Model_Product $product)
100
+ {
101
+ $onClickUrl = null;
102
+ $queryText = Mage::helper('catalogsearch')->getQueryText();
103
+
104
+ if (!empty($queryText)) {
105
+
106
+ $onClickUrl = Mage::getUrl('swiftype/analytics/logclickthrough', array(
107
+ 'id' => $product->getId(),
108
+ 'q' => $queryText
109
+ ));
110
+ }
111
+
112
+ return $onClickUrl;
113
+ }
114
+
115
+ /**
116
+ *
117
+ * @param int $productId
118
+ * @param string $query
119
+ */
120
+ final public function logClickthrough($productId, $query)
121
+ {
122
+ $client = $this->getSwiftypeClient(array(
123
+ 'uri' => array(
124
+ 'engines' => $this->getEngineSlug(),
125
+ 'document_types' => self::DOCUMENT_TYPE,
126
+ 'analytics' => 'log_clickthrough.json'
127
+ ),
128
+ 'raw_data' => array(
129
+ 'enctype' => 'application/json',
130
+ 'data' => Zend_Json::encode(array(
131
+ 'auth_token' => $this->getAuthToken(),
132
+ 'id' => (int)$productId,
133
+ 'q' => (string)$query
134
+ ))
135
+ )
136
+ ));
137
+
138
+ $response = $client->request(Zend_Http_Client::POST);
139
+
140
+ if ($response->getStatus() != 200) {
141
+ Mage::throwException($response->getStatus());
142
+ }
143
+ }
144
+
145
+ final public function getCacheKey()
146
+ {
147
+ $requestProcessor = Mage::getSingleton('swiftype/request_processor');
148
+ /* @var $requestProcessor Swiftype_Swiftype_Model_Request_Processor */
149
+
150
+ return $requestProcessor->getCacheKey();
151
+ }
152
+
153
+ final public function canCacheSwiftypeAutocomplete(Mage_Core_Controller_Request_Http $request = null)
154
+ {
155
+ if (!$this->getUseSwiftypeAutocomplete()
156
+ || !Mage::app()->useCache(Swiftype_Swiftype_Model_Request_Processor::CACHE_KEY_PREFIX)) {
157
+ return false;
158
+ }
159
+
160
+ if (!$request) {
161
+ $request = Mage::app()->getRequest();
162
+ /* @var $request Mage_Core_Controller_Request_Http */
163
+ }
164
+
165
+ if ($request->getModuleName() == 'catalogsearch'
166
+ && $request->getControllerName() == 'ajax'
167
+ && $request->getActionName() == 'suggest') {
168
+ return true;
169
+ } else {
170
+ return false;
171
+ }
172
+ }
173
+
174
+ /**
175
+ *
176
+ * @return array
177
+ */
178
+ final public function getSuggestedProducts()
179
+ {
180
+ $productsAsArray = array();
181
+ $products = Mage::getModel('catalog/product')->getCollection();
182
+ /* @var $products Mage_Catalog_Model_Resource_Product_Collection */
183
+
184
+ $products->addFieldToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
185
+ $products->addFieldToFilter('visibility', array('in' => array(
186
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
187
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH
188
+ )));
189
+
190
+ $queryText = Mage::helper('catalogsearch')->getQueryText();
191
+ $queryText = !empty($queryText) ? $queryText : null;
192
+
193
+ if ($this->getUseSwiftype() && $queryText) {
194
+ $client = $this->getSwiftypeClient(array(
195
+ 'uri' => array(
196
+ 'public' => 'engines',
197
+ 'suggest' => null
198
+ ),
199
+ 'get' => array(
200
+ 'q' => $queryText,
201
+ 'engine_key' => $this->getEngineKey()
202
+ )
203
+ ));
204
+
205
+ $response = $client->request(Zend_Http_Client::GET);
206
+
207
+ if ($response->getStatus() != 200) {
208
+ Mage::throwException($response->getMessage());
209
+ }
210
+
211
+ $records = Zend_Json::decode($response->getBody(), Zend_Json::TYPE_OBJECT);
212
+ $productIds = array();
213
+
214
+ foreach ($records->records->product as $product) {
215
+ $productIds[] = $product->product_id;
216
+ }
217
+
218
+ if (!empty($productIds)) {
219
+ $products = $products->addAttributeToSelect('*')
220
+ ->addFieldToFilter('entity_id', array('in' => $productIds));
221
+
222
+ foreach ($productIds as $productId) {
223
+ if ($product = $products->getItemById($productId)) {
224
+ $productsAsArray[$productId] = $product;
225
+ }
226
+ }
227
+ }
228
+
229
+ }
230
+
231
+ return array_slice($productsAsArray, 0, $this->getAutocompleteLimit());
232
+ }
233
+
234
+ /**
235
+ * Return HTTP client ready to interact with Swiftype
236
+ *
237
+ * @param mixed $store
238
+ * @param array $params
239
+ * @return Zend_Http_Client $client
240
+ */
241
+ final public function getSwiftypeClient(array $params = array())
242
+ {
243
+ $uri = Swiftype_Swiftype_Helper_Data::API_URL;
244
+
245
+ foreach ($params['uri'] as $name => $value) {
246
+ $uri .= "/$name";
247
+ if ($value) {
248
+ $uri .= "/$value";
249
+ }
250
+ }
251
+
252
+ $client = new Zend_Http_Client($uri);
253
+
254
+ if (isset($params['raw_data'])) {
255
+ $client->setRawData($params['raw_data']['data'], $params['raw_data']['enctype']);
256
+ }
257
+
258
+ if (isset($params['get'])) {
259
+ foreach ($params['get'] as $name => $value) {
260
+ $client->setParameterGet($name, $value);
261
+ }
262
+ }
263
+
264
+ return $client;
265
+ }
266
+
267
+ /**
268
+ * Logger for Swiftype-related events
269
+ */
270
+ final public function getSwiftypeLogger($message)
271
+ {
272
+ $filename = 'swiftype-'.date('m-d-y').'.log';
273
+ $message = is_string($message) ? "swiftype\\".$message : $message;
274
+ Mage::log($message, null, $filename, true);
275
+ }
276
+
277
+ final public function getUseSwiftype($store = null)
278
+ {
279
+ if (Mage::getStoreConfig('catalog/search/engine', $store) == self::CATALOG_SEARCH_ENGINE_SWIFTYPE) {
280
+ return true;
281
+ } else {
282
+ return false;
283
+ }
284
+ }
285
+
286
+ final public function getDocumentType()
287
+ {
288
+ return self::DOCUMENT_TYPE;
289
+ }
290
+
291
+ final public function getAuthToken($store = null)
292
+ {
293
+ return $this->getApiKey($store);
294
+ }
295
+
296
+ final public function getApiKey($store = null)
297
+ {
298
+ return Mage::getStoreConfig('catalog/search/swiftype_api_key', $store);
299
+ }
300
+
301
+ final public function getEngineSlug($store = null)
302
+ {
303
+ return Mage::getStoreConfig('catalog/search/swiftype_engine_slug', $store);
304
+ }
305
+
306
+ final public function getEngineKey($store = null)
307
+ {
308
+ return Mage::getStoreConfig('catalog/search/swiftype_engine_key', $store);
309
+ }
310
+
311
+ final public function getAutocompleteLimit($store = null)
312
+ {
313
+ return Mage::getStoreConfig('catalog/search/swiftype_autocomplete_limit', $store);
314
+ }
315
+
316
+ final public function getUseSwiftypeAutocomplete($store = null)
317
+ {
318
+ if (!$this->getUseSwiftype()) {
319
+ return false;
320
+ }
321
+
322
+ return Mage::getStoreConfig('catalog/search/swiftype_autocomplete', $store);
323
+ }
324
+ }
app/code/community/Swiftype/Swiftype/Helper/Log.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Helper_Log
4
+ extends Mage_Core_Helper_Abstract
5
+ {
6
+ final protected function _log($key, $message)
7
+ {
8
+ $logDir = Mage::getBaseDir('log') . DS . 'swiftype';
9
+ if (!is_readable($logDir)) {
10
+ mkdir($logDir, 0777);
11
+ }
12
+ Mage::log("$key: $message", null, 'swiftype/swiftype-' . date('d-m-Y') . '.log', true);
13
+ }
14
+
15
+ final protected function _getLogKey($storeId, array $swiftypeApiUri)
16
+ {
17
+ $logKey = '';
18
+ foreach ($swiftypeApiUri as $name => $value) {
19
+ $logKey .= "/$name";
20
+ if ($value) {
21
+ $logKey .= "/$value";
22
+ }
23
+ }
24
+ return "[$storeId] $logKey";
25
+ }
26
+
27
+ final public function logIndexRequest($storeId, array $swiftypeApiParameters, $productIds)
28
+ {
29
+ $this->_log($this->_getLogKey($storeId, $swiftypeApiParameters['uri']), 'Product IDs: ' . implode(',', $productIds));
30
+ }
31
+
32
+ final public function logIndexResponse($storeId, array $swiftypeApiParameters, Zend_Http_Response $swiftypeApiResponse)
33
+ {
34
+ $this->_log($this->_getLogKey($storeId, $swiftypeApiParameters['uri']), 'Status: ' . $swiftypeApiResponse->getStatus());
35
+ }
36
+
37
+ final public function logDeleteResponse($storeId, array $swiftypeApiParameters, Zend_Http_Response $swiftypeApiResponse)
38
+ {
39
+ $this->_log($this->_getLogKey($storeId, $swiftypeApiParameters['uri']), 'Method: Delete, Status: ' . $swiftypeApiResponse->getStatus());
40
+ }
41
+
42
+ final public function logSearchException($storeId, array $swiftypeApiParameters = array('uri' => array('search' => null)), $exceptionMessage)
43
+ {
44
+ $this->_log($this->_getLogKey($storeId, $swiftypeApiParameters['uri']), $exceptionMessage);
45
+ }
46
+ }
app/code/community/Swiftype/Swiftype/Model/Observer/Adminhtml.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class Swiftype_Swiftype_Model_Observer_Adminhtml
4
+ {
5
+ /**
6
+ * Add Swiftype Document Field Types to Attribute Edit Form
7
+ *
8
+ * @param Varien_Event_Observer $observer
9
+ * @return \Swiftype_Swiftype_Model_Observer_Adminhtml
10
+ */
11
+ final public function adminhtmlCatalogProductAttributeEditPrepareForm(Varien_Event_Observer $observer)
12
+ {
13
+ $form = $observer->getEvent()->getForm();
14
+ /* @var $form Varien_Data_Form */
15
+
16
+ $fieldset = $form->addFieldset('swiftype_fieldset',
17
+ array('legend' => 'Swiftype Properties')
18
+ );
19
+ $fieldset->addField('swiftype_document_field_type', 'select', array(
20
+ 'name' => 'swiftype_document_field_type',
21
+ 'label' => 'Document Field Type',
22
+ 'title' => 'Document Field Type',
23
+ 'note' => 'See <a href="https://swiftype.com/documentation/overview#field_types" target="_blank">Document Field Types</a>.',
24
+ 'values' => array(
25
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_STRING, 'label' => 'String'),
26
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_TEXT, 'label' => 'Text'),
27
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_ENUM, 'label' => 'Enum'),
28
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_INTEGER, 'label' => 'Integer'),
29
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_FLOAT, 'label' => 'Float'),
30
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_DATE, 'label' => 'Date'),
31
+ array('value' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_LOCATION, 'label' => 'Location')
32
+ ),
33
+ ));
34
+ return $this;
35
+ }
36
+ }
app/code/community/Swiftype/Swiftype/Model/Observer/Frontend.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Model_Observer_Frontend
4
+ {
5
+ /**
6
+ * @return Swiftype_Swiftype_Helper_Data
7
+ */
8
+ final protected function _getHelper()
9
+ {
10
+ return Mage::helper('swiftype');
11
+ }
12
+
13
+ /**
14
+ *
15
+ * @param Varien_Event_Observer $observer
16
+ * @return Swiftype_Swiftype_Model_Observer_Frontend
17
+ */
18
+ final public function cacheSwiftypeAutoComplete(Varien_Event_Observer $observer)
19
+ {
20
+ if ($this->_getHelper()->canCacheSwiftypeAutocomplete()) {
21
+ $buffer = ob_get_clean();
22
+
23
+ $cache = Mage::app()->getCache();
24
+ $cacheKey = $this->_getHelper()->getCacheKey();
25
+
26
+ if (!empty($buffer)) {
27
+ $cache->save($buffer, $cacheKey, array(Swiftype_Swiftype_Model_Request_Processor::CACHE_TAG));
28
+ }
29
+
30
+ echo $buffer;
31
+ }
32
+
33
+ return $this;
34
+ }
35
+ }
app/code/community/Swiftype/Swiftype/Model/Observer/Global.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Model_Observer_Global
4
+ {
5
+ final public function cleanSwiftypeAutocompleteCache(Varien_Event_Observer $observer)
6
+ {
7
+ Mage::app()->getCache()->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,
8
+ array(Swiftype_Swiftype_Model_Request_Processor::CACHE_TAG));
9
+ }
10
+
11
+ final public function clearCatalogsearchResults(Varien_Event_Observer $observer)
12
+ {
13
+ $configData = $observer->getEvent()->getConfigData();
14
+ /* @var $configData Mage_Core_Model_Config_Data */
15
+ $path = $configData->getPath();
16
+ $savedValue = $configData->getValue();
17
+ $originalValue = Mage::getStoreConfig($path);
18
+
19
+ if ($configData->getPath() == $path) {
20
+ if ($savedValue != $originalValue) {
21
+ if ($savedValue == Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_SWIFTYPE
22
+ || $originalValue == Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_SWIFTYPE) {
23
+ $indexer = Mage::getModel('index/indexer');
24
+ /* @var $indexer Mage_Index_Model_Indexer */
25
+ $process = $indexer->getProcessByCode('catalogsearch_fulltext');
26
+ $process->changeStatus(Mage_Index_Model_Process::STATUS_REQUIRE_REINDEX);
27
+ $this->_clearCatalogsearchResults();
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ final protected function _clearCatalogsearchResults()
34
+ {
35
+ $resource = Mage::getModel('core/resource');
36
+ /* @var $resource Mage_Core_Model_Resource */
37
+ $adapter = $resource->getConnection('core_write');
38
+ /* @var $adapter Varien_Db_Adapter_Interface */
39
+ $adapter->beginTransaction();
40
+ try {
41
+ $adapter->delete($resource->getTableName('catalogsearch/result'));
42
+ $adapter->commit();
43
+ } catch (Exception $ex) {
44
+ Mage::helper('swiftype')->handleException($ex);
45
+ }
46
+ }
47
+ }
app/code/community/Swiftype/Swiftype/Model/Request/Processor.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Model_Request_Processor
4
+ {
5
+ const CACHE_KEY_PREFIX = 'swiftype_autocomplete';
6
+ const CACHE_TAG = 'SWIFTYPE_AUTOCOMPLETE';
7
+
8
+ final public function extractContent($content = false)
9
+ {
10
+ $cache = Mage::app()->getCache();
11
+ $content = $cache->load($this->getCacheKey());
12
+
13
+ if (!$content) {
14
+ ob_start();
15
+ }
16
+
17
+ return $content;
18
+ }
19
+
20
+ /**
21
+ *
22
+ * @param Mage_Core_Controller_Request_Http $request
23
+ * @return null|string
24
+ */
25
+ final public function getCacheKey(Mage_Core_Controller_Request_Http $request = null)
26
+ {
27
+ if (!$request) {
28
+ $request = Mage::app()->getRequest();
29
+ /* @var $request Mage_Core_Controller_Request_Http */
30
+ }
31
+
32
+ return self::CACHE_KEY_PREFIX . '_' . md5($request->getRequestUri());
33
+ }
34
+ }
app/code/community/Swiftype/Swiftype/Model/Resource/Fulltext.php ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Model_Resource_Fulltext
4
+ extends Mage_CatalogSearch_Model_Resource_Fulltext
5
+ {
6
+ const PER_PAGE = 20;
7
+
8
+ final public function cleanIndex($storeId = null, $productId = null)
9
+ {
10
+ if ($storeId) {
11
+ $this->_setStoreEngine($storeId);
12
+ parent::cleanIndex($storeId, $productId);
13
+ } else {
14
+ foreach (Mage::app()->getStores() as $store) {
15
+ $this->cleanIndex($store->getId(), $productId);
16
+ }
17
+ }
18
+
19
+ return $this;
20
+ }
21
+
22
+ final protected function _rebuildStoreIndex($storeId, $productIds = null)
23
+ {
24
+ $this->_setStoreEngine($storeId);
25
+
26
+ return parent::_rebuildStoreIndex($storeId, $productIds);
27
+ }
28
+
29
+ final protected function _setStoreEngine($storeId)
30
+ {
31
+ $this->_engine = $this->_getStoreEngine($storeId);
32
+ }
33
+
34
+ final protected function _getStoreEngine($storeId)
35
+ {
36
+ $engine = $this->_getHelper()->getUseSwiftype($storeId) ?
37
+ Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_SWIFTYPE
38
+ : Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_DEFAULT;
39
+
40
+ return Mage::getResourceSingleton($engine);
41
+ }
42
+
43
+ final protected function _getSearchableProducts($storeId, array $staticFields, $productIds = null, $lastProductId = 0, $limit = 100)
44
+ {
45
+ if ($this->_getHelper()->getUseSwiftype($storeId)) {
46
+ $limit = null;
47
+ }
48
+
49
+ return parent::_getSearchableProducts($storeId, $staticFields, $productIds, $lastProductId, $limit);
50
+ }
51
+
52
+ /**
53
+ * @return Swiftype_Swiftype_Helper_Data
54
+ */
55
+ final protected function _getHelper()
56
+ {
57
+ return Mage::helper('swiftype');
58
+ }
59
+
60
+ public function prepareResult($object, $queryText, $query)
61
+ {
62
+ $storeId = $query->getStoreId();
63
+
64
+ if (!$this->_getHelper()->getUseSwiftype($storeId)) {
65
+ return parent::prepareResult($object, $queryText, $query);
66
+ } else {
67
+ $this->_updateSearchResults($query, $this->_prepareResults($this->_getResults($query), $query));
68
+ $query->setIsProcessed(1);
69
+ return $this;
70
+ }
71
+ }
72
+
73
+ /**
74
+ *
75
+ * @param Mage_CatalogSearch_Model_Query $query
76
+ * @return array
77
+ */
78
+ final protected function _getResults(Mage_CatalogSearch_Model_Query $query)
79
+ {
80
+ $results = array();
81
+ $result = $this->_getResult($query);
82
+ $results = $result->records->product;
83
+
84
+ if ($result->info->product->num_pages > 1) {
85
+ for ($page = 2; $page <= $result->info->product->num_pages; $page++) {
86
+ $result = $this->_getResult($query, $page);
87
+ $results = array_merge($results, $result->records->product);
88
+ }
89
+ }
90
+
91
+ return $results;
92
+ }
93
+
94
+ /**
95
+ *
96
+ * @param Mage_CatalogSearch_Model_Query $query
97
+ * @param int $page
98
+ * @return array
99
+ */
100
+ final protected function _getResult(Mage_CatalogSearch_Model_Query $query, $page = 1)
101
+ {
102
+ $storeId = $query->getStoreId();
103
+
104
+ $swiftypeApiParameters = array(
105
+ 'uri' => array(
106
+ 'engines' => $this->_getHelper()->getEngineSlug($storeId),
107
+ 'document_types' => Swiftype_Swiftype_Helper_Data::DOCUMENT_TYPE,
108
+ 'search' => null
109
+ ),
110
+ 'get' => array(
111
+ 'auth_token' => $this->_getHelper()->getAuthToken($storeId),
112
+ 'q' => $this->_getQueryText($query),
113
+ 'per_page' => self::PER_PAGE,
114
+ 'page' => $page
115
+ )
116
+ );
117
+
118
+ $swiftypeApiClient = $this->_getHelper()->getSwiftypeClient($swiftypeApiParameters);
119
+ $swiftypeApiResponse = $swiftypeApiClient->request(Zend_Http_Client::GET);
120
+
121
+ if ($swiftypeApiResponse->getStatus() == 200) {
122
+ return Zend_Json::decode($swiftypeApiResponse->getBody(), Zend_Json::TYPE_OBJECT);
123
+ } else {
124
+ Mage::helper('swiftype/log')->logSearchException($storeId, $swiftypeApiParameters, 'Status: ' . $swiftypeApiResponse->getStatus());
125
+ }
126
+ }
127
+
128
+ /**
129
+ *
130
+ * @param array $results
131
+ * @param Mage_CatalogSearch_Model_Query $query
132
+ * @return array
133
+ */
134
+ final protected function _prepareResults(array $results, Mage_CatalogSearch_Model_Query $query)
135
+ {
136
+ $preparedResults = array();
137
+ $resultCount = count($results);
138
+ $customRelevance = false;
139
+
140
+ foreach ($results as $result) {
141
+ /* @var $result stdClass */
142
+ $relevance = isset($result->_score) ? $result->_score : 0;
143
+ // Indicates custom ordering
144
+ if (!$relevance) {
145
+ $customRelevance = true;
146
+ break;
147
+ }
148
+ }
149
+
150
+ foreach ($results as $result) {
151
+ /* @var $result stdClass */
152
+ $relevance = isset($result->_score) ? $result->_score : 0;
153
+
154
+ if ($customRelevance) {
155
+ $relevance = $resultCount;
156
+ $resultCount--;
157
+ }
158
+
159
+ $preparedResults[] = array(
160
+ 'product_id' => (int)$result->product_id,
161
+ 'query_id' => (int)$query->getId(),
162
+ 'relevance' => $relevance
163
+ );
164
+ }
165
+
166
+ return $preparedResults;
167
+ }
168
+
169
+ final protected function _updateSearchResults(Mage_CatalogSearch_Model_Query $query, array $results)
170
+ {
171
+ $adapter = $this->_getWriteAdapter();
172
+ $table = $this->getTable('catalogsearch/result');
173
+
174
+ $adapter->beginTransaction();
175
+ $adapter->delete($table, array('query_id = ?' => $query->getId()));
176
+
177
+ $failedResults = array();
178
+ $relevance = array('relevance');
179
+ foreach ($results as $result) {
180
+ try {
181
+ $adapter->insertOnDuplicate($table, $result, $relevance);
182
+ } catch (Exception $exception) {
183
+ if (!isset($failedResults[$exception->getMessage()])) {
184
+ $failedResults[$exception->getMessage()] = array();
185
+ }
186
+ $failedResults[$exception->getMessage()][] = $result['product_id'];
187
+ }
188
+ }
189
+ $adapter->commit();
190
+
191
+ if(!empty($failedResults)) {
192
+ Mage::helper('swiftype/log')->logSearchException($query->getStoreId(), null, Zend_Json::encode($failedResults));
193
+ }
194
+ }
195
+
196
+ final protected function _getQueryText(Mage_CatalogSearch_Model_Query $query)
197
+ {
198
+ $queryText = $query->getQueryText();
199
+
200
+ if ($query->getSynonymFor()) {
201
+ $queryText = $query->getSynonymFor();
202
+ }
203
+
204
+ return $queryText;
205
+ }
206
+ }
app/code/community/Swiftype/Swiftype/Model/Resource/Fulltext/Engine.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_Model_Resource_Fulltext_Engine
4
+ extends Mage_CatalogSearch_Model_Resource_Fulltext_Engine
5
+ {
6
+ private $_documentFieldTypes = array();
7
+
8
+ /**
9
+ * Create/update Documents in Swiftype
10
+ *
11
+ * @param int $storeId
12
+ * @param array $entityIndexes
13
+ * @param string $entity
14
+ * @return \Swiftype_Swiftype_Model_Resource_Fulltext_Engine
15
+ */
16
+ final public function saveEntityIndexes($storeId, $entityIndexes, $entity = 'product')
17
+ {
18
+ if ($entity == 'product') {
19
+ $bulkCreateOrUpdate = new stdClass();
20
+ $bulkCreateOrUpdate->auth_token = $this->_getHelper()->getAuthToken($storeId);
21
+
22
+ foreach ($entityIndexes as $entityId => $index) {
23
+ $document = new stdClass();
24
+ $document->external_id = $entityId;
25
+
26
+ foreach ($index as $attributeCode => $value) {
27
+ $document->fields[] = array(
28
+ 'name' => $attributeCode,
29
+ 'value' => $value,
30
+ 'type' => $this->_getDocumentFieldType($attributeCode)
31
+ );
32
+ }
33
+ $document->fields[] = array(
34
+ 'name' => 'product_id',
35
+ 'value' => $entityId,
36
+ 'type' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_INTEGER
37
+ );
38
+ $bulkCreateOrUpdate->documents[] = $document;
39
+ }
40
+
41
+ $swiftypeApiParameters = array(
42
+ 'uri' => array(
43
+ 'engines' => $this->_getHelper()->getEngineSlug($storeId),
44
+ 'document_types' => $entity,
45
+ 'documents' => 'bulk_create_or_update_verbose'
46
+ ),
47
+ 'raw_data' => array(
48
+ 'enctype' => 'application/json',
49
+ 'data' => Zend_Json::encode($bulkCreateOrUpdate)
50
+ )
51
+ );
52
+
53
+ $swiftypeApiClient = $this->_getHelper()->getSwiftypeClient($swiftypeApiParameters);
54
+ $swiftypeApiResponse = $swiftypeApiClient->request(Zend_Http_Client::POST);
55
+
56
+ if ($swiftypeApiResponse->getStatus() == 200) {
57
+ $responseMessage = Zend_Json::decode($swiftypeApiResponse->getBody());
58
+ if ($responseMessage[0] !== true) {
59
+ $this->_logError($storeId, $swiftypeApiParameters, array_keys($entityIndexes), $swiftypeApiResponse);
60
+ }
61
+ } else {
62
+ $this->_logError($storeId, $swiftypeApiParameters, array_keys($entityIndexes), $swiftypeApiResponse);
63
+ }
64
+ }
65
+
66
+ return $this;
67
+ }
68
+
69
+ final protected function _logError($storeId, $swiftypeApiParameters, $productIds, $swiftypeApiResponse)
70
+ {
71
+ Mage::helper('swiftype/log')->logIndexRequest($storeId, $swiftypeApiParameters, $productIds);
72
+ Mage::helper('swiftype/log')->logIndexResponse($storeId, $swiftypeApiParameters, $swiftypeApiResponse);
73
+ }
74
+
75
+ /**
76
+ * Get Swiftype Document Field Type for Attribute Code
77
+ *
78
+ * @param string $attributeCode
79
+ * @return string
80
+ */
81
+ final protected function _getDocumentFieldType($attributeCode)
82
+ {
83
+ if (!in_array($attributeCode, $this->_documentFieldTypes)) {
84
+ $entityType = Mage::getModel('eav/entity_type')
85
+ ->loadByCode(Mage_Catalog_Model_Product::ENTITY);
86
+ /* @var $entityType Mage_Eav_Model_Entity_Type */
87
+ $attribute = Mage::getModel('catalog/resource_eav_attribute')
88
+ ->loadByCode($entityType->getId(), $attributeCode);
89
+ /* @var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */
90
+
91
+ if ($attribute->getId()) {
92
+ $documentFieldType = $attribute->getSwiftypeDocumentFieldType();
93
+ } else {
94
+ $documentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_TEXT;
95
+ }
96
+ $this->_documentFieldTypes[$attributeCode] = $documentFieldType;
97
+ }
98
+ return $this->_documentFieldTypes[$attributeCode];
99
+ }
100
+
101
+ /**
102
+ * @return Swiftype_Swiftype_Helper_Data
103
+ */
104
+ final protected function _getHelper()
105
+ {
106
+ return Mage::helper('swiftype');
107
+ }
108
+
109
+ /**
110
+ * Remove Document from Swiftype
111
+ *
112
+ * @param mixed $storeId
113
+ * @param mixed $entityId
114
+ * @param string $entity
115
+ * @return \Swiftype_Swiftype_Model_Resource_Fulltext_Engine
116
+ */
117
+ final public function cleanIndex($storeId = null, $entityId = null, $entity = 'product')
118
+ {
119
+ if ($entity == 'product' && $entityId) {
120
+ if (!is_array($entityId)) {
121
+ $swiftypeApiParameters = array(
122
+ 'uri' => array(
123
+ 'engines' => $this->_getHelper()->getEngineSlug($storeId),
124
+ 'document_types' => $entity,
125
+ 'documents' => $entityId
126
+ ),
127
+ 'get' => array(
128
+ 'auth_token' => $this->_getHelper()->getAuthToken($storeId)
129
+ )
130
+
131
+ );
132
+ $swiftypeApiClient = $this->_getHelper()->getSwiftypeClient($swiftypeApiParameters);
133
+ $swiftypeApiResponse = $swiftypeApiClient->request(Zend_Http_Client::DELETE);
134
+
135
+ if ($swiftypeApiResponse->getStatus() == 200 || $swiftypeApiResponse->getStatus() == 406) {
136
+ return $this;
137
+ }
138
+
139
+ Mage::helper('swiftype/log')->logDeleteResponse($storeId, $swiftypeApiParameters, $swiftypeApiResponse);
140
+ }
141
+ }
142
+ return $this;
143
+ }
144
+
145
+ /**
146
+ *
147
+ * @param array $index Data to index
148
+ * @param string $separator
149
+ * @return array Data formatted ready for indexing
150
+ */
151
+ final public function prepareEntityIndex($index, $separator = ' ')
152
+ {
153
+ $separator = ' ';
154
+ foreach ($index as $attributeCode => $value) {
155
+ if (is_array($value)) {
156
+ reset($value);
157
+ $value = $value[key($value)];
158
+ }
159
+ if (preg_match('/^(Enabled)?$/', $value) || preg_match('/^(in_stock|price)?$/', $attributeCode)) {
160
+ unset($index[$attributeCode]);
161
+ } else {
162
+ $index[$attributeCode] = $value;
163
+ }
164
+ }
165
+ return $index;
166
+ }
167
+ }
app/code/community/Swiftype/Swiftype/Model/System/Config/Source/Catalog/Search/Engine.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class Swiftype_Swiftype_Model_System_Config_Source_Catalog_Search_Engine
4
+ {
5
+ final public function toOptionArray()
6
+ {
7
+ if (class_exists('Enterprise_Search_Model_Adminhtml_System_Config_Source_Engine')) {
8
+ $sourceModel = new Enterprise_Search_Model_Adminhtml_System_Config_Source_Engine();
9
+
10
+ $optionArray = $sourceModel->toOptionArray();
11
+ $optionArray[] = array(
12
+ 'value' => Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_SWIFTYPE,
13
+ 'label' => 'Swiftype'
14
+ );
15
+ } else {
16
+ $optionArray = array(
17
+ array(
18
+ 'value' => Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_SWIFTYPE,
19
+ 'label' => 'Swiftype'
20
+ ),
21
+ array(
22
+ 'value' => Swiftype_Swiftype_Helper_Data::CATALOG_SEARCH_ENGINE_DEFAULT,
23
+ 'label' => 'MySQL'
24
+ )
25
+ );
26
+ }
27
+
28
+ return $optionArray;
29
+ }
30
+ }
app/code/community/Swiftype/Swiftype/controllers/AjaxController.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(Mage::getModuleDir('controllers','Mage_CatalogSearch') . DS . 'AjaxController.php');
4
+
5
+ class Swiftype_Swiftype_AjaxController extends Mage_CatalogSearch_AjaxController
6
+ {
7
+ public function suggestAction()
8
+ {
9
+ $helper = Mage::helper('swiftype');
10
+ /* @var $helper Swiftype_Swiftype_Helper_Data */
11
+
12
+ if ($helper->getUseSwiftypeAutocomplete()) {
13
+ $this->loadLayout();
14
+ $this->renderLayout();
15
+ } else {
16
+ parent::suggestAction();
17
+ }
18
+
19
+ return $this;
20
+ }
21
+ }
app/code/community/Swiftype/Swiftype/controllers/AnalyticsController.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Swiftype_Swiftype_AnalyticsController
4
+ extends Mage_Core_Controller_Front_Action
5
+ {
6
+ /**
7
+ *
8
+ * @return \Swiftype_Swiftype_AnalyticsController
9
+ */
10
+ final public function logclickthroughAction()
11
+ {
12
+ $id = $this->getRequest()->getParam('id');
13
+ $q = $this->getRequest()->getParam('q');
14
+
15
+ $helper = Mage::helper('swiftype');
16
+ /* @var $helper Swiftype_Swiftype_Helper_Data */
17
+ $helper->logClickthrough((int)$id, (string)$q);
18
+
19
+ return $this;
20
+ }
21
+
22
+ final public function onclickautoselectAction()
23
+ {
24
+ $helper = Mage::helper('swiftype');
25
+ /* @var $helper Swiftype_Swiftype_Helper_Data */
26
+ $helper->onClickAutoselect(
27
+ (int)$this->getRequest()->getParam('id'),
28
+ (string)$this->getRequest()->getParam('q'));
29
+ }
30
+ }
app/code/community/Swiftype/Swiftype/etc/config.xml ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <modules>
4
+ <Swiftype_Swiftype>
5
+ <version>1.0.0</version>
6
+ </Swiftype_Swiftype>
7
+ </modules>
8
+ <global>
9
+ <helpers>
10
+ <swiftype>
11
+ <class>Swiftype_Swiftype_Helper</class>
12
+ </swiftype>
13
+ </helpers>
14
+ <blocks>
15
+ <swiftype>
16
+ <class>Swiftype_Swiftype_Block</class>
17
+ </swiftype>
18
+ <catalogsearch>
19
+ <rewrite>
20
+ <autocomplete>Swiftype_Swiftype_Block_Autocomplete</autocomplete>
21
+ </rewrite>
22
+ </catalogsearch>
23
+ </blocks>
24
+ <models>
25
+ <swiftype>
26
+ <class>Swiftype_Swiftype_Model</class>
27
+ <resourceModel>swiftype_resource</resourceModel>
28
+ </swiftype>
29
+ <swiftype_resource>
30
+ <class>Swiftype_Swiftype_Model_Resource</class>
31
+ </swiftype_resource>
32
+ <catalogsearch_resource>
33
+ <rewrite>
34
+ <fulltext>Swiftype_Swiftype_Model_Resource_Fulltext</fulltext>
35
+ </rewrite>
36
+ </catalogsearch_resource>
37
+ </models>
38
+ <resources>
39
+ <swiftype_setup>
40
+ <setup>
41
+ <module>Swiftype_Swiftype</module>
42
+ </setup>
43
+ </swiftype_setup>
44
+ </resources>
45
+ <cache>
46
+ <types>
47
+ <swiftype_autocomplete>
48
+ <label>Swiftype Autocomplete</label>
49
+ <description>Swiftype Autocomplete Cache</description>
50
+ <tags>SWIFTYPE_AUTOCOMPLETE</tags>
51
+ </swiftype_autocomplete>
52
+ </types>
53
+ </cache>
54
+ <events>
55
+ <core_config_data_save_after>
56
+ <observers>
57
+ <swiftype>
58
+ <class>swiftype/observer_global</class>
59
+ <method>clearCatalogsearchResults</method>
60
+ </swiftype>
61
+ </observers>
62
+ </core_config_data_save_after>
63
+ <core_config_data_save_commit_after>
64
+ <observers>
65
+ <swiftype_autocomplete>
66
+ <class>swiftype/observer_global</class>
67
+ <method>cleanSwiftypeAutocompleteCache</method>
68
+ </swiftype_autocomplete>
69
+ </observers>
70
+ </core_config_data_save_commit_after>
71
+ <catalog_product_save_commit_after>
72
+ <observers>
73
+ <swiftype_autocomplete>
74
+ <class>swiftype/observer_global</class>
75
+ <method>cleanSwiftypeAutocompleteCache</method>
76
+ </swiftype_autocomplete>
77
+ </observers>
78
+ </catalog_product_save_commit_after>
79
+ </events>
80
+ </global>
81
+ <frontend>
82
+ <events>
83
+ <controller_front_send_response_after>
84
+ <observers>
85
+ <swiftype_ajax_suggest>
86
+ <class>swiftype/observer_frontend</class>
87
+ <method>cacheSwiftypeAutocomplete</method>
88
+ </swiftype_ajax_suggest>
89
+ </observers>
90
+ </controller_front_send_response_after>
91
+ </events>
92
+ <routers>
93
+ <catalogsearch>
94
+ <args>
95
+ <modules>
96
+ <swiftype before="Mage_CatalogSearch">Swiftype_Swiftype</swiftype>
97
+ </modules>
98
+ </args>
99
+ </catalogsearch>
100
+ <swiftype>
101
+ <use>standard</use>
102
+ <args>
103
+ <module>Swiftype_Swiftype</module>
104
+ <frontName>swiftype</frontName>
105
+ </args>
106
+ </swiftype>
107
+ </routers>
108
+ <layout>
109
+ <updates>
110
+ <swiftype>
111
+ <file>swiftype.xml</file>
112
+ </swiftype>
113
+ </updates>
114
+ </layout>
115
+ </frontend>
116
+ <adminhtml>
117
+ <events>
118
+ <adminhtml_catalog_product_attribute_edit_prepare_form>
119
+ <observers>
120
+ <swiftype>
121
+ <class>swiftype/observer_adminhtml</class>
122
+ <method>adminhtmlCatalogProductAttributeEditPrepareForm</method>
123
+ </swiftype>
124
+ </observers>
125
+ </adminhtml_catalog_product_attribute_edit_prepare_form>
126
+ </events>
127
+ </adminhtml>
128
+ <default>
129
+ <catalog>
130
+ <search>
131
+ <engine>catalogsearch/fulltext_engine</engine>
132
+ <swiftype_autocomplete>1</swiftype_autocomplete>
133
+ <swiftype_autocomplete_limit>3</swiftype_autocomplete_limit>
134
+ </search>
135
+ </catalog>
136
+ </default>
137
+ </config>
app/code/community/Swiftype/Swiftype/etc/system.xml ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <sections>
4
+ <catalog>
5
+ <groups>
6
+ <search translate="label" module="catalogsearch">
7
+ <expanded>true</expanded>
8
+ <fields>
9
+ <max_query_words translate="label comment">
10
+ <depends>
11
+ <engine>catalogsearch/fulltext_engine</engine>
12
+ </depends>
13
+ </max_query_words>
14
+ <search_type translate="label">
15
+ <depends>
16
+ <engine>catalogsearch/fulltext_engine</engine>
17
+ </depends>
18
+ </search_type>
19
+ <engine>
20
+ <label>Search Engine</label>
21
+ <frontend_type>select</frontend_type>
22
+ <source_model>swiftype/system_config_source_catalog_search_engine</source_model>
23
+ <sort_order>30</sort_order>
24
+ <show_in_default>1</show_in_default>
25
+ <show_in_website>0</show_in_website>
26
+ <show_in_store>1</show_in_store>
27
+ </engine>
28
+ <swiftype_api_key>
29
+ <label>API Key</label>
30
+ <frontend_type>password</frontend_type>
31
+ <sort_order>35</sort_order>
32
+ <show_in_default>0</show_in_default>
33
+ <show_in_website>0</show_in_website>
34
+ <show_in_store>1</show_in_store>
35
+ <depends>
36
+ <engine>swiftype/fulltext_engine</engine>
37
+ </depends>
38
+ <validate>required-entry</validate>
39
+ </swiftype_api_key>
40
+ <swiftype_engine_slug>
41
+ <label>Engine Slug</label>
42
+ <frontend_type>text</frontend_type>
43
+ <sort_order>40</sort_order>
44
+ <show_in_default>0</show_in_default>
45
+ <show_in_website>0</show_in_website>
46
+ <show_in_store>1</show_in_store>
47
+ <depends>
48
+ <engine>swiftype/fulltext_engine</engine>
49
+ </depends>
50
+ <validate>required-entry</validate>
51
+ </swiftype_engine_slug>
52
+ <swiftype_engine_key>
53
+ <label>Engine Key</label>
54
+ <frontend_type>text</frontend_type>
55
+ <sort_order>45</sort_order>
56
+ <show_in_default>0</show_in_default>
57
+ <show_in_website>0</show_in_website>
58
+ <show_in_store>1</show_in_store>
59
+ <depends>
60
+ <engine>swiftype/fulltext_engine</engine>
61
+ </depends>
62
+ <validate>required-entry</validate>
63
+ </swiftype_engine_key>
64
+ <swiftype_autocomplete>
65
+ <label>Use Swiftype Autocomplete</label>
66
+ <frontend_type>select</frontend_type>
67
+ <source_model>adminhtml/system_config_source_yesno</source_model>
68
+ <sort_order>50</sort_order>
69
+ <show_in_default>0</show_in_default>
70
+ <show_in_website>0</show_in_website>
71
+ <show_in_store>1</show_in_store>
72
+ <depends>
73
+ <engine>swiftype/fulltext_engine</engine>
74
+ </depends>
75
+ </swiftype_autocomplete>
76
+ <swiftype_autocomplete_limit>
77
+ <label>Autocomplete Limit</label>
78
+ <frontend_type>text</frontend_type>
79
+ <sort_order>55</sort_order>
80
+ <show_in_default>0</show_in_default>
81
+ <show_in_website>0</show_in_website>
82
+ <show_in_store>1</show_in_store>
83
+ <depends>
84
+ <engine>swiftype/fulltext_engine</engine>
85
+ <swiftype_autocomplete>1</swiftype_autocomplete>
86
+ </depends>
87
+ <validate>validate-number</validate>
88
+ </swiftype_autocomplete_limit>
89
+ </fields>
90
+ </search>
91
+ </groups>
92
+ </catalog>
93
+ </sections>
94
+ </config>
app/code/community/Swiftype/Swiftype/sql/swiftype_setup/install-1.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ /* @var $installer Mage_Core_Model_Resource_Setup */
5
+ $installer->startSetup();
6
+
7
+ $installer->getConnection()->addColumn(
8
+ $installer->getTable('catalog/eav_attribute'),
9
+ 'swiftype_document_field_type',
10
+ array(
11
+ 'TYPE' => Varien_Db_Ddl_Table::TYPE_TEXT,
12
+ 'LENGTH' => 255,
13
+ 'COMMENT' => 'Swiftype Document Field Type.',
14
+ 'DEFAULT' => Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_STRING
15
+ ));
16
+
17
+ $attributes = Mage::getResourceModel('catalog/product_attribute_collection');
18
+ /* @var $attributes Mage_Catalog_Model_Resource_Product_Attribute_Collection */
19
+ $attributes->addSearchableAttributeFilter();
20
+
21
+ foreach ($attributes as $attribute) {
22
+ /* @var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */
23
+
24
+ $inputType = $attribute->getFrontend()->getInputType();
25
+
26
+ switch ($inputType) {
27
+ case 'boolean':
28
+ $swiftypeDocumentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_INTEGER;
29
+ break;
30
+ case 'date':
31
+ case 'datetime':
32
+ $swiftypeDocumentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_DATE;
33
+ break;
34
+ case 'multiselect':
35
+ case 'select':
36
+ case 'text':
37
+ $swiftypeDocumentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_STRING;
38
+ break;
39
+ case 'price':
40
+ case 'weight':
41
+ $swiftypeDocumentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_FLOAT;
42
+ break;
43
+ case 'textarea':
44
+ $swiftypeDocumentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_TEXT;
45
+ break;
46
+ default:
47
+ $swiftypeDocumentFieldType = Swiftype_Swiftype_Helper_Data::DOCUMENT_FIELD_TYPE_TEXT;
48
+ break;
49
+ }
50
+
51
+ $attribute->setData('swiftype_document_field_type', $swiftypeDocumentFieldType);
52
+ $attribute->save();
53
+ }
54
+
55
+ $installer->endSetup();
app/design/frontend/base/default/layout/swiftype.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <layout version="0.1.0">
3
+ <catalogsearch_ajax_suggest>
4
+ <block type="swiftype/ajax_suggest" name="root" template="swiftype/ajax/suggest.phtml" />
5
+ </catalogsearch_ajax_suggest>
6
+ </layout>
app/design/frontend/base/default/template/swiftype/ajax/suggest.phtml ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $helper = Mage::helper('swiftype');
3
+ /* @var $helper Swiftype_Swiftype_Helper_Data */
4
+
5
+ $products = $helper->getSuggestedProducts();
6
+ $productCount = count($products);
7
+ $suggestData = array();
8
+ $counter = 0;
9
+ $descriptionLimit = 150;
10
+
11
+ foreach ($products as $product) {
12
+ /* @var $product Mage_Catalog_Model_Product */
13
+ $suggestDatum = array(
14
+ 'product' => $product,
15
+ 'name' => $product->getName(),
16
+ 'title' => $product->getName(),
17
+ 'href' => $product->getUrlInStore(),
18
+ 'description' => (strlen($product->getShortDescription()) > $descriptionLimit) ?
19
+ substr($product->getShortDescription(), 0, $descriptionLimit)."..." : $product->getShortDescription(),
20
+ 'onClick' => $helper->getOnClickAutoselect($product)
21
+ );
22
+
23
+ if ($counter == 1) {
24
+ $suggestDatum['row_class'] = $suggestDatum['row_class'] . ' first';
25
+ } elseif ($counter == $productCount) {
26
+ $suggestDatum['row_class'] = $suggestDatum['row_class'] . ' last';
27
+ }
28
+
29
+ $suggestData[] = $suggestDatum;
30
+ }
31
+ ?>
32
+ <?php if ($productCount) : ?>
33
+ <ul>
34
+ <li style="display:none"></li>
35
+ <?php foreach ($suggestData as $suggestDatum) : ?>
36
+ <li title="<?php echo str_replace('"', '', $suggestDatum['title']);?>" class="<?php echo str_replace('"', '', $suggestDatum['row_class']); ?>" style="background: #fff;">
37
+ <a href="<?php echo $suggestDatum['href']; ?>" onClick="<?php echo $suggestDatum['onClick']; ?>" style="display: block; position: relative; background: #fff; text-decoration: none; margin: 0; padding: 0 0 6px;">
38
+ <img style="position: absolute; left: -2px; top: 2px; margin: 0; border: 0;" src="<?php echo $this->helper('catalog/image')->init($suggestDatum['product'], 'thumbnail')->resize(56, 56); ?>" width="56" height="56"/>
39
+ <div style="padding: 8px 5px 0 60px; font-size: 11px; line-height: 1.4; font-weight: bold;"><?php echo htmlentities($suggestDatum['name']); ?></div>
40
+ <div style="padding: 2px 5px 5px 60px; min-height: 36px; overflow: hidden; font-size: 10px; line-height: 1.33; color: #777;"><?php echo htmlentities($suggestDatum['description']); ?></div>
41
+ </a>
42
+ </li>
43
+ <?php endforeach; ?>
44
+ </ul>
45
+ <?php endif; ?>
app/etc/modules/Swiftype_Swiftype.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <modules>
4
+ <Swiftype_Swiftype>
5
+ <codePool>community</codePool>
6
+ <active>true</active>
7
+ </Swiftype_Swiftype>
8
+ </modules>
9
+ </config>
app/etc/swiftype.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <global>
4
+ <cache>
5
+ <request_processors>
6
+ <swiftype>Swiftype_Swiftype_Model_Request_Processor</swiftype>
7
+ </request_processors>
8
+ </cache>
9
+ </global>
10
+ </config>
package.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Swiftype_Swiftype</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.gnu.org/licenses/agpl.html">AGPL</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Great search for your Magento store.</summary>
10
+ <description>Swiftype provides great search for you Magento store. Some of the features that make search experience better with Swiftype:&#xD;
11
+ &#xD;
12
+ * Powerful Search: Quick and relevant search for your online store.&#xD;
13
+ &#xD;
14
+ * Drag-and-drop result ranking: We work hard to return the most relevant results by default for every search query, but sometimes you'll want to make your own customizations. Simply perform the search in our dashboard then drag-and-drop the results to re-order them.&#xD;
15
+ &#xD;
16
+ * Autocomplete: Sometimes a search isn't even necessary; our search engines come with autocomplete built-in, and your users will thank you for having it.</description>
17
+ <notes>Changelog:&#xD;
18
+ &#xD;
19
+ * Bumped to 1.0.0 version in anticipation of Magento Connect initial release.</notes>
20
+ <authors><author><name>Swiftype</name><user>swiftypeteam</user><email>team@swiftype.com</email></author></authors>
21
+ <date>2014-06-12</date>
22
+ <time>16:56:29</time>
23
+ <contents><target name="magecommunity"><dir name="Swiftype"><dir name="Swiftype"><dir name="Block"><dir name="Ajax"><file name="Suggest.php" hash="e5b55a9c41709eeffe31a6ea266876bc"/></dir><file name="Autocomplete.php" hash="f42063444facdccc951700591a82f8ca"/></dir><dir name="Helper"><file name="Data.php" hash="71cb37ad290d00ef577fb66b6cc8a0a5"/><file name="Log.php" hash="544a2a7075d3dec2d8c81eef0656f8a4"/></dir><dir name="Model"><dir name="Observer"><file name="Adminhtml.php" hash="6a1420a1fe9c7a7d7f3b90b5260af196"/><file name="Frontend.php" hash="923f4348d2b2785dfca43df9f2355e40"/><file name="Global.php" hash="8764a09f34b456cb729543616e0de889"/></dir><dir name="Request"><file name="Processor.php" hash="ca7fbb10d1dc0920ddb9605f7c9e310c"/></dir><dir name="Resource"><dir name="Fulltext"><file name="Engine.php" hash="5d95d1b7ea2d13c05478c4fec69d27d5"/></dir><file name="Fulltext.php" hash="c8117f47d91906e258d2000548befe1d"/></dir><dir name="System"><dir name="Config"><dir name="Source"><dir name="Catalog"><dir name="Search"><file name="Engine.php" hash="a9faa3a31d39cedf2e20fa6e50cc1a66"/></dir></dir></dir></dir></dir></dir><dir name="controllers"><file name="AjaxController.php" hash="0d61e11d4d332041f75981304546ff1b"/><file name="AnalyticsController.php" hash="8c8742944ddcf89182f6a03f49e747c4"/></dir><dir name="etc"><file name="config.xml" hash="3227643a18db99d75417eea199ed136c"/><file name="system.xml" hash="a372d79b32358f00aac38cf8864ca715"/></dir><dir name="sql"><dir name="swiftype_setup"><file name="install-1.php" hash="720c6d503a3493b9be8e34ff9af49528"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="swiftype.xml" hash="b32acb6177dbf205ede8e0b0c2601705"/></dir><dir name="template"><dir name="swiftype"><dir name="ajax"><file name="suggest.phtml" hash="5c527591144181cc4615efd806a128d6"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Swiftype_Swiftype.xml" hash="6894235a6d3edae4c2f5aa04407274d6"/></dir><dir name="."><file name="swiftype.xml" hash="20e640f1fa6945f853c2d29e3c2f94d9"/></dir></target></contents>
24
+ <compatible/>
25
+ <dependencies><required><php><min>5.3.0</min><max>5.7.0</max></php></required></dependencies>
26
+ </package>