Version Notes
Speed Optimizations for Feed Regeneration
Better Root Category skipping logic
Added Support for Amasty Labels
Added Support for TBT Rewards (product earnable points)
Added Support for product qty sales data
Download this release
Release Info
Developer | SearchSpring Development Team |
Extension | SearchSpring_Manager |
Version | 1.9.0 |
Comparing to | |
See all releases |
Code changes from version 1.1.0 to 1.9.0
- app/code/community/SearchSpring/ErrorCodes.php +21 -0
- app/code/community/SearchSpring/Manager/Block/Adminhtml/System/Config/Fieldset/Hint.php +49 -0
- app/code/community/SearchSpring/Manager/Block/Adminhtml/System/Config/Fieldset/Setup.php +53 -0
- app/code/community/SearchSpring/Manager/Entity/IndexingRequestBody.php +3 -1
- app/code/community/SearchSpring/Manager/Entity/RecordsCollection.php +4 -0
- app/code/community/SearchSpring/Manager/Factory/ApiFactory.php +38 -33
- app/code/community/SearchSpring/Manager/Factory/GeneratorFactory.php +46 -1
- app/code/community/SearchSpring/Manager/Generator/ProductGenerator.php +8 -0
- app/code/community/SearchSpring/Manager/Handler/ApiErrorHandler.php +5 -3
- app/code/community/SearchSpring/Manager/Helper/Data.php +131 -3
- app/code/community/SearchSpring/Manager/Helper/Http.php +76 -0
- app/code/community/SearchSpring/Manager/Helper/Oauth.php +205 -0
- app/code/community/SearchSpring/Manager/Helper/Product.php +33 -0
- app/code/community/SearchSpring/Manager/Helper/Webservice.php +115 -0
- app/code/community/SearchSpring/Manager/Model/Api2/Auth/Adapter.php +66 -0
- app/code/community/SearchSpring/Manager/Model/Api2/Indexing/Rest/Admin/V1.php +197 -0
- app/code/community/SearchSpring/Manager/Model/Oauth/Server.php +33 -0
- app/code/community/SearchSpring/Manager/Model/Observer/CategorySaveObserver.php +4 -13
- app/code/community/SearchSpring/Manager/Model/Observer/ConfigObserver.php +95 -0
- app/code/community/SearchSpring/Manager/Model/Observer/LiveIndexer.php +38 -0
- app/code/community/SearchSpring/Manager/Model/Observer/ProductSaveObserver.php +6 -9
- app/code/community/SearchSpring/Manager/Operation/Product.php +15 -0
- app/code/community/SearchSpring/Manager/Operation/Product/SetCategories.php +29 -13
- app/code/community/SearchSpring/Manager/Operation/Product/SetCoreFields.php +168 -152
- app/code/community/SearchSpring/Manager/Operation/Product/SetFields.php +8 -1
- app/code/community/SearchSpring/Manager/Operation/Product/SetReport.php +182 -0
- app/code/community/SearchSpring/Manager/Operation/ProductOperation.php +32 -4
- app/code/community/SearchSpring/Manager/Provider/ProductCollection/FeedProvider.php +1 -3
- app/code/community/SearchSpring/Manager/Request/JSON.php +48 -48
- app/code/community/SearchSpring/Manager/Service/SearchSpring/ApiAdapter.php +5 -5
- app/code/community/SearchSpring/Manager/Service/SearchSpring/IndexingApiAdapter.php +7 -6
- app/code/community/SearchSpring/Manager/Service/SearchSpring/SearchApiAdapter.php +3 -0
- app/code/community/SearchSpring/Manager/Strategy/Pricing/Strategy.php +0 -1
- app/code/community/SearchSpring/Manager/ThirdParty/Amasty/LabelsOperation.php +30 -0
- app/code/community/SearchSpring/Manager/ThirdParty/TBT/RewardsOperation.php +32 -0
- app/code/community/SearchSpring/Manager/Transformer/ProductCollectionToRecordCollectionTransformer.php +69 -0
- app/code/community/SearchSpring/Manager/Validator/ProductValidator.php +4 -2
- app/code/community/SearchSpring/Manager/Writer/Product/Params/FileWriterParams.php +45 -3
- app/code/community/SearchSpring/Manager/controllers/GenerateController.php +203 -95
- app/code/community/SearchSpring/Manager/etc/api2.xml +59 -0
- app/code/community/SearchSpring/Manager/etc/config.xml +78 -6
- app/code/community/SearchSpring/Manager/etc/system.xml +81 -5
- app/code/community/SearchSpring/Manager/sql/searchspring_manager/mysql4-install-1.0.0.php +49 -0
- app/code/community/SearchSpring/Manager/sql/searchspring_manager/mysql4-upgrade-0.4.0-1.0.0.php +49 -0
- app/design/adminhtml/default/default/template/searchspring/manager/system/config/fieldset/hint.phtml +77 -0
- app/design/adminhtml/default/default/template/searchspring/manager/system/config/fieldset/setup.phtml +186 -0
- js/searchspring/jquery-1.11.1.min.js +4 -0
- js/searchspring/setup.js +194 -0
- package.xml +5 -1
app/code/community/SearchSpring/ErrorCodes.php
CHANGED
@@ -43,4 +43,25 @@ class SearchSpring_ErrorCodes
|
|
43 |
* Ids parameter not found
|
44 |
*/
|
45 |
const IDS_NOT_SET = 6;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
}
|
43 |
* Ids parameter not found
|
44 |
*/
|
45 |
const IDS_NOT_SET = 6;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* BAD REQUEST FORMAT
|
49 |
+
*/
|
50 |
+
const BAD_REQUEST = 7;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Unknown Error
|
54 |
+
*/
|
55 |
+
const UNKNOWN_EXCEPTION = 8;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Authentication Credentials - Invalid
|
59 |
+
*/
|
60 |
+
const AUTH_CREDENTIALS_INVALID = 9;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Authentication Credentials - Missing
|
64 |
+
*/
|
65 |
+
const AUTH_CREDENTIALS_MISSING = 10;
|
66 |
+
|
67 |
}
|
app/code/community/SearchSpring/Manager/Block/Adminhtml/System/Config/Fieldset/Hint.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Hint.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Block_Adminhtml_System_Config_Fieldset_Hint
|
10 |
+
*
|
11 |
+
* Display hint above SearchSpring Manager Settings
|
12 |
+
*
|
13 |
+
* @author James Bathgate <james@b7interactive.com>
|
14 |
+
*/
|
15 |
+
|
16 |
+
class SearchSpring_Manager_Block_Adminhtml_System_Config_Fieldset_Hint extends Mage_Adminhtml_Block_Abstract implements Varien_Data_Form_Element_Renderer_Interface
|
17 |
+
{
|
18 |
+
protected $_template = 'searchspring/manager/system/config/fieldset/hint.phtml';
|
19 |
+
|
20 |
+
public function __construct() {
|
21 |
+
parent::__construct();
|
22 |
+
return $this;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Render fieldset html
|
27 |
+
*
|
28 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
29 |
+
* @return string
|
30 |
+
*/
|
31 |
+
public function render(Varien_Data_Form_Element_Abstract $element)
|
32 |
+
{
|
33 |
+
return $this->toHtml();
|
34 |
+
}
|
35 |
+
|
36 |
+
|
37 |
+
public function isSetup() {
|
38 |
+
$hlp = Mage::helper('searchspring_manager');
|
39 |
+
return $hlp->registerMagentoAPIAuthenticationWithSearchSpring(true);
|
40 |
+
}
|
41 |
+
|
42 |
+
public function getVersion() {
|
43 |
+
return (string) Mage::helper('searchspring_manager')->getVersion();
|
44 |
+
}
|
45 |
+
|
46 |
+
public function getModuleUUID() {
|
47 |
+
return Mage::helper('searchspring_manager')->getUUID();
|
48 |
+
}
|
49 |
+
}
|
app/code/community/SearchSpring/Manager/Block/Adminhtml/System/Config/Fieldset/Setup.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Setup.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Block_Adminhtml_System_Config_Fieldset_Setup
|
10 |
+
*
|
11 |
+
* Display connection setup helper
|
12 |
+
*
|
13 |
+
* @author James Bathgate <james@b7interactive.com>
|
14 |
+
*/
|
15 |
+
|
16 |
+
class SearchSpring_Manager_Block_Adminhtml_System_Config_Fieldset_Setup extends Mage_Adminhtml_Block_Abstract implements Varien_Data_Form_Element_Renderer_Interface
|
17 |
+
{
|
18 |
+
protected $_template = 'searchspring/manager/system/config/fieldset/setup.phtml';
|
19 |
+
|
20 |
+
public function __construct() {
|
21 |
+
parent::__construct();
|
22 |
+
|
23 |
+
return $this;
|
24 |
+
}
|
25 |
+
|
26 |
+
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Render fieldset html
|
30 |
+
*
|
31 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
32 |
+
* @return string
|
33 |
+
*/
|
34 |
+
public function render(Varien_Data_Form_Element_Abstract $element)
|
35 |
+
{
|
36 |
+
return $this->toHtml();
|
37 |
+
}
|
38 |
+
|
39 |
+
public function _prepareLayout() {
|
40 |
+
$head = $this->getLayout()->getBlock('head');
|
41 |
+
$head->addItem('js', 'searchspring/jquery-1.11.1.min.js');
|
42 |
+
$head->addItem('js', 'searchspring/setup.js');
|
43 |
+
|
44 |
+
parent::_prepareLayout();
|
45 |
+
return $this;
|
46 |
+
}
|
47 |
+
|
48 |
+
public function isSetup() {
|
49 |
+
$hlp = Mage::helper('searchspring_manager');
|
50 |
+
return $hlp->registerMagentoAPIAuthenticationWithSearchSpring(true);
|
51 |
+
}
|
52 |
+
|
53 |
+
}
|
app/code/community/SearchSpring/Manager/Entity/IndexingRequestBody.php
CHANGED
@@ -78,12 +78,14 @@ class SearchSpring_Manager_Entity_IndexingRequestBody extends SearchSpring_Manag
|
|
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' =>
|
87 |
);
|
88 |
|
89 |
return $body;
|
78 |
*/
|
79 |
public function jsonSerialize()
|
80 |
{
|
81 |
+
$url = Mage::helper('searchspring_manager')->getMageAPIUrlProduct();
|
82 |
+
|
83 |
$body = array(
|
84 |
'type' => $this->type,
|
85 |
'ids' => $this->ids,
|
86 |
'delete' => $this->shouldDelete,
|
87 |
'feedId' => $this->feedId,
|
88 |
+
'generateUrl' => $url
|
89 |
);
|
90 |
|
91 |
return $body;
|
app/code/community/SearchSpring/Manager/Entity/RecordsCollection.php
CHANGED
@@ -115,6 +115,10 @@ class SearchSpring_Manager_Entity_RecordsCollection
|
|
115 |
*/
|
116 |
public function add($key, $element)
|
117 |
{
|
|
|
|
|
|
|
|
|
118 |
$this->fields[$this->key()][$key][] = $element;
|
119 |
|
120 |
return $this;
|
115 |
*/
|
116 |
public function add($key, $element)
|
117 |
{
|
118 |
+
if(isset($this->fields[$this->key()][$key]) && !is_array($this->fields[$this->key()][$key])) {
|
119 |
+
$this->fields[$this->key()][$key] = array($this->fields[$this->key()][$key]);
|
120 |
+
}
|
121 |
+
|
122 |
$this->fields[$this->key()][$key][] = $element;
|
123 |
|
124 |
return $this;
|
app/code/community/SearchSpring/Manager/Factory/ApiFactory.php
CHANGED
@@ -14,57 +14,62 @@
|
|
14 |
*/
|
15 |
class SearchSpring_Manager_Factory_ApiFactory
|
16 |
{
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
* @param $type string The type of API to build (index or search)
|
23 |
*
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
|
42 |
-
|
43 |
-
throw new UnexpectedValueException('SearchSpring: Secret key must be set');
|
44 |
-
}
|
45 |
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
if($type == 'index') {
|
49 |
-
$
|
50 |
-
$curl->setConfig(array('timeout' => 15, 'userpwd' => $siteId . ':' . $secretKey));
|
51 |
|
52 |
$api = new SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter(
|
53 |
$apiErrorHandler,
|
54 |
-
$
|
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 |
-
$
|
64 |
$baseUrl
|
65 |
);
|
66 |
}
|
67 |
|
68 |
-
|
69 |
-
|
70 |
}
|
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 |
+
|
31 |
+
$baseUrl = Mage::helper('searchspring_manager')->getApiBaseUrl();
|
32 |
+
$siteId = Mage::helper('searchspring_manager')->getApiSiteId();
|
33 |
+
$secretKey = Mage::helper('searchspring_manager')->getApiSecretKey();
|
34 |
+
|
35 |
+
if (empty($baseUrl)) {
|
36 |
+
throw new UnexpectedValueException('SearchSpring: Base URL must be set');
|
37 |
+
}
|
38 |
|
39 |
+
if (empty($siteId)) {
|
40 |
+
throw new UnexpectedValueException('SearchSpring: Site ID must be set');
|
41 |
+
}
|
42 |
|
43 |
+
if (empty($secretKey)) {
|
44 |
+
throw new UnexpectedValueException('SearchSpring: Secret key must be set');
|
45 |
+
}
|
46 |
|
47 |
+
$apiErrorHandler = new SearchSpring_Manager_Handler_ApiErrorHandler();
|
|
|
|
|
48 |
|
49 |
+
$client = new Zend_Http_Client();
|
50 |
+
|
51 |
+
$client->setConfig(array(
|
52 |
+
'maxredirects' => 0,
|
53 |
+
'timeout' => 15,
|
54 |
+
'keepalive' => true
|
55 |
+
));
|
56 |
|
57 |
if($type == 'index') {
|
58 |
+
$client->setAuth($siteId, $secretKey);
|
|
|
59 |
|
60 |
$api = new SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter(
|
61 |
$apiErrorHandler,
|
62 |
+
$client,
|
63 |
$baseUrl
|
64 |
);
|
65 |
} else {
|
|
|
|
|
|
|
66 |
$api = new SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter(
|
67 |
$apiErrorHandler,
|
68 |
+
$client,
|
69 |
$baseUrl
|
70 |
);
|
71 |
}
|
72 |
|
73 |
+
return $api;
|
74 |
+
}
|
75 |
}
|
app/code/community/SearchSpring/Manager/Factory/GeneratorFactory.php
CHANGED
@@ -22,6 +22,8 @@ class SearchSpring_Manager_Factory_GeneratorFactory
|
|
22 |
const TYPE_CATEGORY = 'category';
|
23 |
/**#@-*/
|
24 |
|
|
|
|
|
25 |
/**
|
26 |
* Create a generator
|
27 |
*
|
@@ -140,6 +142,7 @@ class SearchSpring_Manager_Factory_GeneratorFactory
|
|
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',
|
@@ -152,6 +155,8 @@ class SearchSpring_Manager_Factory_GeneratorFactory
|
|
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(
|
@@ -183,7 +188,8 @@ class SearchSpring_Manager_Factory_GeneratorFactory
|
|
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);
|
@@ -219,4 +225,43 @@ class SearchSpring_Manager_Factory_GeneratorFactory
|
|
219 |
|
220 |
|
221 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
}
|
22 |
const TYPE_CATEGORY = 'category';
|
23 |
/**#@-*/
|
24 |
|
25 |
+
const XML_PATH_OPERATION_THIRD_PARTY = 'global/searchspring/operation/third_party';
|
26 |
+
|
27 |
/**
|
28 |
* Create a generator
|
29 |
*
|
142 |
$operationsCollection->append($operationsBuilder->build('SetImages'));
|
143 |
$operationsCollection->append($operationsBuilder->build('SetOptions'));
|
144 |
$operationsCollection->append($operationsBuilder->build('SetCategories'));
|
145 |
+
$operationsCollection->append($operationsBuilder->build('SetReport'));
|
146 |
|
147 |
// add pricing factory and if we should display zero priced products as additional data
|
148 |
$operationsCollection->append($operationsBuilder->build('SetPricing',
|
155 |
|
156 |
$operationsCollection->append($operationsBuilder->build('SetRatings'));
|
157 |
|
158 |
+
$this->addThirdPartyOperations($operationsCollection, $operationsBuilder);
|
159 |
+
|
160 |
// dispatch event allowing additional operations to be added before loop starts
|
161 |
$operationsBuilder->setClassPrefix(null);
|
162 |
Mage::dispatchEvent('searchspring_add_operations', array(
|
188 |
$writerParams = new SearchSpring_Manager_Writer_Product_Params_FileWriterParams(
|
189 |
$requestParams,
|
190 |
$collectionProvider->getCollectionCount(),
|
191 |
+
$filename,
|
192 |
+
Mage::helper('searchspring_manager')->getFeedPath()
|
193 |
);
|
194 |
$writer = new SearchSpring_Manager_Writer_Product_FileWriter($xmlWriter, $writerParams);
|
195 |
$generator = new SearchSpring_Manager_Generator_ProductGenerator($collectionProvider, $writer, $transformer);
|
225 |
|
226 |
|
227 |
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Add external/third party operations, these are specified via
|
231 |
+
* core config. These are based on the existence and status of
|
232 |
+
* external modules.
|
233 |
+
*
|
234 |
+
* @param SearchSpring_Manager_Entity_OperationsCollection $operationsCollection
|
235 |
+
* @param SearchSpring_Manager_Builder_OperationBuilder $operationsBuilder
|
236 |
+
*/
|
237 |
+
public function addThirdPartyOperations($operationsCollection, $operationsBuilder) {
|
238 |
+
|
239 |
+
$hlp = Mage::helper('searchspring_manager');
|
240 |
+
|
241 |
+
if (Mage::getConfig()->getNode(self::XML_PATH_OPERATION_THIRD_PARTY)) {
|
242 |
+
|
243 |
+
foreach (Mage::getConfig()->getNode(self::XML_PATH_OPERATION_THIRD_PARTY)->children() as $moduleName => $operations) {
|
244 |
+
|
245 |
+
if (!$operations) continue;
|
246 |
+
|
247 |
+
// Make Sure Third Party Module is enabled
|
248 |
+
if (!$hlp->isModuleEnabled($moduleName)) {
|
249 |
+
continue;
|
250 |
+
}
|
251 |
+
|
252 |
+
// Each Third Party Module Mentioned can have multiple operations
|
253 |
+
foreach($operations->children() as $operationKey => $operationInfo) {
|
254 |
+
|
255 |
+
// TODO - Maybe allow an operation to be disabled as a config within $operationInfo
|
256 |
+
// TODO - Maybe allow an parameters to be passed to operation via $operationInfo
|
257 |
+
$params = array();
|
258 |
+
|
259 |
+
// Create Operation and Append to Collection
|
260 |
+
$operationsCollection->append($operationsBuilder->build((string)$operationInfo->class, $params, false));
|
261 |
+
|
262 |
+
}
|
263 |
+
}
|
264 |
+
}
|
265 |
+
}
|
266 |
+
|
267 |
}
|
app/code/community/SearchSpring/Manager/Generator/ProductGenerator.php
CHANGED
@@ -59,9 +59,17 @@ class SearchSpring_Manager_Generator_ProductGenerator
|
|
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 |
}
|
59 |
*/
|
60 |
public function generate()
|
61 |
{
|
62 |
+
Varien_Profiler::start(__METHOD__.": getCollection()");
|
63 |
$productCollection = $this->collectionProvider->getCollection();
|
64 |
+
Varien_Profiler::stop(__METHOD__.": getCollection()");
|
65 |
+
|
66 |
+
Varien_Profiler::start(__METHOD__.": transform()");
|
67 |
$recordCollection = $this->transformer->transform($productCollection);
|
68 |
+
Varien_Profiler::stop(__METHOD__.": transform()");
|
69 |
+
|
70 |
+
Varien_Profiler::start(__METHOD__.": write()");
|
71 |
$resultMessage = $this->writer->write($recordCollection);
|
72 |
+
Varien_Profiler::stop(__METHOD__.": write()");
|
73 |
|
74 |
return $resultMessage;
|
75 |
}
|
app/code/community/SearchSpring/Manager/Handler/ApiErrorHandler.php
CHANGED
@@ -61,9 +61,11 @@ class SearchSpring_Manager_Handler_ApiErrorHandler
|
|
61 |
*/
|
62 |
private function notifyUser()
|
63 |
{
|
64 |
-
|
65 |
-
|
66 |
-
|
|
|
|
|
67 |
}
|
68 |
|
69 |
/**
|
61 |
*/
|
62 |
private function notifyUser()
|
63 |
{
|
64 |
+
if(Mage::app()->getStore()->isAdmin()) {
|
65 |
+
/** @var Mage_Core_Model_Session $session */
|
66 |
+
$session = Mage::getSingleton('core/session');
|
67 |
+
$session->addNotice('There was a problem while trying to update SearchSpring. This issue will be investigated.');
|
68 |
+
}
|
69 |
}
|
70 |
|
71 |
/**
|
app/code/community/SearchSpring/Manager/Helper/Data.php
CHANGED
@@ -16,15 +16,23 @@ 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
|
|
|
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';
|
@@ -33,6 +41,17 @@ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
|
|
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);
|
@@ -48,6 +67,16 @@ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
|
|
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);
|
@@ -60,7 +89,11 @@ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
|
|
60 |
|
61 |
public function getApiBaseUrl()
|
62 |
{
|
63 |
-
|
|
|
|
|
|
|
|
|
64 |
}
|
65 |
|
66 |
public function getApiSecretKey()
|
@@ -68,6 +101,10 @@ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
|
|
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);
|
@@ -98,6 +135,76 @@ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
|
|
98 |
return Mage::getStoreConfig(self::XML_PATH_SWATCH_WIDTH);
|
99 |
}
|
100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
/**
|
102 |
* Intended for layout action parameter helper calls
|
103 |
*
|
@@ -113,5 +220,26 @@ class SearchSpring_Manager_Helper_Data extends Mage_Core_Helper_Abstract
|
|
113 |
return Mage::app()->getLayout()->getBlock($block)->getTemplate();
|
114 |
}
|
115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
}
|
16 |
{
|
17 |
|
18 |
const XML_PATH_GLOBAL_LIVE_INDEXING_ENABLE_FL = 'ssmanager/ssmanager_general/live_indexing';
|
19 |
+
const XML_PATH_INDEX_ZERO_PRICE = 'ssmanager/ssmanager_general/index_zero_price';
|
20 |
+
const XML_PATH_INDEX_OUT_OF_STOCK = 'ssmanager/ssmanager_general/index_out_of_stock';
|
21 |
+
const XML_PATH_FEED_PATH = 'ssmanager/ssmanager_general/feed_path';
|
22 |
+
|
23 |
const XML_PATH_GLOBAL_CATEGORY_ENABLE_FL = 'ssmanager/ssmanager_catalog/enable_categories';
|
24 |
|
25 |
+
const XML_PATH_API_BASE_URL = 'ssmanager/ssmanager_api/base_url';
|
26 |
+
const ENV_VAR_API_BASE_URL = 'SEARCHSPRING_API_HOST';
|
27 |
|
28 |
const XML_PATH_API_FEED_ID = 'ssmanager/ssmanager_api/feed_id';
|
29 |
const XML_PATH_API_SITE_ID = 'ssmanager/ssmanager_api/site_id';
|
|
|
30 |
const XML_PATH_API_SECRET_KEY = 'ssmanager/ssmanager_api/secret_key';
|
31 |
|
32 |
+
const XML_PATH_API_AUTHENTICATION_METHOD = 'ssmanager/ssmanager_api/authentication_method';
|
33 |
+
const AUTH_METHOD_SIMPLE = 'simple';
|
34 |
+
const AUTH_METHOD_OAUTH = 'oauth';
|
35 |
+
|
36 |
const XML_PATH_GENERATE_CACHE_IMAGES = 'ssmanager/ssmanager_images/generate_cache_images';
|
37 |
const XML_PATH_IMAGE_WIDTH = 'ssmanager/ssmanager_images/image_width';
|
38 |
const XML_PATH_IMAGE_HEIGHT = 'ssmanager/ssmanager_images/image_height';
|
41 |
const XML_PATH_SWATCH_WIDTH = 'ssmanager/ssmanager_images/swatch_width';
|
42 |
const XML_PATH_SWATCH_HEIGHT = 'ssmanager/ssmanager_images/swatch_height';
|
43 |
|
44 |
+
const XML_PATH_UUID = 'ssmanager/ssmanager_track/uuid';
|
45 |
+
|
46 |
+
const MANAGER_API_PATH_PRODUCT_SIMPLE = 'searchspring/generate/index';
|
47 |
+
const MANAGER_API_PATH_GENERATE_SIMPLE = 'searchspring/generate/feed';
|
48 |
+
const MANAGER_API_PATH_PRODUCT_OAUTH = 'api/rest/searchspring/index';
|
49 |
+
const MANAGER_API_PATH_GENERATE_OAUTH = 'api/rest/searchspring/feed';
|
50 |
+
|
51 |
+
public function getVersion() {
|
52 |
+
return Mage::getConfig()->getNode('modules/SearchSpring_Manager/version');
|
53 |
+
}
|
54 |
+
|
55 |
public function isLiveIndexingEnabled()
|
56 |
{
|
57 |
return Mage::getStoreConfigFlag(self::XML_PATH_GLOBAL_LIVE_INDEXING_ENABLE_FL);
|
67 |
return Mage::getStoreConfigFlag(self::XML_PATH_INDEX_ZERO_PRICE);
|
68 |
}
|
69 |
|
70 |
+
public function isOutOfStockIndexingEnabled()
|
71 |
+
{
|
72 |
+
return Mage::getStoreConfigFlag(self::XML_PATH_INDEX_OUT_OF_STOCK);
|
73 |
+
}
|
74 |
+
|
75 |
+
public function getFeedPath()
|
76 |
+
{
|
77 |
+
return Mage::getStoreConfig(self::XML_PATH_FEED_PATH);
|
78 |
+
}
|
79 |
+
|
80 |
public function getApiFeedId()
|
81 |
{
|
82 |
return Mage::getStoreConfig(self::XML_PATH_API_FEED_ID);
|
89 |
|
90 |
public function getApiBaseUrl()
|
91 |
{
|
92 |
+
if($env = getenv(self::ENV_VAR_API_BASE_URL)) {
|
93 |
+
return $env;
|
94 |
+
} else {
|
95 |
+
return Mage::getStoreConfig(self::XML_PATH_API_BASE_URL);
|
96 |
+
}
|
97 |
}
|
98 |
|
99 |
public function getApiSecretKey()
|
101 |
return Mage::getStoreConfig(self::XML_PATH_API_SECRET_KEY);
|
102 |
}
|
103 |
|
104 |
+
public function getAuthenticationMethod() {
|
105 |
+
return Mage::getStoreConfig(self::XML_PATH_API_AUTHENTICATION_METHOD);
|
106 |
+
}
|
107 |
+
|
108 |
public function isCacheImagesEnabled()
|
109 |
{
|
110 |
return Mage::getStoreConfig(self::XML_PATH_GENERATE_CACHE_IMAGES);
|
135 |
return Mage::getStoreConfig(self::XML_PATH_SWATCH_WIDTH);
|
136 |
}
|
137 |
|
138 |
+
public function getUUID() {
|
139 |
+
return Mage::getStoreConfig(self::XML_PATH_UUID);
|
140 |
+
}
|
141 |
+
|
142 |
+
public function getMageAPIPathGenerate() {
|
143 |
+
switch ($this->getAuthenticationMethod()) {
|
144 |
+
case self::AUTH_METHOD_SIMPLE:
|
145 |
+
return self::MANAGER_API_PATH_GENERATE_SIMPLE;
|
146 |
+
case self::AUTH_METHOD_OAUTH:
|
147 |
+
return self::MANAGER_API_PATH_GENERATE_OAUTH;
|
148 |
+
}
|
149 |
+
return false;
|
150 |
+
}
|
151 |
+
|
152 |
+
public function getMageAPIPathProduct() {
|
153 |
+
switch ($this->getAuthenticationMethod()) {
|
154 |
+
case self::AUTH_METHOD_SIMPLE:
|
155 |
+
return self::MANAGER_API_PATH_PRODUCT_SIMPLE;
|
156 |
+
case self::AUTH_METHOD_OAUTH:
|
157 |
+
return self::MANAGER_API_PATH_PRODUCT_OAUTH;
|
158 |
+
}
|
159 |
+
return false;
|
160 |
+
}
|
161 |
+
|
162 |
+
public function getMageAPIUrlGenerate() {
|
163 |
+
return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB, true) . $this->getMageAPIPathGenerate();
|
164 |
+
}
|
165 |
+
|
166 |
+
public function getMageAPIUrlProduct() {
|
167 |
+
return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB, true) . $this->getMageAPIPathProduct();
|
168 |
+
}
|
169 |
+
|
170 |
+
public function writeStoreConfig($path, $value, $scope = 'default', $scopeId = 0) {
|
171 |
+
Mage::getConfig()->saveConfig($path, $value, $scope, $scopeId)->reinit();
|
172 |
+
}
|
173 |
+
|
174 |
+
public function registerMagentoAPIAuthenticationWithSearchSpring($verify = false) {
|
175 |
+
|
176 |
+
$whlp = Mage::helper('searchspring_manager/webservice');
|
177 |
+
|
178 |
+
// TODO -- should we cache responses from verify??
|
179 |
+
|
180 |
+
try {
|
181 |
+
|
182 |
+
switch ($this->getAuthenticationMethod()) {
|
183 |
+
case self::AUTH_METHOD_SIMPLE:
|
184 |
+
if ($verify)
|
185 |
+
$response = $whlp->verifyMageAPIAuthSimple();
|
186 |
+
else
|
187 |
+
$response = $whlp->registerMageAPIAuthSimple();
|
188 |
+
break;
|
189 |
+
case self::AUTH_METHOD_OAUTH:
|
190 |
+
if ($verify)
|
191 |
+
$response = $whlp->verifyMageAPIAuthOAuth();
|
192 |
+
else
|
193 |
+
$response = $whlp->registerMageAPIAuthOAuth();
|
194 |
+
break;
|
195 |
+
default:
|
196 |
+
// TODO - Should we be returning false, this means the setting hasn't been initialized?
|
197 |
+
return false;
|
198 |
+
}
|
199 |
+
|
200 |
+
} catch (Exception $e) {
|
201 |
+
Mage::log(__METHOD__.": Problem while attempting to access the SearchSpring service: " . $e->getMessage());
|
202 |
+
return false;
|
203 |
+
}
|
204 |
+
|
205 |
+
return $response;
|
206 |
+
}
|
207 |
+
|
208 |
/**
|
209 |
* Intended for layout action parameter helper calls
|
210 |
*
|
220 |
return Mage::app()->getLayout()->getBlock($block)->getTemplate();
|
221 |
}
|
222 |
|
223 |
+
/**
|
224 |
+
* Check if module exists and is enabled in global config.
|
225 |
+
*
|
226 |
+
* NOTE: This function most likely exists in the parent, but
|
227 |
+
* may not depending on the version of magento installed.
|
228 |
+
*
|
229 |
+
* @param string $moduleName the full module name, example Mage_Core
|
230 |
+
* @return boolean
|
231 |
+
*/
|
232 |
+
public function isModuleEnabled($moduleName = null)
|
233 |
+
{
|
234 |
+
if (!Mage::getConfig()->getNode('modules/' . $moduleName)) {
|
235 |
+
return false;
|
236 |
+
}
|
237 |
+
|
238 |
+
$isActive = Mage::getConfig()->getNode('modules/' . $moduleName . '/active');
|
239 |
+
if (!$isActive || !in_array((string)$isActive, array('true', '1'))) {
|
240 |
+
return false;
|
241 |
+
}
|
242 |
+
return true;
|
243 |
+
}
|
244 |
|
245 |
}
|
app/code/community/SearchSpring/Manager/Helper/Http.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* File Data.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Helper_Http
|
10 |
+
*
|
11 |
+
* Helper for all things HTTP
|
12 |
+
*
|
13 |
+
* @author Jake Shelby <jake@searchspring.com>
|
14 |
+
*/
|
15 |
+
class SearchSpring_Manager_Helper_Http extends Mage_Core_Helper_Http
|
16 |
+
{
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Validate Simple Authentication, just return wether or
|
20 |
+
* not the basic auth credentials were provided
|
21 |
+
*
|
22 |
+
* @return mixed bool|array
|
23 |
+
*/
|
24 |
+
public function isSimpleAuthProvided($itemized = false) {
|
25 |
+
|
26 |
+
list($username, $password) = $this->getBasicAuthCredentials();
|
27 |
+
|
28 |
+
if ($itemized) {
|
29 |
+
return array(!empty($username), !empty($password));
|
30 |
+
} else {
|
31 |
+
return !(empty($username) || empty($password));
|
32 |
+
}
|
33 |
+
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Validate Simple Authentication, just return wether or
|
38 |
+
* not the basic auth credentials match for Manager's settings
|
39 |
+
*
|
40 |
+
* @return bool
|
41 |
+
*/
|
42 |
+
public function isSimpleAuthValid() {
|
43 |
+
|
44 |
+
if (!$this->isSimpleAuthProvided()) {
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
|
48 |
+
list($username, $password) = $this->getBasicAuthCredentials();
|
49 |
+
|
50 |
+
// Get Valid Credentials from configuration
|
51 |
+
$helper = Mage::helper('searchspring_manager');
|
52 |
+
$feedId = $helper->getApiFeedId();
|
53 |
+
$secretKey = $helper->getApiSecretKey();
|
54 |
+
|
55 |
+
return ($feedId == $username && $secretKey == $password);
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Get Basic Auth Credentials
|
60 |
+
*
|
61 |
+
* @param array $headers
|
62 |
+
* @return array
|
63 |
+
*/
|
64 |
+
public function getBasicAuthCredentials($headers = null)
|
65 |
+
{
|
66 |
+
$creds = $this->authValidate($headers);
|
67 |
+
return $creds;
|
68 |
+
}
|
69 |
+
|
70 |
+
public function authFailed()
|
71 |
+
{
|
72 |
+
// Overriding this to not do anything
|
73 |
+
// Original function sends 'require auth' headers and exits
|
74 |
+
}
|
75 |
+
|
76 |
+
}
|
app/code/community/SearchSpring/Manager/Helper/Oauth.php
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* File Oauth.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Helper_Oauth
|
10 |
+
*
|
11 |
+
* @author Jake Shelby <jake@b7interactive.com>
|
12 |
+
*/
|
13 |
+
class SearchSpring_Manager_Helper_Oauth extends Mage_Core_Helper_Abstract
|
14 |
+
{
|
15 |
+
|
16 |
+
const XML_PATH_REGISTERED_USER = 'ssmanager/ssmanager_api/magento_api_admin_user';
|
17 |
+
const XML_PATH_REGISTERED_USER_ROLE = 'ssmanager/ssmanager_api/magento_api_admin_user_role';
|
18 |
+
const XML_PATH_REGISTERED_CONSUMER = 'ssmanager/ssmanager_api/magento_api_oauth_consumer';
|
19 |
+
|
20 |
+
protected $_adminUser;
|
21 |
+
protected $_adminUserRole;
|
22 |
+
protected $_consumer;
|
23 |
+
|
24 |
+
public function hlp() {
|
25 |
+
return Mage::helper('searchspring_manager');
|
26 |
+
}
|
27 |
+
|
28 |
+
public function getAdminUser() {
|
29 |
+
if (is_null($this->_adminUser)) {
|
30 |
+
$model = Mage::getModel('admin/user')->load($this->getAdminUserId());
|
31 |
+
if (!$model->getId()) $model = false;
|
32 |
+
$this->_adminUser = $model;
|
33 |
+
}
|
34 |
+
|
35 |
+
return $this->_adminUser;
|
36 |
+
}
|
37 |
+
|
38 |
+
public function getAdminUserRole() {
|
39 |
+
if (is_null($this->_adminUserRole)) {
|
40 |
+
$model = Mage::getModel('api2/acl_global_role')->load($this->getAdminUserRoleId());
|
41 |
+
if (!$model->getId()) $model = false;
|
42 |
+
$this->_adminUserRole = $model;
|
43 |
+
}
|
44 |
+
|
45 |
+
return $this->_adminUserRole;
|
46 |
+
}
|
47 |
+
|
48 |
+
public function getConsumer() {
|
49 |
+
if (is_null($this->_consumer)) {
|
50 |
+
$model = Mage::getModel('oauth/consumer')->load($this->getConsumerId());
|
51 |
+
if (!$model->getId()) $model = false;
|
52 |
+
$this->_consumer = $model;
|
53 |
+
}
|
54 |
+
|
55 |
+
return $this->_consumer;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function ensureOAuthResourcesInitialized() {
|
59 |
+
|
60 |
+
return $this->_initializeOAuthResources();
|
61 |
+
|
62 |
+
}
|
63 |
+
|
64 |
+
public function getAdminUserId() {
|
65 |
+
return Mage::getStoreConfig(self::XML_PATH_REGISTERED_USER);
|
66 |
+
}
|
67 |
+
|
68 |
+
public function getAdminUserRoleId() {
|
69 |
+
return Mage::getStoreConfig(self::XML_PATH_REGISTERED_USER_ROLE);
|
70 |
+
}
|
71 |
+
|
72 |
+
public function getConsumerId() {
|
73 |
+
return Mage::getStoreConfig(self::XML_PATH_REGISTERED_CONSUMER);
|
74 |
+
}
|
75 |
+
|
76 |
+
protected function _setAdminUser($model) {
|
77 |
+
$this->hlp()->writeStoreConfig(self::XML_PATH_REGISTERED_USER, $model->getId());
|
78 |
+
return $this->_adminUser = $model;
|
79 |
+
}
|
80 |
+
|
81 |
+
protected function _setAdminUserRole($model) {
|
82 |
+
$this->hlp()->writeStoreConfig(self::XML_PATH_REGISTERED_USER_ROLE, $model->getId());
|
83 |
+
return $this->_adminUserRole = $model;
|
84 |
+
}
|
85 |
+
|
86 |
+
protected function _setConsumer($model) {
|
87 |
+
$this->hlp()->writeStoreConfig(self::XML_PATH_REGISTERED_CONSUMER, $model->getId());
|
88 |
+
return $this->_consumer = $model;
|
89 |
+
}
|
90 |
+
|
91 |
+
protected function _initializeOAuthResources() {
|
92 |
+
|
93 |
+
// Ensure Admin User exists
|
94 |
+
$user = $this->_ensureAdminUser();
|
95 |
+
|
96 |
+
// Ensure REST role exists
|
97 |
+
$this->_ensureRole($user->getId());
|
98 |
+
|
99 |
+
// Ensure oAuth Consumer exists
|
100 |
+
$this->_ensureConsumer();
|
101 |
+
|
102 |
+
}
|
103 |
+
|
104 |
+
protected function _ensureAdminUser() {
|
105 |
+
if ($user = $this->getAdminUser()) return $user;
|
106 |
+
$user = $this->_createAdminUser();
|
107 |
+
return $this->_setAdminUser($user);
|
108 |
+
}
|
109 |
+
|
110 |
+
protected function _ensureRole($userId) {
|
111 |
+
if ($role = $this->getAdminUserRole()) return $role;
|
112 |
+
$role = $this->_createRole($userId);
|
113 |
+
return $this->_setAdminUserRole($role);
|
114 |
+
}
|
115 |
+
|
116 |
+
protected function _ensureConsumer() {
|
117 |
+
if ($consumer = $this->getConsumer()) return $consumer;
|
118 |
+
$consumer = $this->_createConsumer();
|
119 |
+
return $this->_setConsumer($consumer);
|
120 |
+
}
|
121 |
+
|
122 |
+
protected function _createAdminUser() {
|
123 |
+
$ohlp = Mage::helper('oauth');
|
124 |
+
$email = Mage::getConfig()->getNode('global/searchspring/api_auth/admin_user/email');
|
125 |
+
|
126 |
+
$data = array(
|
127 |
+
'username' => Mage::getConfig()->getNode('global/searchspring/api_auth/admin_user/username'),
|
128 |
+
'firstname' => Mage::getConfig()->getNode('global/searchspring/api_auth/admin_user/firstname'),
|
129 |
+
'lastname' => Mage::getConfig()->getNode('global/searchspring/api_auth/admin_user/lastname'),
|
130 |
+
'email' => $email,
|
131 |
+
'password' => $ohlp->generateConsumerKey(), // Something random for security
|
132 |
+
'is_active' => '1',
|
133 |
+
);
|
134 |
+
|
135 |
+
// First Check for user with email, remove it
|
136 |
+
$this->_removeAdminUserWithEmail($email);
|
137 |
+
|
138 |
+
// Create a brand new user with data
|
139 |
+
$user = Mage::getModel('admin/user');
|
140 |
+
$user->setData($data);
|
141 |
+
|
142 |
+
return $user->save();
|
143 |
+
}
|
144 |
+
|
145 |
+
protected function _createRole($userId) {
|
146 |
+
$name = Mage::getConfig()->getNode('global/searchspring/api_auth/role/label');
|
147 |
+
|
148 |
+
$role = Mage::getModel('api2/acl_global_role');
|
149 |
+
$role->setRoleName($name);
|
150 |
+
$role->save();
|
151 |
+
|
152 |
+
$this->_addUserToRole($userId, $role->getId());
|
153 |
+
|
154 |
+
// TODO -- pull resource id and privilege keys from api2 config...
|
155 |
+
$resourceId = 'searchspring';
|
156 |
+
$privileges = array(
|
157 |
+
'create',
|
158 |
+
'retrieve',
|
159 |
+
'update',
|
160 |
+
'delete',
|
161 |
+
);
|
162 |
+
$rule = Mage::getModel('api2/acl_global_rule');
|
163 |
+
foreach($privileges as $privilege) {
|
164 |
+
$rule->setId(null)->isObjectNew(true);
|
165 |
+
$rule->setRoleId($role->getId())
|
166 |
+
->setResourceId($resourceId)
|
167 |
+
->setPrivilege($privilege)
|
168 |
+
->save();
|
169 |
+
}
|
170 |
+
|
171 |
+
return $role;
|
172 |
+
}
|
173 |
+
|
174 |
+
protected function _addUserToRole($userId, $roleId) {
|
175 |
+
$resourceModel = Mage::getResourceModel('api2/acl_global_role');
|
176 |
+
$resourceModel->saveAdminToRoleRelation($userId, $roleId);
|
177 |
+
}
|
178 |
+
|
179 |
+
protected function _createConsumer() {
|
180 |
+
$ohlp = Mage::helper('oauth');
|
181 |
+
|
182 |
+
$consumerName = Mage::getConfig()->getNode('global/searchspring/api_auth/consumer/label');
|
183 |
+
$consumerKey = $ohlp->generateConsumerKey();
|
184 |
+
$consumerSecret = $ohlp->generateConsumerSecret();
|
185 |
+
|
186 |
+
$consumer = Mage::getModel('oauth/consumer');
|
187 |
+
$consumer->setName($consumerName);
|
188 |
+
$consumer->setKey($consumerKey);
|
189 |
+
$consumer->setSecret($consumerSecret);
|
190 |
+
|
191 |
+
return $consumer->save();
|
192 |
+
}
|
193 |
+
|
194 |
+
protected function _removeAdminUserWithEmail($email) {
|
195 |
+
$collection = Mage::getModel('admin/user')->getCollection();
|
196 |
+
$collection->addFieldToFilter('email', $email);
|
197 |
+
|
198 |
+
// If admin user exists, delete it
|
199 |
+
$user = $collection->getFirstItem();
|
200 |
+
if ($user->getId()) {
|
201 |
+
$user->delete();
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
}
|
app/code/community/SearchSpring/Manager/Helper/Product.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* File Product.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Helper_Product
|
10 |
+
*
|
11 |
+
* Some helpful utilities related to products
|
12 |
+
*
|
13 |
+
* @author Jake Shelby <jake@searchspring.com>
|
14 |
+
*/
|
15 |
+
class SearchSpring_Manager_Helper_Product extends Mage_Core_Helper_Abstract
|
16 |
+
{
|
17 |
+
|
18 |
+
public function getAttributeText($product, $attributeCode) {
|
19 |
+
|
20 |
+
$attribute = $product->getResource()->getAttribute($attributeCode);
|
21 |
+
|
22 |
+
// If the attribute type uses a set number of options, we need to resolve the id
|
23 |
+
if ($attribute->getFrontendInput() === 'select' ||
|
24 |
+
$attribute->getFrontendInput() === 'multiselect'
|
25 |
+
) {
|
26 |
+
return $product->getAttributeText($attributeCode);
|
27 |
+
} else {
|
28 |
+
return $product->getData($attributeCode);
|
29 |
+
}
|
30 |
+
|
31 |
+
}
|
32 |
+
|
33 |
+
}
|
app/code/community/SearchSpring/Manager/Helper/Webservice.php
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* File Webservice.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Helper_Webservice
|
10 |
+
*
|
11 |
+
* @author Jake Shelby <jake@searchspring.com>
|
12 |
+
*/
|
13 |
+
class SearchSpring_Manager_Helper_Webservice extends Mage_Core_Helper_Abstract
|
14 |
+
{
|
15 |
+
|
16 |
+
const PATH_FEED_AUTH_METHOD_SET = "/api/manage/feeds/auth-method/%s/set.json";
|
17 |
+
const PATH_FEED_AUTH_METHOD_VERIFY = "/api/manage/feeds/auth-method/%s/verify.json";
|
18 |
+
|
19 |
+
// SearchSpring Specific Authentication Codes
|
20 |
+
const AUTH_METHOD_SIMPLE = 'simple';
|
21 |
+
const AUTH_METHOD_OAUTH = 'o-auth';
|
22 |
+
|
23 |
+
public function verifyMageAPIAuthSimple() {
|
24 |
+
$path = sprintf(self::PATH_FEED_AUTH_METHOD_VERIFY, self::AUTH_METHOD_SIMPLE);
|
25 |
+
$response = $this->callSearchSpringWebservice($path, $this->getAuthMethodParamsForSimple());
|
26 |
+
return $this->isResponseStatusSuccess($response);
|
27 |
+
}
|
28 |
+
|
29 |
+
public function registerMageAPIAuthSimple() {
|
30 |
+
$path = sprintf(self::PATH_FEED_AUTH_METHOD_SET, self::AUTH_METHOD_SIMPLE);
|
31 |
+
$response = $this->callSearchSpringWebservice($path, $this->getAuthMethodParamsForSimple());
|
32 |
+
return $this->isResponseSuccess($response);
|
33 |
+
}
|
34 |
+
|
35 |
+
public function verifyMageAPIAuthOAuth() {
|
36 |
+
$path = sprintf(self::PATH_FEED_AUTH_METHOD_VERIFY, self::AUTH_METHOD_OAUTH);
|
37 |
+
$response = $this->callSearchSpringWebservice($path, $this->getAuthMethodParamsForOAuth());
|
38 |
+
return $this->isResponseStatusSuccess($response);
|
39 |
+
}
|
40 |
+
|
41 |
+
public function registerMageAPIAuthOAuth() {
|
42 |
+
$path = sprintf(self::PATH_FEED_AUTH_METHOD_SET, self::AUTH_METHOD_OAUTH);
|
43 |
+
$response = $this->callSearchSpringWebservice($path, $this->getAuthMethodParamsForOAuth());
|
44 |
+
return $this->isResponseSuccess($response);
|
45 |
+
}
|
46 |
+
|
47 |
+
public function callSearchSpringWebservice($path, $params = array()) {
|
48 |
+
|
49 |
+
$hlp = Mage::helper('searchspring_manager');
|
50 |
+
|
51 |
+
$apiHost = $hlp->getApiBaseUrl();
|
52 |
+
$siteId = $hlp->getApiSiteId();
|
53 |
+
$secret = $hlp->getApiSecretKey();
|
54 |
+
$feedId = $hlp->getApiFeedId();
|
55 |
+
|
56 |
+
if (empty($apiHost) || empty($siteId) || empty($secret) || empty($feedId)) {
|
57 |
+
// Can't do much without these first
|
58 |
+
return false;
|
59 |
+
}
|
60 |
+
|
61 |
+
$url = $apiHost . $path;
|
62 |
+
|
63 |
+
$client = new Zend_Http_Client($url, array(
|
64 |
+
'maxredirects' => 0,
|
65 |
+
'timeout'=>30
|
66 |
+
));
|
67 |
+
$client->setAuth($siteId, $secret);
|
68 |
+
|
69 |
+
$params = array_merge(array('feedId' => $feedId), $params);
|
70 |
+
|
71 |
+
$client->setParameterGet($params);
|
72 |
+
|
73 |
+
$response = $client->request();
|
74 |
+
|
75 |
+
return $response;
|
76 |
+
}
|
77 |
+
|
78 |
+
public function isResponseSuccess($response) {
|
79 |
+
if (!$response) {
|
80 |
+
return false;
|
81 |
+
}
|
82 |
+
return $response->isSuccessful();
|
83 |
+
}
|
84 |
+
|
85 |
+
public function isResponseStatusSuccess($response) {
|
86 |
+
if (!$this->isResponseSuccess($response)) return false;
|
87 |
+
$responseData = Zend_Json::decode($response->getBody(), Zend_Json::TYPE_OBJECT);
|
88 |
+
if (!is_object($responseData)) return false;
|
89 |
+
if ($responseData->status !== 'success') return false;
|
90 |
+
return true;
|
91 |
+
}
|
92 |
+
|
93 |
+
protected function getAuthMethodParamsForSimple() {
|
94 |
+
// Nothing needed
|
95 |
+
return array();
|
96 |
+
}
|
97 |
+
|
98 |
+
protected function getAuthMethodParamsForOAuth() {
|
99 |
+
|
100 |
+
$oahlp = Mage::helper('searchspring_manager/oauth');
|
101 |
+
if (!($consumer = $oahlp->getConsumer())) {
|
102 |
+
// Can't do much without these
|
103 |
+
return array();
|
104 |
+
}
|
105 |
+
$cKey = $consumer->getKey();
|
106 |
+
$cSecret = $consumer->getSecret();
|
107 |
+
|
108 |
+
return array(
|
109 |
+
'consumerKey' => $cKey,
|
110 |
+
'consumerSecret' => $cSecret,
|
111 |
+
'type' => 'magento_indexing',
|
112 |
+
);
|
113 |
+
}
|
114 |
+
|
115 |
+
}
|
app/code/community/SearchSpring/Manager/Model/Api2/Auth/Adapter.php
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* 0-Legged oAuth Authentication adapter, oAuth direct consumer - provider, minus the user
|
4 |
+
*
|
5 |
+
*/
|
6 |
+
class SearchSpring_Manager_Model_Api2_Auth_Adapter extends Mage_Api2_Model_Auth_Adapter_Abstract
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Process request and figure out an API user type and its identifier
|
10 |
+
*
|
11 |
+
* Returns stdClass object with two properties: type and id
|
12 |
+
*
|
13 |
+
* @param Mage_Api2_Model_Request $request
|
14 |
+
* @return stdClass
|
15 |
+
*/
|
16 |
+
public function getUserParams(Mage_Api2_Model_Request $request)
|
17 |
+
{
|
18 |
+
// Get our oauth server, with 0-Legged Support
|
19 |
+
$oauthServer = Mage::getModel('searchspring_manager/oauth_server', $request);
|
20 |
+
$hlp = Mage::helper('searchspring_manager');
|
21 |
+
$oahlp = Mage::helper('searchspring_manager/oauth');
|
22 |
+
|
23 |
+
try {
|
24 |
+
|
25 |
+
// Make sure this Authentication Method is enabled
|
26 |
+
if ($hlp->getAuthenticationMethod() != 'oauth') {
|
27 |
+
throw new Mage_Api2_Exception('Not authorized', Mage_Api2_Model_Server::HTTP_UNAUTHORIZED);
|
28 |
+
}
|
29 |
+
|
30 |
+
// Authenticate Consumer
|
31 |
+
$consumer = $oauthServer->checkDirectConsumerRequest();
|
32 |
+
|
33 |
+
// Validate Consumer Identity, with the one configured to use this API
|
34 |
+
if ($consumer->getId() != $oahlp->getConsumerId()) {
|
35 |
+
throw new Mage_Api2_Exception('Not authorized', Mage_Api2_Model_Server::HTTP_UNAUTHORIZED);
|
36 |
+
}
|
37 |
+
|
38 |
+
// Return user configured to use this API
|
39 |
+
$adminId = $oahlp->getAdminUserId();
|
40 |
+
$userParamsObj = (object) array('type' => Mage_Oauth_Model_Token::USER_TYPE_ADMIN, 'id' => $adminId);
|
41 |
+
|
42 |
+
} catch (Exception $e) {
|
43 |
+
throw new Mage_Api2_Exception($oauthServer->reportProblem($e), Mage_Api2_Model_Server::HTTP_UNAUTHORIZED);
|
44 |
+
}
|
45 |
+
|
46 |
+
return $userParamsObj;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Check if request contains authentication info for adapter
|
51 |
+
*
|
52 |
+
* @param Mage_Api2_Model_Request $request
|
53 |
+
* @return boolean
|
54 |
+
*/
|
55 |
+
public function isApplicableToRequest(Mage_Api2_Model_Request $request)
|
56 |
+
{
|
57 |
+
// Check if request is for searchspring apis, and the request is for oauth
|
58 |
+
$uri = $request->getRequestUri();
|
59 |
+
if (preg_match('!/api/rest/searchspring/.*!i', $uri)) {
|
60 |
+
return true;
|
61 |
+
}
|
62 |
+
|
63 |
+
return false;
|
64 |
+
}
|
65 |
+
|
66 |
+
}
|
app/code/community/SearchSpring/Manager/Model/Api2/Indexing/Rest/Admin/V1.php
ADDED
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class SearchSpring_Manager_Model_Api2_Indexing_Rest_Admin_V1 extends Mage_Api2_Model_Resource
|
3 |
+
{
|
4 |
+
/**
|
5 |
+
* Default starting point if no parameter is set
|
6 |
+
*/
|
7 |
+
const OFFSET_DEFAULT = 0;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Default product limit if no parameter is set
|
11 |
+
*/
|
12 |
+
const COUNT_DEFAULT = 100;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Default store if no parameter is set
|
16 |
+
*/
|
17 |
+
const STORE_DEFAULT = 'default';
|
18 |
+
|
19 |
+
|
20 |
+
// Live Indexing
|
21 |
+
public function _create()
|
22 |
+
{
|
23 |
+
$this->fetchById();
|
24 |
+
}
|
25 |
+
|
26 |
+
// Data Feed Generation
|
27 |
+
public function _retrieve()
|
28 |
+
{
|
29 |
+
$this->generateFeed();
|
30 |
+
}
|
31 |
+
|
32 |
+
public function _update()
|
33 |
+
{
|
34 |
+
throw new Exception('Not Implemented');
|
35 |
+
}
|
36 |
+
|
37 |
+
public function _delete()
|
38 |
+
{
|
39 |
+
throw new Exception('Not Implemented');
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Default action for updating based on product/category id
|
44 |
+
*
|
45 |
+
* Parameters:
|
46 |
+
* type (required): The type of id passed in. Can be 'product' or 'category'
|
47 |
+
* ids (required): An array of ids
|
48 |
+
*/
|
49 |
+
public function fetchById()
|
50 |
+
{
|
51 |
+
// TODO can we get rid of the SearchSpring_Manager_Request_JSON object?
|
52 |
+
// $request = new SearchSpring_Manager_Request_JSON($this->getRequest());
|
53 |
+
|
54 |
+
$request = $this->getRequest()->getBodyParams();
|
55 |
+
|
56 |
+
if(!is_array($request)) {
|
57 |
+
$this->setJsonResponse(
|
58 |
+
array(
|
59 |
+
'status' => 'error',
|
60 |
+
'errorCode' => SearchSpring_ErrorCodes::BAD_REQUEST,
|
61 |
+
'message' => 'Invalid request format'
|
62 |
+
),
|
63 |
+
400
|
64 |
+
);
|
65 |
+
|
66 |
+
return;
|
67 |
+
}
|
68 |
+
|
69 |
+
$type = $request['type'];
|
70 |
+
$ids = $request['ids'];
|
71 |
+
|
72 |
+
if (null === $type) {
|
73 |
+
$this->setJsonResponse(
|
74 |
+
array(
|
75 |
+
'status' => 'error',
|
76 |
+
'errorCode' => SearchSpring_ErrorCodes::TYPE_NOT_SET,
|
77 |
+
'message' => 'Type must be specified'
|
78 |
+
),
|
79 |
+
400
|
80 |
+
);
|
81 |
+
|
82 |
+
return;
|
83 |
+
}
|
84 |
+
|
85 |
+
if (null === $ids) {
|
86 |
+
$this->setJsonResponse(
|
87 |
+
array(
|
88 |
+
'status' => 'error',
|
89 |
+
'errorCode' => SearchSpring_ErrorCodes::IDS_NOT_SET,
|
90 |
+
'message' => 'Ids must be specified'
|
91 |
+
),
|
92 |
+
400
|
93 |
+
);
|
94 |
+
|
95 |
+
return;
|
96 |
+
}
|
97 |
+
|
98 |
+
if(empty($request['store'])) {
|
99 |
+
$request['store'] = self::STORE_DEFAULT;
|
100 |
+
}
|
101 |
+
$store = $request['store'];
|
102 |
+
|
103 |
+
$requestParams = new SearchSpring_Manager_Entity_RequestParams(
|
104 |
+
(int)$request['size'],
|
105 |
+
(int)$request['start'],
|
106 |
+
$store
|
107 |
+
);
|
108 |
+
|
109 |
+
$params = array('ids' => $ids);
|
110 |
+
|
111 |
+
$generatorFactory = new SearchSpring_Manager_Factory_GeneratorFactory();
|
112 |
+
$generator = $generatorFactory->make($type, $requestParams, $params);
|
113 |
+
$message = $generator->generate();
|
114 |
+
|
115 |
+
$this->setJsonResponse($message);
|
116 |
+
}
|
117 |
+
|
118 |
+
function generateFeed() {
|
119 |
+
// check file is writable first
|
120 |
+
if (!is_writable(Mage::getBaseDir())) {
|
121 |
+
$this->setJsonResponse(
|
122 |
+
array(
|
123 |
+
'status' => 'error',
|
124 |
+
'errorCode' => SearchSpring_ErrorCodes::DIR_NOT_WRITABLE,
|
125 |
+
'message' => 'Magento base directory is not writable'
|
126 |
+
),
|
127 |
+
500
|
128 |
+
);
|
129 |
+
|
130 |
+
return;
|
131 |
+
}
|
132 |
+
|
133 |
+
$uniqueFilename = $this->getRequest()->getParam('filename');
|
134 |
+
|
135 |
+
if (null === $uniqueFilename) {
|
136 |
+
$this->setJsonResponse(
|
137 |
+
array(
|
138 |
+
'status' => 'error',
|
139 |
+
'errorCode' => SearchSpring_ErrorCodes::FILENAME_NOT_SET,
|
140 |
+
'message' => 'Unique filename must be passed in'
|
141 |
+
),
|
142 |
+
400
|
143 |
+
);
|
144 |
+
|
145 |
+
return;
|
146 |
+
}
|
147 |
+
|
148 |
+
$requestParams = new SearchSpring_Manager_Entity_RequestParams(
|
149 |
+
(int)$this->getRequest()->getParam('count', self::COUNT_DEFAULT),
|
150 |
+
(int)$this->getRequest()->getParam('start', self::OFFSET_DEFAULT),
|
151 |
+
$this->getRequest()->getParam('store', self::STORE_DEFAULT)
|
152 |
+
);
|
153 |
+
|
154 |
+
$params = array('filename' => $uniqueFilename);
|
155 |
+
|
156 |
+
$generatorFactory = new SearchSpring_Manager_Factory_GeneratorFactory();
|
157 |
+
$generator = $generatorFactory->make(
|
158 |
+
SearchSpring_Manager_Factory_GeneratorFactory::TYPE_FEED,
|
159 |
+
$requestParams,
|
160 |
+
$params
|
161 |
+
);
|
162 |
+
|
163 |
+
$message = $generator->generate();
|
164 |
+
|
165 |
+
$this->setTextResponse($message);
|
166 |
+
|
167 |
+
return;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Set appropriate response variables for a json response
|
172 |
+
*
|
173 |
+
* @param array $message The message that should be sent back
|
174 |
+
* @param int $responseCode The Http response code
|
175 |
+
*/
|
176 |
+
private function setJsonResponse(array $message, $responseCode = 200)
|
177 |
+
{
|
178 |
+
header('Content-type: application/json', true, $responseCode);
|
179 |
+
echo json_encode($message);
|
180 |
+
exit();
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Set a text based response
|
185 |
+
*
|
186 |
+
* @param string $message
|
187 |
+
* @param int $responseCode
|
188 |
+
*/
|
189 |
+
private function setTextResponse($message, $responseCode = 200)
|
190 |
+
{
|
191 |
+
header('Content-type: text/plain', true, $responseCode);
|
192 |
+
echo $message;
|
193 |
+
exit();
|
194 |
+
|
195 |
+
}
|
196 |
+
}
|
197 |
+
?>
|
app/code/community/SearchSpring/Manager/Model/Oauth/Server.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* oAuth Server With 0-Legged Support
|
5 |
+
*
|
6 |
+
*/
|
7 |
+
class SearchSpring_Manager_Model_Oauth_Server extends Mage_Oauth_Model_Server
|
8 |
+
{
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Validate request with consumer token, and empty access token
|
12 |
+
*/
|
13 |
+
public function checkDirectConsumerRequest()
|
14 |
+
{
|
15 |
+
// get parameters from request
|
16 |
+
$this->_fetchParams();
|
17 |
+
|
18 |
+
// make generic validation of request parameters
|
19 |
+
$this->_validateProtocolParams();
|
20 |
+
|
21 |
+
// initialize consumer
|
22 |
+
$this->_initConsumer();
|
23 |
+
|
24 |
+
// generate empty token (to validate against)
|
25 |
+
$this->_token = new Varien_Object(array('secret' => ''));
|
26 |
+
|
27 |
+
// validate signature
|
28 |
+
$this->_validateSignature();
|
29 |
+
|
30 |
+
return $this->_consumer;
|
31 |
+
}
|
32 |
+
|
33 |
+
}
|
app/code/community/SearchSpring/Manager/Model/Observer/CategorySaveObserver.php
CHANGED
@@ -21,13 +21,6 @@ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpr
|
|
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 |
*
|
@@ -40,8 +33,6 @@ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpr
|
|
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 |
}
|
@@ -82,7 +73,7 @@ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpr
|
|
82 |
SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_CATEGORY,
|
83 |
$category->getAllChildren(true)
|
84 |
);
|
85 |
-
$this->
|
86 |
|
87 |
return true;
|
88 |
}
|
@@ -117,7 +108,7 @@ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpr
|
|
117 |
$productIds
|
118 |
);
|
119 |
|
120 |
-
$this->
|
121 |
|
122 |
return true;
|
123 |
}
|
@@ -149,7 +140,7 @@ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpr
|
|
149 |
SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_CATEGORY,
|
150 |
$category->getAllChildren(true)
|
151 |
);
|
152 |
-
$this->
|
153 |
|
154 |
return true;
|
155 |
}
|
@@ -174,7 +165,7 @@ class SearchSpring_Manager_Model_Observer_CategorySaveObserver extends SearchSpr
|
|
174 |
SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_PRODUCT,
|
175 |
$this->getCategoryProductIds($category)
|
176 |
);
|
177 |
-
$this->
|
178 |
|
179 |
return true;
|
180 |
}
|
21 |
*/
|
22 |
private $varienObjectData;
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
/**
|
25 |
* Create a request body
|
26 |
*
|
33 |
*/
|
34 |
public function __construct()
|
35 |
{
|
|
|
|
|
36 |
$this->requestBodyFactory = new SearchSpring_Manager_Factory_IndexingRequestBodyFactory();
|
37 |
$this->varienObjectData = new SearchSpring_Manager_VarienObject_Data();
|
38 |
}
|
73 |
SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_CATEGORY,
|
74 |
$category->getAllChildren(true)
|
75 |
);
|
76 |
+
$this->apiPushProductIds($requestBody);
|
77 |
|
78 |
return true;
|
79 |
}
|
108 |
$productIds
|
109 |
);
|
110 |
|
111 |
+
$this->apiPushProductIds($requestBody);
|
112 |
|
113 |
return true;
|
114 |
}
|
140 |
SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_CATEGORY,
|
141 |
$category->getAllChildren(true)
|
142 |
);
|
143 |
+
$this->apiPushProductIds($requestBody);
|
144 |
|
145 |
return true;
|
146 |
}
|
165 |
SearchSpring_Manager_Entity_IndexingRequestBody::TYPE_PRODUCT,
|
166 |
$this->getCategoryProductIds($category)
|
167 |
);
|
168 |
+
$this->apiPushProductIds($requestBody);
|
169 |
|
170 |
return true;
|
171 |
}
|
app/code/community/SearchSpring/Manager/Model/Observer/ConfigObserver.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* ConfigObserver.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Model_Observer_ConfigObserver
|
10 |
+
*
|
11 |
+
* On a category change, trigger one of these methods
|
12 |
+
*
|
13 |
+
*/
|
14 |
+
class SearchSpring_Manager_Model_Observer_ConfigObserver {
|
15 |
+
|
16 |
+
const NEW_CONNECTION_SETUP_PARAM = 'searchspring_new_connection_fl';
|
17 |
+
|
18 |
+
public function afterSystemConfigSectionChanged(Varien_Event_Observer $event) {
|
19 |
+
|
20 |
+
$hlp = Mage::helper('core');
|
21 |
+
|
22 |
+
// Make sure we are in the admin panel
|
23 |
+
if (!Mage::app()->getStore()->isAdmin()) {
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
|
27 |
+
if ($this->hasNewConnectionBeenSetup()) {
|
28 |
+
|
29 |
+
// TODO - should we require admin permissions here
|
30 |
+
|
31 |
+
try {
|
32 |
+
|
33 |
+
// Initialize Resources needed for auth method
|
34 |
+
$this->_initializeAuthMethod();
|
35 |
+
|
36 |
+
// Register Auth Method With Search Spring API
|
37 |
+
$this->_registerAuthMethodWithSearchSpring();
|
38 |
+
|
39 |
+
} catch (Exception $e) {
|
40 |
+
|
41 |
+
Mage::logException($e);
|
42 |
+
|
43 |
+
$message = $hlp->__('There was a problem while attempting to setup your SearchSpring account [E938]');
|
44 |
+
$session = Mage::getSingleton('adminhtml/session');
|
45 |
+
$session->addWarning($message);
|
46 |
+
|
47 |
+
}
|
48 |
+
|
49 |
+
}
|
50 |
+
|
51 |
+
}
|
52 |
+
|
53 |
+
public function hasNewConnectionBeenSetup() {
|
54 |
+
$param = Mage::app()->getRequest()->getParam(self::NEW_CONNECTION_SETUP_PARAM);
|
55 |
+
return (bool) $param;
|
56 |
+
}
|
57 |
+
|
58 |
+
protected function _initializeAuthMethod() {
|
59 |
+
|
60 |
+
$hlp = Mage::helper('searchspring_manager');
|
61 |
+
switch ($hlp->getAuthenticationMethod()) {
|
62 |
+
|
63 |
+
case SearchSpring_Manager_Helper_Data::AUTH_METHOD_SIMPLE:
|
64 |
+
// Nothing to initialize for this auth method
|
65 |
+
break;
|
66 |
+
|
67 |
+
case SearchSpring_Manager_Helper_Data::AUTH_METHOD_OAUTH:
|
68 |
+
// Initialize oAuth Resources for API access
|
69 |
+
$this->_initializeOAuthResources();
|
70 |
+
break;
|
71 |
+
|
72 |
+
}
|
73 |
+
|
74 |
+
}
|
75 |
+
|
76 |
+
protected function _initializeOAuthResources() {
|
77 |
+
$oahlp = Mage::helper('searchspring_manager/oauth');
|
78 |
+
$oahlp->ensureOAuthResourcesInitialized();
|
79 |
+
}
|
80 |
+
|
81 |
+
protected function _registerAuthMethodWithSearchSpring() {
|
82 |
+
|
83 |
+
$success = Mage::helper('searchspring_manager')
|
84 |
+
->registerMagentoAPIAuthenticationWithSearchSpring();
|
85 |
+
|
86 |
+
if (!$success) {
|
87 |
+
$hlp = Mage::helper('core');
|
88 |
+
$message = $hlp->__('There was a problem while attempting to setup your SearchSpring account [E939]');
|
89 |
+
$session = Mage::getSingleton('adminhtml/session');
|
90 |
+
$session->addWarning($message);
|
91 |
+
}
|
92 |
+
|
93 |
+
}
|
94 |
+
|
95 |
+
}
|
app/code/community/SearchSpring/Manager/Model/Observer/LiveIndexer.php
CHANGED
@@ -13,6 +13,22 @@
|
|
13 |
abstract class SearchSpring_Manager_Model_Observer_LiveIndexer
|
14 |
{
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
/**
|
17 |
* Checks if live indexing is enabled
|
18 |
*
|
@@ -23,4 +39,26 @@ abstract class SearchSpring_Manager_Model_Observer_LiveIndexer
|
|
23 |
return Mage::helper('searchspring_manager')->isLiveIndexingEnabled();
|
24 |
}
|
25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
}
|
13 |
abstract class SearchSpring_Manager_Model_Observer_LiveIndexer
|
14 |
{
|
15 |
|
16 |
+
/**
|
17 |
+
* Api adapter for connecting to SearchSpring api
|
18 |
+
*
|
19 |
+
* @var SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter $api
|
20 |
+
*/
|
21 |
+
protected $api;
|
22 |
+
|
23 |
+
public function api()
|
24 |
+
{
|
25 |
+
if (is_null($this->api)) {
|
26 |
+
$apiFactory = new SearchSpring_Manager_Factory_ApiFactory();
|
27 |
+
$this->api = $apiFactory->make('index');
|
28 |
+
}
|
29 |
+
return $this->api;
|
30 |
+
}
|
31 |
+
|
32 |
/**
|
33 |
* Checks if live indexing is enabled
|
34 |
*
|
39 |
return Mage::helper('searchspring_manager')->isLiveIndexingEnabled();
|
40 |
}
|
41 |
|
42 |
+
protected function apiPushProductIds($request)
|
43 |
+
{
|
44 |
+
try {
|
45 |
+
$this->api()->pushIds($request);
|
46 |
+
} catch (UnexpectedValueException $e) {
|
47 |
+
$this->notifyAdminUser($e->getMessage());
|
48 |
+
} catch (Exception $e) {
|
49 |
+
$this->notifyAdminUser("SearchSpring: Unknown issue occurred while attempting live indexing operation.");
|
50 |
+
Mage::logException($e);
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
protected function notifyAdminUser($message)
|
55 |
+
{
|
56 |
+
// Only if we're in the admin panel
|
57 |
+
if (Mage::app()->getStore()->isAdmin()) {
|
58 |
+
$session = Mage::getSingleton('adminhtml/session');
|
59 |
+
$session->addWarning($message);
|
60 |
+
}
|
61 |
+
return $this;
|
62 |
+
}
|
63 |
+
|
64 |
}
|
app/code/community/SearchSpring/Manager/Model/Observer/ProductSaveObserver.php
CHANGED
@@ -14,12 +14,6 @@
|
|
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
|
@@ -41,8 +35,6 @@ class SearchSpring_Manager_Model_Observer_ProductSaveObserver extends SearchSpr
|
|
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 |
|
@@ -74,6 +66,11 @@ class SearchSpring_Manager_Model_Observer_ProductSaveObserver extends SearchSpr
|
|
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;
|
@@ -117,7 +114,7 @@ class SearchSpring_Manager_Model_Observer_ProductSaveObserver extends SearchSpr
|
|
117 |
}
|
118 |
|
119 |
// send ids to api
|
120 |
-
$this->
|
121 |
|
122 |
return true;
|
123 |
}
|
14 |
*/
|
15 |
class SearchSpring_Manager_Model_Observer_ProductSaveObserver extends SearchSpring_Manager_Model_Observer_LiveIndexer
|
16 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
/**
|
19 |
* Creates a request body
|
35 |
*/
|
36 |
public function __construct()
|
37 |
{
|
|
|
|
|
38 |
$this->requestBodyFactory = new SearchSpring_Manager_Factory_IndexingRequestBodyFactory();
|
39 |
}
|
40 |
|
66 |
* @return bool
|
67 |
*/
|
68 |
public function beforeDeletePushProduct(Varien_Event_Observer $productEvent) {
|
69 |
+
|
70 |
+
if (!$this->isEnabled()) {
|
71 |
+
return true;
|
72 |
+
}
|
73 |
+
|
74 |
$product = $productEvent->getData('product');
|
75 |
$this->_productIds = $this->_getProductIds($product);
|
76 |
return true;
|
114 |
}
|
115 |
|
116 |
// send ids to api
|
117 |
+
$this->apiPushProductIds($requestBody);
|
118 |
|
119 |
return true;
|
120 |
}
|
app/code/community/SearchSpring/Manager/Operation/Product.php
CHANGED
@@ -118,4 +118,19 @@ abstract class SearchSpring_Manager_Operation_Product implements SearchSpring_Ma
|
|
118 |
|
119 |
return $this->parameters[$key];
|
120 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
}
|
118 |
|
119 |
return $this->parameters[$key];
|
120 |
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* {@inheritdoc}
|
124 |
+
*/
|
125 |
+
public function prepareCollection(Mage_Catalog_Model_Resource_Product_Collection $productCollection) {
|
126 |
+
return $this;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* {@inheritdoc}
|
131 |
+
*/
|
132 |
+
public function prepare(Mage_Catalog_Model_Resource_Product_Collection $productCollection) {
|
133 |
+
return $this;
|
134 |
+
}
|
135 |
+
|
136 |
}
|
app/code/community/SearchSpring/Manager/Operation/Product/SetCategories.php
CHANGED
@@ -18,16 +18,16 @@ class SearchSpring_Manager_Operation_Product_SetCategories extends SearchSpring_
|
|
18 |
* Feed Constants
|
19 |
*/
|
20 |
const FEED_CATEGORY_HIERARCHY = 'category_hierarchy';
|
21 |
-
const FEED_CATEGORY_NAME = '
|
22 |
const FEED_CATEGORY_IDS = 'category_ids';
|
23 |
/**#@-*/
|
24 |
|
25 |
/**
|
26 |
-
*
|
27 |
*
|
28 |
-
|
29 |
*/
|
30 |
-
|
31 |
|
32 |
/**
|
33 |
* Sets category data to feed
|
@@ -48,14 +48,14 @@ class SearchSpring_Manager_Operation_Product_SetCategories extends SearchSpring_
|
|
48 |
/** @var Mage_Catalog_Model_Category $category */
|
49 |
foreach($product->getCategoryIds() as $categoryId) {
|
50 |
// load category data
|
51 |
-
$category =
|
52 |
|
53 |
if (!$category->getData('is_active')) {
|
54 |
continue;
|
55 |
}
|
56 |
|
57 |
-
|
58 |
-
if (
|
59 |
continue;
|
60 |
}
|
61 |
|
@@ -69,7 +69,7 @@ class SearchSpring_Manager_Operation_Product_SetCategories extends SearchSpring_
|
|
69 |
$categoryHierarchies[] = $value;
|
70 |
}
|
71 |
|
72 |
-
$categoryNames[] = $
|
73 |
}
|
74 |
|
75 |
$categoryHierarchies = array_unique($categoryHierarchies);
|
@@ -110,16 +110,32 @@ class SearchSpring_Manager_Operation_Product_SetCategories extends SearchSpring_
|
|
110 |
$hierarchy = array();
|
111 |
$currentHierarchy = array();
|
112 |
foreach ($categoryPath as $categoryId) {
|
113 |
-
$category =
|
114 |
|
115 |
-
|
116 |
-
if (
|
117 |
continue;
|
118 |
-
|
119 |
-
|
|
|
120 |
$hierarchy[] = implode('/', $currentHierarchy);
|
121 |
}
|
122 |
|
123 |
return $hierarchy;
|
124 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
}
|
18 |
* Feed Constants
|
19 |
*/
|
20 |
const FEED_CATEGORY_HIERARCHY = 'category_hierarchy';
|
21 |
+
const FEED_CATEGORY_NAME = 'category';
|
22 |
const FEED_CATEGORY_IDS = 'category_ids';
|
23 |
/**#@-*/
|
24 |
|
25 |
/**
|
26 |
+
* Loaded category model cache
|
27 |
*
|
28 |
+
* @var Mage_Catalog_Model_Category[]
|
29 |
*/
|
30 |
+
protected $_categoryCache = array();
|
31 |
|
32 |
/**
|
33 |
* Sets category data to feed
|
48 |
/** @var Mage_Catalog_Model_Category $category */
|
49 |
foreach($product->getCategoryIds() as $categoryId) {
|
50 |
// load category data
|
51 |
+
$category = $this->_getCategory($categoryId);
|
52 |
|
53 |
if (!$category->getData('is_active')) {
|
54 |
continue;
|
55 |
}
|
56 |
|
57 |
+
// Skip this store's root category
|
58 |
+
if ($this->_isRoot($category)) {
|
59 |
continue;
|
60 |
}
|
61 |
|
69 |
$categoryHierarchies[] = $value;
|
70 |
}
|
71 |
|
72 |
+
$categoryNames[] = $category->getName();
|
73 |
}
|
74 |
|
75 |
$categoryHierarchies = array_unique($categoryHierarchies);
|
110 |
$hierarchy = array();
|
111 |
$currentHierarchy = array();
|
112 |
foreach ($categoryPath as $categoryId) {
|
113 |
+
$category = $this->_getCategory($categoryId);
|
114 |
|
115 |
+
// Skip this store's root category
|
116 |
+
if ($this->_isRoot($category)) {
|
117 |
continue;
|
118 |
+
}
|
119 |
+
|
120 |
+
$currentHierarchy[] = $category->getName();
|
121 |
$hierarchy[] = implode('/', $currentHierarchy);
|
122 |
}
|
123 |
|
124 |
return $hierarchy;
|
125 |
}
|
126 |
+
|
127 |
+
protected function _getCategory($categoryId) {
|
128 |
+
if (!isset($this->_categoryCache[$categoryId])) {
|
129 |
+
$this->_categoryCache[$categoryId] = Mage::getModel('catalog/category')->load($categoryId);
|
130 |
+
}
|
131 |
+
return $this->_categoryCache[$categoryId];
|
132 |
+
}
|
133 |
+
|
134 |
+
protected function _isRoot($category) {
|
135 |
+
return (
|
136 |
+
0 === (int)$category->getLevel() ||
|
137 |
+
1 === (int)$category->getLevel()
|
138 |
+
);
|
139 |
+
}
|
140 |
+
|
141 |
}
|
app/code/community/SearchSpring/Manager/Operation/Product/SetCoreFields.php
CHANGED
@@ -14,156 +14,172 @@
|
|
14 |
*/
|
15 |
class SearchSpring_Manager_Operation_Product_SetCoreFields extends SearchSpring_Manager_Operation_Product
|
16 |
{
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
}
|
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 |
+
const FEED_DAYS_OLD = 'days_old';
|
34 |
+
/**#@-*/
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Add SearchSpring core fields to the feed
|
40 |
+
* - sku
|
41 |
+
* - product_type
|
42 |
+
* - quantity
|
43 |
+
* - in_stock
|
44 |
+
* - weight
|
45 |
+
* - manufacturer
|
46 |
+
* - url
|
47 |
+
* - url
|
48 |
+
* - image_url
|
49 |
+
* - thumbnail_url
|
50 |
+
* - cached_thumbnail_url
|
51 |
+
* - name
|
52 |
+
* - description
|
53 |
+
* - short_description
|
54 |
+
* - child_quantity
|
55 |
+
* - child_name
|
56 |
+
* - child_sku
|
57 |
+
* - days_old
|
58 |
+
*
|
59 |
+
* @param Mage_Catalog_Model_Product $product
|
60 |
+
*
|
61 |
+
* @return $this
|
62 |
+
*/
|
63 |
+
public function perform(Mage_Catalog_Model_Product $product)
|
64 |
+
{
|
65 |
+
$phlp = Mage::helper('searchspring_manager/product');
|
66 |
+
|
67 |
+
$webBaseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
|
68 |
+
|
69 |
+
$this->getRecords()->set(self::FEED_SKU, $product->getSku());
|
70 |
+
$this->getRecords()->set(self::FEED_PRODUCT_TYPE, $product->getData('type_id'));
|
71 |
+
$this->getRecords()->set(self::FEED_QUANTITY, number_format($this->getQuantity($product)));
|
72 |
+
$this->getRecords()->set(self::FEED_WEIGHT, number_format((double)$product->getWeight(), 2));
|
73 |
+
$this->getRecords()->set(self::FEED_MANUFACTURER, $phlp->getAttributeText($product, 'manufacturer'));
|
74 |
+
$this->getRecords()->set(self::FEED_URL, $webBaseUrl . $product->getUrlPath());
|
75 |
+
|
76 |
+
$stockItem = $product->getStockItem();
|
77 |
+
$this->getRecords()->set(self::FEED_IN_STOCK, $stockItem->getIsInStock());
|
78 |
+
|
79 |
+
$productName = $this->getSanitizer()->removeNewlinesAndStripTags($product->getName());
|
80 |
+
$description = $this->getSanitizer()->sanitizeForRequest($product->getData('description'));
|
81 |
+
$shortDescription = $this->getSanitizer()->sanitizeForRequest($product->getData('short_description'));
|
82 |
+
$this->getRecords()->set(self::FEED_NAME, $productName);
|
83 |
+
$this->getRecords()->set(self::FEED_DESCRIPTION, $description);
|
84 |
+
$this->getRecords()->set(self::FEED_SHORT_DESCRIPTION, $shortDescription);
|
85 |
+
|
86 |
+
$this->setChildQuantity($product);
|
87 |
+
|
88 |
+
$this->setDaysOld($product);
|
89 |
+
|
90 |
+
return $this;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Helper method to get the quantity from a product
|
95 |
+
*
|
96 |
+
* @param Mage_Catalog_Model_Product $product
|
97 |
+
* @return int
|
98 |
+
*/
|
99 |
+
private function getQuantity(Mage_Catalog_Model_Product $product)
|
100 |
+
{
|
101 |
+
$quantity = $product->getData('stock_item')->getData('qty');
|
102 |
+
|
103 |
+
return (int)$quantity;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* If the product is composite, find the child quantities and set that to the field
|
108 |
+
*/
|
109 |
+
private function setChildQuantity(Mage_Catalog_Model_Product $product)
|
110 |
+
{
|
111 |
+
// default to normal quantity
|
112 |
+
$childQuantity = $this->getRecords()->get(self::FEED_QUANTITY);
|
113 |
+
|
114 |
+
// find the child quantity if it exists
|
115 |
+
switch ($product->getTypeId()) {
|
116 |
+
case Mage_Catalog_Model_Product_Type::TYPE_GROUPED:
|
117 |
+
/** @var Mage_Catalog_Model_Product_Type_Grouped $typeInstance */
|
118 |
+
$typeInstance = $product->getTypeInstance(true);
|
119 |
+
$associated = $typeInstance->getAssociatedProducts($product);
|
120 |
+
$childQuantity = $this->getQuantityForChildren($associated);
|
121 |
+
|
122 |
+
break;
|
123 |
+
case Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE:
|
124 |
+
/** @var Mage_Catalog_Model_Product_Type_Configurable $typeInstance */
|
125 |
+
$typeInstance = $product->getTypeInstance(true);
|
126 |
+
|
127 |
+
/** @var Mage_Catalog_Model_Product_Type_Configurable_Attribute $attribute */
|
128 |
+
foreach ($typeInstance->getConfigurableAttributesAsArray($product) as $attribute) {
|
129 |
+
foreach ($attribute['values'] as $value) {
|
130 |
+
$this->getRecords()->add($attribute['attribute_code'], $value['label']);
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
$children = $typeInstance->getUsedProducts(null, $product);
|
135 |
+
$childQuantity = $this->getQuantityForChildren($children);
|
136 |
+
|
137 |
+
break;
|
138 |
+
case Mage_Catalog_Model_Product_Type::TYPE_BUNDLE:
|
139 |
+
/** @var Mage_Bundle_Model_Product_Type $typeInstance */
|
140 |
+
$typeInstance = $product->getTypeInstance(true);
|
141 |
+
$optionsIds = $typeInstance->getOptionsIds($product);
|
142 |
+
$selections = $typeInstance->getSelectionsCollection($optionsIds, $product);
|
143 |
+
$bundleOptions = $typeInstance->getOptionsByIds($optionsIds, $product);
|
144 |
+
$bundleOptions->appendSelections($selections);
|
145 |
+
|
146 |
+
/** @var Mage_Bundle_Model_Option $bundleOption */
|
147 |
+
foreach ($bundleOptions as $bundleOption) {
|
148 |
+
$childQuantity += $this->getQuantityForChildren($bundleOption->getData('selections'));
|
149 |
+
}
|
150 |
+
|
151 |
+
break;
|
152 |
+
}
|
153 |
+
|
154 |
+
$this->getRecords()->set(self::FEED_CHILD_QUANTITY, $childQuantity);
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Set the hos many days old the product is
|
159 |
+
*/
|
160 |
+
private function setDaysOld($product) {
|
161 |
+
$createdAt = strtotime($product->getCreatedAt());
|
162 |
+
$this->getRecords()->set(self::FEED_DAYS_OLD, floor((time() - $createdAt) / 60 / 60 / 24));
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Loop over an array of products to set field data and calculate total child quantity
|
167 |
+
*
|
168 |
+
* @param array $products
|
169 |
+
* @return int
|
170 |
+
*/
|
171 |
+
private function getQuantityForChildren(array $products)
|
172 |
+
{
|
173 |
+
$quantity = 0;
|
174 |
+
|
175 |
+
/** @var Mage_Catalog_Model_Product $product */
|
176 |
+
foreach ($products as $product) {
|
177 |
+
$this->getRecords()->add(self::FEED_CHILD_SKU, $product->getSku());
|
178 |
+
$this->getRecords()->add(self::FEED_CHILD_NAME, $product->getName());
|
179 |
+
|
180 |
+
$quantity += $this->getQuantity($product);
|
181 |
+
}
|
182 |
+
|
183 |
+
return $quantity;
|
184 |
+
}
|
185 |
}
|
app/code/community/SearchSpring/Manager/Operation/Product/SetFields.php
CHANGED
@@ -24,7 +24,14 @@ class SearchSpring_Manager_Operation_Product_SetFields extends SearchSpring_Mana
|
|
24 |
{
|
25 |
/** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
|
26 |
foreach($product->getAttributes() as $key => $attribute) {
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
|
30 |
return $this;
|
24 |
{
|
25 |
/** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
|
26 |
foreach($product->getAttributes() as $key => $attribute) {
|
27 |
+
$value = $this->getAttributeValue($product, $attribute);
|
28 |
+
if (!is_array($value)) {
|
29 |
+
$this->getRecords()->add($key, $value);
|
30 |
+
} else {
|
31 |
+
foreach($value as $v) {
|
32 |
+
$this->getRecords()->add($key, $v);
|
33 |
+
}
|
34 |
+
}
|
35 |
}
|
36 |
|
37 |
return $this;
|
app/code/community/SearchSpring/Manager/Operation/Product/SetReport.php
ADDED
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* SetReport.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_Operation_Product_SetReport
|
10 |
+
*
|
11 |
+
* Set Sales/Customer/Reporting related product data
|
12 |
+
*
|
13 |
+
* Available reporting functions (from CE version 1.3)
|
14 |
+
* public function addCartsCount()
|
15 |
+
* public function addOrdersCount($from = '', $to = '')
|
16 |
+
* public function addOrderedQty($from = '', $to = '')
|
17 |
+
* public function addViewsCount($from = '', $to = '')
|
18 |
+
*
|
19 |
+
* @author Jake Shelby <jake@b7interactive.com>
|
20 |
+
*/
|
21 |
+
class SearchSpring_Manager_Operation_Product_SetReport extends SearchSpring_Manager_Operation_Product
|
22 |
+
{
|
23 |
+
|
24 |
+
protected $_enabled = true;
|
25 |
+
protected $_reportData;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Feed constants
|
29 |
+
*/
|
30 |
+
const FEED_CART_COUNT = 'report_cart_count';
|
31 |
+
const FEED_ORDERS_COUNT = 'report_orders_count';
|
32 |
+
const FEED_ORDERS_QTY = 'report_orders_qty';
|
33 |
+
|
34 |
+
// Default timespan, 1 year, so we don't accidentally overload their database
|
35 |
+
const DEFAULT_REPORT_TIMESPAN = '1 year';
|
36 |
+
|
37 |
+
public function prepare(Mage_Catalog_Model_Resource_Product_Collection $productCollection) {
|
38 |
+
|
39 |
+
$this->fetchReportData(
|
40 |
+
$productCollection->getAllIds(),
|
41 |
+
$productCollection->getStoreId()
|
42 |
+
);
|
43 |
+
|
44 |
+
return $this;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function perform(Mage_Catalog_Model_Product $product)
|
48 |
+
{
|
49 |
+
|
50 |
+
if ($this->_enabled) {
|
51 |
+
|
52 |
+
$this->setOrderedQty($product);
|
53 |
+
|
54 |
+
// We're not using these just yet
|
55 |
+
// $this->setCartCount($product);
|
56 |
+
// $this->setOrdersCount($product);
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
return $this;
|
61 |
+
}
|
62 |
+
|
63 |
+
public function setCartCount(Mage_Catalog_Model_Product $product) {
|
64 |
+
if ($productReport = $this->getProductReport($product)) {
|
65 |
+
$this->getRecords()->set(self::FEED_CART_COUNT, $productReport->getCounts());
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
public function setOrdersCount(Mage_Catalog_Model_Product $product) {
|
70 |
+
if ($productReport = $this->getProductReport($product)) {
|
71 |
+
$this->getRecords()->set(self::FEED_ORDERS_COUNT, $productReport->getOrders());
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
public function setOrderedQty(Mage_Catalog_Model_Product $product) {
|
76 |
+
if ($productReport = $this->getProductReport($product)) {
|
77 |
+
$this->getRecords()->set(self::FEED_ORDERS_QTY, $productReport->getOrderedQty());
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
public function getProductReport(Mage_Catalog_Model_Product $product) {
|
82 |
+
|
83 |
+
// Make sure we have report data
|
84 |
+
if (!$this->_reportData) {
|
85 |
+
// If we don't have data, then we'll fetch it with the requested product
|
86 |
+
// NOTE: This should only happen if the person using the class didn't request
|
87 |
+
// preparation with a product collection
|
88 |
+
$this->fetchReportData(array($product->getId()), $product->getStoreId());
|
89 |
+
|
90 |
+
// If we still don't have the data, then we can't support this feature
|
91 |
+
if (!$this->_reportData) {
|
92 |
+
return false;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
// Make sure we have data for this product
|
97 |
+
if (!isset($this->_reportData[$product->getId()])) {
|
98 |
+
return false;
|
99 |
+
}
|
100 |
+
|
101 |
+
// Return as an object
|
102 |
+
return new Varien_Object($this->_reportData[$product->getId()]);
|
103 |
+
}
|
104 |
+
|
105 |
+
protected function fetchReportData($productIds, $store) {
|
106 |
+
|
107 |
+
// Start by using the reports collection
|
108 |
+
$reportCollection = $this->createReportCollection($productIds, $store);
|
109 |
+
|
110 |
+
// If we don't have a collection now, we won't ever have one, disable this operation
|
111 |
+
if (!$reportCollection) {
|
112 |
+
$this->_enabled = false;
|
113 |
+
$this->_reportData = null;
|
114 |
+
return;
|
115 |
+
}
|
116 |
+
|
117 |
+
// Get From and To Dates
|
118 |
+
$from = $this->getParamReportStartDate();
|
119 |
+
$to = $this->getParamReportEndDate();
|
120 |
+
|
121 |
+
// Start our resulting data
|
122 |
+
$reportData = array();
|
123 |
+
|
124 |
+
// Ordered Qty
|
125 |
+
$reportCollection->addOrderedQty($from,$to);
|
126 |
+
foreach($reportCollection as $productReport) {
|
127 |
+
$reportData[$productReport->getId()] = array(
|
128 |
+
'ordered_qty' => $productReport->getOrderedQty(),
|
129 |
+
);
|
130 |
+
}
|
131 |
+
|
132 |
+
/* // Order Counts - Add this in when needed
|
133 |
+
$reportCollection = $this->createReportCollection($productIds, $store);
|
134 |
+
$reportCollection->addOrdersCount($from,$to); // TODO Figure out how to add this without breaking the query
|
135 |
+
foreach($reportCollection as $productReport) {
|
136 |
+
$reportData[$productReport->getId()] = array(
|
137 |
+
'orders' => $productReport->getOrders(),
|
138 |
+
);
|
139 |
+
} */
|
140 |
+
|
141 |
+
/* // Cart Counts - Add this in when needed
|
142 |
+
$reportCollection = $this->createReportCollection($productIds, $store);
|
143 |
+
$reportCollection->addCartsCount(); // TODO Figure out how to add this without breaking the query
|
144 |
+
foreach($reportCollection as $productReport) {
|
145 |
+
$reportData[$productReport->getId()] = array(
|
146 |
+
'counts' => $productReport->getCounts(),
|
147 |
+
);
|
148 |
+
} */
|
149 |
+
|
150 |
+
$this->_reportData = $reportData;
|
151 |
+
}
|
152 |
+
|
153 |
+
protected function createReportCollection($productIds, $store) {
|
154 |
+
|
155 |
+
// Start by using the reports collection
|
156 |
+
$reportCollection = Mage::getResourceModel('reports/product_collection');
|
157 |
+
|
158 |
+
// Make sure this magento installation has this report collection
|
159 |
+
if (!is_object($reportCollection)) {
|
160 |
+
return;
|
161 |
+
}
|
162 |
+
|
163 |
+
// Filter By Store ID
|
164 |
+
$reportCollection->setStoreId($store)->addStoreFilter($store);
|
165 |
+
|
166 |
+
// Filter to just certain products
|
167 |
+
$reportCollection->addAttributeToFilter('entity_id', array('in' => $productIds));
|
168 |
+
|
169 |
+
return $reportCollection;
|
170 |
+
}
|
171 |
+
|
172 |
+
// TODO -- Figure out how to add from and to dates as parameters, might need to add them as configuration items as well
|
173 |
+
public function getParamReportStartDate() {
|
174 |
+
return date('Y-m-d H:i:s', strtotime('-' . self::DEFAULT_REPORT_TIMESPAN));
|
175 |
+
}
|
176 |
+
|
177 |
+
public function getParamReportEndDate() {
|
178 |
+
// Up till now
|
179 |
+
return date('Y-m-d H:i:s');
|
180 |
+
}
|
181 |
+
|
182 |
+
}
|
app/code/community/SearchSpring/Manager/Operation/ProductOperation.php
CHANGED
@@ -12,14 +12,32 @@
|
|
12 |
*/
|
13 |
interface SearchSpring_Manager_Operation_ProductOperation
|
14 |
{
|
|
|
15 |
/**
|
16 |
-
|
|
|
|
|
|
|
17 |
*
|
18 |
-
* @param
|
19 |
*
|
20 |
* @return self
|
21 |
*/
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
/**
|
25 |
* Checks validity of operation
|
@@ -31,7 +49,17 @@ interface SearchSpring_Manager_Operation_ProductOperation
|
|
31 |
public function isValid(Mage_Catalog_Model_Product $product);
|
32 |
|
33 |
/**
|
34 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
*
|
36 |
* @param SearchSpring_Manager_Entity_RecordsCollection $records
|
37 |
*
|
12 |
*/
|
13 |
interface SearchSpring_Manager_Operation_ProductOperation
|
14 |
{
|
15 |
+
|
16 |
/**
|
17 |
+
* Prepare the collection, before it's loaded.
|
18 |
+
* This allows for the operation to add filters,
|
19 |
+
* joins, extra data to the collection before
|
20 |
+
* it's loaded.
|
21 |
*
|
22 |
+
* @param Mage_Catalog_Model_Resource_Product_Collection $productCollection
|
23 |
*
|
24 |
* @return self
|
25 |
*/
|
26 |
+
public function prepareCollection(Mage_Catalog_Model_Resource_Product_Collection $productCollection);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Prepare the operation after the collection has
|
30 |
+
* been loaded, but before isValid and perform have
|
31 |
+
* been called. This allows for pulling in data in
|
32 |
+
* mass, since you have been called ahead of time
|
33 |
+
* with the products that you will be performing
|
34 |
+
* operations on.
|
35 |
+
*
|
36 |
+
* @param Mage_Catalog_Model_Resource_Product_Collection $productCollection
|
37 |
+
*
|
38 |
+
* @return self
|
39 |
+
*/
|
40 |
+
public function prepare(Mage_Catalog_Model_Resource_Product_Collection $productCollection);
|
41 |
|
42 |
/**
|
43 |
* Checks validity of operation
|
49 |
public function isValid(Mage_Catalog_Model_Product $product);
|
50 |
|
51 |
/**
|
52 |
+
* Perform an operation
|
53 |
+
*
|
54 |
+
* @param Mage_Catalog_Model_Product $product
|
55 |
+
*
|
56 |
+
* @return self
|
57 |
+
*/
|
58 |
+
public function perform(Mage_Catalog_Model_Product $product);
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Set records collection to operation. This is the
|
62 |
+
* collection that output data should be written to.
|
63 |
*
|
64 |
* @param SearchSpring_Manager_Entity_RecordsCollection $records
|
65 |
*
|
app/code/community/SearchSpring/Manager/Provider/ProductCollection/FeedProvider.php
CHANGED
@@ -57,8 +57,6 @@ class SearchSpring_Manager_Provider_ProductCollection_FeedProvider
|
|
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());
|
@@ -67,7 +65,7 @@ class SearchSpring_Manager_Provider_ProductCollection_FeedProvider
|
|
67 |
$collection->addAttributeToFilter('visibility', Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH);
|
68 |
|
69 |
|
70 |
-
if (
|
71 |
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
|
72 |
}
|
73 |
|
57 |
return $this->collection;
|
58 |
}
|
59 |
|
|
|
|
|
60 |
/** @var Mage_Catalog_Model_Resource_Product_Collection $collection */
|
61 |
$collection = Mage::getModel('catalog/product')->getCollection();
|
62 |
$collection->addStoreFilter(Mage::app()->getStore($this->requestParams->getStore())->getId());
|
65 |
$collection->addAttributeToFilter('visibility', Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH);
|
66 |
|
67 |
|
68 |
+
if (!Mage::helper('searchspring_manager')->isOutOfStockIndexingEnabled()) {
|
69 |
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
|
70 |
}
|
71 |
|
app/code/community/SearchSpring/Manager/Request/JSON.php
CHANGED
@@ -1,56 +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 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
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 = Zend_Json::decode($rawBody);
|
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
CHANGED
@@ -23,11 +23,11 @@ class SearchSpring_Manager_Service_SearchSpring_ApiAdapter
|
|
23 |
const URL_FORMAT = '%s/%s';
|
24 |
|
25 |
/**
|
26 |
-
* The
|
27 |
*
|
28 |
-
* @var
|
29 |
*/
|
30 |
-
protected $
|
31 |
|
32 |
/**
|
33 |
* An API error handler if a request fails
|
@@ -52,11 +52,11 @@ class SearchSpring_Manager_Service_SearchSpring_ApiAdapter
|
|
52 |
*/
|
53 |
public function __construct(
|
54 |
SearchSpring_Manager_Handler_ApiErrorHandler $errorHandler,
|
55 |
-
|
56 |
$baseUrl
|
57 |
) {
|
58 |
$this->errorHandler = $errorHandler;
|
59 |
-
$this->
|
60 |
$this->baseUrl = $baseUrl;
|
61 |
}
|
62 |
|
23 |
const URL_FORMAT = '%s/%s';
|
24 |
|
25 |
/**
|
26 |
+
* The http client
|
27 |
*
|
28 |
+
* @var Zend_Http_Client
|
29 |
*/
|
30 |
+
protected $client;
|
31 |
|
32 |
/**
|
33 |
* An API error handler if a request fails
|
52 |
*/
|
53 |
public function __construct(
|
54 |
SearchSpring_Manager_Handler_ApiErrorHandler $errorHandler,
|
55 |
+
Zend_Http_Client $client,
|
56 |
$baseUrl
|
57 |
) {
|
58 |
$this->errorHandler = $errorHandler;
|
59 |
+
$this->client = $client;
|
60 |
$this->baseUrl = $baseUrl;
|
61 |
}
|
62 |
|
app/code/community/SearchSpring/Manager/Service/SearchSpring/IndexingApiAdapter.php
CHANGED
@@ -49,15 +49,16 @@ class SearchSpring_Manager_Service_SearchSpring_IndexingApiAdapter extends Searc
|
|
49 |
*/
|
50 |
private function request($httpMethod, $url, $body)
|
51 |
{
|
52 |
-
$this->
|
53 |
-
$
|
54 |
-
$
|
55 |
-
$this->curl->close();
|
56 |
|
57 |
-
|
58 |
-
$this->request($httpMethod
|
59 |
}
|
60 |
|
|
|
|
|
61 |
return $responseCode;
|
62 |
}
|
63 |
|
49 |
*/
|
50 |
private function request($httpMethod, $url, $body)
|
51 |
{
|
52 |
+
$this->client->setUri($url);
|
53 |
+
$this->client->setRawData($body, 'application/json');
|
54 |
+
$response = $this->client->request($httpMethod);
|
|
|
55 |
|
56 |
+
while($this->errorHandler->shouldRetry($response)) {
|
57 |
+
$response = $this->client->request($httpMethod);
|
58 |
}
|
59 |
|
60 |
+
$responseCode = $response->getStatus();
|
61 |
+
|
62 |
return $responseCode;
|
63 |
}
|
64 |
|
app/code/community/SearchSpring/Manager/Service/SearchSpring/SearchApiAdapter.php
CHANGED
@@ -56,6 +56,8 @@ class SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter extends SearchS
|
|
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 |
|
@@ -67,6 +69,7 @@ class SearchSpring_Manager_Service_SearchSpring_SearchApiAdapter extends SearchS
|
|
67 |
}
|
68 |
|
69 |
return $responseBody;
|
|
|
70 |
}
|
71 |
|
72 |
}
|
56 |
*/
|
57 |
private function request($httpMethod, $url, $parameters)
|
58 |
{
|
59 |
+
|
60 |
+
// TODO Change this to Zend_Http_Client, parameters shouldn't be a string but an array
|
61 |
$this->curl->write($httpMethod, $url . '?' . $parameters, Zend_Http_Client::HTTP_1, array());
|
62 |
$response = $this->curl->read();
|
63 |
|
69 |
}
|
70 |
|
71 |
return $responseBody;
|
72 |
+
|
73 |
}
|
74 |
|
75 |
}
|
app/code/community/SearchSpring/Manager/Strategy/Pricing/Strategy.php
CHANGED
@@ -183,7 +183,6 @@ abstract class SearchSpring_Manager_Strategy_Pricing_Strategy implements SearchS
|
|
183 |
private function formatPrice($price)
|
184 |
{
|
185 |
$price = (double)$price;
|
186 |
-
$price = (double)number_format($price, 2);
|
187 |
|
188 |
return $price;
|
189 |
}
|
183 |
private function formatPrice($price)
|
184 |
{
|
185 |
$price = (double)$price;
|
|
|
186 |
|
187 |
return $price;
|
188 |
}
|
app/code/community/SearchSpring/Manager/ThirdParty/Amasty/LabelsOperation.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* LabelsOperation.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_ThirdParty_Amasty_LabelsOperation
|
10 |
+
*
|
11 |
+
* Set Amasty labels HTML to the feed
|
12 |
+
*
|
13 |
+
* @author Will Wesley <will@b7interactive.com>
|
14 |
+
*/
|
15 |
+
class SearchSpring_Manager_ThirdParty_Amasty_LabelsOperation extends SearchSpring_Manager_Operation_Product
|
16 |
+
{
|
17 |
+
const FEED_LABEL = 'Amasty_Label_product_labels_html';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Add label HTML
|
21 |
+
*
|
22 |
+
* @param Mage_Catalog_Model_Product $product
|
23 |
+
* @return SearchSpring_Manager_Operation_ProductOperation|void
|
24 |
+
*/
|
25 |
+
public function perform(Mage_Catalog_Model_Product $product)
|
26 |
+
{
|
27 |
+
$this->getRecords()->add(self::FEED_LABEL, Mage::helper('amlabel')->getLabels($product));
|
28 |
+
return $this;
|
29 |
+
}
|
30 |
+
}
|
app/code/community/SearchSpring/Manager/ThirdParty/TBT/RewardsOperation.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* RewardsOperation.php
|
4 |
+
*
|
5 |
+
* @copyright B7 Interactive, LLC. All Rights Reserved.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SearchSpring_Manager_ThirdParty_TBT_RewardsOperation
|
10 |
+
*
|
11 |
+
* Set Rewards Related Data
|
12 |
+
*
|
13 |
+
* @author Jake Shelby <jake@b7interactive.com>
|
14 |
+
*/
|
15 |
+
class SearchSpring_Manager_ThirdParty_TBT_RewardsOperation extends SearchSpring_Manager_Operation_Product
|
16 |
+
{
|
17 |
+
|
18 |
+
public function perform(Mage_Catalog_Model_Product $product)
|
19 |
+
{
|
20 |
+
|
21 |
+
$value = $product->getEarnablePoints();
|
22 |
+
|
23 |
+
if (is_array($value)) {
|
24 |
+
$value = current($value);
|
25 |
+
}
|
26 |
+
|
27 |
+
$this->getRecords()->add('TBT_Rewards_earnable_points', $value);
|
28 |
+
|
29 |
+
return $this;
|
30 |
+
}
|
31 |
+
|
32 |
+
}
|
app/code/community/SearchSpring/Manager/Transformer/ProductCollectionToRecordCollectionTransformer.php
CHANGED
@@ -47,10 +47,24 @@ class SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransf
|
|
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;
|
@@ -58,13 +72,18 @@ class SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransf
|
|
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
|
@@ -75,17 +94,67 @@ class SearchSpring_Manager_Transformer_ProductCollectionToRecordCollectionTransf
|
|
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 |
}
|
47 |
*/
|
48 |
public function transform(Mage_Catalog_Model_Resource_Product_Collection $productCollection)
|
49 |
{
|
50 |
+
Varien_Profiler::start(__METHOD__);
|
51 |
+
|
52 |
+
// Prep our collection
|
53 |
+
$this->prepareCollection($productCollection);
|
54 |
+
|
55 |
+
// Finally load the collection
|
56 |
+
$this->loadCollection($productCollection);
|
57 |
+
|
58 |
+
// Prep our operations with the loaded collection
|
59 |
+
$this->prepareOperations($productCollection);
|
60 |
+
|
61 |
/** @var Mage_Catalog_Model_Product $product */
|
62 |
foreach ($productCollection as $product) {
|
63 |
+
|
64 |
// load product data
|
65 |
+
Varien_Profiler::start(__METHOD__.": product->load()");
|
66 |
$product->load($product->getId());
|
67 |
+
Varien_Profiler::stop(__METHOD__.": product->load()");
|
68 |
|
69 |
// default to valid
|
70 |
$productValid = true;
|
72 |
/** @var SearchSpring_Manager_Operation_Product $operation */
|
73 |
foreach ($this->operationsCollection as $operation) {
|
74 |
// check if any operation validation invalidates this product
|
75 |
+
Varien_Profiler::start(__METHOD__.": operation->isValid()");
|
76 |
+
Varien_Profiler::start(__METHOD__.": operation->isValid() : " . get_class($operation));
|
77 |
if (false === $operation->isValid($product)) {
|
78 |
$productValid = false;
|
79 |
}
|
80 |
+
Varien_Profiler::stop(__METHOD__.": operation->isValid() : " . get_class($operation));
|
81 |
+
Varien_Profiler::stop(__METHOD__.": operation->isValid()");
|
82 |
}
|
83 |
|
84 |
// only set id if product is invalid and continue to next product
|
85 |
if (false === $productValid) {
|
86 |
+
Varien_Profiler::start(__METHOD__.": invalid product, operation set id");
|
87 |
$operation = new SearchSpring_Manager_Operation_Product_SetId(
|
88 |
new SearchSpring_Manager_String_Sanitizer(),
|
89 |
$this->recordsCollection
|
94 |
// increment record
|
95 |
$this->recordsCollection->next();
|
96 |
|
97 |
+
Varien_Profiler::stop(__METHOD__.": invalid product, operation set id");
|
98 |
+
|
99 |
continue;
|
100 |
}
|
101 |
|
102 |
foreach ($this->operationsCollection as $operation) {
|
103 |
+
Varien_Profiler::start(__METHOD__.": operation->perform()");
|
104 |
+
Varien_Profiler::start(__METHOD__.": operation->perform() : " . get_class($operation));
|
105 |
$operation->perform($product);
|
106 |
+
Varien_Profiler::stop(__METHOD__.": operation->perform()");
|
107 |
+
Varien_Profiler::stop(__METHOD__.": operation->perform() : " . get_class($operation));
|
108 |
}
|
109 |
|
110 |
// increment record
|
111 |
$this->recordsCollection->next();
|
112 |
}
|
113 |
|
114 |
+
Varien_Profiler::stop(__METHOD__);
|
115 |
+
|
116 |
return $this->recordsCollection;
|
117 |
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Before the operations validate or perform on each product, we want to
|
121 |
+
* make sure the product collection is prepared. This allows for things
|
122 |
+
* like adding joins/filters/selects on the collection.
|
123 |
+
*
|
124 |
+
* @param Mage_Catalog_Model_Resource_Product_Collection $productCollection
|
125 |
+
*/
|
126 |
+
protected function prepareCollection(Mage_Catalog_Model_Resource_Product_Collection $productCollection) {
|
127 |
+
|
128 |
+
foreach ($this->operationsCollection as $operation) {
|
129 |
+
$operation->prepareCollection($productCollection);
|
130 |
+
}
|
131 |
+
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Before the operations validate or perform on each product, but after the
|
136 |
+
* product collection has been loaded, prepare each operation. This allows
|
137 |
+
* for pulling in extra data in mass, rather than calling the database for
|
138 |
+
* very single product.
|
139 |
+
*
|
140 |
+
* @param Mage_Catalog_Model_Resource_Product_Collection $productCollection
|
141 |
+
*/
|
142 |
+
protected function prepareOperations(Mage_Catalog_Model_Resource_Product_Collection $productCollection) {
|
143 |
+
|
144 |
+
foreach ($this->operationsCollection as $operation) {
|
145 |
+
Varien_Profiler::start(__METHOD__.": operation->prepare() " . get_class($operation));
|
146 |
+
$operation->prepare($productCollection);
|
147 |
+
Varien_Profiler::stop(__METHOD__.": operation->prepare() " . get_class($operation));
|
148 |
+
}
|
149 |
+
|
150 |
+
}
|
151 |
+
|
152 |
+
protected function loadCollection(Mage_Catalog_Model_Resource_Product_Collection $productCollection) {
|
153 |
+
|
154 |
+
Varien_Profiler::start(__METHOD__.": productCollection->load()");
|
155 |
+
$productCollection->load();
|
156 |
+
Varien_Profiler::stop(__METHOD__.": productCollection->load()");
|
157 |
+
|
158 |
+
}
|
159 |
+
|
160 |
}
|
app/code/community/SearchSpring/Manager/Validator/ProductValidator.php
CHANGED
@@ -66,8 +66,10 @@ class SearchSpring_Manager_Validator_ProductValidator implements Zend_Validate_I
|
|
66 |
}
|
67 |
|
68 |
// if product is out of stock
|
69 |
-
$displayOos = Mage::helper('
|
70 |
-
|
|
|
|
|
71 |
return true;
|
72 |
}
|
73 |
|
66 |
}
|
67 |
|
68 |
// if product is out of stock
|
69 |
+
$displayOos = Mage::helper('searchspring_manager')->isOutOfStockIndexingEnabled();
|
70 |
+
$stockItem = $product->getStockItem();
|
71 |
+
|
72 |
+
if (!$stockItem->getIsInStock() && !$displayOos) {
|
73 |
return true;
|
74 |
}
|
75 |
|
app/code/community/SearchSpring/Manager/Writer/Product/Params/FileWriterParams.php
CHANGED
@@ -66,6 +66,13 @@ class SearchSpring_Manager_Writer_Product_Params_FileWriterParams
|
|
66 |
*/
|
67 |
private $filename;
|
68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
/**
|
70 |
* @var SearchSpring_Manager_Entity_RequestParams
|
71 |
*/
|
@@ -77,14 +84,48 @@ class SearchSpring_Manager_Writer_Product_Params_FileWriterParams
|
|
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 |
*
|
@@ -131,7 +172,7 @@ class SearchSpring_Manager_Writer_Product_Params_FileWriterParams
|
|
131 |
public function getTempFilename()
|
132 |
{
|
133 |
if (null === $this->tempFilename) {
|
134 |
-
$this->tempFilename =
|
135 |
. DS
|
136 |
. sprintf(self::FILENAME_TEMP_PATTERN, $this->requestParams->getStore(), $this->uniqueFilename);
|
137 |
}
|
@@ -147,9 +188,10 @@ class SearchSpring_Manager_Writer_Product_Params_FileWriterParams
|
|
147 |
public function getFilename()
|
148 |
{
|
149 |
if (null === $this->filename) {
|
150 |
-
$this->filename =
|
151 |
}
|
152 |
|
153 |
return $this->filename;
|
154 |
}
|
|
|
155 |
}
|
66 |
*/
|
67 |
private $filename;
|
68 |
|
69 |
+
/**
|
70 |
+
* The base directory
|
71 |
+
*
|
72 |
+
* @var string $filename
|
73 |
+
*/
|
74 |
+
private $baseDir;
|
75 |
+
|
76 |
/**
|
77 |
* @var SearchSpring_Manager_Entity_RequestParams
|
78 |
*/
|
84 |
* @param SearchSpring_Manager_Entity_RequestParams $requestParams
|
85 |
* @param int $totalProducts
|
86 |
* @param string $uniqueFilename
|
87 |
+
* @param string $baseDir Relative to the magento installation directory
|
88 |
*/
|
89 |
+
public function __construct(SearchSpring_Manager_Entity_RequestParams $requestParams, $totalProducts, $uniqueFilename, $baseDir)
|
90 |
{
|
91 |
$this->requestParams = $requestParams;
|
92 |
$this->totalProducts = $totalProducts;
|
93 |
$this->uniqueFilename = $uniqueFilename;
|
94 |
+
$this->baseDir = Mage::getBaseDir() . DS . $baseDir;
|
95 |
+
|
96 |
+
$this->validateBaseDir();
|
97 |
}
|
98 |
|
99 |
+
/**
|
100 |
+
* Validate base directory
|
101 |
+
*
|
102 |
+
* @return string
|
103 |
+
*/
|
104 |
+
public function validateBaseDir() {
|
105 |
+
|
106 |
+
// Make sure the directory exists
|
107 |
+
if (file_exists($this->baseDir)) {
|
108 |
+
// Make sure the directory isn't a file
|
109 |
+
if (!is_dir($this->baseDir)) {
|
110 |
+
throw new Exception("Output directory is not a directory; cannot write file.");
|
111 |
+
}
|
112 |
+
// Make sure the directory is writable
|
113 |
+
if (!is_dir_writeable($this->baseDir)) {
|
114 |
+
throw new Exception("Output directory is not a writable; cannot write file.");
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
// Try to create the directory path
|
119 |
+
else {
|
120 |
+
$oldUmask = umask(0);
|
121 |
+
if (!@mkdir($this->baseDir, 0777, true)) {
|
122 |
+
throw new Exception("Output directory path could not be created, most likely because of insufficient privileges; cannot write file.");
|
123 |
+
}
|
124 |
+
umask($oldUmask);
|
125 |
+
}
|
126 |
+
|
127 |
+
}
|
128 |
+
|
129 |
/**
|
130 |
* Get the request parameters object
|
131 |
*
|
172 |
public function getTempFilename()
|
173 |
{
|
174 |
if (null === $this->tempFilename) {
|
175 |
+
$this->tempFilename = $this->baseDir
|
176 |
. DS
|
177 |
. sprintf(self::FILENAME_TEMP_PATTERN, $this->requestParams->getStore(), $this->uniqueFilename);
|
178 |
}
|
188 |
public function getFilename()
|
189 |
{
|
190 |
if (null === $this->filename) {
|
191 |
+
$this->filename = $this->baseDir . DS . sprintf(self::FILENAME_FINAL_PATTERN, $this->requestParams->getStore());
|
192 |
}
|
193 |
|
194 |
return $this->filename;
|
195 |
}
|
196 |
+
|
197 |
}
|
app/code/community/SearchSpring/Manager/controllers/GenerateController.php
CHANGED
@@ -27,135 +27,242 @@ class SearchSpring_Manager_GenerateController extends Mage_Core_Controller_Front
|
|
27 |
*/
|
28 |
const STORE_DEFAULT = 'default';
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
$request = new SearchSpring_Manager_Request_JSON($this->getRequest());
|
40 |
|
41 |
$type = $request->getParam('type');
|
42 |
$ids = $request->getParam('ids');
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
|
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 |
-
|
71 |
-
|
72 |
-
|
73 |
-
$request->getParam('store', self::STORE_DEFAULT)
|
74 |
-
);
|
75 |
|
76 |
-
|
|
|
|
|
|
|
|
|
77 |
|
78 |
-
|
79 |
-
$generator = $generatorFactory->make($type, $requestParams, $params);
|
80 |
-
$message = $generator->generate();
|
81 |
|
82 |
-
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
84 |
|
|
|
|
|
85 |
|
86 |
/**
|
87 |
* Generate an xml feed of all products
|
88 |
*
|
89 |
* Parameters:
|
90 |
-
*
|
91 |
-
*
|
92 |
-
*
|
93 |
-
*
|
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->
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
}
|
125 |
|
126 |
-
|
127 |
-
|
128 |
-
(int)$this->getRequest()->getParam('start', self::OFFSET_DEFAULT),
|
129 |
-
$this->getRequest()->getParam('store', self::STORE_DEFAULT)
|
130 |
-
);
|
131 |
|
132 |
-
|
|
|
|
|
|
|
133 |
|
134 |
-
|
135 |
-
$generator = $generatorFactory->make(
|
136 |
-
SearchSpring_Manager_Factory_GeneratorFactory::TYPE_FEED,
|
137 |
-
$requestParams,
|
138 |
-
$params
|
139 |
-
);
|
140 |
-
$message = $generator->generate();
|
141 |
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
-
|
145 |
}
|
146 |
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
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 =
|
159 |
$this->getResponse()->setBody($responseBody);
|
160 |
}
|
161 |
|
@@ -171,4 +278,5 @@ class SearchSpring_Manager_GenerateController extends Mage_Core_Controller_Front
|
|
171 |
$this->getResponse()->setHttpResponseCode($responseCode);
|
172 |
$this->getResponse()->setBody($message);
|
173 |
}
|
|
|
174 |
}
|
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->respondWithError('Type must be specified', SearchSpring_ErrorCodes::TYPE_NOT_SET, 400);
|
46 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
+
if (null === $ids) {
|
49 |
+
$this->respondWithError('Ids must be specified', SearchSpring_ErrorCodes::IDS_NOT_SET, 400);
|
50 |
+
}
|
|
|
|
|
51 |
|
52 |
+
$requestParams = new SearchSpring_Manager_Entity_RequestParams(
|
53 |
+
(int)$request->getParam('size', null),
|
54 |
+
(int)$request->getParam('start', null),
|
55 |
+
$request->getParam('store', self::STORE_DEFAULT)
|
56 |
+
);
|
57 |
|
58 |
+
$params = array('ids' => $ids);
|
|
|
|
|
59 |
|
60 |
+
try {
|
61 |
+
$generatorFactory = new SearchSpring_Manager_Factory_GeneratorFactory();
|
62 |
+
$generator = $generatorFactory->make($type, $requestParams, $params);
|
63 |
+
$message = $generator->generate();
|
64 |
+
} catch (Exception $e) {
|
65 |
+
$this->handleException($e);
|
66 |
+
}
|
67 |
|
68 |
+
$this->setJsonResponse($message);
|
69 |
+
}
|
70 |
|
71 |
/**
|
72 |
* Generate an xml feed of all products
|
73 |
*
|
74 |
* Parameters:
|
75 |
+
* filename (required): A unique filename when creating temporary files.
|
76 |
+
* start (optional): The starting point for fetching products. Defaults to 0.
|
77 |
+
* count (optional): The number of products to fetch. Defaults to 100.
|
78 |
+
* store (optional): The store name as a string. Defaults to 'default'
|
79 |
*/
|
80 |
public function feedAction()
|
81 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
$uniqueFilename = $this->getRequest()->getParam('filename');
|
83 |
|
84 |
if (null === $uniqueFilename) {
|
85 |
+
$this->respondWithError('Filename must be specified', SearchSpring_ErrorCodes::FILENAME_NOT_SET, 400);
|
86 |
+
}
|
87 |
+
|
88 |
+
$requestParams = new SearchSpring_Manager_Entity_RequestParams(
|
89 |
+
(int)$this->getRequest()->getParam('count', self::COUNT_DEFAULT),
|
90 |
+
(int)$this->getRequest()->getParam('start', self::OFFSET_DEFAULT),
|
91 |
+
$this->getRequest()->getParam('store', self::STORE_DEFAULT)
|
92 |
+
);
|
93 |
+
|
94 |
+
$params = array('filename' => $uniqueFilename);
|
95 |
+
|
96 |
+
try {
|
97 |
+
|
98 |
+
$generatorFactory = new SearchSpring_Manager_Factory_GeneratorFactory();
|
99 |
+
$generator = $generatorFactory->make(
|
100 |
+
SearchSpring_Manager_Factory_GeneratorFactory::TYPE_FEED,
|
101 |
+
$requestParams,
|
102 |
+
$params
|
103 |
+
);
|
104 |
+
$message = $generator->generate();
|
105 |
+
|
106 |
+
} catch (Exception $e) {
|
107 |
+
$this->handleException($e);
|
108 |
}
|
109 |
|
110 |
+
$this->setTextResponse($message);
|
111 |
+
}
|
|
|
|
|
|
|
112 |
|
113 |
+
public function preDispatch()
|
114 |
+
{
|
115 |
+
// Do not start standart session
|
116 |
+
$this->setFlag('', self::FLAG_NO_START_SESSION, 1);
|
117 |
|
118 |
+
parent::preDispatch();
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
|
120 |
+
try {
|
121 |
+
|
122 |
+
// Make sure this access method is enabled
|
123 |
+
if (!$this->isEnabled()) {
|
124 |
+
$this->_redirect('noroute');
|
125 |
+
$this->setFlag('',self::FLAG_NO_DISPATCH,true);
|
126 |
+
return $this;
|
127 |
+
}
|
128 |
+
|
129 |
+
// Validate Authentication
|
130 |
+
$this->_authenticate();
|
131 |
+
|
132 |
+
} catch (Exception $e) {
|
133 |
+
$this->handleException($e);
|
134 |
+
}
|
135 |
|
136 |
+
return $this;
|
137 |
}
|
138 |
|
139 |
+
protected function isEnabled() {
|
140 |
+
$helper = Mage::helper('searchspring_manager');
|
141 |
+
// For now, this controller only handles the 'simple' auth method
|
142 |
+
$authenticationMethod = $helper->getAuthenticationMethod();
|
143 |
+
return $authenticationMethod == 'simple';
|
144 |
+
}
|
145 |
+
|
146 |
+
protected function _authenticate() {
|
147 |
+
|
148 |
+
$helper = Mage::helper('searchspring_manager/http');
|
149 |
+
list($username, $password) = $helper->isSimpleAuthProvided(true); // true, itemized
|
150 |
+
|
151 |
+
// Make sure auth username was provided
|
152 |
+
if(empty($username)) {
|
153 |
+
$this->respondWithError(
|
154 |
+
'Authentication Failed: Missing username',
|
155 |
+
SearchSpring_ErrorCodes::AUTH_CREDENTIALS_MISSING,
|
156 |
+
401
|
157 |
+
);
|
158 |
+
}
|
159 |
+
|
160 |
+
// Make sure auth password was provided
|
161 |
+
if(empty($password)) {
|
162 |
+
$this->respondWithError(
|
163 |
+
'Authentication Failed: Missing password',
|
164 |
+
SearchSpring_ErrorCodes::AUTH_CREDENTIALS_MISSING,
|
165 |
+
401
|
166 |
+
);
|
167 |
+
}
|
168 |
+
|
169 |
+
// Make sure auth credentials are valid
|
170 |
+
if(!$helper->isSimpleAuthValid()) {
|
171 |
+
$this->respondWithError(
|
172 |
+
'Authentication Failed: Invalid credentials',
|
173 |
+
SearchSpring_ErrorCodes::AUTH_CREDENTIALS_INVALID,
|
174 |
+
401
|
175 |
+
);
|
176 |
+
}
|
177 |
+
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Action to forward/redirect/fork to when an unhandled
|
182 |
+
* exception/error is encountered.
|
183 |
+
*
|
184 |
+
* @return void
|
185 |
+
*/
|
186 |
+
public function exceptionAction() {
|
187 |
+
|
188 |
+
// Get Message and Status Code from controller flag
|
189 |
+
$message = $this->getFlag('', 'unhandled-exception-message');
|
190 |
+
$errorCode = $this->getFlag('', 'unhandled-exception-error-code');
|
191 |
+
$responseCode = $this->getFlag('', 'unhandled-exception-http-response-code');
|
192 |
+
|
193 |
+
// Default Message and Code
|
194 |
+
if (!$message) $message = "Unknown Issue";
|
195 |
+
if (!$errorCode) $errorCode = "Unknown Issue";
|
196 |
+
if (!$responseCode) $responseCode = 500;
|
197 |
+
|
198 |
+
// Return all issues as json response
|
199 |
+
$this->setJsonResponse(
|
200 |
+
array(
|
201 |
+
'status' => 'error',
|
202 |
+
'errorCode' => $errorCode,
|
203 |
+
'message' => $message,
|
204 |
+
),
|
205 |
+
$responseCode
|
206 |
+
);
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Respond with error, and optional error code / http status codes
|
211 |
+
*
|
212 |
+
* @param Exception $e Any exception that needs to be handled
|
213 |
+
*
|
214 |
+
* @throws Mage_Core_Controller_Varien_Exception always, to bubble up to parent
|
215 |
+
*/
|
216 |
+
protected function handleException(Exception $e) {
|
217 |
+
|
218 |
+
// If it's already a controller exception, just bubble it up
|
219 |
+
if ($e instanceof Mage_Core_Controller_Varien_Exception) {
|
220 |
+
throw $e;
|
221 |
+
}
|
222 |
+
|
223 |
+
// Convert to controller exception, as unknown, 500
|
224 |
+
$this->respondWithError($e->getMessage());
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Respond with error, and optional error code / http status codes
|
229 |
+
*
|
230 |
+
* @param string $message The message that should be sent back
|
231 |
+
* @param int $errorCode The SpringSpring Error Code
|
232 |
+
* @param int $responseCode The Http response code
|
233 |
+
*
|
234 |
+
* @throws Mage_Core_Controller_Varien_Exception always, to bubble up to parent
|
235 |
+
*/
|
236 |
+
protected function respondWithError(
|
237 |
+
$message,
|
238 |
+
$errorCode = SearchSpring_ErrorCodes::UNKNOWN_EXCEPTION,
|
239 |
+
$responseCode = 500
|
240 |
+
) {
|
241 |
+
$controllerException = new Mage_Core_Controller_Varien_Exception;
|
242 |
+
$controllerException->prepareFork('exception');
|
243 |
+
$controllerException->prepareFlag('exception', 'unhandled-exception-message', $message);
|
244 |
+
$controllerException->prepareFlag('exception', 'unhandled-exception-error-code', $errorCode);
|
245 |
+
$controllerException->prepareFlag('exception', 'unhandled-exception-http-response-code', $responseCode);
|
246 |
+
throw $controllerException;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Set appropriate response variables for a json response
|
251 |
+
*
|
252 |
+
* @param array $message The message that should be sent back
|
253 |
+
* @param int $responseCode The Http response code
|
254 |
+
*/
|
255 |
private function setJsonResponse(array $message, $responseCode = 200)
|
256 |
{
|
257 |
+
// If we are requiring authentication, set the correct header as well
|
258 |
+
if ($responseCode == 401) {
|
259 |
+
$this->getResponse()->setHeader('WWW-Authenticate','Basic realm="SearchSpring Manager"');
|
260 |
+
}
|
261 |
+
|
262 |
$this->getResponse()->setHeader('Content-type', 'application/json');
|
263 |
$this->getResponse()->setHttpResponseCode($responseCode);
|
264 |
|
265 |
+
$responseBody = Zend_Json::encode($message);
|
266 |
$this->getResponse()->setBody($responseBody);
|
267 |
}
|
268 |
|
278 |
$this->getResponse()->setHttpResponseCode($responseCode);
|
279 |
$this->getResponse()->setBody($message);
|
280 |
}
|
281 |
+
|
282 |
}
|
app/code/community/SearchSpring/Manager/etc/api2.xml
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<api2>
|
4 |
+
<resource_groups>
|
5 |
+
<searchspring translate="title" module="searchspring_manager">
|
6 |
+
<title>SearchSpring Indexing</title>
|
7 |
+
<sort_order>10</sort_order>
|
8 |
+
</searchspring>
|
9 |
+
</resource_groups>
|
10 |
+
<resources>
|
11 |
+
<searchspring translate="title" module="searchspring_manager">
|
12 |
+
<group>searchspring</group>
|
13 |
+
<model>searchspring_manager/api2_indexing</model>
|
14 |
+
<title>SearchSpring Indexing</title>
|
15 |
+
<sort_order>10</sort_order>
|
16 |
+
<privileges>
|
17 |
+
<admin>
|
18 |
+
<!-- We don't actually need these all yet, but in case we do let's set them up for permissions -->
|
19 |
+
<create>1</create>
|
20 |
+
<retrieve>1</retrieve>
|
21 |
+
<update>1</update>
|
22 |
+
<delete>1</delete>
|
23 |
+
</admin>
|
24 |
+
</privileges>
|
25 |
+
<attributes>
|
26 |
+
<type>Request Type</type>
|
27 |
+
<ids>Request Ids</ids>
|
28 |
+
<size>Request Size</size>
|
29 |
+
<start>Request Start</start>
|
30 |
+
<count>Request Feed Count</count>
|
31 |
+
<filename>Feed Filename</filename>
|
32 |
+
<store>Store</store>
|
33 |
+
</attributes>
|
34 |
+
<force_attributes>
|
35 |
+
<admin>
|
36 |
+
<type>Request Type</type>
|
37 |
+
<ids>Request Ids</ids>
|
38 |
+
<size>Request Size</size>
|
39 |
+
<start>Request Start</start>
|
40 |
+
<count>Request Feed Count</count>
|
41 |
+
<filename>Feed Filename</filename>
|
42 |
+
<store>Store</store>
|
43 |
+
</admin>
|
44 |
+
</force_attributes>
|
45 |
+
<routes>
|
46 |
+
<route_feed>
|
47 |
+
<route>/searchspring/feed</route>
|
48 |
+
<action_type>entity</action_type>
|
49 |
+
</route_feed>
|
50 |
+
<route_index>
|
51 |
+
<route>/searchspring/index</route>
|
52 |
+
<action_type>collection</action_type>
|
53 |
+
</route_index>
|
54 |
+
</routes>
|
55 |
+
<versions>1</versions>
|
56 |
+
</searchspring>
|
57 |
+
</resources>
|
58 |
+
</api2>
|
59 |
+
</config>
|
app/code/community/SearchSpring/Manager/etc/config.xml
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
<config>
|
3 |
<modules>
|
4 |
<SearchSpring_Manager>
|
5 |
-
<version>1.
|
6 |
</SearchSpring_Manager>
|
7 |
</modules>
|
8 |
<global>
|
@@ -28,14 +28,15 @@
|
|
28 |
</searchspring_manager>
|
29 |
</helpers>
|
30 |
<events>
|
31 |
-
|
|
|
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 |
-
</
|
39 |
<catalog_product_delete_before>
|
40 |
<observers>
|
41 |
<searchspring_manager_before_delete_push_product>
|
@@ -52,14 +53,14 @@
|
|
52 |
</searchspring_manager_after_delete_push_product>
|
53 |
</observers>
|
54 |
</catalog_product_delete_after_done>
|
55 |
-
<
|
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 |
-
</
|
63 |
<category_move>
|
64 |
<observers>
|
65 |
<searchspring_manager_after_move_update_product_category>
|
@@ -98,13 +99,40 @@
|
|
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>
|
@@ -125,14 +153,58 @@
|
|
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>
|
2 |
<config>
|
3 |
<modules>
|
4 |
<SearchSpring_Manager>
|
5 |
+
<version>1.9.0</version>
|
6 |
</SearchSpring_Manager>
|
7 |
</modules>
|
8 |
<global>
|
28 |
</searchspring_manager>
|
29 |
</helpers>
|
30 |
<events>
|
31 |
+
<!-- use catalog_product_save_commit_after otherwise we can get stale product data if our request runs before commit -->
|
32 |
+
<catalog_product_save_commit_after>
|
33 |
<observers>
|
34 |
<searchspring_manager_after_save_push_product>
|
35 |
<class>searchspring_manager/observer_productSaveObserver</class>
|
36 |
<method>afterSavePushProduct</method>
|
37 |
</searchspring_manager_after_save_push_product>
|
38 |
</observers>
|
39 |
+
</catalog_product_save_commit_after>
|
40 |
<catalog_product_delete_before>
|
41 |
<observers>
|
42 |
<searchspring_manager_before_delete_push_product>
|
53 |
</searchspring_manager_after_delete_push_product>
|
54 |
</observers>
|
55 |
</catalog_product_delete_after_done>
|
56 |
+
<catalog_category_save_commit_after>
|
57 |
<observers>
|
58 |
<searchspring_manager_after_save_update_product_category>
|
59 |
<class>searchspring_manager/observer_categorySaveObserver</class>
|
60 |
<method>afterSaveUpdateProductCategory</method>
|
61 |
</searchspring_manager_after_save_update_product_category>
|
62 |
</observers>
|
63 |
+
</catalog_category_save_commit_after>
|
64 |
<category_move>
|
65 |
<observers>
|
66 |
<searchspring_manager_after_move_update_product_category>
|
99 |
<searchspring_manager>
|
100 |
<setup>
|
101 |
<module>SearchSpring_Manager</module>
|
|
|
102 |
</setup>
|
103 |
<connection>
|
104 |
<use>core_setup</use>
|
105 |
</connection>
|
106 |
</searchspring_manager>
|
107 |
</resources>
|
108 |
+
<searchspring>
|
109 |
+
<api_host>https://api-beta.searchspring.net</api_host>
|
110 |
+
<!--<api_host>http://23.23.23.2</api_host>-->
|
111 |
+
<api_auth>
|
112 |
+
<consumer>
|
113 |
+
<label>SearchSpring</label>
|
114 |
+
</consumer>
|
115 |
+
<role>
|
116 |
+
<label>SearchSpring</label>
|
117 |
+
</role>
|
118 |
+
<admin_user>
|
119 |
+
<username>searchspring_api</username>
|
120 |
+
<firstname>SearchSpring</firstname>
|
121 |
+
<lastname>SearchSpring</lastname>
|
122 |
+
<email>dev+magentoapi@searchspring.com</email>
|
123 |
+
</admin_user>
|
124 |
+
</api_auth>
|
125 |
+
</searchspring>
|
126 |
+
<api2>
|
127 |
+
<auth_adapters>
|
128 |
+
<oauth_zero_legged>
|
129 |
+
<model>searchspring_manager/api2_auth_adapter</model>
|
130 |
+
<label>oAuth Zero Legged</label>
|
131 |
+
<enabled>1</enabled>
|
132 |
+
<order>5</order>
|
133 |
+
</oauth_zero_legged>
|
134 |
+
</auth_adapters>
|
135 |
+
</api2>
|
136 |
</global>
|
137 |
<frontend>
|
138 |
<routers>
|
153 |
<!-- </updates> -->
|
154 |
<!-- </layout> -->
|
155 |
</frontend>
|
156 |
+
<adminhtml>
|
157 |
+
<events>
|
158 |
+
<admin_system_config_changed_section_ssmanager>
|
159 |
+
<observers>
|
160 |
+
<searchspring_manager_after_config_section_changed>
|
161 |
+
<class>searchspring_manager/observer_configObserver</class>
|
162 |
+
<method>afterSystemConfigSectionChanged</method>
|
163 |
+
</searchspring_manager_after_config_section_changed>
|
164 |
+
</observers>
|
165 |
+
</admin_system_config_changed_section_ssmanager>
|
166 |
+
</events>
|
167 |
+
</adminhtml>
|
168 |
<default>
|
169 |
<ssmanager>
|
170 |
<ssmanager_general>
|
171 |
<live_indexing>0</live_indexing>
|
172 |
+
<feed_path>media/searchspring/feed/</feed_path>
|
173 |
</ssmanager_general>
|
174 |
<ssmanager_catalog>
|
175 |
<enable_categories>0</enable_categories>
|
176 |
</ssmanager_catalog>
|
177 |
+
<ssmanager_images>
|
178 |
+
<generate_cache_images>1</generate_cache_images>
|
179 |
+
</ssmanager_images>
|
180 |
+
<ssmanager_api>
|
181 |
+
<authentication_method>simple</authentication_method>
|
182 |
+
</ssmanager_api>
|
183 |
</ssmanager>
|
184 |
</default>
|
185 |
+
|
186 |
+
<global>
|
187 |
+
<searchspring>
|
188 |
+
<operation>
|
189 |
+
<third_party>
|
190 |
+
|
191 |
+
<!-- Feed Operations for Third Party Modules -->
|
192 |
+
|
193 |
+
<TBT_Rewards>
|
194 |
+
<loyalty>
|
195 |
+
<class>SearchSpring_Manager_ThirdParty_TBT_RewardsOperation</class>
|
196 |
+
</loyalty>
|
197 |
+
</TBT_Rewards>
|
198 |
+
|
199 |
+
<Amasty_Label>
|
200 |
+
<labels>
|
201 |
+
<class>SearchSpring_Manager_ThirdParty_Amasty_LabelsOperation</class>
|
202 |
+
</labels>
|
203 |
+
</Amasty_Label>
|
204 |
+
|
205 |
+
</third_party>
|
206 |
+
</operation>
|
207 |
+
</searchspring>
|
208 |
+
</global>
|
209 |
+
|
210 |
</config>
|
app/code/community/SearchSpring/Manager/etc/system.xml
CHANGED
@@ -16,6 +16,31 @@
|
|
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>
|
@@ -43,12 +68,30 @@
|
|
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>
|
52 |
<show_in_default>1</show_in_default>
|
53 |
<show_in_website>1</show_in_website>
|
54 |
<show_in_store>1</show_in_store>
|
@@ -105,7 +148,7 @@
|
|
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>
|
@@ -114,7 +157,6 @@
|
|
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>
|
@@ -123,7 +165,6 @@
|
|
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>
|
@@ -146,9 +187,44 @@
|
|
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>
|
16 |
<show_in_website>1</show_in_website>
|
17 |
<show_in_store>1</show_in_store>
|
18 |
<groups>
|
19 |
+
<hint>
|
20 |
+
<frontend_model>searchspring_manager/adminhtml_system_config_fieldset_hint</frontend_model>
|
21 |
+
<sort_order>0</sort_order>
|
22 |
+
<show_in_default>1</show_in_default>
|
23 |
+
<show_in_website>1</show_in_website>
|
24 |
+
<show_in_store>1</show_in_store>
|
25 |
+
</hint>
|
26 |
+
<setup>
|
27 |
+
<frontend_model>searchspring_manager/adminhtml_system_config_fieldset_setup</frontend_model>
|
28 |
+
<sort_order>10</sort_order>
|
29 |
+
<show_in_default>1</show_in_default>
|
30 |
+
<show_in_website>1</show_in_website>
|
31 |
+
<show_in_store>1</show_in_store>
|
32 |
+
</setup>
|
33 |
+
<ssmanager_track translate="label" moudle="searchspring_manager">
|
34 |
+
<label>Tracking Data</label>
|
35 |
+
<show_in_default>0</show_in_default>
|
36 |
+
<show_in_website>0</show_in_website>
|
37 |
+
<show_in_store>0</show_in_store>
|
38 |
+
<fields>
|
39 |
+
<uuid translate="label">
|
40 |
+
<label>UUID</label>
|
41 |
+
</uuid>
|
42 |
+
</fields>
|
43 |
+
</ssmanager_track>
|
44 |
<ssmanager_general translate="label" module="searchspring_manager">
|
45 |
<label>Indexing Configuration</label>
|
46 |
<frontend_type>text</frontend_type>
|
68 |
<show_in_website>1</show_in_website>
|
69 |
<show_in_store>1</show_in_store>
|
70 |
</index_zero_price>
|
71 |
+
<index_out_of_stock translate="label">
|
72 |
+
<label>Display out of stock products</label>
|
73 |
+
<frontend_type>select</frontend_type>
|
74 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
75 |
+
<sort_order>15</sort_order>
|
76 |
+
<show_in_default>1</show_in_default>
|
77 |
+
<show_in_website>1</show_in_website>
|
78 |
+
<show_in_store>1</show_in_store>
|
79 |
+
</index_out_of_stock>
|
80 |
+
<feed_path translate="label">
|
81 |
+
<label>Feed Path</label>
|
82 |
+
<comment>Relative to the base Magento installation directory. Must be writable. Recommended: media/searchspring/feed/</comment>
|
83 |
+
<frontend_type>text</frontend_type>
|
84 |
+
<sort_order>20</sort_order>
|
85 |
+
<show_in_default>1</show_in_default>
|
86 |
+
<show_in_website>0</show_in_website>
|
87 |
+
<show_in_store>0</show_in_store>
|
88 |
+
</feed_path>
|
89 |
</fields>
|
90 |
</ssmanager_general>
|
91 |
<ssmanager_images translate="label" module="searchspring_manager">
|
92 |
<label>Image Settings</label>
|
93 |
<frontend_type>text</frontend_type>
|
94 |
+
<sort_order>150</sort_order>
|
95 |
<show_in_default>1</show_in_default>
|
96 |
<show_in_website>1</show_in_website>
|
97 |
<show_in_store>1</show_in_store>
|
148 |
</fields>
|
149 |
</ssmanager_catalog>
|
150 |
<ssmanager_api translate="label" module="searchspring_manager">
|
151 |
+
<label>SearchSpring Connection Settings (Advanced)</label>
|
152 |
<frontend_type>select</frontend_type>
|
153 |
<sort_order>300</sort_order>
|
154 |
<show_in_default>1</show_in_default>
|
157 |
<fields>
|
158 |
<base_url translate="label">
|
159 |
<label>Base URL</label>
|
|
|
160 |
<frontend_type>text</frontend_type>
|
161 |
<sort_order>15</sort_order>
|
162 |
<show_in_default>1</show_in_default>
|
165 |
</base_url>
|
166 |
<site_id translate="label">
|
167 |
<label>Site ID</label>
|
|
|
168 |
<frontend_type>text</frontend_type>
|
169 |
<sort_order>20</sort_order>
|
170 |
<show_in_default>1</show_in_default>
|
187 |
<show_in_website>1</show_in_website>
|
188 |
<show_in_store>1</show_in_store>
|
189 |
</secret_key>
|
190 |
+
<authentication_method translate="label">
|
191 |
+
<label>Authentication Method</label>
|
192 |
+
<frontend_type>text</frontend_type>
|
193 |
+
<sort_order>15</sort_order>
|
194 |
+
<show_in_default>1</show_in_default>
|
195 |
+
<show_in_website>1</show_in_website>
|
196 |
+
<show_in_store>1</show_in_store>
|
197 |
+
</authentication_method>
|
198 |
+
<magento_api_admin_user>
|
199 |
+
<label>Magento API - Admin User</label>
|
200 |
+
<!-- TODO change type to dropdown -->
|
201 |
+
<frontend_type>text</frontend_type>
|
202 |
+
<sort_order>50</sort_order>
|
203 |
+
<show_in_default>1</show_in_default>
|
204 |
+
<show_in_website>1</show_in_website>
|
205 |
+
<show_in_store>1</show_in_store>
|
206 |
+
</magento_api_admin_user>
|
207 |
+
<magento_api_admin_user_role>
|
208 |
+
<label>Magento API - Admin User REST Role</label>
|
209 |
+
<!-- TODO change type to dropdown -->
|
210 |
+
<frontend_type>text</frontend_type>
|
211 |
+
<sort_order>50</sort_order>
|
212 |
+
<show_in_default>1</show_in_default>
|
213 |
+
<show_in_website>1</show_in_website>
|
214 |
+
<show_in_store>1</show_in_store>
|
215 |
+
</magento_api_admin_user_role>
|
216 |
+
<magento_api_oauth_consumer>
|
217 |
+
<label>Magento API - oAuth Consumer</label>
|
218 |
+
<!-- TODO change type to dropdown -->
|
219 |
+
<frontend_type>text</frontend_type>
|
220 |
+
<sort_order>50</sort_order>
|
221 |
+
<show_in_default>1</show_in_default>
|
222 |
+
<show_in_website>1</show_in_website>
|
223 |
+
<show_in_store>1</show_in_store>
|
224 |
+
</magento_api_oauth_consumer>
|
225 |
</fields>
|
226 |
</ssmanager_api>
|
227 |
</groups>
|
228 |
</ssmanager>
|
229 |
</sections>
|
230 |
+
</config>
|
app/code/community/SearchSpring/Manager/sql/searchspring_manager/mysql4-install-1.0.0.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$this->startSetup();
|
4 |
+
|
5 |
+
$apiHost = Mage::getConfig()->getNode('global/searchspring/api_host');
|
6 |
+
|
7 |
+
$config = new Mage_Core_Model_Config();
|
8 |
+
$uuid = getUUIDv4();
|
9 |
+
$config->saveConfig('ssmanager/ssmanager_track/uuid', $uuid);
|
10 |
+
|
11 |
+
//file_get_contents($apiHost . "/api/track/ga-track.png?ec=Magento&ea=Install&el=" . $_SERVER['HTTP_HOST'] . "&cid=" . $uuid);
|
12 |
+
|
13 |
+
$client = new Zend_Http_Client();
|
14 |
+
$client->setUri($apiHost . "/api/track/ga-track.json");
|
15 |
+
$client->setConfig(array('maxredirects'=>0, 'timeout'=>30));
|
16 |
+
$client->setParameterGet(array(
|
17 |
+
'ec' => 'Magento',
|
18 |
+
'ea' => 'Install',
|
19 |
+
'el' => $_SERVER['HTTP_HOST'],
|
20 |
+
'cid' => $uuid
|
21 |
+
));
|
22 |
+
$response = $client->request();
|
23 |
+
|
24 |
+
$this->endSetup();
|
25 |
+
|
26 |
+
function getUUIDv4()
|
27 |
+
{
|
28 |
+
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
29 |
+
// 32 bits for "time_low"
|
30 |
+
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
|
31 |
+
|
32 |
+
// 16 bits for "time_mid"
|
33 |
+
mt_rand( 0, 0xffff ),
|
34 |
+
|
35 |
+
// 16 bits for "time_hi_and_version",
|
36 |
+
// four most significant bits holds version number 4
|
37 |
+
mt_rand( 0, 0x0fff ) | 0x4000,
|
38 |
+
|
39 |
+
// 16 bits, 8 bits for "clk_seq_hi_res",
|
40 |
+
// 8 bits for "clk_seq_low",
|
41 |
+
// two most significant bits holds zero and one for variant DCE1.1
|
42 |
+
mt_rand( 0, 0x3fff ) | 0x8000,
|
43 |
+
|
44 |
+
// 48 bits for "node"
|
45 |
+
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
|
46 |
+
);
|
47 |
+
}
|
48 |
+
|
49 |
+
|
app/code/community/SearchSpring/Manager/sql/searchspring_manager/mysql4-upgrade-0.4.0-1.0.0.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
|
5 |
+
$this->startSetup();
|
6 |
+
|
7 |
+
$apiHost = Mage::getConfig()->getNode('global/searchspring/api_host');
|
8 |
+
|
9 |
+
$config = new Mage_Core_Model_Config();
|
10 |
+
$uuid = getUUIDv4();
|
11 |
+
$config->saveConfig('ssmanager/ssmanager_track/uuid', $uuid);
|
12 |
+
|
13 |
+
$client = new Zend_Http_Client();
|
14 |
+
$client->setUri($apiHost . "/api/track/ga-track.json");
|
15 |
+
$client->setConfig(array('maxredirects'=>0, 'timeout'=>30));
|
16 |
+
$client->setParameterGet(array(
|
17 |
+
'ec' => 'Magento',
|
18 |
+
'ea' => 'Install',
|
19 |
+
'el' => $_SERVER['HTTP_HOST'],
|
20 |
+
'cid' => $uuid
|
21 |
+
));
|
22 |
+
$response = $client->request();
|
23 |
+
|
24 |
+
$this->endSetup();
|
25 |
+
|
26 |
+
function getUUIDv4()
|
27 |
+
{
|
28 |
+
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
29 |
+
// 32 bits for "time_low"
|
30 |
+
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
|
31 |
+
|
32 |
+
// 16 bits for "time_mid"
|
33 |
+
mt_rand( 0, 0xffff ),
|
34 |
+
|
35 |
+
// 16 bits for "time_hi_and_version",
|
36 |
+
// four most significant bits holds version number 4
|
37 |
+
mt_rand( 0, 0x0fff ) | 0x4000,
|
38 |
+
|
39 |
+
// 16 bits, 8 bits for "clk_seq_hi_res",
|
40 |
+
// 8 bits for "clk_seq_low",
|
41 |
+
// two most significant bits holds zero and one for variant DCE1.1
|
42 |
+
mt_rand( 0, 0x3fff ) | 0x8000,
|
43 |
+
|
44 |
+
// 48 bits for "node"
|
45 |
+
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
|
46 |
+
);
|
47 |
+
}
|
48 |
+
|
49 |
+
|
app/design/adminhtml/default/default/template/searchspring/manager/system/config/fieldset/hint.phtml
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script type="text/javascript">
|
2 |
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
3 |
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
4 |
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
5 |
+
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
6 |
+
|
7 |
+
ga('create', 'UA-90531-3', 'auto');
|
8 |
+
ga('send', 'event', 'Magento', 'Load Settings Web', '<?php echo $_SERVER['HTTP_HOST'] ?>');
|
9 |
+
</script>
|
10 |
+
|
11 |
+
<div class="searchspring-hint">
|
12 |
+
<a onClick="ga('send', 'event', 'Magento', 'Click Logo', '<?php echo $_SERVER['HTTP_HOST'] ?>')" class="searchspring-logo" href="http://www.searchspring.com"><img src="//cdn.searchspring.net/magento/logo.png" /></a>
|
13 |
+
|
14 |
+
<div class="main-text">
|
15 |
+
<?php if($this->isSetup()): ?>
|
16 |
+
<h3>Thanks!</h3>
|
17 |
+
<p>
|
18 |
+
<strong>It looks like you're all setup.</strong> If you need any questions or concerns please contact
|
19 |
+
<a onClick="ga('send', 'event', 'Magento', 'Click Support', '<?php echo $_SERVER['HTTP_HOST'] ?>')" target="_new" href="https://searchspring.zendesk.com/hc/en-us/requests/new">SearchSpring Support</a>.
|
20 |
+
We're here to help.
|
21 |
+
</p>
|
22 |
+
<?php else: ?>
|
23 |
+
<h3>Welcome!</h3>
|
24 |
+
<p>
|
25 |
+
<strong>It looks like you're not setup yet.</strong> If you're not a SearchSpring customer yet you can
|
26 |
+
<a onClick="ga('send', 'event', 'Magento', 'Request Demo', '<?php echo $_SERVER['HTTP_HOST'] ?>')" target="_new" href="http://www.searchspring.com/request-a-demo">request a demo</a>. If
|
27 |
+
you need help with setup please contact <a onClick="ga('send', 'event', 'Magento', 'Click Support', '<?php echo $_SERVER['HTTP_HOST'] ?>')" target="_new" href="https://searchspring.zendesk.com/hc/en-us/requests/new">SearchSpring Support</a>.
|
28 |
+
</p>
|
29 |
+
<?php endif; ?>
|
30 |
+
<p>This module links your Magento store to your SearchSpring account. This allows us to update your SearchSpring Search & Navigation in near real-time.</p>
|
31 |
+
</div>
|
32 |
+
|
33 |
+
<div class="bottom-links">
|
34 |
+
<a onClick="ga('send', 'event', 'Magento', 'Click Support', '<?php echo $_SERVER['HTTP_HOST'] ?>')" href="https://searchspring.zendesk.com/hc/en-us/requests/new" target="_new">SearchSpring Support</a> |
|
35 |
+
<a onClick="ga('send', 'event', 'Magento', 'Click Knowledge Base', '<?php echo $_SERVER['HTTP_HOST'] ?>')" href="https://searchspring.zendesk.com/hc/en-us/categories/200117639-Knowledge-Base" target="_new">Knowledge Base</a>
|
36 |
+
<span class="version">
|
37 |
+
v<?php echo $this->getVersion() ?>
|
38 |
+
</span>
|
39 |
+
</div>
|
40 |
+
|
41 |
+
<div style="clear:both"></div>
|
42 |
+
</div>
|
43 |
+
|
44 |
+
|
45 |
+
<style type="text/css">
|
46 |
+
.searchspring-hint {
|
47 |
+
margin: 5px 0;
|
48 |
+
padding: 5px 8px;
|
49 |
+
background-color: #e6eef0;
|
50 |
+
border:1px solid #cadcdc;
|
51 |
+
position: relative;
|
52 |
+
}
|
53 |
+
|
54 |
+
.searchspring-logo {
|
55 |
+
float: left;
|
56 |
+
margin-right: 15px;
|
57 |
+
}
|
58 |
+
|
59 |
+
.bottom-links {
|
60 |
+
position: absolute;
|
61 |
+
bottom: 5px;
|
62 |
+
right: 8px;
|
63 |
+
}
|
64 |
+
|
65 |
+
.main-text {
|
66 |
+
margin: 15px 0;
|
67 |
+
}
|
68 |
+
|
69 |
+
.main-text h3 {
|
70 |
+
color: #01214f;
|
71 |
+
}
|
72 |
+
|
73 |
+
.version {
|
74 |
+
color: #999999;
|
75 |
+
margin-left: 20px;
|
76 |
+
}
|
77 |
+
</style>
|
app/design/adminhtml/default/default/template/searchspring/manager/system/config/fieldset/setup.phtml
ADDED
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="section-config active ss-login">
|
2 |
+
<input id="searchspring_new_connection_fl" name="searchspring_new_connection_fl" type="hidden" value="0"/>
|
3 |
+
<div class="entry-edit-head ">
|
4 |
+
<a>SearchSpring Connection</a>
|
5 |
+
</div>
|
6 |
+
<fieldset class="ss-login-connected config collapseable" id="ssmanager_ssmanager_setup">
|
7 |
+
<legend>SearchSpring Connection</legend>
|
8 |
+
<table cellspacing="0" class="form-list">
|
9 |
+
<colgroup class="label"></colgroup>
|
10 |
+
<colgroup class="value"></colgroup>
|
11 |
+
<colgroup class="scope-label"></colgroup>
|
12 |
+
<colgroup class=""></colgroup>
|
13 |
+
<tbody>
|
14 |
+
<tr id="row_ssmanager_ssmanager_setup_connection" class="ss-step1">
|
15 |
+
<td class="label">
|
16 |
+
<label for="ssmanager_ssmanager_setup_connection">Connection</label>
|
17 |
+
</td>
|
18 |
+
<td class="value">
|
19 |
+
<div class="ss-connect-message">Connected</div>
|
20 |
+
<button id="ss-reconnect-button" title="Re-connect" type="button" class="scalable" style=""><span><span><span>Reset Connection</span></span></span></button>
|
21 |
+
</td>
|
22 |
+
<td class="scope-label"></td>
|
23 |
+
<td class=""></td>
|
24 |
+
</tr>
|
25 |
+
</tbody>
|
26 |
+
</table>
|
27 |
+
</fieldset>
|
28 |
+
<fieldset class="ss-login-not_connected config collapseable" id="ssmanager_ssmanager_setup">
|
29 |
+
<legend>SearchSpring Connection</legend>
|
30 |
+
<table cellspacing="0" class="form-list">
|
31 |
+
<colgroup class="label"></colgroup>
|
32 |
+
<colgroup class="value"></colgroup>
|
33 |
+
<colgroup></colgroup>
|
34 |
+
<tbody>
|
35 |
+
<tr>
|
36 |
+
<td class="ss-has-account-header" colspan="2">Have a SearchSpring Account?</td>
|
37 |
+
<td class="ss-no-account-header ss-no-account">Don't have a SearchSpring Account yet?</td>
|
38 |
+
</tr>
|
39 |
+
<tr id="row_ssmanager_ssmanager_setup_username" class="ss-step1">
|
40 |
+
<td class="label">
|
41 |
+
<label for="ssmanager_ssmanager_setup_username">SearchSpring Username</label>
|
42 |
+
</td>
|
43 |
+
<td class="value">
|
44 |
+
<input id="ssmanager_ssmanager_setup_username" tabindex="1" name="groups[ssmanager_setup][fields][username][value]" class=" input-text" type="text">
|
45 |
+
</td>
|
46 |
+
<td class="ss-no-account" rowspan="3">
|
47 |
+
<a href="http://www.searchspring.com/request-a-demo">
|
48 |
+
<button id="ss-demo-button" title="Request A Demo" type="button" class="scalable" style=""><span><span><span>Request A Demo</span></span></span></button>
|
49 |
+
</a>
|
50 |
+
</td>
|
51 |
+
</tr>
|
52 |
+
<tr id="row_ssmanager_ssmanager_setup_password" class="ss-step1">
|
53 |
+
<td class="label"><label for="ssmanager_ssmanager_setup_password">SearchSpring Password</label></td>
|
54 |
+
<td class="value">
|
55 |
+
<input id="ssmanager_ssmanager_setup_password" tabindex="2" name="groups[ssmanager_setup][fields][password][value]" class=" input-text" type="password">
|
56 |
+
</td>
|
57 |
+
<td class="scope-label"></td>
|
58 |
+
<td class=""></td>
|
59 |
+
</tr>
|
60 |
+
<tr id="row_ssmanager_ssmanager_setup_authentication_method" class="ss-step1">
|
61 |
+
<td class="label"><label for="ssmanager_ssmanager_setup_authentication_method">Authenticaion Method</label></td>
|
62 |
+
<td class="value">
|
63 |
+
<select id="ssmanager_ssmanager_setup_authentication_method" tabindex="2" name="groups[ssmanager_setup][fields][authentication_method][value]" class=" input-text">
|
64 |
+
<option value="simple">Simple Authentication</option>
|
65 |
+
<option value="oauth">OAuth API</option>
|
66 |
+
</select>
|
67 |
+
<p class="note">If you're not sure your Magento install supports OAuth API use Simple Authentication</p>
|
68 |
+
</td>
|
69 |
+
<td class="scope-label"></td>
|
70 |
+
<td class=""></td>
|
71 |
+
</tr>
|
72 |
+
<tr id="row_ssmanager_ssmanager_setup_login" class="ss-step1">
|
73 |
+
<td class="label"></td>
|
74 |
+
<td class="value">
|
75 |
+
<button id="ss-login-button" title="Login" type="button" class="scalable" style=""><span><span><span>Login</span></span></span></button>
|
76 |
+
</td>
|
77 |
+
<td></td>
|
78 |
+
</tr>
|
79 |
+
<tr id="row_ssmanager_ssmanager_setup_website" class="ss-step2">
|
80 |
+
<td class="label">
|
81 |
+
<label for="ssmanager_ssmanager_setup_website">Website</label>
|
82 |
+
</td>
|
83 |
+
<td class="value">
|
84 |
+
<select disabled id="ssmanager_ssmanager_setup_website" name="groups[ssmanager_setup][fields][website][value]" class=" select"></select>
|
85 |
+
</td>
|
86 |
+
<td class="ss-no-account" rowspan="3">
|
87 |
+
<a href="http://www.searchspring.com/request-a-demo">
|
88 |
+
<button id="ss-demo-button" title="Request A Demo" type="button" class="scalable" style=""><span><span><span>Request A Demo</span></span></span></button>
|
89 |
+
</a>
|
90 |
+
</td>
|
91 |
+
</tr>
|
92 |
+
<tr id="row_ssmanager_ssmanager_setup_feed" class="ss-step2">
|
93 |
+
<td class="label">
|
94 |
+
<label for="ssmanager_ssmanager_setup_feed">Feed</label>
|
95 |
+
</td>
|
96 |
+
<td class="value">
|
97 |
+
<select disabled id="ssmanager_ssmanager_setup_feed" name="groups[ssmanager_setup][fields][feed][value]" class=" select"></select>
|
98 |
+
</td>
|
99 |
+
<td></td>
|
100 |
+
</tr>
|
101 |
+
<tr id="row_ssmanager_ssmanager_setup_connect" class="ss-step2">
|
102 |
+
<td class="label"></td>
|
103 |
+
<td class="value">
|
104 |
+
<button disabled id="ss-connect-button" title="Connect" type="button" class="scalable disabled" style=""><span><span><span>Connect</span></span></span></button>
|
105 |
+
</td>
|
106 |
+
<td></td>
|
107 |
+
</tr>
|
108 |
+
</tbody>
|
109 |
+
</table>
|
110 |
+
</fieldset>
|
111 |
+
</div>
|
112 |
+
|
113 |
+
<div id="ss-confirm-connect">
|
114 |
+
<h2>SearchSpring OAuth</h2>
|
115 |
+
<p class="ss-disclaimer">
|
116 |
+
In order to keep your data secure SearchSpring will now create a Magento API User, Role and Consumer and send
|
117 |
+
the API information to SearchSpring.
|
118 |
+
</p>
|
119 |
+
<p class="ss-details">
|
120 |
+
SearchSpring prides itself in keeping your data safe. We use OAuth to secure your data. This way only
|
121 |
+
SearchSpring can access your product data. The API User and Role created only have access to the SearchSpring
|
122 |
+
created endpoints to access your data.
|
123 |
+
</p>
|
124 |
+
<div class="ss-buttons">
|
125 |
+
<button id="ss-cancel-button" title="Cancel" type="button" class="scalable back" style=""><span><span><span>Cancel</span></span></span></button>
|
126 |
+
<button id="ss-confirm-button" title="OK" type="button" class="scalable save" style=""><span><span><span>Connect</span></span></span></button>
|
127 |
+
</div>
|
128 |
+
</div>
|
129 |
+
|
130 |
+
<style type="text/css">
|
131 |
+
<?php if(!$this->isSetup()): ?>
|
132 |
+
.section-config .ss-login-connected { display: none; }
|
133 |
+
<?php else: ?>
|
134 |
+
.section-config .ss-login-not_connected { display: none; }
|
135 |
+
<?php endif; ?>
|
136 |
+
.ss-connect-message { font-weight: bold; color: #007700;}
|
137 |
+
.section-config.ss-login .entry-edit-head a { text-decoration: none; }
|
138 |
+
.ss-step2 { display: none; }
|
139 |
+
.ss-has-account-header { color: #007700; font-weight: bold; text-align: center; width: 50%;}
|
140 |
+
.ss-no-account-header { color: #770000; font-weight: bold; width: 50%;}
|
141 |
+
.ss-no-account { border-left: 1px solid #CCC; padding-left: 15px; text-align: center;}
|
142 |
+
#ss-demo-button { padding: 5px 15px; margin-top: 15px;}
|
143 |
+
|
144 |
+
#ss-confirm-connect {
|
145 |
+
display: none;
|
146 |
+
position:absolute;
|
147 |
+
top:200px;
|
148 |
+
left:50%;
|
149 |
+
width:400px; /* adjust as per your needs */
|
150 |
+
height:180px; /* adjust as per your needs */
|
151 |
+
margin-left:-200px; /* negative half of width above */
|
152 |
+
border: 1px solid #d6d6d6;
|
153 |
+
background-color: #fafafa;
|
154 |
+
z-index: 999;
|
155 |
+
padding: 10px;
|
156 |
+
-webkit-box-shadow: 3px 3px 5px 0px rgba(61,61,61,.2);
|
157 |
+
-moz-box-shadow: 3px 3px 5px 0px rgba(61,61,61,.2);
|
158 |
+
box-shadow: 3px 3px 5px 0px rgba(61,61,61,.2);
|
159 |
+
}
|
160 |
+
|
161 |
+
#ss-confirm-connect h2 {
|
162 |
+
text-align: center;
|
163 |
+
font-size: 16px;
|
164 |
+
}
|
165 |
+
|
166 |
+
#ss-confirm-connect .ss-disclaimer {
|
167 |
+
font-weight: bold;
|
168 |
+
}
|
169 |
+
|
170 |
+
#ss-confirm-connect .ss-details {
|
171 |
+
margin-top: 10px;
|
172 |
+
font-size: 10px;
|
173 |
+
color: #777;
|
174 |
+
}
|
175 |
+
|
176 |
+
#ss-cancel-button span {
|
177 |
+
padding: 0;
|
178 |
+
background-image: none;
|
179 |
+
}
|
180 |
+
|
181 |
+
#ss-confirm-connect .ss-buttons {
|
182 |
+
margin-top: 10px;
|
183 |
+
text-align: center;
|
184 |
+
}
|
185 |
+
</style>
|
186 |
+
|
js/searchspring/jquery-1.11.1.min.js
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
|
2 |
+
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
|
3 |
+
if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
|
4 |
+
},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
|
js/searchspring/setup.js
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery.noConflict();
|
2 |
+
|
3 |
+
(function($){
|
4 |
+
|
5 |
+
$(function() {
|
6 |
+
var websites;
|
7 |
+
|
8 |
+
var configForm = $('#config_edit_form');
|
9 |
+
|
10 |
+
var reconnectButton = $('#ss-reconnect-button');
|
11 |
+
|
12 |
+
var usernameField = $('#ssmanager_ssmanager_setup_username');
|
13 |
+
var passwordField = $('#ssmanager_ssmanager_setup_password');
|
14 |
+
var authSelect = $('#ssmanager_ssmanager_setup_authentication_method');
|
15 |
+
var loginButton = $('#ss-login-button');
|
16 |
+
|
17 |
+
var websiteSelect = $('#ssmanager_ssmanager_setup_website');
|
18 |
+
var feedSelect = $('#ssmanager_ssmanager_setup_feed');
|
19 |
+
var connectButton = $('#ss-connect-button');
|
20 |
+
|
21 |
+
var confirmConnectWindow = $('#ss-confirm-connect');
|
22 |
+
var cancelButton = $('#ss-cancel-button');
|
23 |
+
var confirmButton = $('#ss-confirm-button');
|
24 |
+
|
25 |
+
var apiUrlEl = $('#ssmanager_ssmanager_api_base_url');
|
26 |
+
var siteIdEl = $('#ssmanager_ssmanager_api_site_id');
|
27 |
+
var feedIdEl = $('#ssmanager_ssmanager_api_feed_id');
|
28 |
+
var secretKeyEl = $('#ssmanager_ssmanager_api_secret_key');
|
29 |
+
|
30 |
+
var authEl = $('#ssmanager_ssmanager_api_authentication_method');
|
31 |
+
|
32 |
+
// Hide Connection Settings Block
|
33 |
+
apiUrlEl.parents('.section-config').hide();
|
34 |
+
|
35 |
+
reconnectButton.bind('click', reconnect);
|
36 |
+
|
37 |
+
loginButton.bind('click', loginSearchSpring);
|
38 |
+
usernameField.bind('keypress', loginEnter);
|
39 |
+
passwordField.bind('keypress', loginEnter);
|
40 |
+
|
41 |
+
function reconnect() {
|
42 |
+
$('.ss-login-connected').hide();
|
43 |
+
$('.ss-login-not_connected').show();
|
44 |
+
}
|
45 |
+
|
46 |
+
function loginEnter(ev) {
|
47 |
+
if (ev.keyCode == 13) {
|
48 |
+
loginSearchSpring();
|
49 |
+
return false;
|
50 |
+
}
|
51 |
+
return true;
|
52 |
+
}
|
53 |
+
|
54 |
+
function loginSearchSpring() {
|
55 |
+
loginButton.addClass('loading');
|
56 |
+
$.ajax({
|
57 |
+
url: 'https://api.searchspring.net/api/manage/users/get-feeds.json',
|
58 |
+
//url: 'http://23.23.23.2/api/manage/users/get-feeds.json',
|
59 |
+
type: 'POST',
|
60 |
+
dataType: 'jsonp',
|
61 |
+
success : login,
|
62 |
+
data : {
|
63 |
+
username : usernameField.val(),
|
64 |
+
password : passwordField.val()
|
65 |
+
}
|
66 |
+
});
|
67 |
+
|
68 |
+
}
|
69 |
+
|
70 |
+
function login(data, status, req) {
|
71 |
+
loginButton.removeClass('loading');
|
72 |
+
|
73 |
+
if(data.status == 'error') {
|
74 |
+
alert(data.message);
|
75 |
+
return;
|
76 |
+
}
|
77 |
+
|
78 |
+
websites = data.websites;
|
79 |
+
|
80 |
+
$('.ss-step1').hide();
|
81 |
+
setSiteDropdown();
|
82 |
+
$('.ss-step2').show();
|
83 |
+
}
|
84 |
+
|
85 |
+
function setSiteDropdown() {
|
86 |
+
|
87 |
+
websiteSelect.empty();
|
88 |
+
feedSelect.empty().attr('disabled', '').unbind('change');
|
89 |
+
disableConnect();
|
90 |
+
|
91 |
+
var websitesLength = getObjectLength(websites);
|
92 |
+
var option;
|
93 |
+
|
94 |
+
if(websitesLength > 1) {
|
95 |
+
option = $('<option />').val(0).text('Select a website');
|
96 |
+
websiteSelect.append(option);
|
97 |
+
}
|
98 |
+
|
99 |
+
for(var websiteId in websites) {
|
100 |
+
if(websites.hasOwnProperty(websiteId)) {
|
101 |
+
var website = websites[websiteId];
|
102 |
+
option = $('<option />').val(websiteId).data('siteId', website.siteId).data('secretKey', website.secretKey).text(website.name);
|
103 |
+
websiteSelect.append(option);
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
websiteSelect.removeAttr('disabled').bind('change', setFeedDropdown);
|
108 |
+
|
109 |
+
if(websitesLength == 1) {
|
110 |
+
setFeedDropdown();
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
function setFeedDropdown() {
|
115 |
+
var feeds = websites[websiteSelect.val()].feeds;
|
116 |
+
|
117 |
+
feedSelect.empty();
|
118 |
+
disableConnect();
|
119 |
+
|
120 |
+
if(!websites[websiteSelect.val()].secretKey) {
|
121 |
+
alert('Secret Key not found. Please contact SearchSpring Support (support@searchspring.com)');
|
122 |
+
return;
|
123 |
+
}
|
124 |
+
|
125 |
+
var feedsLength = getObjectLength(feeds);
|
126 |
+
var option;
|
127 |
+
if(feedsLength > 1) {
|
128 |
+
option = $('<option />').val(0).text('Select a feed');
|
129 |
+
feedSelect.append(option);
|
130 |
+
}
|
131 |
+
|
132 |
+
for(var feedId in feeds) {
|
133 |
+
if(feeds.hasOwnProperty(feedId)) {
|
134 |
+
option = $('<option />').val(feedId).text(feeds[feedId]);
|
135 |
+
feedSelect.append(option);
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
feedSelect.removeAttr('disabled').bind('change', enableConnect);
|
140 |
+
|
141 |
+
if(feedsLength == 1) {
|
142 |
+
enableConnect();
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
function enableConnect() {
|
147 |
+
if(authSelect.val() == 'simple') {
|
148 |
+
connectButton.removeClass('disabled').removeAttr('disabled').bind('click', connectSettings);
|
149 |
+
} else {
|
150 |
+
connectButton.removeClass('disabled').removeAttr('disabled').bind('click', confirmConnect);
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
function disableConnect() {
|
155 |
+
connectButton.addClass('disabled').attr('disabled', '').unbind('click');
|
156 |
+
}
|
157 |
+
|
158 |
+
function confirmConnect() {
|
159 |
+
confirmConnectWindow.show();
|
160 |
+
confirmButton.bind('click', connectSettings);
|
161 |
+
cancelButton.bind('click', cancelConnect);
|
162 |
+
}
|
163 |
+
|
164 |
+
function cancelConnect() {
|
165 |
+
confirmButton.unbind('click');
|
166 |
+
cancelButton.unbind('click');
|
167 |
+
confirmConnectWindow.hide();
|
168 |
+
}
|
169 |
+
|
170 |
+
function connectSettings() {
|
171 |
+
confirmButton.addClass('loading');
|
172 |
+
apiUrlEl.val('https://api-beta.searchspring.net');
|
173 |
+
//apiUrlEl.val('https://23.23.23.2');
|
174 |
+
siteIdEl.val(websites[websiteSelect.val()].siteId);
|
175 |
+
secretKeyEl.val(websites[websiteSelect.val()].secretKey);
|
176 |
+
feedIdEl.val(feedSelect.val());
|
177 |
+
authEl.val(authSelect.val());
|
178 |
+
|
179 |
+
// Send flag to setup new connection
|
180 |
+
$('#searchspring_new_connection_fl').val('1');
|
181 |
+
|
182 |
+
configForm.submit();
|
183 |
+
}
|
184 |
+
|
185 |
+
function getObjectLength(obj) {
|
186 |
+
var length = 0;
|
187 |
+
for (var key in obj) {
|
188 |
+
if (obj.hasOwnProperty(key)) length++;
|
189 |
+
}
|
190 |
+
return length;
|
191 |
+
}
|
192 |
+
});
|
193 |
+
|
194 |
+
})(jQuery);
|
package.xml
CHANGED
@@ -1,2 +1,6 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
-
<package><name>SearchSpring_Manager</name><version>1.
|
|
|
|
|
|
|
|
1 |
<?xml version="1.0"?>
|
2 |
+
<package><name>SearchSpring_Manager</name><version>1.9.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>Speed Optimizations for Feed Regeneration
|
3 |
+
Better Root Category skipping logic
|
4 |
+
Added Support for Amasty Labels
|
5 |
+
Added Support for TBT Rewards (product earnable points)
|
6 |
+
Added Support for product qty sales data</notes><authors><author><name>SearchSpring Development Team</name><user>searchspring</user><email>info@searchspring.com</email></author></authors><date>2015-02-05</date><time>10:12:29</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="fd8f65524b8f8708471f106aad294d0a"/><dir name="Manager"><file name="Collection.php" hash="0ebd621892bccd5d59ed3edcafada89b"/><file name="Iterator.php" hash="6f68effbe5452c1a6abd97cbf4b528ee"/><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Fieldset"><file name="Hint.php" hash="82ff17ab23c59b1c5fd095dfdfe153bb"/><file name="Setup.php" hash="5f467825780852b12cba72abfbe9e596"/></dir></dir></dir></dir><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="a8b0e0bd6d735ce33ca7ed09ecea3a24"/></dir><dir name="Entity"><file name="IndexingRequestBody.php" hash="6bdab2264cbb8dbafa666f493bf9bd60"/><file name="OperationsCollection.php" hash="be19c06bb1be447bd57601e67ac87557"/><file name="RecordsCollection.php" hash="c336526a10625a859c093d2b25dff911"/><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="api2.xml" hash="c958f2f25b6fcd0b467dc20fb945f1b4"/><file name="composer.json" hash="6a36df77eee995788585f7a8a535fa27"/><file name="config.xml" hash="6d87f085ce5b36604a47db301a0b4527"/><file name="readme.md" hash="30d24e1aae89696090dcf22203802ad2"/><file name="system.xml" hash="2d51547a4619f7f81eab4bdba265211e"/></dir><dir name="Factory"><file name="ApiFactory.php" hash="2a0b31683e75cfa4c10bf6476a7346bf"/><file name="GeneratorFactory.php" hash="83662be86412dff1705b32bf2ba76402"/><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="69147879853105d0a388596b479a0b81"/></dir><dir name="Handler"><file name="ApiErrorHandler.php" hash="a287ed86d8d83a88b5a8f555015f2e80"/></dir><dir name="Helper"><file name="Data.php" hash="78fddf7a5cae29ea3054309d4c4f46bd"/><file name="Http.php" hash="13d41de08d623892f6526817bf4ee971"/><file name="Oauth.php" hash="b6dd04c07e1322c38b76668b88d7d1e4"/><file name="Product.php" hash="6cdbcdaa30691ff3023b93557018b497"/><file name="Webservice.php" hash="054d23d7db9da73b44bcbad083bd813e"/></dir><dir name="Model"><file name="Layer.php" hash="54afafde0cff2fcf2de2240bbb6cdb20"/><dir name="Api2"><dir name="Auth"><file name="Adapter.php" hash="909c416381a44ef80b0665926a9e4ab2"/></dir><dir name="Indexing"><dir name="Rest"><dir name="Admin"><file name="V1.php" hash="3a306d75dccab962e7c81ecb82d35c60"/></dir></dir></dir></dir><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="Oauth"><file name="Server.php" hash="c5c2df03bc419caa49c8b1e3fe7d50ac"/></dir><dir name="Observer"><file name="CategorySaveObserver.php" hash="cfbbaabb8eb31d79d8106d490758f3d7"/><file name="ConfigObserver.php" hash="209a8f8b97e5b7a0c1c7539d493371e6"/><file name="LiveIndexer.php" hash="0fb8e5738d9431c9e00a928477343d2f"/><file name="ProductSaveObserver.php" hash="68bd69b0e7a810e9eb9c29c09cc61d39"/></dir></dir><dir name="Operation"><file name="Product.php" hash="e190aa6864082e1b8dfe011f41f9f1df"/><file name="ProductOperation.php" hash="84df2df9055b52753680d4ccd94b6f68"/><dir name="Product"><file name="SetCategories.php" hash="0762e4f5975c98e87c6dcedbb9bb621c"/><file name="SetCoreFields.php" hash="bbd2f4a5506da61201edd0dfe783efe8"/><file name="SetFields.php" hash="4ced933a717710d1a4ad80c533570ff5"/><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"/><file name="SetReport.php" hash="834e7c62388c3e3655c80e2a96020ba2"/></dir></dir><dir name="Provider"><file name="ProductCollectionProvider.php" hash="e639e1667ed0f2bc59f2272ad36d3107"/><dir name="ProductCollection"><file name="FeedProvider.php" hash="50ea56af40e5dc044d6c6cbb4b7c64c3"/><file name="ProductProvider.php" hash="bdb5b820343e060bb7cf2bea807a7607"/></dir></dir><dir name="Request"><file name="JSON.php" hash="541d9331b620bc92820abe5630824e41"/></dir><dir name="Service"><dir name="SearchSpring"><file name="ApiAdapter.php" hash="6878323855ef8a7bd3de8e707e104610"/><file name="IndexingApiAdapter.php" hash="92fa5e7682ec42e3c1f8b62c490488cc"/><file name="SearchApiAdapter.php" hash="bda32ec16f53054e75fb331ffcc0c155"/></dir></dir><dir name="sql"><dir name="searchspring_manager"><file name="mysql4-install-1.0.0.php" hash="c0de6de0b74290978f65d7896768e89f"/><file name="mysql4-upgrade-0.4.0-1.0.0.php" hash="1f59325ef181499292deb7775a5e4dc2"/></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="9d0fda2a4ff168ab319fd0c2240501fd"/></dir></dir><dir name="String"><file name="Sanitizer.php" hash="e89fa5f86714621ea554aea9aa3f796c"/></dir><dir name="ThirdParty"><dir name="Amasty"><file name="LabelsOperation.php" hash="a1536050cafdf2ede093cb11530d723c"/></dir><dir name="TBT"><file name="RewardsOperation.php" hash="cd83a97c7231b1ec623b78247f2bd93e"/></dir></dir><dir name="Transformer"><file name="ProductCollectionToRecordCollectionTransformer.php" hash="44bdf6ecf2c06c6a5bc0f96cff427b4e"/></dir><dir name="Validator"><file name="ProductValidator.php" hash="6051f15723e69b05a2fe5fbae62a841d"/></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="24080fdf866bdac26ed4571c4c7fc9f9"/><file name="ResponseWriterParams.php" hash="bf66753a5952a78c0049f69ccaa6e74d"/></dir></dir></dir></dir></dir></dir></dir><dir name="design"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="searchspring"><dir name="manager"><dir name="system"><dir name="config"><dir name="fieldset"><file name="hint.phtml" hash="a0e3da8cd870432cce2849a41aa1c5b9"/><file name="setup.phtml" hash="e019e039a41992ecf72a0ad956de7630"/></dir></dir></dir></dir></dir></dir></dir></dir></dir><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="js"><dir name="searchspring"><file name="jquery-1.11.1.min.js" hash="8101d596b2b8fa35fe3a634ea342d7c3"/><file name="setup.js" hash="7754359957784a96bd0f9c188979b136"/></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>
|