Version Notes
Correctly handle custom boost fields.
Download this release
Release Info
Developer | Carlos Escribano Rey |
Extension | Doofinder_Feed |
Version | 1.6.5 |
Comparing to | |
See all releases |
Version 1.6.5
- app/code/community/Doofinder/Feed/Block/Adminhtml/Log/View.php +76 -0
- app/code/community/Doofinder/Feed/Block/Adminhtml/Map/Additional.php +161 -0
- app/code/community/Doofinder/Feed/Block/Integration.php +34 -0
- app/code/community/Doofinder/Feed/Block/Settings/Buttons/Generate.php +44 -0
- app/code/community/Doofinder/Feed/Block/Settings/Buttons/ViewLog.php +34 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Cron.php +52 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Crondescription.php +15 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Datetime.php +49 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Description.php +54 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/File.php +68 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Hashdescription.php +6 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Layerdescription.php +16 -0
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Message.php +72 -0
- app/code/community/Doofinder/Feed/Helper/Data.php +552 -0
- app/code/community/Doofinder/Feed/Helper/Log.php +55 -0
- app/code/community/Doofinder/Feed/Helper/Search.php +80 -0
- app/code/community/Doofinder/Feed/Helper/Tax.php +24 -0
- app/code/community/Doofinder/Feed/Model/Adminhtml/System/Config/Backend/Cron.php +54 -0
- app/code/community/Doofinder/Feed/Model/Adminhtml/System/Config/Validation/Hashid.php +19 -0
- app/code/community/Doofinder/Feed/Model/CatalogSearch/Resource/Fulltext.php +137 -0
- app/code/community/Doofinder/Feed/Model/Config.php +200 -0
- app/code/community/Doofinder/Feed/Model/Cron.php +40 -0
- app/code/community/Doofinder/Feed/Model/Generator.php +837 -0
- app/code/community/Doofinder/Feed/Model/Log.php +19 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Abstract.php +645 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Associated.php +172 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Bundle.php +75 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Configurable.php +200 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Downloadable.php +20 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Grouped.php +62 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Simple.php +20 -0
- app/code/community/Doofinder/Feed/Model/Map/Product/Virtual.php +20 -0
- app/code/community/Doofinder/Feed/Model/Mysql4/Cron.php +17 -0
- app/code/community/Doofinder/Feed/Model/Mysql4/Cron/Collection.php +17 -0
- app/code/community/Doofinder/Feed/Model/Mysql4/Log.php +17 -0
- app/code/community/Doofinder/Feed/Model/Mysql4/Log/Collection.php +18 -0
- app/code/community/Doofinder/Feed/Model/Observers/Feed.php +350 -0
- app/code/community/Doofinder/Feed/Model/Observers/Logs.php +45 -0
- app/code/community/Doofinder/Feed/Model/Observers/Schedule.php +278 -0
- app/code/community/Doofinder/Feed/Model/Resource/Mysql4/Setup.php +14 -0
- app/code/community/Doofinder/Feed/Model/System/Config/Backend/Map/Additional.php +24 -0
- app/code/community/Doofinder/Feed/Model/System/Config/Backend/Total/Limit.php +17 -0
- app/code/community/Doofinder/Feed/Model/System/Config/Reset.php +18 -0
- app/code/community/Doofinder/Feed/Model/System/Config/Source/Product/Attributes.php +48 -0
- app/code/community/Doofinder/Feed/Model/Tools.php +383 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index.php +47 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index/fixtures/testConfig.yaml +1 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index/fixtures/testFeed.yaml +6 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index/fixtures/testIndex.yaml +1 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index/providers/testConfig.yaml +1 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index/providers/testFeed.yaml +2 -0
- app/code/community/Doofinder/Feed/Test/Controller/Index/providers/testIndex.yaml +1 -0
- app/code/community/Doofinder/Feed/Test/Model/Product.php +59 -0
- app/code/community/Doofinder/Feed/Test/Model/Product/expectations/testGenerator.yaml +12 -0
- app/code/community/Doofinder/Feed/Test/Model/Product/fixtures/testGenerator.yaml +118 -0
- app/code/community/Doofinder/Feed/Test/Model/Product/providers/testGenerator.yaml +12 -0
- app/code/community/Doofinder/Feed/controllers/DoofinderFeedFeedController.php +43 -0
- app/code/community/Doofinder/Feed/controllers/DoofinderFeedLogController.php +22 -0
- app/code/community/Doofinder/Feed/controllers/FeedController.php +311 -0
- app/code/community/Doofinder/Feed/controllers/IndexController.php +32 -0
- app/code/community/Doofinder/Feed/etc/config.xml +337 -0
- app/code/community/Doofinder/Feed/etc/system.xml +515 -0
- app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-install-1.5.4.php +59 -0
- app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-install-1.5.7.php +163 -0
- app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-upgrade-1.5.4-1.5.5.php +18 -0
- app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-upgrade-1.5.5-1.5.6.php +101 -0
- app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-upgrade-1.5.6-1.5.7.php +19 -0
- app/design/adminhtml/default/default/layout/doofinder.xml +16 -0
- app/design/frontend/base/default/layout/doofinder.xml +7 -0
- app/etc/modules/Doofinder_Feed.xml +9 -0
- js/doofinder/admin.js +41 -0
- lib/Doofinder/doofinder_api.php +804 -0
- lib/Doofinder/doofinder_management_api.php +408 -0
- lib/Doofinder/errors.php +48 -0
- package.xml +58 -0
- skin/adminhtml/default/default/doofinder/styles.css +50 -0
app/code/community/Doofinder/Feed/Block/Adminhtml/Log/View.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Adminhtml_Log_View extends Mage_Adminhtml_Block_Widget_Grid
|
13 |
+
{
|
14 |
+
protected $_defaultSort = 'id';
|
15 |
+
protected $_defaultDir = 'desc';
|
16 |
+
|
17 |
+
protected $_processId = null;
|
18 |
+
|
19 |
+
public function __construct()
|
20 |
+
{
|
21 |
+
parent::__construct();
|
22 |
+
|
23 |
+
$this->_processId = Mage::app()->getRequest()->getParam('processId', false);
|
24 |
+
}
|
25 |
+
|
26 |
+
protected function _prepareCollection()
|
27 |
+
{
|
28 |
+
$collection = Mage::getResourceModel('doofinder_feed/log_collection');
|
29 |
+
|
30 |
+
if ($this->_processId) {
|
31 |
+
$collection->getSelect()->where("process_id = $this->_processId");
|
32 |
+
}
|
33 |
+
|
34 |
+
$this->setCollection($collection);
|
35 |
+
|
36 |
+
return parent::_prepareCollection();
|
37 |
+
}
|
38 |
+
|
39 |
+
protected function _prepareColumns()
|
40 |
+
{
|
41 |
+
$this->addColumn('id', array(
|
42 |
+
'header' => Mage::helper('doofinder_feed')->__('ID'),
|
43 |
+
'index' => 'id',
|
44 |
+
'type' => 'number',
|
45 |
+
));
|
46 |
+
|
47 |
+
if (!$this->_processId) {
|
48 |
+
$this->addColumn('process_id', array(
|
49 |
+
'header' => Mage::helper('doofinder_feed')->__('Process ID'),
|
50 |
+
'index' => 'process_id',
|
51 |
+
'type' => 'number',
|
52 |
+
));
|
53 |
+
}
|
54 |
+
|
55 |
+
$this->addColumn('time', array(
|
56 |
+
'header' => Mage::helper('doofinder_feed')->__('Time'),
|
57 |
+
'index' => 'time',
|
58 |
+
'type' => 'datetime',
|
59 |
+
));
|
60 |
+
|
61 |
+
$this->addColumn('type', array(
|
62 |
+
'header' => Mage::helper('doofinder_feed')->__('Type'),
|
63 |
+
'index' => 'type',
|
64 |
+
'type' => 'options',
|
65 |
+
'options' => Mage::helper('doofinder_feed/log')->listLogTypes(),
|
66 |
+
));
|
67 |
+
|
68 |
+
$this->addColumn('message', array(
|
69 |
+
'header' => Mage::helper('doofinder_feed')->__('Message'),
|
70 |
+
'index' => 'message',
|
71 |
+
'type' => 'text',
|
72 |
+
));
|
73 |
+
|
74 |
+
return parent::_prepareColumns();
|
75 |
+
}
|
76 |
+
}
|
app/code/community/Doofinder/Feed/Block/Adminhtml/Map/Additional.php
ADDED
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Adminhtml_Map_Additional extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
protected $_addRowButtonHtml = array();
|
15 |
+
protected $_removeRowButtonHtml = array();
|
16 |
+
|
17 |
+
protected $_rows = 0;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Returns html part of the setting
|
21 |
+
*
|
22 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
26 |
+
{
|
27 |
+
$this->setElement($element);
|
28 |
+
|
29 |
+
$html = '<table style="display:none"><tbody id="doofinder_feed_additional_mapping_template">';
|
30 |
+
$html .= $this->_getRowTemplateHtml(-1);
|
31 |
+
$html .= '</tbody></table>';
|
32 |
+
|
33 |
+
$html .= '<table>';
|
34 |
+
$html .= '<thead><tr>';
|
35 |
+
$html .= '<th>' . $this->__('Label') . '</th><th>' . $this->__('Field') . '</th><th>' . $this->__('Attribute') . '</th>';
|
36 |
+
$html .= '</tr></thead>';
|
37 |
+
$html .= '<tbody id="doofinder_feed_additional_mapping_container">';
|
38 |
+
|
39 |
+
$count = 0;
|
40 |
+
if ($this->_getValue('additional_mapping')) {
|
41 |
+
foreach ($this->_getValue('additional_mapping') as $i => $f) {
|
42 |
+
$html .= $this->_getRowTemplateHtml($count++);
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
$html .= '</tbody></table>';
|
47 |
+
$html .= $this->_getAddRowButtonHtml();
|
48 |
+
|
49 |
+
$html .= '<script type="text/javascript">';
|
50 |
+
ob_start();
|
51 |
+
?>
|
52 |
+
var DoofinderFeedMapAdditionalRowGenerator = function() {
|
53 |
+
this.count = <?php print $count; ?>;
|
54 |
+
};
|
55 |
+
|
56 |
+
DoofinderFeedMapAdditionalRowGenerator.prototype.add = function() {
|
57 |
+
var html = $('doofinder_feed_additional_mapping_template').innerHTML;
|
58 |
+
html = html.replace(/\[additional_mapping\]\[-1\]/g, '[additional_mapping][' + (this.count++) + ']');
|
59 |
+
Element.insert($('doofinder_feed_additional_mapping_container'), {bottom: html});
|
60 |
+
};
|
61 |
+
|
62 |
+
var doofinderFeedMapAdditionalRowGenerator = new DoofinderFeedMapAdditionalRowGenerator();
|
63 |
+
<?php
|
64 |
+
$html .= ob_get_clean();
|
65 |
+
$html .= '</script>';
|
66 |
+
|
67 |
+
return $html;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Retrieve html template for setting
|
72 |
+
*
|
73 |
+
* @param int $rowIndex
|
74 |
+
* @return string
|
75 |
+
*/
|
76 |
+
protected function _getRowTemplateHtml($rowIndex = null)
|
77 |
+
{
|
78 |
+
$value = $rowIndex !== null ? (array) $this->_getValue('additional_mapping/' . $rowIndex) : array();
|
79 |
+
$value += array('field' => '', 'label' => '', 'attribute' => '');
|
80 |
+
$html = '<tr>';
|
81 |
+
|
82 |
+
$html .= '<td>';
|
83 |
+
$html .= '<input name="'
|
84 |
+
. $this->getElement()->getName() . '[additional_mapping][' . $rowIndex . '][label]" value="'
|
85 |
+
. $value['label'] . '" ' . $this->_getDisabled() . '/> ';
|
86 |
+
$html .= '</td><td>';
|
87 |
+
$html .= '<input name="'
|
88 |
+
. $this->getElement()->getName() . '[additional_mapping][' . $rowIndex . '][field]" value="'
|
89 |
+
. $value['field'] . '" ' . $this->_getDisabled() . '/> ';
|
90 |
+
$html .= '</td><td>';
|
91 |
+
$html .= '<select name="'
|
92 |
+
. $this->getElement()->getName() . '[additional_mapping][' . $rowIndex . '][attribute]" ' . $this->_getDisabled() . '>';
|
93 |
+
foreach (Mage::getSingleton('doofinder_feed/system_config_source_product_attributes')->toOptionArray() as $key => $label) {
|
94 |
+
$html .= '<option value="' . $key . '"'. ($value['attribute'] == $key ? 'selected="selected"' : '') . '>' . $label . '</option>';
|
95 |
+
}
|
96 |
+
$html .= '</select> ';
|
97 |
+
$html .= '</td><td>';
|
98 |
+
$html .= $this->_getRemoveRowButtonHtml();
|
99 |
+
$html .= '</td>';
|
100 |
+
$html .= '</tr>';
|
101 |
+
|
102 |
+
return $html;
|
103 |
+
}
|
104 |
+
|
105 |
+
protected function _getDisabled()
|
106 |
+
{
|
107 |
+
return $this->getElement()->getDisabled() ? 'disabled' : '';
|
108 |
+
}
|
109 |
+
|
110 |
+
protected function _getValue($key)
|
111 |
+
{
|
112 |
+
return $this->getElement()->getData('value/' . $key);
|
113 |
+
}
|
114 |
+
|
115 |
+
protected function _getSelected($key, $value)
|
116 |
+
{
|
117 |
+
return $this->getElement()->getData('value/' . $key) == $value ? 'selected="selected"' : '';
|
118 |
+
}
|
119 |
+
|
120 |
+
protected function _getAddRowButtonHtml()
|
121 |
+
{
|
122 |
+
$container = isset($container) ? $container : null;
|
123 |
+
|
124 |
+
if (!isset($this->_addRowButtonHtml[$container])) {
|
125 |
+
$_cssClass = 'add';
|
126 |
+
|
127 |
+
if (version_compare(Mage::getVersion(), '1.6', '<')) {
|
128 |
+
$_cssClass .= ' ' . $this->_getDisabled();
|
129 |
+
}
|
130 |
+
|
131 |
+
$this->_addRowButtonHtml[$container] = $this->getLayout()->createBlock('adminhtml/widget_button')
|
132 |
+
->setType('button')
|
133 |
+
->setClass($_cssClass)
|
134 |
+
->setLabel($this->__('Add'))
|
135 |
+
->setOnClick("doofinderFeedMapAdditionalRowGenerator.add()")
|
136 |
+
->setDisabled($this->_getDisabled())
|
137 |
+
->toHtml();
|
138 |
+
}
|
139 |
+
return $this->_addRowButtonHtml[$container];
|
140 |
+
}
|
141 |
+
|
142 |
+
protected function _getRemoveRowButtonHtml()
|
143 |
+
{
|
144 |
+
if (!$this->_removeRowButtonHtml) {
|
145 |
+
$_cssClass = 'delete v-middle';
|
146 |
+
|
147 |
+
if (version_compare(Mage::getVersion(), '1.6', '<')) {
|
148 |
+
$_cssClass .= ' ' . $this->_getDisabled();
|
149 |
+
}
|
150 |
+
|
151 |
+
$this->_removeRowButtonHtml = $this->getLayout()->createBlock('adminhtml/widget_button')
|
152 |
+
->setType('button')
|
153 |
+
->setClass($_cssClass)
|
154 |
+
->setLabel($this->__('Delete'))
|
155 |
+
->setOnClick("Element.remove($(this).up('tr'))")
|
156 |
+
->setDisabled($this->_getDisabled())
|
157 |
+
->toHtml();
|
158 |
+
}
|
159 |
+
return $this->_removeRowButtonHtml;
|
160 |
+
}
|
161 |
+
}
|
app/code/community/Doofinder/Feed/Block/Integration.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Integration extends Mage_Core_Block_Abstract
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* Produce the integration script
|
16 |
+
*
|
17 |
+
* @return string
|
18 |
+
*/
|
19 |
+
protected function _toHtml()
|
20 |
+
{
|
21 |
+
$enabled = Mage::getStoreConfig('doofinder_search/layer_settings/enabled', Mage::app()->getStore());
|
22 |
+
$script = Mage::getStoreConfig('doofinder_search/layer_settings/script', Mage::app()->getStore());
|
23 |
+
|
24 |
+
if ($enabled) {
|
25 |
+
$script .= '<script type="text/javascript">';
|
26 |
+
$script .= "if (typeof Varien.searchForm !== 'undefined') Varien.searchForm.prototype.initAutocomplete = function() { $('search_autocomplete').hide(); };";
|
27 |
+
$script .= '</script>';
|
28 |
+
|
29 |
+
return $script;
|
30 |
+
} else {
|
31 |
+
return '';
|
32 |
+
}
|
33 |
+
}
|
34 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Buttons/Generate.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Buttons_Generate extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
15 |
+
{
|
16 |
+
$this->setElement($element);
|
17 |
+
$element->setScopeLabel('');
|
18 |
+
|
19 |
+
$storeCode = Mage::app()->getRequest()->getParam('store');
|
20 |
+
$url = Mage::helper("adminhtml")->getUrl('adminhtml/doofinderFeedFeed/generate', array('store' => $storeCode));
|
21 |
+
|
22 |
+
$script = "<script type=\"text/javascript\">
|
23 |
+
function generateFeed() {
|
24 |
+
var call = new Ajax.Request('" . $url . "', {
|
25 |
+
method: 'get',
|
26 |
+
onComplete: function(transport) {
|
27 |
+
alert(transport.responseText);
|
28 |
+
window.location.reload();
|
29 |
+
}
|
30 |
+
});
|
31 |
+
}
|
32 |
+
</script>";
|
33 |
+
|
34 |
+
$html = $this->getLayout()->createBlock('adminhtml/widget_button')
|
35 |
+
->setType('button')
|
36 |
+
->setClass('generate-feed')
|
37 |
+
->setLabel('Start Feed Generation Now')
|
38 |
+
->setOnClick("confirm('No changes will be saved, feed will be rescheduled (if there\'s a process running it will be stopped and the feed will be reset). Do you want to proceed?') && generateFeed()")
|
39 |
+
->setAfterHtml($script)
|
40 |
+
->toHtml();
|
41 |
+
return $html;
|
42 |
+
}
|
43 |
+
|
44 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Buttons/ViewLog.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Buttons_ViewLog extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
|
15 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
16 |
+
{
|
17 |
+
$this->setElement($element);
|
18 |
+
$element->setScopeLabel('');
|
19 |
+
|
20 |
+
$process = Mage::getModel('doofinder_feed/cron')->load(Mage::app()->getRequest()->getParam('store'), 'store_code');
|
21 |
+
|
22 |
+
$url = Mage::helper("adminhtml")->getUrl('adminhtml/doofinderFeedLog/view/processId/' . $process->getId());
|
23 |
+
|
24 |
+
$html = $this->getLayout()->createBlock('adminhtml/widget_button')
|
25 |
+
->setType('button')
|
26 |
+
->setClass('view-log')
|
27 |
+
->setLabel('View log')
|
28 |
+
->setOnClick("setLocation('$url')")
|
29 |
+
->toHtml();
|
30 |
+
|
31 |
+
return $html;
|
32 |
+
}
|
33 |
+
|
34 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Cron.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Doofinder_Feed_Block_Settings_Panel_Cron extends Mage_Adminhtml_Block_System_Config_Form_Field
|
3 |
+
{
|
4 |
+
// 12 Hours in seconds
|
5 |
+
const ALLOWED_TIME = 43200;
|
6 |
+
|
7 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
8 |
+
{
|
9 |
+
$lastSchedule = Mage::getModel('cron/schedule')->getCollection()
|
10 |
+
->setOrder('finished_at', 'desc')
|
11 |
+
->getFirstItem();
|
12 |
+
|
13 |
+
$message = '';
|
14 |
+
if ($lastSchedule && count($lastSchedule->getData()) > 0) {
|
15 |
+
$scheduleTime = strtotime($lastSchedule->getFinishedAt());
|
16 |
+
$currentTime = time();
|
17 |
+
|
18 |
+
// Difference in seconds
|
19 |
+
$dif = ($currentTime - $scheduleTime);
|
20 |
+
|
21 |
+
// If difference is bigger than allowed, display message
|
22 |
+
if ($dif > self::ALLOWED_TIME) {
|
23 |
+
|
24 |
+
$message = sprintf('Cron was run for the last time at %s. Taking into account the settings of the step delay option, there might be problems with the cron\'s configuration.', $lastSchedule->getFinishedAt());
|
25 |
+
Mage::helper('doofinder_feed')->__($message);
|
26 |
+
}
|
27 |
+
} else {
|
28 |
+
$message = Mage::helper('doofinder_feed')->__('There are no registered cron tasks. Please, check your system\'s crontab configuration.');
|
29 |
+
}
|
30 |
+
|
31 |
+
return '<p class="error">' . $message . '</p>';
|
32 |
+
}
|
33 |
+
|
34 |
+
public function render(Varien_Data_Form_Element_Abstract $element)
|
35 |
+
{
|
36 |
+
$html = '<td class="label"></td>' .
|
37 |
+
'<td class="value" colspan="3">' . $this->_getElementHtml($element) . '</td>';
|
38 |
+
return $this->_decorateRowHtml($element, $html);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Decorate field row html
|
43 |
+
*
|
44 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
45 |
+
* @param string $html
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
protected function _decorateRowHtml($element, $html)
|
49 |
+
{
|
50 |
+
return '<tr id="row_' . $element->getHtmlId() . '">' . $html . '</tr>';
|
51 |
+
}
|
52 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Crondescription.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Panel_CronDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
13 |
+
{
|
14 |
+
protected $description = 'THIS FEATURE IS CURRENTLY IN BETA.<br>Feeds can be generated directly in your server to save computer resources. See <a href="http://www.doofinder.com/support/topics/plugins/magento">this article</a> for more information.';
|
15 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Datetime.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Panel_Datetime extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
15 |
+
{
|
16 |
+
$this->setElement($element);
|
17 |
+
$name = $element->getName();
|
18 |
+
$element->setScopeLabel('');
|
19 |
+
$code = Mage::app()->getRequest()->getParam('store');
|
20 |
+
$field = $this->_getField($name);
|
21 |
+
$html = '';
|
22 |
+
if ($field && $code) {
|
23 |
+
$datetime = Mage::getModel('doofinder_feed/cron')->load($code, 'store_code')->getData($field);
|
24 |
+
if ($datetime) {
|
25 |
+
$msg = $datetime;
|
26 |
+
|
27 |
+
try {
|
28 |
+
$dateTimestamp = Mage::getModel('core/date')->timestamp(strtotime($datetime));
|
29 |
+
$msg = Mage::helper('core')->formatDate(date('Y-m-d H:i:s', $dateTimestamp), null, true);
|
30 |
+
} catch (Exception $e) {}
|
31 |
+
|
32 |
+
$class = 'feed-datetime';
|
33 |
+
$html = "<p class='{$class}'>{$msg}</p>";
|
34 |
+
}
|
35 |
+
}
|
36 |
+
return $html;
|
37 |
+
}
|
38 |
+
|
39 |
+
private function _getField($name = null)
|
40 |
+
{
|
41 |
+
$pattern = '/groups\[panel\]\[fields\]\[([a-z_-]*)\]\[value\]/';
|
42 |
+
$preg = preg_match($pattern, $name, $match);
|
43 |
+
if ($preg && isset($match[1])) {
|
44 |
+
return $match[1];
|
45 |
+
} else {
|
46 |
+
return false;
|
47 |
+
}
|
48 |
+
}
|
49 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Description.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Panel_Description extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
const INFO = 'info';
|
15 |
+
const WARNING = 'warning';
|
16 |
+
|
17 |
+
protected $level = self::INFO;
|
18 |
+
protected $description = 'You can set the rest of the options for each store separately by modifying the Current Configuration Scope.';
|
19 |
+
|
20 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
21 |
+
{
|
22 |
+
$text = '';
|
23 |
+
|
24 |
+
if (!Mage::app()->getRequest()->getParam('store'))
|
25 |
+
{
|
26 |
+
$text = $this->description;
|
27 |
+
}
|
28 |
+
|
29 |
+
$this->setElement($element);
|
30 |
+
$name = $element->getName();
|
31 |
+
$element->setScopeLabel('');
|
32 |
+
|
33 |
+
return '<p class="doofinder-' . $this->level . '">' . $text . '</p>';
|
34 |
+
}
|
35 |
+
|
36 |
+
public function render(Varien_Data_Form_Element_Abstract $element)
|
37 |
+
{
|
38 |
+
$html = '<td class="label"></td>' .
|
39 |
+
'<td class="value" colspan="3">' . $this->_getElementHtml($element) . '</td>';
|
40 |
+
return $this->_decorateRowHtml($element, $html);
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Decorate field row html
|
45 |
+
*
|
46 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
47 |
+
* @param string $html
|
48 |
+
* @return string
|
49 |
+
*/
|
50 |
+
protected function _decorateRowHtml($element, $html)
|
51 |
+
{
|
52 |
+
return '<tr id="row_' . $element->getHtmlId() . '">' . $html . '</tr>';
|
53 |
+
}
|
54 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/File.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Panel_File extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* Error prefix
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
const ERROR_PREFIX = "#error#";
|
19 |
+
|
20 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
21 |
+
{
|
22 |
+
$this->setElement($element);
|
23 |
+
$name = $element->getName();
|
24 |
+
$element->setScopeLabel('');
|
25 |
+
$store_code = Mage::app()->getRequest()->getParam('store');
|
26 |
+
|
27 |
+
$stores = array();
|
28 |
+
|
29 |
+
if ($store_code) {
|
30 |
+
$stores[$store_code] = Mage::getModel('core/store')->load($store_code);
|
31 |
+
} else {
|
32 |
+
foreach (Mage::app()->getStores() as $store) {
|
33 |
+
if ($store->getIsActive()) {
|
34 |
+
$stores[$store->getCode()] = $store;
|
35 |
+
}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
$files = array();
|
40 |
+
|
41 |
+
foreach ($stores as $store) {
|
42 |
+
$process = Mage::getModel('doofinder_feed/cron')->load($store->getCode(), 'store_code');
|
43 |
+
$lastGeneratedName = $process->getLastFeedName();
|
44 |
+
|
45 |
+
$fileUrl = Mage::getBaseUrl('media').'doofinder'.DS.$lastGeneratedName;
|
46 |
+
$fileDir = Mage::getBaseDir('media').DS.'doofinder'.DS.$lastGeneratedName;
|
47 |
+
if ($lastGeneratedName && file_exists($fileDir)) {
|
48 |
+
$files[$store->getCode()] = "<a href='{$fileUrl}' target='_blank'>" . (count($stores) > 1 ? $fileUrl : "Get {$lastGeneratedName}") . "</a>";
|
49 |
+
} else {
|
50 |
+
$files[$store->getCode()] = "Currently there is no file to preview.";
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
$html = '';
|
55 |
+
|
56 |
+
if (count($files) > 1) {
|
57 |
+
$html .= '<ul>';
|
58 |
+
foreach ($files as $code => $file) {
|
59 |
+
$html .= '<li><b>' . $stores[$code]->getName() . ':</b><div>' . $file . '</div></li>';
|
60 |
+
}
|
61 |
+
$html .= '</ul>';
|
62 |
+
} else {
|
63 |
+
$html .= reset($files);
|
64 |
+
}
|
65 |
+
|
66 |
+
return $html;
|
67 |
+
}
|
68 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Hashdescription.php
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Doofinder_Feed_Block_Settings_Panel_HashDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
3 |
+
{
|
4 |
+
protected $level = self::WARNING;
|
5 |
+
protected $description = '<b>IMPORTANT:</b> You must configure a "hashid" for each store view. Use the "Current Configuration Scope" selector at the top left side of the page to choose a store view.';
|
6 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Layerdescription.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Panel_LayerDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
13 |
+
{
|
14 |
+
protected $level = self::WARNING;
|
15 |
+
protected $description = '<b>IMPORTANT:</b> You must configure a different Layer script for each store view. Use the "Current Configuration Scope" selector at the top left side of the page to choose a store view.';
|
16 |
+
}
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Message.php
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category blocks
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Block_Settings_Panel_Message extends Mage_Adminhtml_Block_System_Config_Form_Field
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* Error prefix
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
const ERROR_PREFIX = "#error#";
|
19 |
+
|
20 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
21 |
+
{
|
22 |
+
$this->setElement($element);
|
23 |
+
$name = $element->getName();
|
24 |
+
$element->setScopeLabel('');
|
25 |
+
$code = Mage::app()->getRequest()->getParam('store');
|
26 |
+
$field = $this->_getField($name);
|
27 |
+
$html = '';
|
28 |
+
if ($field && $code) {
|
29 |
+
$process = Mage::getModel('doofinder_feed/cron')->load($code, 'store_code');
|
30 |
+
|
31 |
+
if (!$process->getId()) {
|
32 |
+
switch ($field) {
|
33 |
+
case 'status':
|
34 |
+
$msg = Mage::helper('doofinder_feed')->__('Not created');
|
35 |
+
break;
|
36 |
+
|
37 |
+
case 'message':
|
38 |
+
$msg = Mage::helper('doofinder_feed')->__('Process not created yet, it will be created automatically by cron job');
|
39 |
+
break;
|
40 |
+
|
41 |
+
default:
|
42 |
+
$msg = '';
|
43 |
+
}
|
44 |
+
|
45 |
+
} else {
|
46 |
+
$msg = $process->getData($field);
|
47 |
+
}
|
48 |
+
|
49 |
+
$class = 'feed-message ';
|
50 |
+
|
51 |
+
// Mark message as an error
|
52 |
+
if (strpos($msg, self::ERROR_PREFIX) !== false) {
|
53 |
+
$msg = str_replace(self::ERROR_PREFIX, '', $msg);
|
54 |
+
$class .= 'error';
|
55 |
+
}
|
56 |
+
|
57 |
+
$html = "<p class='{$class}'>{$msg}</p>";
|
58 |
+
}
|
59 |
+
return $html;
|
60 |
+
}
|
61 |
+
|
62 |
+
private function _getField($name = null) {
|
63 |
+
|
64 |
+
$pattern = '/groups\[panel\]\[fields\]\[([a-z_-]*)\]\[value\]/';
|
65 |
+
$preg = preg_match($pattern, $name, $match);
|
66 |
+
if ($preg && isset($match[1])) {
|
67 |
+
return $match[1];
|
68 |
+
} else {
|
69 |
+
return false;
|
70 |
+
}
|
71 |
+
}
|
72 |
+
}
|
app/code/community/Doofinder/Feed/Helper/Data.php
ADDED
@@ -0,0 +1,552 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Helpers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Data helper for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Helper_Data extends Mage_Core_Helper_Abstract
|
19 |
+
{
|
20 |
+
private $store = null;
|
21 |
+
|
22 |
+
private $currencyConvert = false;
|
23 |
+
|
24 |
+
private $useMinimalPrice = false;
|
25 |
+
|
26 |
+
private $groupConfigurables = true;
|
27 |
+
|
28 |
+
private $minTierPrice = null;
|
29 |
+
|
30 |
+
const CRON_DAILY = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_DAILY;
|
31 |
+
const CRON_WEEKLY = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_WEEKLY;
|
32 |
+
const CRON_MONTHLY = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_MONTHLY;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Panel info messages.
|
36 |
+
*/
|
37 |
+
const STATUS_DISABLED = 'Disabled';
|
38 |
+
const STATUS_PENDING = Mage_Cron_Model_Schedule::STATUS_PENDING;
|
39 |
+
const STATUS_RUNNING = Mage_Cron_Model_Schedule::STATUS_RUNNING;
|
40 |
+
const STATUS_SUCCESS = Mage_Cron_Model_Schedule::STATUS_SUCCESS;
|
41 |
+
const STATUS_MISSED = Mage_Cron_Model_Schedule::STATUS_MISSED;
|
42 |
+
const STATUS_WAITING = 'Waiting...';
|
43 |
+
const STATUS_ERROR = Mage_Cron_Model_Schedule::STATUS_ERROR;
|
44 |
+
const JOB_CODE = 'doofinder_feed_generate';
|
45 |
+
|
46 |
+
const MSG_EMPTY = "Currently there is no message.";
|
47 |
+
const MSG_PENDING = "The new process of generating the feed has been registered and it's waiting to be activated.";
|
48 |
+
const MSG_DISABLED = "The feed generator for this view is currently disabled.";
|
49 |
+
const MSG_WAITING = "Waiting for registering the new process of generating the feed.";
|
50 |
+
|
51 |
+
|
52 |
+
/**
|
53 |
+
* $product => Product instance
|
54 |
+
* $oStore => Store instance
|
55 |
+
* $currencyConvert => Boolean, Convert prices to $oStore currency.
|
56 |
+
* $useMinimalPrice => Boolean, See below.
|
57 |
+
* $groupConfigurables => Boolean
|
58 |
+
*
|
59 |
+
* If $useMinimalPrice == true then, the price is checked against tier
|
60 |
+
* prices. If there is a smaller price in the tier then that price is used
|
61 |
+
* instead the regular one.
|
62 |
+
*
|
63 |
+
* So, if there is a special price defined and it is greater than the
|
64 |
+
* minimal price found in tier, then it is not returned as the "sale_price".
|
65 |
+
*
|
66 |
+
* ----
|
67 |
+
*
|
68 |
+
* If a Fixed Product Tax exists for the product, then it is applied if
|
69 |
+
* the $oStore settings are configured to do so.
|
70 |
+
*
|
71 |
+
* NOTICE: FPT are ALWAYS applied to prices including taxes. Configuration
|
72 |
+
* is only applied to prices excluding taxes.
|
73 |
+
*/
|
74 |
+
public function collectProductPrices(Mage_Catalog_Model_Product $product, $oStore, $currencyConvert=false, $useMinimalPrice=false, $groupConfigurables=true)
|
75 |
+
{
|
76 |
+
$this->store = $oStore;
|
77 |
+
$this->currencyConvert = $currencyConvert;
|
78 |
+
$this->useMinimalPrice = $useMinimalPrice;
|
79 |
+
$this->groupConfigurables = $groupConfigurables;
|
80 |
+
|
81 |
+
$weeeHelper = Mage::helper('weee');
|
82 |
+
$taxHelper = Mage::helper('tax');
|
83 |
+
$coreHelper = Mage::helper('core');
|
84 |
+
|
85 |
+
// Tier Prices
|
86 |
+
|
87 |
+
$tierPrices = $this->getProductTierPrices($product, $oStore);
|
88 |
+
|
89 |
+
foreach ($tierPrices as $tier)
|
90 |
+
{
|
91 |
+
if ( is_null($this->minTierPrice) || $tier['base_price_excl_tax'] < $this->minTierPrice['base_price_excl_tax'] )
|
92 |
+
{
|
93 |
+
$this->minTierPrice = $tier;
|
94 |
+
continue;
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
if ( $product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED )
|
99 |
+
{
|
100 |
+
$prices = $this->_getGroupedProductPrice($product);
|
101 |
+
}
|
102 |
+
elseif ( $product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE )
|
103 |
+
{
|
104 |
+
$prices = $this->_getBundleProductPrice($product);
|
105 |
+
}
|
106 |
+
else /* ! $product->isGrouped */
|
107 |
+
{
|
108 |
+
$prices = $this->_getProductPrice($product);
|
109 |
+
}
|
110 |
+
|
111 |
+
$prices = $this->_cleanPrices($prices);
|
112 |
+
|
113 |
+
foreach ( array('price', 'sale_price') as $priceType )
|
114 |
+
{
|
115 |
+
if ( !isset($prices[$priceType]) )
|
116 |
+
continue;
|
117 |
+
foreach ( $prices[$priceType] as $priceMode => $priceValue )
|
118 |
+
{
|
119 |
+
if ( $currencyConvert ) {
|
120 |
+
$priceValue = $oStore->convertPrice($priceValue, false, false);
|
121 |
+
}
|
122 |
+
$prices[$priceType][$priceMode] = $priceValue;
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
return $prices;
|
127 |
+
}
|
128 |
+
|
129 |
+
protected function _cleanPrices($prices)
|
130 |
+
{
|
131 |
+
if (!isset($prices['price'])) return $prices;
|
132 |
+
if ( isset($prices['sale_price']['excluding_tax']) &&
|
133 |
+
$prices['price']['excluding_tax'] <= $prices['sale_price']['excluding_tax'] )
|
134 |
+
{
|
135 |
+
unset($prices['sale_price']['excluding_tax']);
|
136 |
+
unset($prices['sale_price']['including_tax']);
|
137 |
+
}
|
138 |
+
|
139 |
+
if ( $prices['price']['excluding_tax'] <= 0 )
|
140 |
+
{
|
141 |
+
unset($prices['price']['excluding_tax']);
|
142 |
+
unset($prices['price']['including_tax']);
|
143 |
+
}
|
144 |
+
|
145 |
+
return $prices;
|
146 |
+
}
|
147 |
+
|
148 |
+
protected function _getProductPrice($product)
|
149 |
+
{
|
150 |
+
$prices = array();
|
151 |
+
|
152 |
+
$weeeHelper = Mage::helper('weee');
|
153 |
+
$taxHelper = Mage::helper('tax');
|
154 |
+
$coreHelper = Mage::helper('core');
|
155 |
+
|
156 |
+
$prices['price_type'] = 'normal';
|
157 |
+
|
158 |
+
$weeeTaxAmount = $weeeHelper->getAmountForDisplay($product);
|
159 |
+
|
160 |
+
$weeeTaxAttributes = null;
|
161 |
+
|
162 |
+
if ( $weeeHelper->typeOfDisplay($product, array(1, 2, 4), null, $this->store) )
|
163 |
+
{
|
164 |
+
$weeeTaxAmount = $weeeHelper->getAmount($product, null, null, $this->store->getWebsiteId(), false);
|
165 |
+
$weeeTaxAttributes = $weeeHelper->getProductWeeeAttributesForDisplay($product);
|
166 |
+
}
|
167 |
+
|
168 |
+
// Precios originales y finales (segun Magento) sin Weee
|
169 |
+
|
170 |
+
$base_price_excl_tax = $taxHelper->getPrice($product, $product->getPrice(), false, null, null, null, $this->store, null);
|
171 |
+
$base_price_incl_tax = $taxHelper->getPrice($product, $product->getPrice(), true, null, null, null, $this->store, null);
|
172 |
+
|
173 |
+
$final_price_excl_tax = $taxHelper->getPrice($product, $product->getFinalPrice(), false, null, null, null, $this->store, null);
|
174 |
+
$final_price_incl_tax = $taxHelper->getPrice($product, $product->getFinalPrice(), true, null, null, null, $this->store, null);
|
175 |
+
|
176 |
+
if ( $this->minTierPrice && $this->useMinimalPrice
|
177 |
+
&& $this->minTierPrice['base_price_excl_tax'] < $final_price_excl_tax)
|
178 |
+
{
|
179 |
+
$prices['price_type'] = 'minimal';
|
180 |
+
|
181 |
+
$base_price_excl_tax = $this->minTierPrice['base_price_excl_tax'];
|
182 |
+
$base_price_incl_tax = $this->minTierPrice['base_price_incl_tax'];
|
183 |
+
}
|
184 |
+
|
185 |
+
// Algunas preguntas
|
186 |
+
|
187 |
+
$inclFptOnly = $weeeHelper->typeOfDisplay($product, 0, null, $this->store); // Including FPT only
|
188 |
+
$inclFptAndDescription = $weeeHelper->typeOfDisplay($product, 1, null, $this->store); // Including FPT and FPT description
|
189 |
+
$exclFptAndDescriptionFinalPrice = $weeeHelper->typeOfDisplay($product, 2, null, $this->store); // Excluding FPT, FPT description, final price
|
190 |
+
$exclFpt = $weeeHelper->typeOfDisplay($product, 3, null, $this->store); // Excluding FPT
|
191 |
+
$inclFptAndDescriptionWithTaxes = $weeeHelper->typeOfDisplay($product, 4, null, $this->store); // Including FPT and FPT description [incl. FPT VAT]
|
192 |
+
|
193 |
+
// Elegimos y calculamos los precios finales
|
194 |
+
|
195 |
+
if ( $final_price_excl_tax >= $base_price_excl_tax )
|
196 |
+
{
|
197 |
+
$prices['price']['excluding_tax'] = $base_price_excl_tax;
|
198 |
+
$prices['price']['including_tax'] = $base_price_incl_tax;
|
199 |
+
|
200 |
+
if ( $weeeTaxAmount )
|
201 |
+
{
|
202 |
+
$prices['price']['including_tax'] += $weeeTaxAmount;
|
203 |
+
|
204 |
+
if ( $inclFptOnly || $inclFptAndDescription || $inclFptAndDescriptionWithTaxes )
|
205 |
+
$prices['price']['excluding_tax'] += $weeeTaxAmount;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
else
|
209 |
+
{
|
210 |
+
$prices['price']['excluding_tax'] = $base_price_excl_tax;
|
211 |
+
$prices['price']['including_tax'] = $base_price_incl_tax;
|
212 |
+
|
213 |
+
$prices['sale_price']['excluding_tax'] = $final_price_excl_tax;
|
214 |
+
$prices['sale_price']['including_tax'] = $final_price_incl_tax;
|
215 |
+
|
216 |
+
$originalWeeeTaxAmount = $weeeHelper->getOriginalAmount($product);
|
217 |
+
|
218 |
+
if ( $weeeTaxAmount )
|
219 |
+
{
|
220 |
+
$prices['price']['including_tax'] += $originalWeeeTaxAmount;
|
221 |
+
$prices['sale_price']['including_tax'] += $weeeTaxAmount;
|
222 |
+
|
223 |
+
if ( $inclFptOnly || $inclFptAndDescription || $inclFptAndDescriptionWithTaxes )
|
224 |
+
{
|
225 |
+
$prices['price']['excluding_tax'] += $originalWeeeTaxAmount;
|
226 |
+
$prices['sale_price']['excluding_tax'] += $weeeTaxAmount;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
}
|
230 |
+
|
231 |
+
if ( $product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE && $this->groupConfigurables && $this->useMinimalPrice )
|
232 |
+
{
|
233 |
+
$prices = $this->_getConfigurableProductPrice($product);
|
234 |
+
}
|
235 |
+
return $prices;
|
236 |
+
}
|
237 |
+
|
238 |
+
protected function _getConfigurableProductPrice($product, $prices)
|
239 |
+
{
|
240 |
+
$childProducts = $product->getTypeInstance()->getUsedProducts();
|
241 |
+
|
242 |
+
foreach ( $childProducts as $child )
|
243 |
+
{
|
244 |
+
$childPrices = $this->collectProductPrices($child, $this->store, false, $this->useMinimalPrice, $this->groupConfigurables);
|
245 |
+
|
246 |
+
// Compare regular price
|
247 |
+
if ( $childPrices['price']['excluding_tax'] < $prices['price']['excluding_tax'] )
|
248 |
+
{
|
249 |
+
$prices['price']['excluding_tax'] = $childPrices['price']['excluding_tax'];
|
250 |
+
$prices['price']['including_tax'] = $childPrices['price']['including_tax'];
|
251 |
+
$prices['price']['overriden'] = true;
|
252 |
+
}
|
253 |
+
|
254 |
+
// Compare sale price
|
255 |
+
if ( array_key_exists('sale_price', $childPrices) )
|
256 |
+
{
|
257 |
+
if ( ! array_key_exists('sale_price', $prices)
|
258 |
+
|| $childPrices['sale_price']['excluding_tax'] < $prices['sale_price']['excluding_tax'] )
|
259 |
+
{
|
260 |
+
$prices['sale_price']['excluding_tax'] = $childPrices['sale_price']['excluding_tax'];
|
261 |
+
$prices['sale_price']['including_tax'] = $childPrices['sale_price']['including_tax'];
|
262 |
+
$prices['sale_price']['overriden'] = true;
|
263 |
+
}
|
264 |
+
}
|
265 |
+
}
|
266 |
+
return $prices;
|
267 |
+
}
|
268 |
+
|
269 |
+
protected function _getGroupedProductPrice($product)
|
270 |
+
{
|
271 |
+
$weeeHelper = Mage::helper('weee');
|
272 |
+
$taxHelper = Mage::helper('tax');
|
273 |
+
$coreHelper = Mage::helper('core');
|
274 |
+
|
275 |
+
$minimal_prices = array(
|
276 |
+
'price' => array(
|
277 |
+
'including_tax' => 0,
|
278 |
+
'excluding_tax' => 0
|
279 |
+
),
|
280 |
+
'sale_price' => array(
|
281 |
+
'including_tax' => 0,
|
282 |
+
'excluding_tax' => 0
|
283 |
+
)
|
284 |
+
);
|
285 |
+
|
286 |
+
$childrenIds = $product->getTypeInstance()->getChildrenIds($product->getId());
|
287 |
+
$childrenIds = $childrenIds[Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED];
|
288 |
+
|
289 |
+
if (empty($childrenIds) || !is_array($childrenIds)) {
|
290 |
+
return $minimal_prices;
|
291 |
+
}
|
292 |
+
|
293 |
+
$collection = Mage::getModel('catalog/product')->getCollection();
|
294 |
+
$collection
|
295 |
+
->addIdFilter($childrenIds)
|
296 |
+
->addAttributeToSelect('*')
|
297 |
+
->load();
|
298 |
+
|
299 |
+
foreach($collection as $product)
|
300 |
+
{
|
301 |
+
$sub_prices = $this->collectProductPrices($product, $this->store, $this->currencyConvert, $this->useMinimalPrice, $this->groupConfigurables);
|
302 |
+
|
303 |
+
if (! empty($sub_prices['price']['excluding_tax'])) {
|
304 |
+
if ($minimal_prices['price']['excluding_tax'] === 0 ||
|
305 |
+
$minimal_prices['price']['excluding_tax'] > $sub_prices['price']['excluding_tax'])
|
306 |
+
$minimal_prices = $sub_prices;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
return $minimal_prices;
|
311 |
+
}
|
312 |
+
|
313 |
+
protected function _getBundleProductPrice($product)
|
314 |
+
{
|
315 |
+
$prices = array();
|
316 |
+
|
317 |
+
$weeeHelper = Mage::helper('weee');
|
318 |
+
$taxHelper = Mage::helper('tax');
|
319 |
+
$coreHelper = Mage::helper('core');
|
320 |
+
|
321 |
+
if ( method_exists($product->getPriceModel(), 'getTotalPrices') )
|
322 |
+
{
|
323 |
+
$bundle_price_excl_tax = $product->getPriceModel()->getTotalPrices($product, 'min', false, true);
|
324 |
+
$bundle_price_incl_tax = $product->getPriceModel()->getTotalPrices($product, 'min', true, true);
|
325 |
+
}
|
326 |
+
else // Magento 1.5.0.1 + 1.5.1.0
|
327 |
+
{
|
328 |
+
$bundle_price_excl_tax = $product->getPriceModel()->getPricesDependingOnTax($product, 'min', false);
|
329 |
+
$bundle_price_incl_tax = $product->getPriceModel()->getPricesDependingOnTax($product, 'min', true);
|
330 |
+
}
|
331 |
+
|
332 |
+
if ( $bundle_price_excl_tax )
|
333 |
+
{
|
334 |
+
$prices['price_type'] = 'minimal';
|
335 |
+
|
336 |
+
$prices['price']['excluding_tax'] = $bundle_price_excl_tax;
|
337 |
+
$prices['price']['including_tax'] = $bundle_price_incl_tax;
|
338 |
+
}
|
339 |
+
|
340 |
+
return $prices;
|
341 |
+
}
|
342 |
+
|
343 |
+
public function getProductTierPrices(Mage_Catalog_Model_Product $product, $oStore)
|
344 |
+
{
|
345 |
+
if (is_null($product))
|
346 |
+
return array();
|
347 |
+
|
348 |
+
$prices = array();
|
349 |
+
$taxHelper = Mage::helper('tax');
|
350 |
+
|
351 |
+
// Get Tier Prices
|
352 |
+
|
353 |
+
$tierPrices = $product->getTierPrice(null);
|
354 |
+
|
355 |
+
if (! is_array($tierPrices))
|
356 |
+
$tierPrices = (array) $tierPrices;
|
357 |
+
|
358 |
+
foreach ( $tierPrices as $price )
|
359 |
+
{
|
360 |
+
$result = array();
|
361 |
+
|
362 |
+
if ( $price['website_id'] != $oStore->getWebsiteId() && $price['website_id'] != 0 )
|
363 |
+
continue;
|
364 |
+
|
365 |
+
$result['price_qty'] = $price['price_qty'] * 1; // make int
|
366 |
+
|
367 |
+
if ( $price['price'] < $product->getFinalPrice() )
|
368 |
+
$result['save_percent'] = ceil(100 - ((100 / $product->getFinalPrice()) * $price['price']));
|
369 |
+
|
370 |
+
$result['base_price_excl_tax'] = $taxHelper->getPrice($product, $price['website_price'], false, null, null, null, $oStore, null);
|
371 |
+
$result['base_price_incl_tax'] = $taxHelper->getPrice($product, $price['website_price'], true, null, null, null, $oStore, null);
|
372 |
+
|
373 |
+
$prices[] = $result;
|
374 |
+
}
|
375 |
+
|
376 |
+
return $prices;
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Gets store config for cron settings.
|
381 |
+
* @param string $storeCode
|
382 |
+
* @return array
|
383 |
+
*/
|
384 |
+
public function getStoreConfig($storeCode = '') {
|
385 |
+
$xmlName = Mage::getStoreConfig('doofinder_cron/schedule_settings/name', $storeCode);
|
386 |
+
$config = array(
|
387 |
+
'enabled' => Mage::getStoreConfig('doofinder_cron/schedule_settings/enabled', $storeCode),
|
388 |
+
'display_price' => Mage::getStoreConfig('doofinder_cron/feed_settings/display_price', $storeCode),
|
389 |
+
'grouped' => Mage::getStoreConfig('doofinder_cron/feed_settings/grouped', $storeCode),
|
390 |
+
'image_size' => Mage::getStoreConfig('doofinder_cron/feed_settings/image_size', $storeCode),
|
391 |
+
'stepSize' => Mage::getStoreConfig('doofinder_cron/schedule_settings/step', $storeCode),
|
392 |
+
'stepDelay' => Mage::getStoreConfig('doofinder_cron/schedule_settings/delay', $storeCode),
|
393 |
+
'frequency' => Mage::getStoreConfig('doofinder_cron/schedule_settings/frequency', $storeCode),
|
394 |
+
'time' => explode(',', Mage::getStoreConfig('doofinder_cron/schedule_settings/time', $storeCode)),
|
395 |
+
'storeCode' => $storeCode,
|
396 |
+
'xmlName' => $this->_processXmlName($xmlName, $storeCode),
|
397 |
+
'reset' => Mage::getStoreConfig('doofinder_cron/schedule_settings/reset', $storeCode),
|
398 |
+
);
|
399 |
+
return $config;
|
400 |
+
}
|
401 |
+
|
402 |
+
/**
|
403 |
+
* Process xml filename
|
404 |
+
* @param string $name
|
405 |
+
* @return bool
|
406 |
+
*/
|
407 |
+
private function _processXmlName($name = 'doofinder-{store_code}.xml', $code = 'default') {
|
408 |
+
$pattern = '/\{\s*store_code\s*\}/';
|
409 |
+
|
410 |
+
$newName = preg_replace($pattern, $code, $name);
|
411 |
+
return $newName;
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Create cron expr string
|
416 |
+
* @param string $time
|
417 |
+
* @return mixed
|
418 |
+
*/
|
419 |
+
private function _getCronExpr($time = null, $frequency = null) {
|
420 |
+
|
421 |
+
if (!$time) return false;
|
422 |
+
$time = explode(',', $time);
|
423 |
+
|
424 |
+
$cronExprArray = array(
|
425 |
+
intval($time[1]),
|
426 |
+
intval($time[0]),
|
427 |
+
($frequency == self::CRON_MONTHLY) ? '1' : '*',
|
428 |
+
'*',
|
429 |
+
($frequency == self::CRON_WEEKLY) ? '1' : '*',
|
430 |
+
);
|
431 |
+
$cronExprString = join(' ', $cronExprArray);
|
432 |
+
|
433 |
+
return $cronExprString;
|
434 |
+
}
|
435 |
+
|
436 |
+
/**
|
437 |
+
* Creates new schedule entry.
|
438 |
+
* @param Doofinder_Feed_Model_Cron $process
|
439 |
+
*/
|
440 |
+
|
441 |
+
public function createNewSchedule(Doofinder_Feed_Model_Cron $process) {
|
442 |
+
$helper = Mage::helper('doofinder_feed');
|
443 |
+
|
444 |
+
$config = $helper->getStoreConfig($process->getStoreCode());
|
445 |
+
|
446 |
+
// Set new schedule time
|
447 |
+
$delayInMin = intval($config['stepDelay']);
|
448 |
+
$timescheduled = strftime("%Y-%m-%d %H:%M:%S", mktime(date("H"), date("i") + $delayInMin, date("s"), date("m"), date("d"), date("Y")));
|
449 |
+
|
450 |
+
// Prepare new process data
|
451 |
+
$status = $helper::STATUS_RUNNING;
|
452 |
+
$nextRun = '-';
|
453 |
+
|
454 |
+
// Set process data and save
|
455 |
+
$process->setStatus($status)
|
456 |
+
->setNextRun('-')
|
457 |
+
->setNextIteration($timescheduled)
|
458 |
+
->save();
|
459 |
+
|
460 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $helper->__('Scheduling the next step for %s', $timescheduled));
|
461 |
+
}
|
462 |
+
|
463 |
+
public function getScheduledAt($time = null, $frequency = null, $timezoneOffset = true) {
|
464 |
+
$parts = array($time[0], $time[1], $time[2], date('m'), date('d'));
|
465 |
+
$offset = $this->getTimezoneOffset();
|
466 |
+
|
467 |
+
$now = time();
|
468 |
+
$start = mktime($parts[0] - $offset, $parts[1], $parts[2], $parts[3], $parts[4]);
|
469 |
+
|
470 |
+
if ($start < $now) {
|
471 |
+
switch ($frequency) {
|
472 |
+
case self::CRON_MONTHLY:
|
473 |
+
$parts[3] += 1;
|
474 |
+
break;
|
475 |
+
|
476 |
+
case self::CRON_WEEKLY:
|
477 |
+
$parts[4] += 7;
|
478 |
+
break;
|
479 |
+
|
480 |
+
case self::CRON_DAILY:
|
481 |
+
$parts[4] += 1;
|
482 |
+
break;
|
483 |
+
}
|
484 |
+
}
|
485 |
+
|
486 |
+
if ($timezoneOffset) {
|
487 |
+
$parts[0] -= $offset;
|
488 |
+
}
|
489 |
+
|
490 |
+
return strftime("%Y-%m-%d %H:%M:%S", mktime($parts[0], $parts[1], $parts[2], $parts[3], $parts[4]));
|
491 |
+
}
|
492 |
+
|
493 |
+
public function getTimezoneOffset() {
|
494 |
+
$timezone = Mage::getStoreConfig('general/locale/timezone');
|
495 |
+
$backTimezone = date_default_timezone_get();
|
496 |
+
// Set relative timezone
|
497 |
+
date_default_timezone_set($timezone);
|
498 |
+
$offset = (date('Z') / 60 / 60);
|
499 |
+
// Revoke server timezone
|
500 |
+
date_default_timezone_set($backTimezone);
|
501 |
+
return $offset;
|
502 |
+
}
|
503 |
+
|
504 |
+
/**
|
505 |
+
* Get path to feed file.
|
506 |
+
*
|
507 |
+
* @return string
|
508 |
+
*/
|
509 |
+
public function getFeedDirectory()
|
510 |
+
{
|
511 |
+
return Mage::getBaseDir('media').DS.'doofinder';
|
512 |
+
}
|
513 |
+
|
514 |
+
/**
|
515 |
+
* Get path to feed file.
|
516 |
+
*
|
517 |
+
* @return string
|
518 |
+
*/
|
519 |
+
public function getFeedPath($storeCode)
|
520 |
+
{
|
521 |
+
$config = $this->getStoreConfig($storeCode);
|
522 |
+
|
523 |
+
return $this->getFeedDirectory().DS.$config['xmlName'];
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Get path to feed file.
|
528 |
+
*
|
529 |
+
* @return string
|
530 |
+
*/
|
531 |
+
public function getFeedTemporaryPath($storeCode)
|
532 |
+
{
|
533 |
+
return $this->getFeedPath($storeCode) . '.tmp';
|
534 |
+
}
|
535 |
+
|
536 |
+
/**
|
537 |
+
* Creates feed directory.
|
538 |
+
*
|
539 |
+
* @param string $dir
|
540 |
+
* @return bool
|
541 |
+
*/
|
542 |
+
public function createFeedDirectory()
|
543 |
+
{
|
544 |
+
$dir = $this->getFeedDirectory();
|
545 |
+
|
546 |
+
if ((!file_exists($dir) && !mkdir($dir, 0777, true)) || !is_dir($dir)) {
|
547 |
+
Mage::throwException('Could not create directory: '.$dir);
|
548 |
+
}
|
549 |
+
|
550 |
+
return true;
|
551 |
+
}
|
552 |
+
}
|
app/code/community/Doofinder/Feed/Helper/Log.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Helpers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Log helper for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Helper_Log extends Mage_Core_Helper_Abstract
|
19 |
+
{
|
20 |
+
const STATUS = 'status';
|
21 |
+
const WARNING = 'warning';
|
22 |
+
const ERROR = 'error';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Log the feed event.
|
26 |
+
*
|
27 |
+
* @param Doofinder_Feed_Model_Cron $process
|
28 |
+
* @param string $type
|
29 |
+
* @param string $message
|
30 |
+
*/
|
31 |
+
function log(Doofinder_Feed_Model_Cron $process, $type, $message)
|
32 |
+
{
|
33 |
+
$entry = Mage::getModel('doofinder_feed/log')
|
34 |
+
->setProcessId($process->getId())
|
35 |
+
->setType($type)
|
36 |
+
->setMessage($message)
|
37 |
+
->save();
|
38 |
+
|
39 |
+
return $this;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Get available log types
|
44 |
+
*
|
45 |
+
* @return array
|
46 |
+
*/
|
47 |
+
function listLogTypes()
|
48 |
+
{
|
49 |
+
return array(
|
50 |
+
static::STATUS => $this->__('Status'),
|
51 |
+
static::WARNING => $this->__('Warning'),
|
52 |
+
static::ERROR => $this->__('Error'),
|
53 |
+
);
|
54 |
+
}
|
55 |
+
}
|
app/code/community/Doofinder/Feed/Helper/Search.php
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
require_once(Mage::getBaseDir('lib') . DS. 'Doofinder' . DS .'doofinder_api.php');
|
3 |
+
|
4 |
+
class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
5 |
+
{
|
6 |
+
const DOOFINDER_PAGE_LIMIT = 100;
|
7 |
+
const DOOFINDER_RESULTS_LIMIT = 1000;
|
8 |
+
|
9 |
+
protected $_lastSearch = null;
|
10 |
+
protected $_lastResults = null;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Perform a doofinder search on given key.
|
14 |
+
*
|
15 |
+
* @param string $queryText
|
16 |
+
* @param int $limit
|
17 |
+
* @param int $offset
|
18 |
+
*
|
19 |
+
* @return array - The array od product ids from first page
|
20 |
+
*/
|
21 |
+
public function performDoofinderSearch($queryText)
|
22 |
+
{
|
23 |
+
$hashId = Mage::getStoreConfig('doofinder_search/internal_settings/hash_id', Mage::app()->getStore());
|
24 |
+
$apiKey = Mage::getStoreConfig('doofinder_search/internal_settings/api_key', Mage::app()->getStore());
|
25 |
+
$limit = Mage::getStoreConfig('doofinder_search/internal_settings/request_limit', Mage::app()->getStore());
|
26 |
+
$ids = false;
|
27 |
+
|
28 |
+
$df = new DoofinderApi($hashId, $apiKey);
|
29 |
+
$dfResults = $df->query($queryText, null, array('rpp' => $limit, 'transformer' => 'onlyid', 'filter' => array()));
|
30 |
+
|
31 |
+
// Store objects
|
32 |
+
$this->_lastSearch = $df;
|
33 |
+
$this->_lastResults = $dfResults;
|
34 |
+
|
35 |
+
return $this->retrieveIds($dfResults);
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Retrieve ids from Doofinder Results
|
40 |
+
*
|
41 |
+
* @param DoofinderResults $dfResults
|
42 |
+
* @return array
|
43 |
+
*/
|
44 |
+
protected function retrieveIds(DoofinderResults $dfResults)
|
45 |
+
{
|
46 |
+
$ids = array();
|
47 |
+
foreach($dfResults->getResults() as $result) {
|
48 |
+
$ids[] = $result['id'];
|
49 |
+
}
|
50 |
+
|
51 |
+
return $ids;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Fetch all results of last doofinder search
|
56 |
+
*
|
57 |
+
* @return array - The array of products ids from all pages
|
58 |
+
*/
|
59 |
+
public function getAllResults()
|
60 |
+
{
|
61 |
+
$limit = Mage::getStoreConfig('doofinder_search/internal_settings/total_limit', Mage::app()->getStore());
|
62 |
+
$ids = $this->retrieveIds($this->_lastResults);
|
63 |
+
|
64 |
+
while (count($ids) < $limit && ($dfResults = $this->_lastSearch->nextPage())) {
|
65 |
+
$ids = array_merge($ids, $this->retrieveIds($dfResults));
|
66 |
+
}
|
67 |
+
|
68 |
+
return $ids;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Returns fetched results count
|
73 |
+
*
|
74 |
+
* @return int
|
75 |
+
*/
|
76 |
+
public function getResultsCount()
|
77 |
+
{
|
78 |
+
return $this->_lastResults->getProperty('total');
|
79 |
+
}
|
80 |
+
}
|
app/code/community/Doofinder/Feed/Helper/Tax.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Helpers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Tax helper for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Helper_Tax extends Mage_Tax_Helper_Data
|
19 |
+
{
|
20 |
+
public function needPriceConversion($store = null)
|
21 |
+
{
|
22 |
+
return true;
|
23 |
+
}
|
24 |
+
}
|
app/code/community/Doofinder/Feed/Model/Adminhtml/System/Config/Backend/Cron.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Adminhtml_System_Config_Backend_Cron extends Mage_Core_Model_Config_Data {
|
13 |
+
|
14 |
+
const CRON_STRING_PATH = 'crontab/jobs/doofinder_feed_generate/schedule/cron_expr';
|
15 |
+
|
16 |
+
protected function _afterSave() {
|
17 |
+
$time = $this->getData('groups/settings/fields/time/value');
|
18 |
+
$frequency = $this->getData('groups/settings/fields/frequency/value');
|
19 |
+
$frequencyDaily = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_DAILY;
|
20 |
+
$frequencyWeekly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_WEEKLY;
|
21 |
+
$frequencyMonthly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_MONTHLY;
|
22 |
+
|
23 |
+
$cronDayOfWeek = date('N');
|
24 |
+
|
25 |
+
$cronExprArray = array(
|
26 |
+
intval($time[1]), # Minute
|
27 |
+
intval($time[0]), # Hour
|
28 |
+
($frequency == $frequencyMonthly) ? '1' : '*', # Day of the Month
|
29 |
+
'*', # Month of the Year
|
30 |
+
($frequency == $frequencyWeekly) ? '1' : '*', # Day of the Week
|
31 |
+
);
|
32 |
+
|
33 |
+
$cronExprArray = array(
|
34 |
+
'*/1',
|
35 |
+
'*',
|
36 |
+
'*',
|
37 |
+
'*',
|
38 |
+
'*',
|
39 |
+
);
|
40 |
+
$cronExprString = join(' ', $cronExprArray);
|
41 |
+
|
42 |
+
try {
|
43 |
+
Mage::getModel('core/config_data')
|
44 |
+
->load(self::CRON_STRING_PATH, 'path')
|
45 |
+
->setValue($cronExprString)
|
46 |
+
->setPath(self::CRON_STRING_PATH)
|
47 |
+
->save();
|
48 |
+
}
|
49 |
+
catch (Exception $e) {
|
50 |
+
throw new Exception(Mage::helper('cron')->__('Unable to save the cron expression.'));
|
51 |
+
|
52 |
+
}
|
53 |
+
}
|
54 |
+
}
|
app/code/community/Doofinder/Feed/Model/Adminhtml/System/Config/Validation/Hashid.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Doofinder_Feed_Model_Adminhtml_System_Config_Validation_Hashid extends Mage_Core_Model_Config_Data {
|
3 |
+
public function save() {
|
4 |
+
// Hash id to save
|
5 |
+
$hashId = $this->getValue();
|
6 |
+
$stores = Mage::app()->getStores();
|
7 |
+
foreach ($stores as $store) {
|
8 |
+
if ($this->getStoreCode() === $store->getCode())
|
9 |
+
continue;
|
10 |
+
$code = $store->getCode();
|
11 |
+
$scopeHashId = Mage::getStoreConfig('doofinder_search/internal_settings/hash_id', $code);
|
12 |
+
if ($hashId !== '' && $hashId === $scopeHashId) {
|
13 |
+
Mage::throwException("HashID ".$hashId." is already used in ".$code." store. It must have a unique value.");
|
14 |
+
exit;
|
15 |
+
}
|
16 |
+
}
|
17 |
+
return parent::save();
|
18 |
+
}
|
19 |
+
}
|
app/code/community/Doofinder/Feed/Model/CatalogSearch/Resource/Fulltext.php
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Doofinder_Feed_Model_CatalogSearch_Resource_Fulltext extends Mage_CatalogSearch_Model_Resource_Fulltext
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* Get stored results select
|
7 |
+
*
|
8 |
+
* @param int $query_id
|
9 |
+
* @param int $attr
|
10 |
+
* @return Varien_Db_Select
|
11 |
+
*/
|
12 |
+
protected function getStoredResultsSelect($query_id, $attr = 'product_id')
|
13 |
+
{
|
14 |
+
$adapter = $this->_getReadAdapter();
|
15 |
+
|
16 |
+
$select = $adapter->select()
|
17 |
+
->from($this->getTable('catalogsearch/result'), $attr)
|
18 |
+
->where('query_id = ?', $query_id)
|
19 |
+
->order('relevance desc');
|
20 |
+
|
21 |
+
return $select;
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Get stored results in CatalogSearch cache
|
26 |
+
*
|
27 |
+
* @param int $query_id
|
28 |
+
* @param int $limit
|
29 |
+
* @return array
|
30 |
+
*/
|
31 |
+
protected function getStoredResults($query_id, $limit)
|
32 |
+
{
|
33 |
+
$adapter = $this->_getReadAdapter();
|
34 |
+
$select = $this->getStoredResultsSelect($query_id);
|
35 |
+
$select->limit($limit);
|
36 |
+
|
37 |
+
$results = array();
|
38 |
+
foreach ($adapter->fetchAll($select) as $result) {
|
39 |
+
$results[] = $result['product_id'];
|
40 |
+
}
|
41 |
+
|
42 |
+
return $results;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get number of stored results in CatalogSearch cache
|
47 |
+
*
|
48 |
+
* @param int $query_id
|
49 |
+
* @return array
|
50 |
+
*/
|
51 |
+
protected function getStoredResultsCount($query_id)
|
52 |
+
{
|
53 |
+
$adapter = $this->_getReadAdapter();
|
54 |
+
$select = $this->getStoredResultsSelect($query_id, 'COUNT(*)');
|
55 |
+
|
56 |
+
return (int) $adapter->fetchOne($select);
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Override prepareResult.
|
61 |
+
*
|
62 |
+
* @param Mage_CatalogSearch_Model_Fulltext $object
|
63 |
+
* @param string $queryText
|
64 |
+
* @param Mage_CatalogSearch_Model_Query $query
|
65 |
+
*
|
66 |
+
* @return Doofinder_Feed_Model_CatalogSearch_Resource_Fulltext
|
67 |
+
*/
|
68 |
+
public function prepareResult($object, $queryText, $query)
|
69 |
+
{
|
70 |
+
if(!Mage::getStoreConfigFlag('doofinder_search/internal_settings/enable', Mage::app()->getStore())) {
|
71 |
+
return parent::prepareResult($object, $queryText, $query);
|
72 |
+
}
|
73 |
+
|
74 |
+
$helper = Mage::helper('doofinder_feed/search');
|
75 |
+
|
76 |
+
// Fetch initial results
|
77 |
+
$results = $helper->performDoofinderSearch($queryText);
|
78 |
+
|
79 |
+
$adapter = $this->_getWriteAdapter();
|
80 |
+
|
81 |
+
if ($query->getIsProcessed()) {
|
82 |
+
$storedResults = $this->getStoredResults($query->getId(), count($results));
|
83 |
+
$maxResults = Mage::getStoreConfig('doofinder_search/internal_settings/total_limit', Mage::app()->getStore());
|
84 |
+
|
85 |
+
// Compare results count and checksum
|
86 |
+
if (min($helper->getResultsCount(), $maxResults) == $this->getStoredResultsCount($query->getId()) &&
|
87 |
+
$this->calculateChecksum($results) == $this->calculateChecksum($storedResults)) {
|
88 |
+
return $this;
|
89 |
+
}
|
90 |
+
|
91 |
+
// Delete results
|
92 |
+
$select = $adapter->select()
|
93 |
+
->from($this->getTable('catalogsearch/result'), 'product_id')
|
94 |
+
->where('query_id = ?', $query->getId());
|
95 |
+
$adapter->query($adapter->deleteFromSelect($select, $this->getTable('catalogsearch/result')));
|
96 |
+
}
|
97 |
+
|
98 |
+
try {
|
99 |
+
|
100 |
+
// Fetch all results
|
101 |
+
$results = $helper->getAllResults();
|
102 |
+
|
103 |
+
if (!empty($results)) {
|
104 |
+
$data = array();
|
105 |
+
$relevance = count($results);
|
106 |
+
foreach($results as $product_id) {
|
107 |
+
$data[] = array(
|
108 |
+
'query_id' => $query->getId(),
|
109 |
+
'product_id' => $product_id,
|
110 |
+
'relevance' => $relevance--,
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
$adapter->insertOnDuplicate($this->getTable('catalogsearch/result'), $data);
|
115 |
+
}
|
116 |
+
|
117 |
+
$query->setIsProcessed(1);
|
118 |
+
|
119 |
+
} catch (Exception $e) {
|
120 |
+
Mage::logException($e);
|
121 |
+
return parent::prepareResult($object, $queryText, $query);
|
122 |
+
}
|
123 |
+
|
124 |
+
return $this;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Calculate results checksum
|
129 |
+
*
|
130 |
+
* @param array[int] $results
|
131 |
+
* @return string
|
132 |
+
*/
|
133 |
+
protected function calculateChecksum(array $results)
|
134 |
+
{
|
135 |
+
return hash('sha256', implode(',', $results));
|
136 |
+
}
|
137 |
+
}
|
app/code/community/Doofinder/Feed/Model/Config.php
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Config model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Config extends Mage_Core_Model_Config_Data
|
19 |
+
{
|
20 |
+
const DEFAULT_SECTION = 'feed';
|
21 |
+
const BASE_CONFIG_PATH = 'doofinder';
|
22 |
+
|
23 |
+
protected $_directives = null;
|
24 |
+
protected $_product_attribute_codes = null;
|
25 |
+
protected $_product_directives = null;
|
26 |
+
|
27 |
+
const OUT_OF_STOCK = 'out of stock';
|
28 |
+
const IN_STOCK = 'in stock';
|
29 |
+
|
30 |
+
public function getOutOfStockStatus() {
|
31 |
+
return self::OUT_OF_STOCK;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function getInStockStatus() {
|
35 |
+
return self::IN_STOCK;
|
36 |
+
}
|
37 |
+
|
38 |
+
//
|
39 |
+
// Get Config
|
40 |
+
//
|
41 |
+
|
42 |
+
public function getConfigVar($key, $storeId = null,
|
43 |
+
$section = self::DEFAULT_SECTION)
|
44 |
+
{
|
45 |
+
if ($key == 'field_map')
|
46 |
+
return $this->getConfigVarFieldMap($key, $storeId, $section);
|
47 |
+
|
48 |
+
$path = self::BASE_CONFIG_PATH . '/' . $section . '/' . $key;
|
49 |
+
|
50 |
+
return Mage::getStoreConfig($path, $storeId);
|
51 |
+
}
|
52 |
+
|
53 |
+
public function getConfigVarFieldMap($key, $storeId = null,
|
54 |
+
$section = self::DEFAULT_SECTION)
|
55 |
+
{
|
56 |
+
$path = self::BASE_CONFIG_PATH . '/' . $section . '/' . $key;
|
57 |
+
$data = Mage::getStoreConfig($path, $storeId);
|
58 |
+
|
59 |
+
if (empty($data))
|
60 |
+
$data = $this->convertDefaultFieldMap($storeId);
|
61 |
+
|
62 |
+
return $data;
|
63 |
+
}
|
64 |
+
|
65 |
+
public function getMultipleSelectVar($key, $storeId = null,
|
66 |
+
$section = self::DEFAULT_SECTION)
|
67 |
+
{
|
68 |
+
$str = $this->getConfigVar($key, $storeId, $section);
|
69 |
+
$values = array();
|
70 |
+
|
71 |
+
if (!empty($str))
|
72 |
+
{
|
73 |
+
$values = explode(',', $str);
|
74 |
+
}
|
75 |
+
|
76 |
+
return array_filter($values);
|
77 |
+
}
|
78 |
+
|
79 |
+
|
80 |
+
//
|
81 |
+
// Defaults
|
82 |
+
//
|
83 |
+
|
84 |
+
public function convertDefaultFieldMap($storeId = null)
|
85 |
+
{
|
86 |
+
$result = array();
|
87 |
+
$defaultMapping = $this->getConfigVar('default_field_map', $storeId);
|
88 |
+
|
89 |
+
foreach ($defaultMapping as $field => $config)
|
90 |
+
{
|
91 |
+
$result[] = array(
|
92 |
+
'label' => $config['label'],
|
93 |
+
'attribute' => $config['attribute'],
|
94 |
+
'field' => $field,
|
95 |
+
);
|
96 |
+
}
|
97 |
+
|
98 |
+
return $result;
|
99 |
+
}
|
100 |
+
|
101 |
+
|
102 |
+
//
|
103 |
+
// Checks
|
104 |
+
//
|
105 |
+
|
106 |
+
public function isDirective($code, $storeId = null)
|
107 |
+
{
|
108 |
+
if (is_null($this->_directives))
|
109 |
+
$this->_directives = $this->getConfigVar('directives', $storeId);
|
110 |
+
|
111 |
+
return isset($this->_directives[$code]);
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Returns 1 if the current version is greater than the specified in the
|
116 |
+
* parameter. 0 if is equal. -1 if is lower.
|
117 |
+
*/
|
118 |
+
public function compareMagentoVersion($infoArray)
|
119 |
+
{
|
120 |
+
$v = Mage::getVersionInfo();
|
121 |
+
|
122 |
+
foreach (array('major', 'minor', 'revision', 'patch') as $key)
|
123 |
+
{
|
124 |
+
if ($v[$key] != $infoArray[$key])
|
125 |
+
return $v[$key] > $infoArray[$key] ? 1 : -1;
|
126 |
+
}
|
127 |
+
|
128 |
+
return 0;
|
129 |
+
}
|
130 |
+
|
131 |
+
|
132 |
+
//
|
133 |
+
// Tools for Dropdowns
|
134 |
+
//
|
135 |
+
|
136 |
+
// protected function _loadProductAttributeCodes($storeId = null)
|
137 |
+
// {
|
138 |
+
// if (!is_null($this->_product_attribute_codes))
|
139 |
+
// return;
|
140 |
+
|
141 |
+
// $config = Mage::getModel('eav/config');
|
142 |
+
|
143 |
+
// $this->_product_attribute_codes = array();
|
144 |
+
|
145 |
+
// $excludedAttrs = $this->getMultipleSelectVar('excluded_attributes');
|
146 |
+
// $attributesCodes = $config->getEntityAttributeCodes(
|
147 |
+
// 'catalog_product',
|
148 |
+
// new Varien_Object(array('store_id' => $storeId))
|
149 |
+
// );
|
150 |
+
|
151 |
+
// foreach ($attributesCodes as $attrCode)
|
152 |
+
// {
|
153 |
+
// if (array_search($attrCode, $excludedAttrs) !== false)
|
154 |
+
// continue;
|
155 |
+
|
156 |
+
// $attr = $config->getAttribute('catalog_product', $attrCode);
|
157 |
+
|
158 |
+
// if ($attr !== false && $attr->getAttributeId() > 0)
|
159 |
+
// {
|
160 |
+
// $code = $attr->getAttributeCode();
|
161 |
+
// $this->_product_attribute_codes[$code] = addslashes(
|
162 |
+
// $attr->getFrontend()->getLabel().' ('.$code.')'
|
163 |
+
// );
|
164 |
+
// }
|
165 |
+
// }
|
166 |
+
|
167 |
+
// asort($this->_product_attribute_codes);
|
168 |
+
// }
|
169 |
+
|
170 |
+
protected function _loadProductDirectives($storeId = null)
|
171 |
+
{
|
172 |
+
if (!is_null($this->_product_directives))
|
173 |
+
return;
|
174 |
+
|
175 |
+
$this->_product_directives = array();
|
176 |
+
|
177 |
+
foreach ($this->getConfigVar('directives', $storeId) as $code => $cfg)
|
178 |
+
{
|
179 |
+
$this->_product_directives[$code] = $cfg['label'];
|
180 |
+
}
|
181 |
+
|
182 |
+
asort($this->_product_directives);
|
183 |
+
}
|
184 |
+
|
185 |
+
public function getProductAttributesCodes($storeId = null,
|
186 |
+
$includeDirectives = true)
|
187 |
+
{
|
188 |
+
$this->_loadProductAttributeCodes($storeId);
|
189 |
+
|
190 |
+
if ($includeDirectives === true)
|
191 |
+
{
|
192 |
+
$this->_loadProductDirectives($storeId);
|
193 |
+
|
194 |
+
return array_merge($this->_product_directives,
|
195 |
+
$this->_product_attribute_codes);
|
196 |
+
}
|
197 |
+
|
198 |
+
return $this->_product_attribute_codes;
|
199 |
+
}
|
200 |
+
}
|
app/code/community/Doofinder/Feed/Model/Cron.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Cron extends Mage_Core_Model_Abstract {
|
13 |
+
|
14 |
+
|
15 |
+
protected function _construct() {
|
16 |
+
$this->_init('doofinder_feed/cron');
|
17 |
+
|
18 |
+
}
|
19 |
+
|
20 |
+
public function modeDisabled() {
|
21 |
+
$helper = Mage::helper('doofinder_feed');
|
22 |
+
$this->setStatus($helper::STATUS_DISABLED)
|
23 |
+
->setOffset(0)
|
24 |
+
->setComplete(null)
|
25 |
+
->setNextRun(null)
|
26 |
+
->setNextIteration(null)
|
27 |
+
->setMessage($helper::MSG_DISABLED)
|
28 |
+
->save();
|
29 |
+
}
|
30 |
+
|
31 |
+
public function modeWaiting() {
|
32 |
+
$helper = Mage::helper('doofinder_feed');
|
33 |
+
$this->setStatus($helper::STATUS_WAITING)
|
34 |
+
->setMessage($helper::MSG_WAITING)
|
35 |
+
->save();
|
36 |
+
}
|
37 |
+
|
38 |
+
|
39 |
+
}
|
40 |
+
|
app/code/community/Doofinder/Feed/Model/Generator.php
ADDED
@@ -0,0 +1,837 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Generator model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
if (!defined('DS'))
|
19 |
+
define('DS', DIRECTORY_SEPARATOR);
|
20 |
+
|
21 |
+
class Doofinder_Feed_Model_Generator extends Varien_Object
|
22 |
+
{
|
23 |
+
const DEFAULT_BATCH_SIZE = 100;
|
24 |
+
const PRODUCT_ELEMENT = 'item';
|
25 |
+
const CATEGORY_SEPARATOR = '%%';
|
26 |
+
const CATEGORY_TREE_SEPARATOR = '>';
|
27 |
+
const VALUE_SEPARATOR = '/';
|
28 |
+
|
29 |
+
|
30 |
+
protected $_badChars = array('"',"\r\n","\n","\r","\t", "|");
|
31 |
+
protected $_repChars = array(""," "," "," "," ", "");
|
32 |
+
|
33 |
+
protected $_store;
|
34 |
+
protected $_oRootCategory;
|
35 |
+
|
36 |
+
protected $_maxProductId;
|
37 |
+
|
38 |
+
protected $_attributes = array();
|
39 |
+
protected $_categories = array();
|
40 |
+
protected $_fieldMap;
|
41 |
+
|
42 |
+
protected $_iDumped = 0;
|
43 |
+
protected $_iSkipped = 0;
|
44 |
+
|
45 |
+
protected $_oXmlWriter;
|
46 |
+
|
47 |
+
protected $_response;
|
48 |
+
|
49 |
+
protected $_errors = array();
|
50 |
+
|
51 |
+
protected $_lastProcessedProductId;
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Log to doofinder generator logfile
|
55 |
+
*
|
56 |
+
* @param string $message
|
57 |
+
* @param integer $level
|
58 |
+
*/
|
59 |
+
public function logError($message)
|
60 |
+
{
|
61 |
+
$this->_errors[] = $message;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Log to doofinder generator logfile only once
|
66 |
+
*
|
67 |
+
* @param string $message
|
68 |
+
*/
|
69 |
+
public function logErrorOnce($message)
|
70 |
+
{
|
71 |
+
if (!in_array($message, $this->getErrors())) {
|
72 |
+
$this->logError($message);
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
//
|
77 |
+
// public::Export
|
78 |
+
//
|
79 |
+
|
80 |
+
public function run()
|
81 |
+
{
|
82 |
+
// This must NOT depend on cron being enabled because it's used
|
83 |
+
// by the front controller!!!
|
84 |
+
|
85 |
+
Doofinder_Feed_Model_Map_Product_Configurable::setGrouped($this->getData('grouped'));
|
86 |
+
// Some config
|
87 |
+
$this->_oRootCategory = $this->getRootCategory();
|
88 |
+
|
89 |
+
// Generate Feed
|
90 |
+
$this->_loadAdditionalAttributes();
|
91 |
+
$this->_maxProductId = $this->getMaxProductId();
|
92 |
+
|
93 |
+
// Clear errors
|
94 |
+
$this->_errors = array();
|
95 |
+
|
96 |
+
// Perform run
|
97 |
+
$this->_initFeed();
|
98 |
+
$this->_batchProcessProducts(
|
99 |
+
$this->getData('_offset_'),
|
100 |
+
$this->getData('_limit_')
|
101 |
+
);
|
102 |
+
|
103 |
+
// Only close feed if close empty flag is set to true or there was at least one processed product
|
104 |
+
if ($this->getData('close_empty') || $this->getLastProcessedProductId() != $this->getData('_offset_')) {
|
105 |
+
$this->_closeFeed();
|
106 |
+
}
|
107 |
+
|
108 |
+
return $this->_response;
|
109 |
+
}
|
110 |
+
|
111 |
+
public function getSQL()
|
112 |
+
{
|
113 |
+
return $this->_getProductCollection(
|
114 |
+
$this->getData('_offset_'), $this->getData('_limit_')
|
115 |
+
)->getSelect()->assemble();
|
116 |
+
}
|
117 |
+
|
118 |
+
public function getProductCount()
|
119 |
+
{
|
120 |
+
return $this->_getProductCollection()->getSize();
|
121 |
+
}
|
122 |
+
|
123 |
+
public function getMaxProductId()
|
124 |
+
{
|
125 |
+
$collection = $this->_getProductCollection();
|
126 |
+
$collection->getSelect()->limit(1);
|
127 |
+
$collection->getSelect()->order('e.entity_id DESC');
|
128 |
+
$item = $collection->fetchItem();
|
129 |
+
|
130 |
+
return $item ? $item->getEntityId() : 0;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Is the feed done, are there any products
|
135 |
+
* left to process.
|
136 |
+
*
|
137 |
+
* @return boolean
|
138 |
+
*/
|
139 |
+
public function isFeedDone()
|
140 |
+
{
|
141 |
+
return $this->_lastProcessedProductId >= $this->_maxProductId;
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Get the ID of the last processed product.
|
146 |
+
*
|
147 |
+
* @return integer
|
148 |
+
*/
|
149 |
+
public function getLastProcessedProductId()
|
150 |
+
{
|
151 |
+
return $this->_lastProcessedProductId;
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Get generator progress, it is what part
|
156 |
+
* of products has been processed yet.
|
157 |
+
*
|
158 |
+
* @return double
|
159 |
+
*/
|
160 |
+
public function getProgress()
|
161 |
+
{
|
162 |
+
$collection = $this->_getProductCollection();
|
163 |
+
|
164 |
+
$all = $collection->getSize();
|
165 |
+
|
166 |
+
$collection = $this->_getProductCollection();
|
167 |
+
$collection->addAttributeToFilter('entity_id', array('lteq' => $this->_lastProcessedProductId));
|
168 |
+
$now = $collection->getSize();
|
169 |
+
|
170 |
+
return $now / $all;
|
171 |
+
}
|
172 |
+
|
173 |
+
public function addProductToFeed($args)
|
174 |
+
{
|
175 |
+
try
|
176 |
+
{
|
177 |
+
$row = $args['row'];
|
178 |
+
|
179 |
+
$this->_lastProcessedProductId = $row['entity_id'];
|
180 |
+
|
181 |
+
$parentEntityId = null;
|
182 |
+
|
183 |
+
$map = $this->_getProductMapModel($row['type_id'], array());
|
184 |
+
|
185 |
+
if (is_null($map)) {
|
186 |
+
Mage::throwException("There is no map definition for product with type {$row['type_id']}");
|
187 |
+
}
|
188 |
+
|
189 |
+
|
190 |
+
$product = Mage::getModel('catalog/product');
|
191 |
+
$product->setData($row)
|
192 |
+
->setStoreId($this->getStoreId())
|
193 |
+
->setCustomerGroupId($this->getData('customer_group_id'));
|
194 |
+
|
195 |
+
$product->getResource()->load($product, $row['entity_id']);
|
196 |
+
$map->setGenerator($this)
|
197 |
+
->setProduct($product)
|
198 |
+
->setFieldsMap($this->_getFieldsMap())
|
199 |
+
->initialize();
|
200 |
+
|
201 |
+
if ($map->checkSkipSubmission()->isSkip())
|
202 |
+
return;
|
203 |
+
|
204 |
+
if ($this->_addProductToXml($map))
|
205 |
+
$this->_iDumped++;
|
206 |
+
|
207 |
+
$map->unsetData();
|
208 |
+
}
|
209 |
+
catch (Exception $e)
|
210 |
+
{
|
211 |
+
$this->logError('Error processing product (ID: ' . $row['entity_id'] . '): ' . $e->getMessage(), Zend_Log::ERR);
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
|
216 |
+
//
|
217 |
+
// protected::Export
|
218 |
+
//
|
219 |
+
|
220 |
+
protected function _batchProcessProducts($offset, $limit)
|
221 |
+
{
|
222 |
+
// Make sure we have this initialized
|
223 |
+
// in case of an empty collection
|
224 |
+
$this->_lastProcessedProductId = $offset;
|
225 |
+
|
226 |
+
$collection = $this->_getProductCollection($offset, $limit);
|
227 |
+
|
228 |
+
Mage::getSingleton('core/resource_iterator')->walk(
|
229 |
+
$collection->getSelect(),
|
230 |
+
array(array($this, 'addProductToFeed'))
|
231 |
+
);
|
232 |
+
$this->_flushFeed();
|
233 |
+
|
234 |
+
}
|
235 |
+
|
236 |
+
protected function _addProductToXml(
|
237 |
+
Doofinder_Feed_Model_Map_Product_Abstract $productMap)
|
238 |
+
{
|
239 |
+
|
240 |
+
$iDumped = 0;
|
241 |
+
$displayPrice = $this->getDisplayPrice();
|
242 |
+
|
243 |
+
try
|
244 |
+
{
|
245 |
+
if ($productMap->isSkip())
|
246 |
+
{
|
247 |
+
$this->_iSkipped++;
|
248 |
+
return $this;
|
249 |
+
}
|
250 |
+
|
251 |
+
$productData = $productMap->map();
|
252 |
+
|
253 |
+
if ($productMap->getProduct()->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE
|
254 |
+
&& $productMap->hasAssocMaps()
|
255 |
+
&& $productMap->getIsVariants())
|
256 |
+
{
|
257 |
+
foreach ($productMap->getAssocMaps() as $assocMap)
|
258 |
+
if ($assocMap->isSkip())
|
259 |
+
$this->_iSkipped++;
|
260 |
+
}
|
261 |
+
|
262 |
+
if (($iProducts = count($productData)) > 1)
|
263 |
+
{
|
264 |
+
$productData[0] = array_filter($productData[0]);
|
265 |
+
// $productData[0]['assoc_id'] = $productData[0]['id'];
|
266 |
+
|
267 |
+
for ($i = 1; $i < $iProducts; $i++)
|
268 |
+
{
|
269 |
+
$productData[$i] = array_merge(
|
270 |
+
$productData[0],
|
271 |
+
array_filter($productData[$i])
|
272 |
+
);
|
273 |
+
// $productData[$i]['assoc_id'] = $productData[0]['id'];
|
274 |
+
}
|
275 |
+
}
|
276 |
+
|
277 |
+
foreach ($productData as $data)
|
278 |
+
{
|
279 |
+
$this->_oXmlWriter->startElement(self::PRODUCT_ELEMENT);
|
280 |
+
|
281 |
+
if (!isset($data['description']))
|
282 |
+
{
|
283 |
+
if (isset($data['long_description'])) {
|
284 |
+
$data['description'] = $data['long_description'];
|
285 |
+
unset($data['long_description']);
|
286 |
+
} else {
|
287 |
+
$data['description'] = '';
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
krsort($data);
|
292 |
+
|
293 |
+
foreach ($data as $field => $value)
|
294 |
+
{
|
295 |
+
|
296 |
+
if (!is_array($value))
|
297 |
+
{
|
298 |
+
$value = trim($value);
|
299 |
+
}
|
300 |
+
|
301 |
+
if ($field != 'description' && empty($value))
|
302 |
+
{
|
303 |
+
continue;
|
304 |
+
}
|
305 |
+
|
306 |
+
if (!$displayPrice && ($field === 'price' || $field === 'sale_price'))
|
307 |
+
{
|
308 |
+
continue;
|
309 |
+
}
|
310 |
+
|
311 |
+
$this->_oXmlWriter->startElement($field);
|
312 |
+
|
313 |
+
// Make sure $value is a flat array
|
314 |
+
if (!is_array($value))
|
315 |
+
{
|
316 |
+
$value = array($value);
|
317 |
+
}
|
318 |
+
else if (!$this->_isArrayFlat($value))
|
319 |
+
{
|
320 |
+
$this->logErrorOnce("Value of $field field is a multidimensional array, encoded value: " . json_encode($value));
|
321 |
+
$value = $this->_flattenArray($value);
|
322 |
+
}
|
323 |
+
|
324 |
+
$value = implode(self::VALUE_SEPARATOR, array_filter($value));
|
325 |
+
|
326 |
+
$written = @$this->_oXmlWriter->writeCData($value);
|
327 |
+
if ( ! $written )
|
328 |
+
{
|
329 |
+
$this->_oXmlWriter->writeComment("Cannot write the value for the $field field.");
|
330 |
+
|
331 |
+
$this->logErrorOnce("Cannot write the value for the $field field, encoded value: " . json_encode($value));
|
332 |
+
}
|
333 |
+
|
334 |
+
$this->_oXmlWriter->endElement();
|
335 |
+
}
|
336 |
+
|
337 |
+
$this->_oXmlWriter->endElement();
|
338 |
+
|
339 |
+
$iDumped++;
|
340 |
+
}
|
341 |
+
}
|
342 |
+
catch (Exception $e)
|
343 |
+
{
|
344 |
+
if ($this->getConfigVar('debug') == 1)
|
345 |
+
$this->_debug($e->getMessage());
|
346 |
+
}
|
347 |
+
|
348 |
+
return $iDumped > 0;
|
349 |
+
}
|
350 |
+
|
351 |
+
|
352 |
+
//
|
353 |
+
// public::Configuration
|
354 |
+
//
|
355 |
+
|
356 |
+
public function getContentType()
|
357 |
+
{
|
358 |
+
return self::CONTENT_TYPE;
|
359 |
+
}
|
360 |
+
|
361 |
+
public function getConfig()
|
362 |
+
{
|
363 |
+
return Mage::getSingleton('doofinder_feed/config');
|
364 |
+
}
|
365 |
+
|
366 |
+
public function getConfigVar($key, $storeId = null,
|
367 |
+
$section = Doofinder_Feed_Model_Config::DEFAULT_SECTION)
|
368 |
+
{
|
369 |
+
return $this->getConfig()->getConfigVar($key, $storeId, $section);
|
370 |
+
}
|
371 |
+
|
372 |
+
|
373 |
+
//
|
374 |
+
// public::Tools
|
375 |
+
//
|
376 |
+
|
377 |
+
public function getStore()
|
378 |
+
{
|
379 |
+
if (is_null($this->_store))
|
380 |
+
$this->_loadStore();
|
381 |
+
|
382 |
+
return $this->_store;
|
383 |
+
}
|
384 |
+
|
385 |
+
public function getStoreId()
|
386 |
+
{
|
387 |
+
return $this->getStore()->getStoreId();
|
388 |
+
}
|
389 |
+
|
390 |
+
public function getStoreCode()
|
391 |
+
{
|
392 |
+
return $this->getStore()->getCode();
|
393 |
+
}
|
394 |
+
|
395 |
+
public function getWebsiteId()
|
396 |
+
{
|
397 |
+
return $this->getStore()->getWebsiteId();
|
398 |
+
}
|
399 |
+
|
400 |
+
public function getErrors()
|
401 |
+
{
|
402 |
+
return $this->_errors;
|
403 |
+
}
|
404 |
+
|
405 |
+
public function getRootCategory()
|
406 |
+
{
|
407 |
+
if (is_null($this->_oRootCategory))
|
408 |
+
{
|
409 |
+
$this->_oRootCategory = Mage::getModel('catalog/category')->load(
|
410 |
+
$this->getStore()->getRootCategoryId()
|
411 |
+
);
|
412 |
+
}
|
413 |
+
|
414 |
+
return $this->_oRootCategory;
|
415 |
+
}
|
416 |
+
|
417 |
+
public function getCategories($product)
|
418 |
+
{
|
419 |
+
$categories = array();
|
420 |
+
|
421 |
+
$prodCategories = Mage::getResourceModel('catalog/category_collection')
|
422 |
+
->addIdFilter($product->getCategoryIds())
|
423 |
+
->addFieldToFilter('path', array('like' => $this->_oRootCategory->getPath() . '/%'))
|
424 |
+
->addFieldToFilter('is_active', array('eq'=>'1'));
|
425 |
+
|
426 |
+
$include_in_menu = Mage::getStoreConfig(
|
427 |
+
'doofinder_cron/feed_settings/categories_in_navigation',
|
428 |
+
$this->getStoreId()
|
429 |
+
);
|
430 |
+
|
431 |
+
if($include_in_menu == 1) {
|
432 |
+
$prodCategories->addFieldToFilter('include_in_menu', array('eq'=> '1'));
|
433 |
+
}
|
434 |
+
|
435 |
+
$prodCategories = $prodCategories->getItems();
|
436 |
+
|
437 |
+
$prodCategories = array_keys($prodCategories);
|
438 |
+
|
439 |
+
foreach ($prodCategories as $id)
|
440 |
+
{
|
441 |
+
if (isset($this->_categories[$id]))
|
442 |
+
$tree = $this->_categories[$id];
|
443 |
+
else
|
444 |
+
$tree = $this->_getCategoryTree($id);
|
445 |
+
|
446 |
+
if (strlen($tree))
|
447 |
+
$categories[] = $tree;
|
448 |
+
}
|
449 |
+
|
450 |
+
sort($categories);
|
451 |
+
|
452 |
+
$nbcategories = count($categories);
|
453 |
+
$result = array();
|
454 |
+
|
455 |
+
for ($i = 1; $i < $nbcategories; $i++)
|
456 |
+
{
|
457 |
+
if (strpos($categories[$i], $categories[$i - 1]) === 0)
|
458 |
+
continue;
|
459 |
+
$result[] = $this->_cleanFieldValue($categories[$i - 1]);
|
460 |
+
}
|
461 |
+
|
462 |
+
if (!empty($categories[$i - 1]))
|
463 |
+
$result[] = $this->_cleanFieldValue($categories[$i - 1]);
|
464 |
+
|
465 |
+
return $result;
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* Get all parent category names (including itself) for selected category ID
|
470 |
+
* @param int $catId Category ID
|
471 |
+
* @return string Category names concat'd by CATEGORY_TREE_SEPARATOR
|
472 |
+
*/
|
473 |
+
protected function _getCategoryTree($catId)
|
474 |
+
{
|
475 |
+
$category = Mage::getModel('catalog/category')->load($catId);
|
476 |
+
$tree = array();
|
477 |
+
|
478 |
+
$path = $category->getPath();
|
479 |
+
$ids = explode('/', $path);
|
480 |
+
|
481 |
+
unset($ids[0]);
|
482 |
+
|
483 |
+
$categories = Mage::getModel('catalog/category')
|
484 |
+
->getCollection()
|
485 |
+
->setStoreId($this->getStoreId())
|
486 |
+
->addIdFilter($ids)
|
487 |
+
->addAttributeToSort('path', 'asc')
|
488 |
+
->addAttributeToSelect('*');
|
489 |
+
|
490 |
+
foreach ($categories as $category)
|
491 |
+
{
|
492 |
+
if ($category->getId() != $this->_oRootCategory->getId())
|
493 |
+
{
|
494 |
+
if (strlen($category->getName()))
|
495 |
+
{
|
496 |
+
$tree[] = strip_tags($category->getName());
|
497 |
+
}
|
498 |
+
}
|
499 |
+
}
|
500 |
+
|
501 |
+
$tree = $this->_sanitizeData($tree);
|
502 |
+
$tree = implode(self::CATEGORY_TREE_SEPARATOR, $tree);
|
503 |
+
$this->_categories[$catId] = $tree;
|
504 |
+
|
505 |
+
return $this->_categories[$catId];
|
506 |
+
}
|
507 |
+
|
508 |
+
public function getAttribute($attrCode)
|
509 |
+
{
|
510 |
+
if (isset($this->_attributes[$attrCode]))
|
511 |
+
return $this->_attributes[$attrCode];
|
512 |
+
|
513 |
+
return false;
|
514 |
+
}
|
515 |
+
|
516 |
+
public function getTools()
|
517 |
+
{
|
518 |
+
return Mage::getSingleton('doofinder_feed/tools');
|
519 |
+
}
|
520 |
+
|
521 |
+
|
522 |
+
//
|
523 |
+
// protected::Output
|
524 |
+
//
|
525 |
+
|
526 |
+
protected function _initFeed()
|
527 |
+
{
|
528 |
+
$this->_oXmlWriter = new XMLWriter();
|
529 |
+
$this->_oXmlWriter->openMemory();
|
530 |
+
if (!$this->getData('_offset_'))
|
531 |
+
{
|
532 |
+
$this->_oXmlWriter->startDocument('1.0', 'UTF-8');
|
533 |
+
|
534 |
+
// Output the parent rss tag
|
535 |
+
$this->_oXmlWriter->startElement('rss');
|
536 |
+
$this->_oXmlWriter->writeAttribute('version', '2.0');
|
537 |
+
|
538 |
+
$this->_oXmlWriter->startElement('channel');
|
539 |
+
$this->_oXmlWriter->writeElement('title', 'Product feed');
|
540 |
+
$this->_oXmlWriter->startElement('link');
|
541 |
+
$this->_oXmlWriter->writeCData(Mage::getBaseUrl().'doofinder/feed');
|
542 |
+
$this->_oXmlWriter->endElement();
|
543 |
+
$this->_oXmlWriter->writeElement('pubDate', strftime('%a, %d %b %Y %H:%M:%S %Z'));
|
544 |
+
$this->_oXmlWriter->writeElement('generator', 'Doofinder/'.Mage::getConfig()->getModuleConfig("Doofinder_Feed")->version);
|
545 |
+
$this->_oXmlWriter->writeElement('description', 'Magento Product feed for Doofinder');
|
546 |
+
|
547 |
+
$this->_flushFeed();
|
548 |
+
}
|
549 |
+
}
|
550 |
+
|
551 |
+
protected function _flushFeed()
|
552 |
+
{
|
553 |
+
$this->_response .= $this->_oXmlWriter->flush(true);
|
554 |
+
}
|
555 |
+
|
556 |
+
protected function _closeFeed()
|
557 |
+
{
|
558 |
+
if ($this->isFeedDone())
|
559 |
+
{
|
560 |
+
if (!$this->getData('_offset_'))
|
561 |
+
{
|
562 |
+
$this->_oXmlWriter->endElement(); // Channel
|
563 |
+
$this->_oXmlWriter->endElement(); // RSS
|
564 |
+
$this->_oXmlWriter->endDocument();
|
565 |
+
|
566 |
+
$this->_flushFeed();
|
567 |
+
} else
|
568 |
+
{
|
569 |
+
$this->_response .= '</channel></rss>';
|
570 |
+
}
|
571 |
+
}
|
572 |
+
}
|
573 |
+
|
574 |
+
protected function _debug($m)
|
575 |
+
{
|
576 |
+
// $this->_response .= '<pre>';
|
577 |
+
// var_dump($m);
|
578 |
+
// $this->_response .= '</pre>';
|
579 |
+
}
|
580 |
+
|
581 |
+
protected function _sanitizeData($data)
|
582 |
+
{
|
583 |
+
$sanitized = array();
|
584 |
+
|
585 |
+
foreach ($data as $key => $value)
|
586 |
+
$sanitized[$key] = str_replace($this->_badChars,
|
587 |
+
$this->_repChars,
|
588 |
+
$value);
|
589 |
+
|
590 |
+
return $sanitized;
|
591 |
+
}
|
592 |
+
|
593 |
+
protected function _addProductTypeToFilter($collection)
|
594 |
+
{
|
595 |
+
$disabled = array_diff(
|
596 |
+
array(
|
597 |
+
Mage_Catalog_Model_Product_Type::TYPE_BUNDLE,
|
598 |
+
Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE,
|
599 |
+
Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE,
|
600 |
+
Mage_Catalog_Model_Product_Type::TYPE_GROUPED,
|
601 |
+
Mage_Catalog_Model_Product_Type::TYPE_SIMPLE,
|
602 |
+
Mage_Catalog_Model_Product_Type::TYPE_VIRTUAL,
|
603 |
+
),
|
604 |
+
explode(',', $this->getConfigVar('product_types'))
|
605 |
+
);
|
606 |
+
|
607 |
+
// Check if we should disable specific types
|
608 |
+
if (count($disabled) > 0)
|
609 |
+
$collection->addAttributeToFilter('type_id',
|
610 |
+
array('nin' => $disabled));
|
611 |
+
|
612 |
+
return $collection;
|
613 |
+
}
|
614 |
+
|
615 |
+
|
616 |
+
//
|
617 |
+
// protected::Tools
|
618 |
+
//
|
619 |
+
|
620 |
+
protected function _loadStore()
|
621 |
+
{
|
622 |
+
if (!$this->hasData('store_code'))
|
623 |
+
$this->setData('store_code', Mage_Core_Model_Store::DEFAULT_CODE);
|
624 |
+
|
625 |
+
try
|
626 |
+
{
|
627 |
+
$this->_store = Mage::app()->getStore($this->getData('store_code'));
|
628 |
+
}
|
629 |
+
catch (Exception $e)
|
630 |
+
{
|
631 |
+
$e->setMessage('Invalid Store Code.');
|
632 |
+
$this->_stopOnException($e);
|
633 |
+
}
|
634 |
+
}
|
635 |
+
|
636 |
+
protected function _loadAdditionalAttributes()
|
637 |
+
{
|
638 |
+
$storeId = $this->getStoreId();
|
639 |
+
|
640 |
+
$attributeCodes = $this->getConfig()
|
641 |
+
->getMultipleSelectVar('additional_attributes', $storeId);
|
642 |
+
$model = Mage::getModel('catalog/product')->setStoreId($storeId);
|
643 |
+
|
644 |
+
foreach ($attributeCodes as $attrCode)
|
645 |
+
{
|
646 |
+
$attribute = $model->getResource()->getAttribute($attrCode);
|
647 |
+
$this->_attributes[$attribute->getAttributeCode()] = $attribute;
|
648 |
+
}
|
649 |
+
}
|
650 |
+
|
651 |
+
protected function _getProductCollection($offset = 0, $limit = 0)
|
652 |
+
{
|
653 |
+
$collection = $this->getProductCollection($offset, $limit);
|
654 |
+
|
655 |
+
if (count($this->getProducts()))
|
656 |
+
$collection->addAttributeToFilter('entity_id', array('in' => $this->getProducts()));
|
657 |
+
|
658 |
+
if ($limit && $limit > 0)
|
659 |
+
$collection->getSelect()->limit($limit, 0);
|
660 |
+
|
661 |
+
if ($offset)
|
662 |
+
$collection->addAttributeToFilter('entity_id', array('gt' => $offset));
|
663 |
+
|
664 |
+
|
665 |
+
return $collection;
|
666 |
+
}
|
667 |
+
|
668 |
+
public function getProductCollection()
|
669 |
+
{
|
670 |
+
$collection = Mage::getModel('catalog/product')
|
671 |
+
->getCollection()
|
672 |
+
->addStoreFilter($this->getStoreId());
|
673 |
+
|
674 |
+
$this->_addProductTypeToFilter($collection);
|
675 |
+
|
676 |
+
$collection->addAttributeToFilter('status', 1);
|
677 |
+
$collection->addAttributeToFilter('visibility', array(
|
678 |
+
Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
|
679 |
+
Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH
|
680 |
+
));
|
681 |
+
$collection->addAttributeToSelect('*');
|
682 |
+
|
683 |
+
return $collection;
|
684 |
+
}
|
685 |
+
|
686 |
+
protected function _getProductMapModel($typeId, $args = array())
|
687 |
+
{
|
688 |
+
$isAssoc = isset($args['is_assoc']) && $args['is_assoc'] ? true : false;
|
689 |
+
|
690 |
+
switch ($typeId)
|
691 |
+
{
|
692 |
+
case 'simple':
|
693 |
+
if ($isAssoc)
|
694 |
+
$model = 'doofinder_feed/map_product_associated';
|
695 |
+
else
|
696 |
+
$model = 'doofinder_feed/map_product_simple';
|
697 |
+
break;
|
698 |
+
|
699 |
+
case 'abstract':
|
700 |
+
case 'bundle':
|
701 |
+
case 'configurable':
|
702 |
+
case 'downloadable':
|
703 |
+
case 'grouped':
|
704 |
+
case 'virtual':
|
705 |
+
$model = 'doofinder_feed/map_product_'.$typeId;
|
706 |
+
break;
|
707 |
+
|
708 |
+
default:
|
709 |
+
return null;
|
710 |
+
}
|
711 |
+
|
712 |
+
return Mage::getModel($model, array(
|
713 |
+
'store_code' => $this->getStoreCode(),
|
714 |
+
'store_id' => $this->getStoreId(),
|
715 |
+
'website_id' => $this->getWebsiteId(),
|
716 |
+
));
|
717 |
+
}
|
718 |
+
|
719 |
+
protected function _getFieldsMap()
|
720 |
+
{
|
721 |
+
if (!is_null($this->_fieldMap))
|
722 |
+
return $this->_fieldMap;
|
723 |
+
|
724 |
+
$product = Mage::getModel('catalog/product')
|
725 |
+
->setStoreId($this->getStoreId());
|
726 |
+
|
727 |
+
|
728 |
+
$this->_fieldMap = array();
|
729 |
+
|
730 |
+
$fields = $this->getConfigVar('fields');
|
731 |
+
|
732 |
+
$map = Mage::getStoreConfig('doofinder_cron/attributes_mapping', $this->getStore());
|
733 |
+
$additional = array();
|
734 |
+
if (isset($map['additional'])) {
|
735 |
+
$additional = unserialize($map['additional']);
|
736 |
+
}
|
737 |
+
|
738 |
+
unset($map['additional']);
|
739 |
+
|
740 |
+
if (!empty($additional['additional_mapping']))
|
741 |
+
{
|
742 |
+
foreach ($additional['additional_mapping'] as $data)
|
743 |
+
{
|
744 |
+
if (isset($map[$data['field']])) continue;
|
745 |
+
|
746 |
+
$fields[$data['field']] = array('label' => $data['label']);
|
747 |
+
$map[$data['field']] = $data['attribute'];
|
748 |
+
}
|
749 |
+
}
|
750 |
+
|
751 |
+
foreach ($map as $key => $attName)
|
752 |
+
{
|
753 |
+
if (!isset($fields[$key])) continue;
|
754 |
+
|
755 |
+
if (!$this->getConfig()->isDirective($attName,
|
756 |
+
$this->getStoreId()))
|
757 |
+
{
|
758 |
+
$att = $product->getResource()->getAttribute($attName);
|
759 |
+
|
760 |
+
if ($att === false)
|
761 |
+
{
|
762 |
+
continue;
|
763 |
+
}
|
764 |
+
|
765 |
+
$att->setStoreId($this->getStoreId());
|
766 |
+
$this->_attributes[$att->getAttributeCode()] = $att;
|
767 |
+
}
|
768 |
+
|
769 |
+
$this->_fieldMap[$key] = array(
|
770 |
+
'label' => $fields[$key]['label'],
|
771 |
+
'attribute' => $attName,
|
772 |
+
'field' => $key,
|
773 |
+
);
|
774 |
+
}
|
775 |
+
return $this->_fieldMap;
|
776 |
+
}
|
777 |
+
|
778 |
+
protected function _stopOnException(Exception $e)
|
779 |
+
{
|
780 |
+
Mage::logError($e->getMessage());
|
781 |
+
}
|
782 |
+
|
783 |
+
protected function _cleanFieldValue($field)
|
784 |
+
{
|
785 |
+
// http://stackoverflow.com/questions/4224141/php-removing-invalid-utf-8-characters-in-xml-using-filter
|
786 |
+
$valid_utf8 = '/([\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|./x';
|
787 |
+
|
788 |
+
$field = preg_replace('#<br(\s?/)?>#i', ' ', $field);
|
789 |
+
$field = strip_tags($field);
|
790 |
+
$field = preg_replace('/[ ]{2,}/', ' ', $field);
|
791 |
+
$field = trim($field);
|
792 |
+
$exField = explode(self::CATEGORY_TREE_SEPARATOR, $field);
|
793 |
+
$newField = array();
|
794 |
+
foreach ($exField as $el) {
|
795 |
+
$newField[] = html_entity_decode($el, null, 'UTF-8');
|
796 |
+
}
|
797 |
+
$field = implode(self::CATEGORY_TREE_SEPARATOR, $newField );
|
798 |
+
|
799 |
+
return preg_replace($valid_utf8, '$1', $field);
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* Check if array is flat (not multidimensional)
|
804 |
+
*
|
805 |
+
* @param array $arr
|
806 |
+
* @return boolean
|
807 |
+
*/
|
808 |
+
protected function _isArrayFlat(array $arr)
|
809 |
+
{
|
810 |
+
$isFlat = true;
|
811 |
+
|
812 |
+
foreach ($arr as $item)
|
813 |
+
{
|
814 |
+
if (is_array($item))
|
815 |
+
{
|
816 |
+
$isFlat = false;
|
817 |
+
break;
|
818 |
+
}
|
819 |
+
}
|
820 |
+
|
821 |
+
return $isFlat;
|
822 |
+
}
|
823 |
+
|
824 |
+
/**
|
825 |
+
* Flatten array recursively
|
826 |
+
*
|
827 |
+
* @notice This requires PHP5.3+
|
828 |
+
*
|
829 |
+
* @param array @arr
|
830 |
+
* @return array
|
831 |
+
*/
|
832 |
+
protected function _flattenArray(array $arr) {
|
833 |
+
$flattenedArray = array();
|
834 |
+
array_walk_recursive($arr, function($item) use (&$flattenedArray) { $flattenedArray[] = $item; });
|
835 |
+
return $flattenedArray;
|
836 |
+
}
|
837 |
+
}
|
app/code/community/Doofinder/Feed/Model/Log.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Log extends Mage_Core_Model_Abstract {
|
13 |
+
|
14 |
+
protected function _construct() {
|
15 |
+
$this->_init('doofinder_feed/log');
|
16 |
+
}
|
17 |
+
|
18 |
+
}
|
19 |
+
|
app/code/community/Doofinder/Feed/Model/Map/Product/Abstract.php
ADDED
@@ -0,0 +1,645 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Abstract Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Abstract extends Varien_Object
|
19 |
+
{
|
20 |
+
protected $_field_map = null;
|
21 |
+
protected $skip = false;
|
22 |
+
protected $_attributeSetModel;
|
23 |
+
|
24 |
+
|
25 |
+
public function initialize()
|
26 |
+
{
|
27 |
+
$currency_code = Mage::app()
|
28 |
+
->getStore($this->getData('store_code'))
|
29 |
+
->getCurrentCurrencyCode();
|
30 |
+
|
31 |
+
$images_url_prefix = Mage::app()
|
32 |
+
->getStore($this->getData('store_id'))
|
33 |
+
->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA, false);
|
34 |
+
$images_url_prefix .= 'catalog/product';
|
35 |
+
|
36 |
+
$images_path_prefix = Mage::getSingleton('catalog/product_media_config')
|
37 |
+
->getBaseMediaPath();
|
38 |
+
|
39 |
+
$this->setData('store_currency_code', $currency_code);
|
40 |
+
$this->setData('images_url_prefix', $images_url_prefix);
|
41 |
+
$this->setData('images_path_prefix', $images_path_prefix);
|
42 |
+
|
43 |
+
$this->_attributeSetModel = Mage::getModel('eav/entity_attribute_set');
|
44 |
+
|
45 |
+
return $this;
|
46 |
+
}
|
47 |
+
|
48 |
+
public function map()
|
49 |
+
{
|
50 |
+
$this->_beforeMap();
|
51 |
+
$rows = $this->_map();
|
52 |
+
$this->_afterMap($rows);
|
53 |
+
|
54 |
+
return $rows;
|
55 |
+
}
|
56 |
+
|
57 |
+
public function _beforeMap()
|
58 |
+
{
|
59 |
+
return $this;
|
60 |
+
}
|
61 |
+
|
62 |
+
public function _afterMap($rows)
|
63 |
+
{
|
64 |
+
return $this;
|
65 |
+
}
|
66 |
+
|
67 |
+
|
68 |
+
//
|
69 |
+
// protected::Mapping
|
70 |
+
//
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @return array('column' => 'value')
|
74 |
+
*/
|
75 |
+
protected function _map()
|
76 |
+
{
|
77 |
+
$fields = array();
|
78 |
+
|
79 |
+
foreach ($this->_field_map as $column => $arr)
|
80 |
+
$fields[$column] = $this->mapField($column);
|
81 |
+
|
82 |
+
// $fields['magento_store'] = $this->getData('store_code');
|
83 |
+
|
84 |
+
$this->_attributeSetModel->load(
|
85 |
+
$this->getProduct()->getAttributeSetId());
|
86 |
+
// $fields['attribute_set'] = $this->_attributeSetModel
|
87 |
+
// ->getAttributeSetName();
|
88 |
+
|
89 |
+
$i = 0;
|
90 |
+
$categories = $this->getGenerator()->getCategories($this->getProduct());
|
91 |
+
$fields['categories'] = implode(
|
92 |
+
Doofinder_Feed_Model_Generator::CATEGORY_SEPARATOR,
|
93 |
+
$categories
|
94 |
+
);
|
95 |
+
|
96 |
+
return array($fields);
|
97 |
+
}
|
98 |
+
|
99 |
+
protected function mapField($column)
|
100 |
+
{
|
101 |
+
$value = "";
|
102 |
+
|
103 |
+
if (!isset($this->_field_map[$column]))
|
104 |
+
return $value;
|
105 |
+
|
106 |
+
$args = array('map' => $this->_field_map[$column]);
|
107 |
+
$method = 'mapField' . $this->_camelize($column);
|
108 |
+
|
109 |
+
if (method_exists($this, $method))
|
110 |
+
$value = $this->$method($args);
|
111 |
+
else
|
112 |
+
$value = $this->getFieldValue($args);
|
113 |
+
|
114 |
+
return $value;
|
115 |
+
}
|
116 |
+
|
117 |
+
protected function mapAttribute($params = array())
|
118 |
+
{
|
119 |
+
$map = $params['map'];
|
120 |
+
$product = $this->getProduct();
|
121 |
+
$fieldData = '';
|
122 |
+
|
123 |
+
$attribute = $this->getGenerator()->getAttribute($map['attribute']);
|
124 |
+
if ($attribute === false)
|
125 |
+
$this->_attributeDoesNotExist($map['attribute']);
|
126 |
+
|
127 |
+
$fieldData = $this->getAttributeValue($product, $attribute);
|
128 |
+
|
129 |
+
return $this->cleanField($fieldData);
|
130 |
+
}
|
131 |
+
|
132 |
+
protected function mapDoofinderAttribute($attribute, $product = null)
|
133 |
+
{
|
134 |
+
if (is_null($product))
|
135 |
+
$product = $this->getProduct();
|
136 |
+
|
137 |
+
if ($attribute === false)
|
138 |
+
$this->_attributeDoesNotExist($map['attribute']);
|
139 |
+
|
140 |
+
$fieldData = $this->getAttributeValue($product, $attribute);
|
141 |
+
|
142 |
+
return $this->cleanField($fieldData);
|
143 |
+
}
|
144 |
+
|
145 |
+
|
146 |
+
//
|
147 |
+
// protected::Mapping::Attributes
|
148 |
+
//
|
149 |
+
|
150 |
+
protected function mapAttributeDescription($params = array())
|
151 |
+
{
|
152 |
+
$map = $params['map'];
|
153 |
+
$product = $this->getProduct();
|
154 |
+
$fieldData = "";
|
155 |
+
|
156 |
+
$attribute = $this->getGenerator()
|
157 |
+
->getAttribute($map['attribute']);
|
158 |
+
|
159 |
+
if ($attribute === false)
|
160 |
+
$this->_attributeDoesNotExist($map['attribute']);
|
161 |
+
|
162 |
+
$description = $this->getAttributeValue($product, $attribute);
|
163 |
+
|
164 |
+
return $this->cleanField($description);
|
165 |
+
}
|
166 |
+
|
167 |
+
|
168 |
+
//
|
169 |
+
// protected::Mapping::Directives
|
170 |
+
//
|
171 |
+
|
172 |
+
protected function mapDirectiveId()
|
173 |
+
{
|
174 |
+
// $storeCode = $this->getStoreCode();
|
175 |
+
$fieldData = $this->getProduct()->getId();
|
176 |
+
// $fieldData .= '_'.preg_replace('/[^a-zA-Z0-9]/', '', $storeCode);
|
177 |
+
|
178 |
+
return $this->cleanField($fieldData);
|
179 |
+
}
|
180 |
+
|
181 |
+
protected function mapDirectiveUrl()
|
182 |
+
{
|
183 |
+
$product = $this->getProduct();
|
184 |
+
return $product->getUrlModel()->getUrl($product, array('_nosid' => true));
|
185 |
+
}
|
186 |
+
|
187 |
+
protected function mapDirectiveImageLink($args, $attributeName = 'image')
|
188 |
+
{
|
189 |
+
$product = $this->getProduct();
|
190 |
+
$image = $product->getData('image');
|
191 |
+
|
192 |
+
if ($image != 'no_selection' && $image != "") {
|
193 |
+
$image = Mage::helper('catalog/image')
|
194 |
+
->init($product, $attributeName);
|
195 |
+
|
196 |
+
if ($size = $this->getGenerator()->getData('image_size')) {
|
197 |
+
$image->resize($size);
|
198 |
+
}
|
199 |
+
|
200 |
+
return (string) $image;
|
201 |
+
}
|
202 |
+
|
203 |
+
return "";
|
204 |
+
}
|
205 |
+
|
206 |
+
protected function mapDirectiveImageLinkThumbnail($args)
|
207 |
+
{
|
208 |
+
return $this->mapDirectiveImageLink($args, 'thumbnail');
|
209 |
+
}
|
210 |
+
|
211 |
+
protected function mapDirectiveImageLinkSmall($args)
|
212 |
+
{
|
213 |
+
return $this->mapDirectiveImageLink($args, 'small_image');
|
214 |
+
}
|
215 |
+
|
216 |
+
public function collectProductPrices()
|
217 |
+
{
|
218 |
+
if ( ! $this->getData('collected_product_prices') )
|
219 |
+
{
|
220 |
+
$dataHelper = Mage::helper('doofinder_feed');
|
221 |
+
$taxHelper = Mage::helper('tax');
|
222 |
+
|
223 |
+
$datum = $dataHelper->collectProductPrices(
|
224 |
+
$this->getProduct(),
|
225 |
+
$this->getGenerator()->getStore(),
|
226 |
+
true,
|
227 |
+
$this->getGenerator()->getData('minimal_price'),
|
228 |
+
$this->getGenerator()->getData('grouped')
|
229 |
+
);
|
230 |
+
|
231 |
+
$priceDisplayType = $taxHelper->getPriceDisplayType($this->getGenerator()->getStore());
|
232 |
+
|
233 |
+
if ( $priceDisplayType == Mage_Tax_Model_Config::DISPLAY_TYPE_INCLUDING_TAX
|
234 |
+
|| $priceDisplayType == Mage_Tax_Model_Config::DISPLAY_TYPE_BOTH )
|
235 |
+
{
|
236 |
+
$priceKey = 'including_tax';
|
237 |
+
}
|
238 |
+
else
|
239 |
+
{
|
240 |
+
$priceKey = 'excluding_tax';
|
241 |
+
}
|
242 |
+
|
243 |
+
$priceType = isset($datum['price_type']) ? $datum['price_type'] : false;
|
244 |
+
|
245 |
+
$prices = array(
|
246 |
+
'price_type' => $priceType,
|
247 |
+
);
|
248 |
+
|
249 |
+
foreach ( $datum as $priceType => $data ) {
|
250 |
+
if ( !is_array($data) ) continue;
|
251 |
+
|
252 |
+
foreach ( $data as $key => $price ) {
|
253 |
+
if ( $key == $priceKey ) {
|
254 |
+
$prices[$priceType] = $data[$key];
|
255 |
+
}
|
256 |
+
}
|
257 |
+
}
|
258 |
+
|
259 |
+
$this->setData('collected_product_prices', $prices);
|
260 |
+
}
|
261 |
+
|
262 |
+
return $this->getData('collected_product_prices');
|
263 |
+
}
|
264 |
+
|
265 |
+
protected function mapDirectivePrice()
|
266 |
+
{
|
267 |
+
$prices = $this->collectProductPrices();
|
268 |
+
|
269 |
+
if ( ! array_key_exists('price', $prices) )
|
270 |
+
return null;
|
271 |
+
|
272 |
+
$fieldData = $this->cleanField($prices['price']);
|
273 |
+
|
274 |
+
if ( $fieldData < 0 )
|
275 |
+
$this->skip = true;
|
276 |
+
|
277 |
+
return $fieldData;
|
278 |
+
}
|
279 |
+
|
280 |
+
protected function mapDirectiveSalePrice()
|
281 |
+
{
|
282 |
+
$prices = $this->collectProductPrices();
|
283 |
+
|
284 |
+
if ( ! array_key_exists('sale_price', $prices) )
|
285 |
+
return null;
|
286 |
+
|
287 |
+
$fieldData = $this->cleanField($prices['sale_price']);
|
288 |
+
|
289 |
+
if ( $fieldData <= 0 )
|
290 |
+
return null;
|
291 |
+
|
292 |
+
return $fieldData;
|
293 |
+
}
|
294 |
+
|
295 |
+
protected function mapDirectiveCurrency()
|
296 |
+
{
|
297 |
+
return $this->getData('store_currency_code');
|
298 |
+
}
|
299 |
+
|
300 |
+
protected function mapDirectiveAvailability($params = array())
|
301 |
+
{
|
302 |
+
$map = $params['map'];
|
303 |
+
$product = $this->getProduct();
|
304 |
+
|
305 |
+
$defaultVal = isset($map['default_value']) ? $map['default_value'] : "";
|
306 |
+
|
307 |
+
if ($defaultVal != "")
|
308 |
+
{
|
309 |
+
$stock_status = $defaultVal;
|
310 |
+
$stock_status = trim(strtolower($stock_status));
|
311 |
+
|
312 |
+
if (false === array_search($stock_status,
|
313 |
+
$this->getConfig()->getAllowedStockStatuses()))
|
314 |
+
$stock_status = $this->getConfig()->getOutOfStockStatus();
|
315 |
+
|
316 |
+
$fieldData = $stock_status;
|
317 |
+
$fieldData = $this->cleanField($fieldData);
|
318 |
+
|
319 |
+
return $fieldData;
|
320 |
+
}
|
321 |
+
|
322 |
+
$fieldData = $this->getConfig()->getOutOfStockStatus();
|
323 |
+
|
324 |
+
$stockItem = Mage::getModel('cataloginventory/stock_item');
|
325 |
+
$stockItem->setStoreId($this->getStoreId());
|
326 |
+
$stockItem->getResource()->loadByProductId($stockItem, $product->getId());
|
327 |
+
$stockItem->setOrigData();
|
328 |
+
|
329 |
+
if ($stockItem->getId() && $stockItem->getIsInStock())
|
330 |
+
$fieldData = $this->getConfig()->getInStockStatus();
|
331 |
+
|
332 |
+
return $fieldData;
|
333 |
+
}
|
334 |
+
|
335 |
+
protected function mapDirectiveCondition($params = array())
|
336 |
+
{
|
337 |
+
$map = $params['map'];
|
338 |
+
$product = $this->getProduct();
|
339 |
+
|
340 |
+
$defaultVal = isset($map['default_value']) ? $map['default_value'] : "";
|
341 |
+
$defaultVal = trim(strtolower($defaultVal));
|
342 |
+
|
343 |
+
if (false === array_search($defaultVal,
|
344 |
+
$this->getConfig()->getAllowedConditions()))
|
345 |
+
$defaultVal = $this->getConfig()->getConditionNew();
|
346 |
+
|
347 |
+
$fieldData = $defaultVal;
|
348 |
+
$fieldData = $this->cleanField($fieldData);
|
349 |
+
|
350 |
+
return $fieldData;
|
351 |
+
}
|
352 |
+
|
353 |
+
|
354 |
+
//
|
355 |
+
// Mapping::Fields
|
356 |
+
//
|
357 |
+
|
358 |
+
public function mapFieldProductType($params = array())
|
359 |
+
{
|
360 |
+
$args = array('map' => $params['map']);
|
361 |
+
$value = "";
|
362 |
+
|
363 |
+
$map_by_category = $this->getConfig()->getMapCategorySorted(
|
364 |
+
'product_type_by_category',
|
365 |
+
$this->getStoreId()
|
366 |
+
);
|
367 |
+
|
368 |
+
$category_ids = $this->getProduct()->getCategoryIds();
|
369 |
+
|
370 |
+
if (!empty($category_ids) && count($map_by_category) > 0)
|
371 |
+
{
|
372 |
+
foreach ($map_by_category as $arr)
|
373 |
+
{
|
374 |
+
if (array_search($arr['category'], $category_ids) !== false)
|
375 |
+
{
|
376 |
+
$value = $arr['value'];
|
377 |
+
break;
|
378 |
+
}
|
379 |
+
}
|
380 |
+
}
|
381 |
+
|
382 |
+
if ($value != "")
|
383 |
+
return htmlspecialchars_decode($value);
|
384 |
+
|
385 |
+
$value = $this->getFieldValue($args);
|
386 |
+
|
387 |
+
return htmlspecialchars_decode($value);
|
388 |
+
}
|
389 |
+
|
390 |
+
|
391 |
+
//
|
392 |
+
// public::Tools
|
393 |
+
//
|
394 |
+
|
395 |
+
public function getFieldValue($args = array())
|
396 |
+
{
|
397 |
+
$value = "";
|
398 |
+
$attName = $args['map']['attribute'];
|
399 |
+
|
400 |
+
if ($this->getConfig()->isDirective($attName, $this->getStoreId()))
|
401 |
+
{
|
402 |
+
$attName = str_replace('df_directive_', '', $attName);
|
403 |
+
$method = 'mapDirective' . $this->_camelize($attName);
|
404 |
+
|
405 |
+
if (method_exists($this, $method))
|
406 |
+
$value = $this->$method($args);
|
407 |
+
}
|
408 |
+
else
|
409 |
+
{
|
410 |
+
$method = 'mapAttribute' . $this->_camelize($attName);
|
411 |
+
|
412 |
+
if (method_exists($this, $method))
|
413 |
+
$value = $this->$method($args);
|
414 |
+
else
|
415 |
+
$value = $this->mapAttribute($args);
|
416 |
+
}
|
417 |
+
|
418 |
+
return $value;
|
419 |
+
}
|
420 |
+
|
421 |
+
public function getAttributeValue($product, $attribute)
|
422 |
+
{
|
423 |
+
$attrCode = $attribute->getAttributeCode();
|
424 |
+
|
425 |
+
if ($attribute->getFrontendInput() == 'select'
|
426 |
+
|| $attribute->getFrontendInput() == 'multiselect')
|
427 |
+
{
|
428 |
+
if (!is_null($product->getResource()->getAttribute($attrCode)))
|
429 |
+
$value = $product->getAttributeText($attrCode);
|
430 |
+
}
|
431 |
+
else
|
432 |
+
{
|
433 |
+
$value = $product->getData($attrCode);
|
434 |
+
}
|
435 |
+
|
436 |
+
return $value;
|
437 |
+
}
|
438 |
+
|
439 |
+
public function loadAssocIds($product, $storeId)
|
440 |
+
{
|
441 |
+
$assocIds = array();
|
442 |
+
|
443 |
+
if ($product->getTypeId() != Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE)
|
444 |
+
return false;
|
445 |
+
|
446 |
+
$as = $this->getTools()->getChildsIds($product->getId());
|
447 |
+
if ($as === false)
|
448 |
+
return $assocIds;
|
449 |
+
|
450 |
+
$as = $this->getTools()->getProductInStoresIds($as);
|
451 |
+
foreach ($as as $assocId => $s)
|
452 |
+
{
|
453 |
+
$attr = $this->getGenerator()->getAttribute('status');
|
454 |
+
$status = $this->getTools()->getProductAttributeValueBySql(
|
455 |
+
$attr,
|
456 |
+
$attr->getBackendType(),
|
457 |
+
$assocId,
|
458 |
+
$storeId
|
459 |
+
);
|
460 |
+
|
461 |
+
if ($status != Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
|
462 |
+
continue;
|
463 |
+
|
464 |
+
if (is_array($s) && array_search($storeId, $s) !== false)
|
465 |
+
$assocIds[] = $assocId;
|
466 |
+
}
|
467 |
+
|
468 |
+
return $assocIds;
|
469 |
+
}
|
470 |
+
|
471 |
+
public function getPrice()
|
472 |
+
{
|
473 |
+
return $this->getProduct()->getPrice();
|
474 |
+
}
|
475 |
+
|
476 |
+
public function calcMinimalPrice($product)
|
477 |
+
{
|
478 |
+
return $product->getMinimalPrice();
|
479 |
+
}
|
480 |
+
|
481 |
+
public function getSpecialPrice()
|
482 |
+
{
|
483 |
+
return $this->getProduct()->getSpecialPrice();
|
484 |
+
}
|
485 |
+
|
486 |
+
public function hasSpecialPrice()
|
487 |
+
{
|
488 |
+
$has = false;
|
489 |
+
$product = $this->getProduct();
|
490 |
+
|
491 |
+
if ($this->getSpecialPrice() <= 0)
|
492 |
+
return $has;
|
493 |
+
if (is_empty_date($product->getSpecialFromDate()))
|
494 |
+
return $has;
|
495 |
+
|
496 |
+
$cDate = Mage::app()->getLocale()->date(null, null, Mage::app()->getLocale()->getDefaultLocale());
|
497 |
+
$timezone = Mage::app()->getStore($this->getStoreId())->getConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_TIMEZONE);
|
498 |
+
|
499 |
+
$fromDate = new Zend_Date(null, null, Mage::app()->getLocale()->getDefaultLocale());
|
500 |
+
if ($timezone) $fromDate->setTimezone($timezone);
|
501 |
+
$fromDate->setDate(substr($product->getSpecialFromDate(), 0, 10), 'yyyy-MM-dd');
|
502 |
+
$fromDate->setTime(substr($product->getSpecialFromDate(), 11, 8), 'HH:mm:ss');
|
503 |
+
|
504 |
+
$toDate = new Zend_Date(null, null, Mage::app()->getLocale()->getDefaultLocale());
|
505 |
+
if (!is_empty_date($product->getSpecialToDate())) {
|
506 |
+
if ($timezone) $toDate->setTimezone($timezone);
|
507 |
+
$toDate->setDate(substr($product->getSpecialToDate(), 0, 10), 'yyyy-MM-dd');
|
508 |
+
$toDate->setTime('23:59:59', 'HH:mm:ss');
|
509 |
+
} else {
|
510 |
+
if ($timezone) $toDate->setTimezone($timezone);
|
511 |
+
$toDate->setDate($cDate->toString('yyyy-MM-dd'), 'yyyy-MM-dd');
|
512 |
+
$toDate->setTime('23:59:59', 'HH:mm:ss');
|
513 |
+
$toDate->add(7, Zend_Date::DAY);
|
514 |
+
}
|
515 |
+
|
516 |
+
if (($fromDate->compare($cDate) == -1
|
517 |
+
|| $fromDate->compare($cDate) == 0)
|
518 |
+
&& ($toDate->compare($cDate) == 1
|
519 |
+
|| $toDate->compare($cDate) == 0))
|
520 |
+
{
|
521 |
+
$has = true;
|
522 |
+
}
|
523 |
+
|
524 |
+
return $has;
|
525 |
+
}
|
526 |
+
|
527 |
+
|
528 |
+
//
|
529 |
+
// protected::Tools
|
530 |
+
//
|
531 |
+
|
532 |
+
// protected function hasImage($product)
|
533 |
+
// {
|
534 |
+
// $image = $product->getData('image');
|
535 |
+
// $validator = new Zend_Validate_File_Exists;
|
536 |
+
|
537 |
+
// if ($image != 'no_selection' && $image != "")
|
538 |
+
// {
|
539 |
+
// // if ($validator->isValid($this->getData('images_path_prefix') . $image) != 'fileExistsDoesNotExist')
|
540 |
+
// // return false;
|
541 |
+
// if (!is_file($this->getData('images_path_prefix') . $image))
|
542 |
+
// return false;
|
543 |
+
// }
|
544 |
+
// else
|
545 |
+
// {
|
546 |
+
// return false;
|
547 |
+
// }
|
548 |
+
|
549 |
+
// return true;
|
550 |
+
// }
|
551 |
+
|
552 |
+
protected function cleanField($field)
|
553 |
+
{
|
554 |
+
if (is_array($field))
|
555 |
+
{
|
556 |
+
foreach ($field as &$value)
|
557 |
+
{
|
558 |
+
$value = $this->cleanFieldValue($value);
|
559 |
+
unset($value);
|
560 |
+
}
|
561 |
+
}
|
562 |
+
else
|
563 |
+
{
|
564 |
+
$field = $this->cleanFieldValue($field);
|
565 |
+
}
|
566 |
+
|
567 |
+
return $field;
|
568 |
+
}
|
569 |
+
|
570 |
+
/**
|
571 |
+
* Cleans invalid utf8 characters, strips tags and trims
|
572 |
+
*
|
573 |
+
* @param string|array $field
|
574 |
+
*/
|
575 |
+
protected function cleanFieldValue($field)
|
576 |
+
{
|
577 |
+
// Do nothing if field is empty
|
578 |
+
if (!$field) return $field;
|
579 |
+
|
580 |
+
$cleaned = $this->cleanFieldValueArray((array) $field);
|
581 |
+
return is_array($field) ? $cleaned : $cleaned[0];
|
582 |
+
}
|
583 |
+
|
584 |
+
protected function cleanFieldValueArray($fields)
|
585 |
+
{
|
586 |
+
return array_map(array($this, '_cleanFieldValue'), $fields);
|
587 |
+
}
|
588 |
+
|
589 |
+
protected function _cleanFieldValue($field)
|
590 |
+
{
|
591 |
+
// http://stackoverflow.com/questions/4224141/php-removing-invalid-utf-8-characters-in-xml-using-filter
|
592 |
+
$valid_utf8 = '/([\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|./x';
|
593 |
+
|
594 |
+
$field = preg_replace('#<br(\s?/)?>#i', ' ', $field);
|
595 |
+
$field = strip_tags($field);
|
596 |
+
$field = preg_replace('/[ ]{2,}/', ' ', $field);
|
597 |
+
$field = trim($field);
|
598 |
+
$field = html_entity_decode($field, null, 'UTF-8');
|
599 |
+
|
600 |
+
return preg_replace($valid_utf8, '$1', $field);
|
601 |
+
}
|
602 |
+
|
603 |
+
protected function _attributeDoesNotExist($attName)
|
604 |
+
{
|
605 |
+
Mage::throwException($attName . ' attribute does not exist!');
|
606 |
+
}
|
607 |
+
|
608 |
+
|
609 |
+
//
|
610 |
+
// public::Config
|
611 |
+
//
|
612 |
+
|
613 |
+
public function getConfig()
|
614 |
+
{
|
615 |
+
return $this->getGenerator()->getConfig();
|
616 |
+
}
|
617 |
+
|
618 |
+
public function getConfigVar($key,
|
619 |
+
$section = Doofinder_Feed_Model_Config::DEFAULT_SECTION)
|
620 |
+
{
|
621 |
+
return $this->getGenerator()->getConfigVar($key, null, $section);
|
622 |
+
}
|
623 |
+
|
624 |
+
public function getTools()
|
625 |
+
{
|
626 |
+
return $this->getGenerator()->getTools();
|
627 |
+
}
|
628 |
+
|
629 |
+
public function isSkip()
|
630 |
+
{
|
631 |
+
return $this->skip;
|
632 |
+
}
|
633 |
+
|
634 |
+
public function checkSkipSubmission()
|
635 |
+
{
|
636 |
+
return $this;
|
637 |
+
}
|
638 |
+
|
639 |
+
public function setFieldsMap($arr)
|
640 |
+
{
|
641 |
+
$this->_field_map = $arr;
|
642 |
+
|
643 |
+
return $this;
|
644 |
+
}
|
645 |
+
}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Associated.php
ADDED
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Associated Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Associated
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{
|
21 |
+
protected function mapField($column)
|
22 |
+
{
|
23 |
+
$value = parent::mapField($column);
|
24 |
+
|
25 |
+
if ($value == "")
|
26 |
+
$value = $this->getParentMap()->mapField($column);
|
27 |
+
|
28 |
+
return $value;
|
29 |
+
}
|
30 |
+
|
31 |
+
public function mapFieldDescription($params = array())
|
32 |
+
{
|
33 |
+
$value = $this->getCellValue(array('map' => $params['map']));
|
34 |
+
|
35 |
+
if ($value == "")
|
36 |
+
$value = $this->getParentMap()->mapField('description');
|
37 |
+
|
38 |
+
return $value;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function mapFieldLink($params = array())
|
42 |
+
{
|
43 |
+
$product = $this->getProduct();
|
44 |
+
|
45 |
+
if ($product->isVisibleInSiteVisibility())
|
46 |
+
{
|
47 |
+
$value = $this->getCellValue(array('map' => $params['map']));
|
48 |
+
}
|
49 |
+
else
|
50 |
+
{
|
51 |
+
$value = $this->getParentMap()->mapField('link');
|
52 |
+
|
53 |
+
if ($this->getConfigVar('associated_products_link_add_unique', 'columns'))
|
54 |
+
$value = $this->addUrlUniqueParams(
|
55 |
+
$value,
|
56 |
+
$product,
|
57 |
+
$this->getParentMap()->getConfigurableAttributeCodes()
|
58 |
+
);
|
59 |
+
}
|
60 |
+
|
61 |
+
return $value;
|
62 |
+
}
|
63 |
+
|
64 |
+
protected function addUrlUniqueParams($value, $product, $codes)
|
65 |
+
{
|
66 |
+
$params = array();
|
67 |
+
|
68 |
+
foreach ($codes as $attrCode)
|
69 |
+
{
|
70 |
+
$data = $product->getData($attrCode);
|
71 |
+
|
72 |
+
if (empty($data))
|
73 |
+
{
|
74 |
+
$this->skip = true;
|
75 |
+
return $value;
|
76 |
+
}
|
77 |
+
|
78 |
+
$params[$attrCode] = $data;
|
79 |
+
}
|
80 |
+
|
81 |
+
$uri = Zend_Uri::factory($value);
|
82 |
+
$scheme = $uri->getScheme();
|
83 |
+
$query = $uri->getQueryAsArray();
|
84 |
+
$port = $uri->getPort();
|
85 |
+
|
86 |
+
if ($uri->valid())
|
87 |
+
{
|
88 |
+
$params = array_merge($query, $params);
|
89 |
+
$uri->setQuery($params);
|
90 |
+
|
91 |
+
if ($uri->valid())
|
92 |
+
return $uri->getUri();
|
93 |
+
|
94 |
+
$this->skip = true;
|
95 |
+
}
|
96 |
+
|
97 |
+
return $value;
|
98 |
+
}
|
99 |
+
|
100 |
+
public function mapFieldImageLink($params = array())
|
101 |
+
{
|
102 |
+
$value = $this->getCellValue(array('map' => $params['map']));
|
103 |
+
|
104 |
+
if ($value == '')
|
105 |
+
$value = $this->getParentMap()->mapField('image_link');
|
106 |
+
|
107 |
+
return $value;
|
108 |
+
}
|
109 |
+
|
110 |
+
public function mapFieldAvailability($params = array())
|
111 |
+
{
|
112 |
+
$args = array('map' => $params['map']);
|
113 |
+
$value = "";
|
114 |
+
$value = $this->getParentMap()->mapField('availability');
|
115 |
+
// gets out of stock if parent is out of stock
|
116 |
+
if (strcasecmp($this->getConfig()->getOutOfStockStatus(), $value) == 0)
|
117 |
+
return $value;
|
118 |
+
|
119 |
+
$value = $this->getCellValue($args);
|
120 |
+
|
121 |
+
return $value;
|
122 |
+
}
|
123 |
+
|
124 |
+
public function mapFieldBrand($params = array())
|
125 |
+
{
|
126 |
+
$args = array('map' => $params['map']);
|
127 |
+
$value = "";
|
128 |
+
|
129 |
+
// get value from parent first
|
130 |
+
$value = $this->getParentMap()->mapField('brand');
|
131 |
+
if ($value != "")
|
132 |
+
return $value;
|
133 |
+
|
134 |
+
$value = $this->getCellValue($args);
|
135 |
+
|
136 |
+
return $value;
|
137 |
+
}
|
138 |
+
|
139 |
+
public function mapFieldProductType($params = array())
|
140 |
+
{
|
141 |
+
$args = array('map' => $params['map']);
|
142 |
+
$value = "";
|
143 |
+
|
144 |
+
// get value from parent first
|
145 |
+
$value = $this->getParentMap()->mapField('product_type');
|
146 |
+
if ($value != "")
|
147 |
+
return htmlspecialchars_decode($value);
|
148 |
+
|
149 |
+
$map_by_category = $this->getConfig()->getMapCategorySorted('product_type_by_category', $this->getStoreId());
|
150 |
+
$category_ids = $this->getProduct()->getCategoryIds();
|
151 |
+
if (empty($category_ids))
|
152 |
+
$category_ids = $this->getParentMap()->getProduct()->getCategoryIds();
|
153 |
+
if (!empty($category_ids) && count($map_by_category) > 0)
|
154 |
+
{
|
155 |
+
foreach ($map_by_category as $arr)
|
156 |
+
{
|
157 |
+
if (array_search($arr['category'], $category_ids) !== false)
|
158 |
+
{
|
159 |
+
$value = $arr['value'];
|
160 |
+
break;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
if ($value != "")
|
166 |
+
return htmlspecialchars_decode($value);
|
167 |
+
|
168 |
+
$value = $this->getCellValue($args);
|
169 |
+
|
170 |
+
return htmlspecialchars_decode($value);
|
171 |
+
}
|
172 |
+
}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Bundle.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Bundle Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Bundle
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{
|
21 |
+
public function getPrice()
|
22 |
+
{
|
23 |
+
$price = 0.0;
|
24 |
+
|
25 |
+
if (!$this->hasSpecialPrice())
|
26 |
+
{
|
27 |
+
$price = $this->calcMinimalPrice($this->getProduct());
|
28 |
+
}
|
29 |
+
else
|
30 |
+
{
|
31 |
+
$price = $this->calcMinimalPrice($this->getProduct());
|
32 |
+
}
|
33 |
+
|
34 |
+
if ($price <= 0)
|
35 |
+
$this->skip = true;
|
36 |
+
|
37 |
+
return $price;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function calcMinimalPrice($product) {
|
41 |
+
$price = 0.0;
|
42 |
+
|
43 |
+
if ($this->getConfig()->compareMagentoVersion(
|
44 |
+
array('major' => 1, 'minor' => 6, 'revision' => 0, 'patch' => 0)))
|
45 |
+
$_prices = $product->getPriceModel()->getPrices($product);
|
46 |
+
else
|
47 |
+
$_prices = $product->getPriceModel()->getTotalPrices($product);
|
48 |
+
|
49 |
+
if (is_array($_prices))
|
50 |
+
$price = min($_prices);
|
51 |
+
else
|
52 |
+
$price = $_prices;
|
53 |
+
|
54 |
+
return $price;
|
55 |
+
}
|
56 |
+
|
57 |
+
public function getSpecialPrice()
|
58 |
+
{
|
59 |
+
$price = $this->calcMinimalPrice($this->getProduct());
|
60 |
+
|
61 |
+
$special_price_percent = $this->getProduct()->getSpecialPrice();
|
62 |
+
|
63 |
+
if ($special_price_percent <= 0 || $special_price_percent > 100)
|
64 |
+
return 0;
|
65 |
+
|
66 |
+
$special_price = (($special_price = (100 - $special_price_percent) * $price / 100) > 0 ? $special_price : 0);
|
67 |
+
|
68 |
+
return $special_price;
|
69 |
+
}
|
70 |
+
|
71 |
+
protected function mapDirectiveSalePrice($params = array())
|
72 |
+
{
|
73 |
+
return null;
|
74 |
+
}
|
75 |
+
}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Configurable.php
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Configurable Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Configurable
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{
|
21 |
+
protected static $_grouped = false;
|
22 |
+
|
23 |
+
public static function setGrouped($v)
|
24 |
+
{
|
25 |
+
self::$_grouped = (bool)$v;
|
26 |
+
}
|
27 |
+
|
28 |
+
protected $_assoc_ids;
|
29 |
+
protected $_assocs;
|
30 |
+
protected $_cache_configurable_attribute_codes;
|
31 |
+
|
32 |
+
public function _beforeMap()
|
33 |
+
{
|
34 |
+
$this->_assocs = array();
|
35 |
+
$assocIds = $this->getAssocIds();
|
36 |
+
|
37 |
+
$assoc = Mage::getModel('catalog/product');
|
38 |
+
$assoc->setStoreId($this->getStoreId());
|
39 |
+
|
40 |
+
$associatedProducts = $assoc
|
41 |
+
->getCollection()
|
42 |
+
->addIdFilter($assocIds)
|
43 |
+
->addAttributeToSelect('*')
|
44 |
+
->load();
|
45 |
+
|
46 |
+
foreach ($associatedProducts as $associated)
|
47 |
+
{
|
48 |
+
$this->_assocs[$associated->getId()] = $associated;
|
49 |
+
}
|
50 |
+
|
51 |
+
$assocMapArr = array();
|
52 |
+
foreach ($this->_assocs as $assoc)
|
53 |
+
{
|
54 |
+
$assocMap = $this->getAssocMapModel($assoc);
|
55 |
+
|
56 |
+
if ($assocMap->checkSkipSubmission()->isSkip())
|
57 |
+
continue;
|
58 |
+
|
59 |
+
$assocMapArr[$assoc->getId()] = $assocMap;
|
60 |
+
}
|
61 |
+
|
62 |
+
$this->setAssocMaps($assocMapArr);
|
63 |
+
|
64 |
+
return parent::_beforeMap();
|
65 |
+
}
|
66 |
+
|
67 |
+
public function _map()
|
68 |
+
{
|
69 |
+
$rows = array();
|
70 |
+
// $grouped = ($this->getConfigVar('group_configurable_products') == 1);
|
71 |
+
$grouped = self::$_grouped;
|
72 |
+
|
73 |
+
$skipFields = array(
|
74 |
+
'id',
|
75 |
+
'title',
|
76 |
+
'description',
|
77 |
+
'price',
|
78 |
+
'normal_price',
|
79 |
+
'sale_price'
|
80 |
+
);
|
81 |
+
|
82 |
+
// Check if this product should be in the feed
|
83 |
+
if (!$this->isSkip())
|
84 |
+
{
|
85 |
+
$masterData = parent::_map();
|
86 |
+
reset($masterData);
|
87 |
+
$masterData = current($masterData);
|
88 |
+
|
89 |
+
// Only add the master data is we don't group products
|
90 |
+
if ($grouped)
|
91 |
+
$rows[] = $masterData;
|
92 |
+
}
|
93 |
+
|
94 |
+
// Map all child products
|
95 |
+
foreach ($this->getAssocMaps() as $assocId => $assocMap)
|
96 |
+
{
|
97 |
+
if (!$assocMap->isSkip())
|
98 |
+
{
|
99 |
+
$row = $assocMap->map();
|
100 |
+
reset($row);
|
101 |
+
$row = current($row);
|
102 |
+
|
103 |
+
// We can group multiple configurable products into the master product
|
104 |
+
if (!$grouped)
|
105 |
+
{
|
106 |
+
foreach ($row as $name => $value)
|
107 |
+
{
|
108 |
+
if (in_array($name, $skipFields))
|
109 |
+
{
|
110 |
+
continue;
|
111 |
+
}
|
112 |
+
|
113 |
+
$masterData = $this->_mapGrouped($name, $value, $masterData);
|
114 |
+
}
|
115 |
+
}
|
116 |
+
else
|
117 |
+
{
|
118 |
+
$rows[] = $row; // Add each product as separate product
|
119 |
+
}
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
if (!$grouped) {
|
124 |
+
// Make sure boost field has single value
|
125 |
+
if (isset($masterData['boost']) && is_array($masterData['boost'])) {
|
126 |
+
$masterData['boost'] = max($masterData['boost']);
|
127 |
+
}
|
128 |
+
$rows[] = $masterData; // Add the complete master data object
|
129 |
+
}
|
130 |
+
|
131 |
+
return $rows;
|
132 |
+
}
|
133 |
+
|
134 |
+
protected function _mapGrouped($name, $childValue, $masterData)
|
135 |
+
{
|
136 |
+
$value = $masterData[$name];
|
137 |
+
|
138 |
+
if (!is_array($value)) {
|
139 |
+
$value = array($value);
|
140 |
+
}
|
141 |
+
if (!is_array($childValue)) {
|
142 |
+
$childValue = array($childValue);
|
143 |
+
}
|
144 |
+
|
145 |
+
$value = array_merge($value, $childValue);
|
146 |
+
|
147 |
+
// Remove duplicates
|
148 |
+
$value = array_values(array_unique($value));
|
149 |
+
|
150 |
+
// Remove array if value is single
|
151 |
+
if (count($value) == 1) {
|
152 |
+
$value = $value[0];
|
153 |
+
}
|
154 |
+
|
155 |
+
$masterData[$name] = $value;
|
156 |
+
return $masterData;
|
157 |
+
}
|
158 |
+
|
159 |
+
public function getAssocIds()
|
160 |
+
{
|
161 |
+
if (is_null($this->_assoc_ids))
|
162 |
+
$this->_assoc_ids = $this->loadAssocIds(
|
163 |
+
$this->getProduct(),
|
164 |
+
$this->getStoreId()
|
165 |
+
);
|
166 |
+
|
167 |
+
asort($this->_assoc_ids);
|
168 |
+
|
169 |
+
return $this->_assoc_ids;
|
170 |
+
}
|
171 |
+
|
172 |
+
protected function getAssocMapModel($oProduct)
|
173 |
+
{
|
174 |
+
$params = array(
|
175 |
+
'store_code' => $this->getData('store_code'),
|
176 |
+
'store_id' => $this->getData('store_id'),
|
177 |
+
'website_id' => $this->getData('website_id'),
|
178 |
+
);
|
179 |
+
|
180 |
+
$productMap = Mage::getModel('doofinder_feed/map_product_associated',
|
181 |
+
$params);
|
182 |
+
|
183 |
+
$productMap->setGenerator($this->getGenerator())
|
184 |
+
->setProduct($oProduct)
|
185 |
+
->setFieldsMap($this->_field_map)
|
186 |
+
->setParentMap($this)
|
187 |
+
->initialize();
|
188 |
+
|
189 |
+
return $productMap;
|
190 |
+
}
|
191 |
+
|
192 |
+
public function getConfigurableAttributeCodes()
|
193 |
+
{
|
194 |
+
if (is_null($this->_cache_configurable_attribute_codes))
|
195 |
+
$this->_cache_configurable_attribute_codes = $this->getTools()
|
196 |
+
->getConfigurableAttributeCodes($this->getProduct()->getId());
|
197 |
+
|
198 |
+
return $this->_cache_configurable_attribute_codes;
|
199 |
+
}
|
200 |
+
}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Downloadable.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Downloadable Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Downloadable
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Grouped.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Grouped Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Grouped
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{
|
21 |
+
/**
|
22 |
+
* Grouped products doesn't have special price.
|
23 |
+
*
|
24 |
+
* @return float
|
25 |
+
*/
|
26 |
+
public function getPrice()
|
27 |
+
{
|
28 |
+
// $price = $this->calcGroupPrice($this->getProduct());
|
29 |
+
$price = $this->getMinPrice($this->getProduct());
|
30 |
+
|
31 |
+
if ($price <= 0)
|
32 |
+
$this->skip = true;
|
33 |
+
|
34 |
+
return $price;
|
35 |
+
}
|
36 |
+
|
37 |
+
public function calcGroupPrice($product)
|
38 |
+
{
|
39 |
+
$price = 0.0;
|
40 |
+
$ap = $product->getTypeInstance()->getAssociatedProducts();
|
41 |
+
|
42 |
+
foreach ($ap as $associatedProduct)
|
43 |
+
$price += $associatedProduct->getPrice();
|
44 |
+
|
45 |
+
return $price; // Total price
|
46 |
+
}
|
47 |
+
|
48 |
+
public function getMinPrice($product)
|
49 |
+
{
|
50 |
+
$price = null;
|
51 |
+
|
52 |
+
foreach ($product->getTypeInstance()->getAssociatedProducts() as $ap)
|
53 |
+
{
|
54 |
+
if (is_null($price))
|
55 |
+
$price = $ap->getPrice();
|
56 |
+
else
|
57 |
+
$price = min($price, $ap->getPrice());
|
58 |
+
}
|
59 |
+
|
60 |
+
return is_null($price) ? 0.0 : $price;
|
61 |
+
}
|
62 |
+
}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Simple.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Simple Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Simple
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{}
|
app/code/community/Doofinder/Feed/Model/Map/Product/Virtual.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Virtual Product Map Model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Map_Product_Virtual
|
19 |
+
extends Doofinder_Feed_Model_Map_Product_Abstract
|
20 |
+
{}
|
app/code/community/Doofinder/Feed/Model/Mysql4/Cron.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Mysql4_Cron extends Mage_Core_Model_Mysql4_Abstract {
|
13 |
+
|
14 |
+
protected function _construct() {
|
15 |
+
$this->_init('doofinder_feed/cron', 'id');
|
16 |
+
}
|
17 |
+
}
|
app/code/community/Doofinder/Feed/Model/Mysql4/Cron/Collection.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Mysql4_Cron_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
13 |
+
protected function _construct()
|
14 |
+
{
|
15 |
+
$this->_init('doofinder_feed/cron');
|
16 |
+
}
|
17 |
+
}
|
app/code/community/Doofinder/Feed/Model/Mysql4/Log.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Mysql4_Log extends Mage_Core_Model_Mysql4_Abstract {
|
13 |
+
|
14 |
+
protected function _construct() {
|
15 |
+
$this->_init('doofinder_feed/log', 'id');
|
16 |
+
}
|
17 |
+
}
|
app/code/community/Doofinder/Feed/Model/Mysql4/Log/Collection.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Mysql4_Log_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
|
13 |
+
{
|
14 |
+
protected function _construct()
|
15 |
+
{
|
16 |
+
$this->_init('doofinder_feed/log');
|
17 |
+
}
|
18 |
+
}
|
app/code/community/Doofinder/Feed/Model/Observers/Feed.php
ADDED
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
require_once(Mage::getBaseDir('lib') . DS. 'Doofinder' . DS .'doofinder_management_api.php');
|
13 |
+
|
14 |
+
class Doofinder_Feed_Model_Observers_Feed
|
15 |
+
{
|
16 |
+
|
17 |
+
private $config;
|
18 |
+
|
19 |
+
private $storeCode;
|
20 |
+
|
21 |
+
private $productCount;
|
22 |
+
|
23 |
+
|
24 |
+
public function updateSearchEngineIndexes($observer) {
|
25 |
+
|
26 |
+
$helper = Mage::helper('doofinder_feed');
|
27 |
+
|
28 |
+
$product = $observer->getProduct();
|
29 |
+
$products[] = $product->getId();
|
30 |
+
|
31 |
+
$storeCodes = array();
|
32 |
+
$store = Mage::getModel('core/store')->load($product->getStoreId());
|
33 |
+
|
34 |
+
// If current store is admin then get an array of all possible stores for a website
|
35 |
+
if ($store->getCode() !== 'admin') {
|
36 |
+
$storeCodes[] = $store->getCode();
|
37 |
+
} else {
|
38 |
+
foreach(Mage::app()->getStores() as $store) {
|
39 |
+
$storeCodes[] = $store->getCode();
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
// Filter out disabled stores
|
44 |
+
foreach (array_keys($storeCodes) as $key) {
|
45 |
+
$storeCode = $storeCodes[$key];
|
46 |
+
|
47 |
+
$engineEnabled = Mage::getStoreConfig('doofinder_search/internal_settings/enable', $storeCode);
|
48 |
+
$atomicUpdatesEnabled = Mage::getStoreConfig('doofinder_cron/feed_settings/atomic_updates_enabled', $storeCode);
|
49 |
+
|
50 |
+
if (!$engineEnabled || !$atomicUpdatesEnabled) {
|
51 |
+
unset($storeCodes[$key]);
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
// Terminate updates where there is no store enabled
|
56 |
+
if (empty($storeCodes)) return;
|
57 |
+
|
58 |
+
// Get search engines
|
59 |
+
$apiKey = Mage::getStoreConfig('doofinder_search/internal_settings/api_key', Mage::app()->getStore());
|
60 |
+
$dma = new DoofinderManagementApi($apiKey);
|
61 |
+
$searchEngines = $dma->getSearchEngines();
|
62 |
+
|
63 |
+
// Set engines array key as hashid
|
64 |
+
foreach ($searchEngines as $key => $searchEngine) {
|
65 |
+
$searchEngines[$searchEngine->hashid] = $searchEngine;
|
66 |
+
unset($searchEngines[$key]);
|
67 |
+
}
|
68 |
+
|
69 |
+
|
70 |
+
// Loop over all stores and update relevant search engines
|
71 |
+
foreach ($storeCodes as $storeCode) {
|
72 |
+
// Set store code
|
73 |
+
$this->storeCode = $storeCode;
|
74 |
+
|
75 |
+
// Get store config
|
76 |
+
$this->config = $helper->getStoreConfig($this->storeCode);
|
77 |
+
|
78 |
+
|
79 |
+
|
80 |
+
// Set options
|
81 |
+
$options = array(
|
82 |
+
'close_empty' => true, // close xml even if there are no items
|
83 |
+
'products' => $products, // list of products in feed
|
84 |
+
'store_code' => $this->config['storeCode'],
|
85 |
+
'grouped' => $this->_getBoolean($this->config['grouped']),
|
86 |
+
'display_price' => $this->_getBoolean($this->config['display_price']),
|
87 |
+
'minimal_price' => $this->_getBoolean('minimal_price', false),
|
88 |
+
'image_size' => $this->config['image_size'],
|
89 |
+
'customer_group_id' => 0,
|
90 |
+
);
|
91 |
+
|
92 |
+
$generator = Mage::getModel('doofinder_feed/generator', $options);
|
93 |
+
|
94 |
+
$xmlData = $generator->run();
|
95 |
+
|
96 |
+
if ($xmlData) {
|
97 |
+
$rss = simplexml_load_string($xmlData);
|
98 |
+
|
99 |
+
$hashId = Mage::getStoreConfig('doofinder_search/internal_settings/hash_id', $this->storeCode);
|
100 |
+
if ($hashId === '') {
|
101 |
+
|
102 |
+
$warning = sprintf('HashID is not set for the \'%s\' store view, therefore, search indexes haven\'t been
|
103 |
+
updated for
|
104 |
+
this store view. To fix this problem set HashID for a given stor view or disable Internal Search in Doofinder
|
105 |
+
Search Configuration.', $this->storeCode);
|
106 |
+
Mage::getSingleton('adminhtml/session')->addWarning($warning);
|
107 |
+
continue;
|
108 |
+
}
|
109 |
+
|
110 |
+
$searchEngine = $searchEngines[$hashId];
|
111 |
+
|
112 |
+
// Check if search engine exists and skip foreach iteration if not.
|
113 |
+
if (!$searchEngine) {
|
114 |
+
$error = sprintf('Search engine with HashID %s doesn\'t exists. Please, check your configuration.', $hashId);
|
115 |
+
Mage::getSingleton('adminhtml/session')->addError($error);
|
116 |
+
continue;
|
117 |
+
}
|
118 |
+
|
119 |
+
// Declare array of products to update
|
120 |
+
$products = array();
|
121 |
+
foreach ($rss->channel->item as $item) {
|
122 |
+
$product = array();
|
123 |
+
foreach ($item as $key => $value) {
|
124 |
+
$product[$key] = (string)$value;
|
125 |
+
}
|
126 |
+
$products[] = $product;
|
127 |
+
}
|
128 |
+
if (count($products))
|
129 |
+
$searchEngine->updateItems('product', $products);
|
130 |
+
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
|
135 |
+
}
|
136 |
+
|
137 |
+
public function generateFeed($observer)
|
138 |
+
{
|
139 |
+
$stores = Mage::app()->getStores();
|
140 |
+
$helper = Mage::helper('doofinder_feed');
|
141 |
+
|
142 |
+
// Get doofinder process model
|
143 |
+
$collection = Mage::getModel('doofinder_feed/cron')->getCollection();
|
144 |
+
$collection
|
145 |
+
->addFieldToFilter('status', array('in' => array($helper::STATUS_PENDING, $helper::STATUS_RUNNING)))
|
146 |
+
->addFieldToFilter('next_iteration', array('lteq' => Mage::getModel('core/date')->date('Y-m-d H:i:s')))
|
147 |
+
->setOrder('next_iteration', 'asc');
|
148 |
+
$collection->getSelect()->limit(1);
|
149 |
+
|
150 |
+
$process = $collection->fetchItem();
|
151 |
+
|
152 |
+
if (!$process || !$process->getId()) {
|
153 |
+
return;
|
154 |
+
}
|
155 |
+
|
156 |
+
$scheduleId = $process->getScheduleId();
|
157 |
+
|
158 |
+
// Get store code
|
159 |
+
$this->storeCode = $process->getStoreCode();
|
160 |
+
|
161 |
+
// Set store context
|
162 |
+
Mage::app()->setCurrentStore($this->storeCode);
|
163 |
+
|
164 |
+
// Get store config
|
165 |
+
$this->config = $helper->getStoreConfig($this->storeCode);
|
166 |
+
|
167 |
+
try {
|
168 |
+
// Clear out the message
|
169 |
+
$process->setMessage($helper::MSG_EMPTY);
|
170 |
+
|
171 |
+
// Get data model for store cron
|
172 |
+
$dataModel = Mage::getModel('cron/schedule');
|
173 |
+
|
174 |
+
|
175 |
+
// Get store cron data
|
176 |
+
$data = $dataModel->load($scheduleId);
|
177 |
+
|
178 |
+
// Get current offset
|
179 |
+
$offset = intval($process->getOffset());
|
180 |
+
|
181 |
+
// Get step size
|
182 |
+
$stepSize = intval($this->config['stepSize']);
|
183 |
+
|
184 |
+
// Set paths
|
185 |
+
$path = $helper->getFeedPath($this->storeCode);
|
186 |
+
$tmpPath = $helper->getFeedTemporaryPath($this->storeCode);
|
187 |
+
|
188 |
+
// Get job code
|
189 |
+
$jobCode = $helper::JOB_CODE;
|
190 |
+
|
191 |
+
// Set options for cron generator
|
192 |
+
$options = array(
|
193 |
+
'_limit_' => $stepSize,
|
194 |
+
'_offset_' => $offset,
|
195 |
+
'store_code' => $this->config['storeCode'],
|
196 |
+
'grouped' => $this->_getBoolean($this->config['grouped']),
|
197 |
+
'display_price' => $this->_getBoolean($this->config['display_price']),
|
198 |
+
'minimal_price' => $this->_getBoolean('minimal_price', false),
|
199 |
+
'image_size' => $this->config['image_size'],
|
200 |
+
'customer_group_id' => 0,
|
201 |
+
);
|
202 |
+
|
203 |
+
$generator = Mage::getModel('doofinder_feed/generator', $options);
|
204 |
+
|
205 |
+
$xmlData = $generator->run();
|
206 |
+
|
207 |
+
// If there were errors log them
|
208 |
+
if ($errors = $generator->getErrors()) {
|
209 |
+
$process->setErrorStack($process->getErrorStack() + count($errors));
|
210 |
+
|
211 |
+
foreach ($errors as $error) {
|
212 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::ERROR, $error);
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
$message = $helper->__('Processed products with ids in range %d - %d', $offset + 1, $generator->getLastProcessedProductId());
|
217 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $message);
|
218 |
+
|
219 |
+
// If there is new data append to xml.tmp else convert into xml
|
220 |
+
if ($xmlData) {
|
221 |
+
$dir = Mage::getBaseDir('media').DS.'doofinder';
|
222 |
+
|
223 |
+
// If directory doesn't exist create one
|
224 |
+
if (!file_exists($dir)) {
|
225 |
+
$helper->createFeedDirectory($dir);
|
226 |
+
}
|
227 |
+
|
228 |
+
// If file can not be save throw an error
|
229 |
+
if (!$success = file_put_contents($tmpPath, $xmlData, FILE_APPEND | LOCK_EX)) {
|
230 |
+
Mage::throwException($helper->__("File can not be saved: {$tmpPath}"));
|
231 |
+
}
|
232 |
+
|
233 |
+
$this->productCount = $generator->getProductCount();
|
234 |
+
} else {
|
235 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::WARNING, $helper->__('No data added to feed'));
|
236 |
+
}
|
237 |
+
|
238 |
+
// Set process offset and progress
|
239 |
+
$process->setOffset($generator->getLastProcessedProductId());
|
240 |
+
$process->setComplete(sprintf('%0.1f%%', $generator->getProgress() * 100));
|
241 |
+
|
242 |
+
if (!$generator->isFeedDone()) {
|
243 |
+
$helper->createNewSchedule($process);
|
244 |
+
} else {
|
245 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $helper->__('Feed generation completed'));
|
246 |
+
|
247 |
+
if (!rename($tmpPath, $path)) {
|
248 |
+
Mage::throwException($helper->__("Cannot rename {$tmpPath} to {$path}"));
|
249 |
+
}
|
250 |
+
|
251 |
+
$process->setMessage($helper->__('Last process successfully completed. Now waiting for new schedule.'));
|
252 |
+
$this->_endProcess($process);
|
253 |
+
}
|
254 |
+
|
255 |
+
} catch (Exception $e) {
|
256 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::ERROR, $e->getMessage());
|
257 |
+
$process->setErrorStack($process->getErrorStack() + 1);
|
258 |
+
$process->setMessage('#error#' . $e->getMessage());
|
259 |
+
$helper->createNewSchedule($process);
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Cast any value to bool
|
265 |
+
* @param mixed $value
|
266 |
+
* @param bool $defaultValue
|
267 |
+
* @return bool
|
268 |
+
*/
|
269 |
+
protected function _getBoolean($value, $defaultValue = false)
|
270 |
+
{
|
271 |
+
if (is_numeric($value)) {
|
272 |
+
if ($value)
|
273 |
+
return true;
|
274 |
+
else
|
275 |
+
return false;
|
276 |
+
}
|
277 |
+
|
278 |
+
$yes = array('true', 'on', 'yes');
|
279 |
+
$no = array('false', 'off', 'no');
|
280 |
+
|
281 |
+
if ( in_array($value, $yes) )
|
282 |
+
return true;
|
283 |
+
|
284 |
+
if ( in_array($value, $no) )
|
285 |
+
return false;
|
286 |
+
|
287 |
+
return $defaultValue;
|
288 |
+
}
|
289 |
+
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Converts time string into array.
|
293 |
+
* @param string $time
|
294 |
+
* @return array
|
295 |
+
*/
|
296 |
+
protected function timeToArray($time = null) {
|
297 |
+
// Declare new time
|
298 |
+
$newTime;
|
299 |
+
// Validate $time variable
|
300 |
+
if(!$time || !is_string($time) || substr_count($time, ',') < 2) {
|
301 |
+
Mage::throwException('Incorrect time string.');
|
302 |
+
return false;
|
303 |
+
}
|
304 |
+
|
305 |
+
list($min, $day, $month,) = explode(',', $time);
|
306 |
+
|
307 |
+
$newTime = array(
|
308 |
+
'min' => $min,
|
309 |
+
'day' => $day,
|
310 |
+
'month' => $month,
|
311 |
+
);
|
312 |
+
return $newTime;
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* Concludes process.
|
317 |
+
* @param Doofinder_Feed_Model_Cron $process
|
318 |
+
*/
|
319 |
+
private function _endProcess(Doofinder_Feed_Model_Cron $process) {
|
320 |
+
$helper = Mage::helper('doofinder_feed');
|
321 |
+
// Prepare data
|
322 |
+
$data = array(
|
323 |
+
'status' => $helper::STATUS_WAITING,
|
324 |
+
'next_run' => '-',
|
325 |
+
'next_iteration' => '-',
|
326 |
+
'last_feed_name' => $this->config['xmlName'],
|
327 |
+
'schedule_id' => null,
|
328 |
+
);
|
329 |
+
|
330 |
+
$process->addData($data)->save();
|
331 |
+
}
|
332 |
+
|
333 |
+
|
334 |
+
public function addButtons($observer) {
|
335 |
+
$block = $observer->getBlock();
|
336 |
+
|
337 |
+
if ($block instanceof Mage_Adminhtml_Block_System_Config_Edit && $block->getRequest()->getParam('section') == 'doofinder_cron') {
|
338 |
+
$html = $block->getChild('save_button')->toHtml();
|
339 |
+
|
340 |
+
$html .= $block->getLayout()->createBlock('doofinder_feed/adminhtml_widget_button_reschedule')->toHtml();
|
341 |
+
|
342 |
+
$block->setChild('save_button',
|
343 |
+
$block->getLayout()->createBlock('core/text')->setText($html)
|
344 |
+
);
|
345 |
+
}
|
346 |
+
}
|
347 |
+
|
348 |
+
|
349 |
+
|
350 |
+
}
|
app/code/community/Doofinder/Feed/Model/Observers/Logs.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Observers_Logs
|
13 |
+
{
|
14 |
+
const MAX_SIZE = 1000;
|
15 |
+
const BATCH_LIMIT = 100;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Clear logs that are beyond the limit
|
19 |
+
*
|
20 |
+
* @param Varien_Event_Observer $observer
|
21 |
+
*/
|
22 |
+
public function clearLogs($observer)
|
23 |
+
{
|
24 |
+
$collection = Mage::getModel('doofinder_feed/log')->getCollection();
|
25 |
+
|
26 |
+
$size = $collection->getSize();
|
27 |
+
|
28 |
+
if ($size > static::MAX_SIZE) {
|
29 |
+
$collection->setOrder('id', $collection::SORT_ORDER_DESC);
|
30 |
+
|
31 |
+
$offset = max(static::MAX_SIZE, $size - static::BATCH_LIMIT);
|
32 |
+
|
33 |
+
$collection->getSelect()
|
34 |
+
->limit(static::BATCH_LIMIT, $offset);
|
35 |
+
|
36 |
+
$ids = array();
|
37 |
+
foreach ($collection->getItems() as $item) {
|
38 |
+
$item->delete();
|
39 |
+
$ids[] = $item->id;
|
40 |
+
}
|
41 |
+
|
42 |
+
Mage::log($ids, null, 'debug.log');
|
43 |
+
}
|
44 |
+
}
|
45 |
+
}
|
app/code/community/Doofinder/Feed/Model/Observers/Schedule.php
ADDED
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Observers_Schedule
|
13 |
+
{
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Register missing / reset schedules after configuration saves.
|
17 |
+
*
|
18 |
+
* @param Varien_Event_Observer $observer
|
19 |
+
*/
|
20 |
+
public function saveNewSchedule($observer)
|
21 |
+
{
|
22 |
+
// Get store code
|
23 |
+
$currentStoreCode = $observer->getStore();
|
24 |
+
|
25 |
+
// Stores array holding all store codes
|
26 |
+
$codes = array();
|
27 |
+
|
28 |
+
// Create stores codes array
|
29 |
+
if ($currentStoreCode) {
|
30 |
+
$codes[] = $currentStoreCode;
|
31 |
+
} else {
|
32 |
+
$stores = Mage::app()->getStores();
|
33 |
+
foreach ($stores as $store) {
|
34 |
+
if ($store->getIsActive()) {
|
35 |
+
$codes[] = $store->getCode();
|
36 |
+
}
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
// Check if user wants to reset the schedule
|
41 |
+
$reset = (bool) Mage::app()->getRequest()->getParam('reset');
|
42 |
+
|
43 |
+
foreach ($codes as $storeCode) {
|
44 |
+
$this->updateProcess($storeCode, $reset, $reset);
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Regenerate finished shcedules.
|
50 |
+
*
|
51 |
+
* @param Varien_Event_Observer $observer
|
52 |
+
*/
|
53 |
+
public function regenerateSchedule()
|
54 |
+
{
|
55 |
+
// Get store
|
56 |
+
$stores = Mage::app()->getStores();
|
57 |
+
|
58 |
+
foreach ($stores as $store) {
|
59 |
+
if ($store->getIsActive()) {
|
60 |
+
$this->updateProcess($store->getCode());
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Gets process for given store code
|
67 |
+
*
|
68 |
+
* @param string $storeCode
|
69 |
+
* @return Doofinder_Feed_Model_Cron
|
70 |
+
*/
|
71 |
+
private function _getProcessByStoreCode($storeCode = 'default')
|
72 |
+
{
|
73 |
+
$process = Mage::getModel('doofinder_feed/cron')->load($storeCode, 'store_code');
|
74 |
+
return $process->getId() ? $process : null;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Checks if process is registered in doofinder cron table
|
79 |
+
*
|
80 |
+
* @param string $storeCode
|
81 |
+
* @return bool
|
82 |
+
*/
|
83 |
+
private function _isProcessRegistered($storeCode = 'default')
|
84 |
+
{
|
85 |
+
$process = $this->_getProcessByStoreCode($storeCode);
|
86 |
+
return $process ? true : false;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Update process for given store code.
|
91 |
+
* If process does not exits - create it.
|
92 |
+
* Reschedule the process if it needs it.
|
93 |
+
*
|
94 |
+
* @param Doofinder_Feed_Model_Cron $process
|
95 |
+
* @param boolean $reset
|
96 |
+
* @param boolean $now
|
97 |
+
* @param boolean $force
|
98 |
+
*/
|
99 |
+
public function updateProcess($storeCode = 'default', $reset = false, $now = false, $force = false)
|
100 |
+
{
|
101 |
+
// Get store
|
102 |
+
$helper = Mage::helper('doofinder_feed');
|
103 |
+
$config = $helper->getStoreConfig($storeCode);
|
104 |
+
$store = Mage::getModel('core/store')->load($storeCode);
|
105 |
+
|
106 |
+
// Override time if $now is enabled
|
107 |
+
if ($now) {
|
108 |
+
$config['time'] = array(date('H') + $helper->getTimezoneOffset(), date('i'), date('s'));
|
109 |
+
}
|
110 |
+
|
111 |
+
$isEnabled = (bool) $config['enabled'];
|
112 |
+
|
113 |
+
// Try loading store process
|
114 |
+
$process = $this->_getProcessByStoreCode($storeCode);
|
115 |
+
|
116 |
+
// Create new process if it not exists
|
117 |
+
if (!$process) {
|
118 |
+
$process = $this->_registerProcess($storeCode);
|
119 |
+
}
|
120 |
+
|
121 |
+
// Enable/disable process if it needs to
|
122 |
+
if ($isEnabled || $force) {
|
123 |
+
if ($process->getStatus() == $helper::STATUS_DISABLED) {
|
124 |
+
$this->_enableProcess($process);
|
125 |
+
}
|
126 |
+
} else {
|
127 |
+
if ($process->getStatus() != $helper::STATUS_DISABLED) {
|
128 |
+
|
129 |
+
Mage::getSingleton('adminhtml/session')->addSuccess($helper->__('Process for store "%s" has been disabled', $store->getName()));
|
130 |
+
$this->_removeTmpXml($storeCode);
|
131 |
+
$this->_disableProcess($process);
|
132 |
+
}
|
133 |
+
return $this;
|
134 |
+
}
|
135 |
+
|
136 |
+
// Do not process the schedule if it has insufficient file permissions
|
137 |
+
if (!$this->_checkFeedFilePermission($storeCode)) {
|
138 |
+
Mage::getSingleton('adminhtml/session')->addError($helper->__('Insufficient file permissions for store: %s. Check if the feed file is writeable', $store->getName()));
|
139 |
+
return $this;
|
140 |
+
}
|
141 |
+
|
142 |
+
// Reschedule the process if it needs to
|
143 |
+
if ($reset || $process->getStatus() == $helper::STATUS_WAITING) {
|
144 |
+
Mage::getSingleton('adminhtml/session')->addSuccess($helper->__('Process for store "%s" has been rescheduled', $store->getName()));
|
145 |
+
$this->_removeTmpXml($storeCode);
|
146 |
+
$this->_rescheduleProcess($config, $process);
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Register a new process
|
152 |
+
*
|
153 |
+
* @return Doofinder_Feed_Model_Cron
|
154 |
+
*/
|
155 |
+
private function _registerProcess($storeCode = 'default')
|
156 |
+
{
|
157 |
+
$helper = Mage::helper('doofinder_feed');
|
158 |
+
$config = $helper->getStoreConfig($storeCode);
|
159 |
+
if (empty($status)) {
|
160 |
+
$status = $config['enabled'] ? $helper::STATUS_WAITING : $helper::STATUS_DISABLED;
|
161 |
+
}
|
162 |
+
|
163 |
+
$data = array(
|
164 |
+
'store_code' => $storeCode,
|
165 |
+
'status' => $status,
|
166 |
+
'message' => $helper::MSG_EMPTY,
|
167 |
+
'complete' => '-',
|
168 |
+
'next_run' => '-',
|
169 |
+
'next_iteration'=> '-',
|
170 |
+
'last_feed_name'=> 'None',
|
171 |
+
);
|
172 |
+
$process = Mage::getModel('doofinder_feed/cron')->setData($data)->save();
|
173 |
+
|
174 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $helper->__('Process has been registered'));
|
175 |
+
|
176 |
+
return $process;
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Enable the process
|
181 |
+
*
|
182 |
+
* @param Doofinder_Feed_Model_Cron $process
|
183 |
+
*/
|
184 |
+
private function _enableProcess(Doofinder_Feed_Model_Cron $process)
|
185 |
+
{
|
186 |
+
$helper = Mage::helper('doofinder_feed');
|
187 |
+
$process->setStatus($helper::STATUS_WAITING)->save();
|
188 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $helper->__('Process has been enabled'));
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Disable the process
|
193 |
+
*
|
194 |
+
* @param Doofinder_Feed_Model_Cron $process
|
195 |
+
*/
|
196 |
+
private function _disableProcess(Doofinder_Feed_Model_Cron $process)
|
197 |
+
{
|
198 |
+
$helper = Mage::helper('doofinder_feed');
|
199 |
+
$process->setStatus($helper::STATUS_DISABLED)->save();
|
200 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $helper->__('Process has been disabled'));
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Remove tmp xml file.
|
205 |
+
*
|
206 |
+
* @param string $store_code
|
207 |
+
* @return bool
|
208 |
+
*/
|
209 |
+
private function _removeTmpXml($store_code = null)
|
210 |
+
{
|
211 |
+
if (empty($store_code)) {
|
212 |
+
return false;
|
213 |
+
}
|
214 |
+
$helper = Mage::helper('doofinder_feed');
|
215 |
+
$config = $helper->getStoreConfig($store_code);
|
216 |
+
$filePath = Mage::getBaseDir('media').DS.'doofinder'.DS.$config['xmlName'].'.tmp';
|
217 |
+
if (file_exists($filePath)) {
|
218 |
+
$success = unlink($filePath);
|
219 |
+
if ($success) {
|
220 |
+
Mage::getSingleton('core/session')->addSuccess("Temporary xml file: {$filePath} has beed removed.");
|
221 |
+
return true;
|
222 |
+
} else {
|
223 |
+
Mage::getSingleton('core/session')->addError("Could not remove {$filePath}; This can lead to some errors. Remove this file manually.");
|
224 |
+
return false;
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
return false;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Validate file permissions for feed generation.
|
233 |
+
*
|
234 |
+
* @return boolean
|
235 |
+
*/
|
236 |
+
protected function _checkFeedFilePermission($storeCode)
|
237 |
+
{
|
238 |
+
$helper = Mage::helper('doofinder_feed');
|
239 |
+
|
240 |
+
try {
|
241 |
+
$helper->createFeedDirectory();
|
242 |
+
} catch (Exception $e) {
|
243 |
+
return false;
|
244 |
+
}
|
245 |
+
|
246 |
+
$dir = $helper->getFeedDirectory();
|
247 |
+
$path = $helper->getFeedPath($storeCode);
|
248 |
+
$tmpPath = $helper->getFeedTemporaryPath($storeCode);
|
249 |
+
|
250 |
+
return is_writeable($dir) && (!file_exists($path) || is_writeable($path)) && (!file_exists($tmpPath) || is_writeable($tmpPath));
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Reschedule the process accordingly to process configuration.
|
255 |
+
*
|
256 |
+
* @param array $storeConfig
|
257 |
+
* @param Doofinder_Feed_Model_Cron $process
|
258 |
+
*/
|
259 |
+
protected function _rescheduleProcess($config, Doofinder_Feed_Model_Cron $process)
|
260 |
+
{
|
261 |
+
$helper = Mage::helper('doofinder_feed');
|
262 |
+
|
263 |
+
$timecreated = strftime("%Y-%m-%d %H:%M:%S", mktime(date("H"), date("i"), date("s"), date("m"), date("d"), date("Y")));
|
264 |
+
$timescheduled = $helper->getScheduledAt($config['time'], $config['frequency']);
|
265 |
+
$jobCode = $helper::JOB_CODE;
|
266 |
+
|
267 |
+
$process->setStatus($helper::STATUS_PENDING)
|
268 |
+
->setComplete('0%')
|
269 |
+
->setNextRun($timescheduled)
|
270 |
+
->setNextIteration($timescheduled)
|
271 |
+
->setOffset(0)
|
272 |
+
->setMessage($helper::MSG_PENDING)
|
273 |
+
->setErrorStack(0)
|
274 |
+
->save();
|
275 |
+
|
276 |
+
Mage::helper('doofinder_feed/log')->log($process, Doofinder_Feed_Helper_Log::STATUS, $helper->__('Process has been scheduled'));
|
277 |
+
}
|
278 |
+
}
|
app/code/community/Doofinder/Feed/Model/Resource/Mysql4/Setup.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_Resource_Mysql4_Setup extends Mage_Core_Model_Resource_Setup {
|
13 |
+
|
14 |
+
}
|
app/code/community/Doofinder/Feed/Model/System/Config/Backend/Map/Additional.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_System_Config_Backend_Map_Additional extends Mage_Adminhtml_Model_System_Config_Backend_Serialized
|
13 |
+
{
|
14 |
+
protected function _beforeSave()
|
15 |
+
{
|
16 |
+
$_value = $this->getValue();
|
17 |
+
|
18 |
+
unset($_value['additional_mapping'][-1]);
|
19 |
+
|
20 |
+
$this->setValue($_value);
|
21 |
+
|
22 |
+
parent::_beforeSave();
|
23 |
+
}
|
24 |
+
}
|
app/code/community/Doofinder/Feed/Model/System/Config/Backend/Total/Limit.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Doofinder_Feed_Model_System_Config_Backend_Total_Limit extends Mage_Core_Model_Config_Data
|
4 |
+
{
|
5 |
+
protected function _beforeSave()
|
6 |
+
{
|
7 |
+
if (!$this->getValue()) {
|
8 |
+
throw new Exception(Mage::helper('doofinder_feed')->__('Total limit is required.'));
|
9 |
+
} else if (!is_numeric($this->getValue())) {
|
10 |
+
throw new Exception(Mage::helper('doofinder_feed')->__('Total limit is not a number.'));
|
11 |
+
}
|
12 |
+
|
13 |
+
$this->setValue(intval($this->getValue()));
|
14 |
+
|
15 |
+
return parent::_beforeSave();
|
16 |
+
}
|
17 |
+
}
|
app/code/community/Doofinder/Feed/Model/System/Config/Reset.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_System_Config_Reset extends Mage_Core_Model_Config_Data
|
13 |
+
{
|
14 |
+
protected function _afterLoad()
|
15 |
+
{
|
16 |
+
$this->setValue(0);
|
17 |
+
}
|
18 |
+
}
|
app/code/community/Doofinder/Feed/Model/System/Config/Source/Product/Attributes.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_Model_System_Config_Source_Product_Attributes
|
13 |
+
{
|
14 |
+
protected $_options;
|
15 |
+
|
16 |
+
public function toOptionArray()
|
17 |
+
{
|
18 |
+
if (!$this->_options) {
|
19 |
+
$attributes = array();
|
20 |
+
|
21 |
+
$result = Mage::getResourceModel('catalog/product_attribute_collection')->load();
|
22 |
+
|
23 |
+
foreach ($result as $attribute) {
|
24 |
+
$code = $attribute->getAttributeCode();
|
25 |
+
$label = $attribute->getFrontendLabel();
|
26 |
+
$attributes[$code] = 'Attribute: ' . $code . ($label ? ' (' . $label . ')' : '');
|
27 |
+
}
|
28 |
+
|
29 |
+
$this->_options = array_merge(
|
30 |
+
$this->_getDoofinderDirectivesOptionArray(),
|
31 |
+
$attributes
|
32 |
+
);
|
33 |
+
}
|
34 |
+
|
35 |
+
return $this->_options;
|
36 |
+
}
|
37 |
+
|
38 |
+
protected function _getDoofinderDirectivesOptionArray()
|
39 |
+
{
|
40 |
+
$options = array();
|
41 |
+
|
42 |
+
foreach (Mage::getSingleton('doofinder_feed/config')->getConfigVar('directives') as $directive => $info) {
|
43 |
+
$options[$directive] = 'Doofinder: ' . $info['label'];
|
44 |
+
}
|
45 |
+
|
46 |
+
return $options;
|
47 |
+
}
|
48 |
+
}
|
app/code/community/Doofinder/Feed/Model/Tools.php
ADDED
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category Models
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Tools model for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_Model_Tools extends Varien_Object
|
19 |
+
{
|
20 |
+
public function _construct()
|
21 |
+
{
|
22 |
+
parent::_construct();
|
23 |
+
$this->loadEntityType('catalog_product');
|
24 |
+
}
|
25 |
+
|
26 |
+
public function loadEntityType($type)
|
27 |
+
{
|
28 |
+
if (is_array($type))
|
29 |
+
{
|
30 |
+
foreach ($type as $t)
|
31 |
+
if (is_string($t))
|
32 |
+
$this->loadEntityType($t);
|
33 |
+
}
|
34 |
+
else
|
35 |
+
{
|
36 |
+
$entityType = Mage::getModel('eav/config')->getEntityType('catalog_product');
|
37 |
+
|
38 |
+
Mage::unregister('doofinder_feed/entity_type/'.$type);
|
39 |
+
Mage::register('doofinder_feed/entity_type/'.$type, $entityType);
|
40 |
+
}
|
41 |
+
return $this;
|
42 |
+
}
|
43 |
+
|
44 |
+
public function getEntityType($type)
|
45 |
+
{
|
46 |
+
return Mage::registry('doofinder_feed/entity_type/'.$type);
|
47 |
+
}
|
48 |
+
|
49 |
+
public function getProductAttributeValueBySql($attribute, $type = "text", $productId, $storeId = null, $strict = false, $debug = false)
|
50 |
+
{
|
51 |
+
if (array_search($type, array('text', 'int', 'decimal', 'varchar', 'datetime')) === false)
|
52 |
+
{
|
53 |
+
Mage::throwException(sprintf("Unknown attribute backend type %s for attribute code %s.", $type, $attribute->getAttributeCode()));
|
54 |
+
}
|
55 |
+
|
56 |
+
if (is_null($storeId))
|
57 |
+
{
|
58 |
+
return $this->getProductAttributeValueBySql($attribute, $type, $productId, Mage_Core_Model_App::ADMIN_STORE_ID, true, $debug);
|
59 |
+
}
|
60 |
+
|
61 |
+
$attributeId = $attribute->getAttributeId();
|
62 |
+
|
63 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
64 |
+
$query = $conn->select()
|
65 |
+
->from(array('val' => $this->getRes()->getTableName('catalog/product')."_".$type),
|
66 |
+
array('value'))
|
67 |
+
->joinInner(array('eav' => $this->getRes()->getTableName('eav/attribute')),
|
68 |
+
'val.attribute_id=eav.attribute_id',
|
69 |
+
array())
|
70 |
+
->where('val.entity_id = ?', $productId)
|
71 |
+
->where('val.entity_type_id = ?', $this->getEntityType('catalog_product')->getEntityTypeId())
|
72 |
+
->where('val.store_id = ?', $storeId)
|
73 |
+
->where('val.attribute_id = ?', $attributeId);
|
74 |
+
|
75 |
+
$value = $this->getConnRead()->fetchCol($query);
|
76 |
+
if (is_array($value) && @$value[0] === null)
|
77 |
+
$value = null;
|
78 |
+
elseif (is_array($value) && isset($value[0]))
|
79 |
+
$value = $value[0];
|
80 |
+
else if (is_array($value) && count($value) == 0)
|
81 |
+
$value = null;
|
82 |
+
|
83 |
+
if (is_null($value) && $storeId != Mage_Core_Model_App::ADMIN_STORE_ID && $strict === false)
|
84 |
+
{
|
85 |
+
return $this->getProductAttributeValueBySql($attribute, $type, $productId, Mage_Core_Model_App::ADMIN_STORE_ID, true, $debug);
|
86 |
+
}
|
87 |
+
|
88 |
+
return $value;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Check if there is a parent of type (configurable, ..)
|
93 |
+
*
|
94 |
+
* @param string $type_id
|
95 |
+
* @param string $sku
|
96 |
+
* @param string $parent_type_id
|
97 |
+
* @return array|false
|
98 |
+
*/
|
99 |
+
public function isChildOfProductType($type_id, $sku, $parent_type_id)
|
100 |
+
{
|
101 |
+
$data = false;
|
102 |
+
|
103 |
+
if ($type_id != Mage_Catalog_Model_Product_Type::TYPE_SIMPLE)
|
104 |
+
return $data;
|
105 |
+
|
106 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
107 |
+
$query = $conn->select()
|
108 |
+
->from(array('cpe' => $this->getRes()->getTableName('catalog/product')),
|
109 |
+
array('entity_id' => 'cpe.entity_id',
|
110 |
+
'sku' => 'cpe.sku',
|
111 |
+
'parent_entity_id' => 'cpe_parent.entity_id',
|
112 |
+
'parent_sku' => 'cpe_parent.sku'))
|
113 |
+
->joinInner(array('cpsl' => $this->getRes()->getTableName('catalog/product_super_link')),
|
114 |
+
'cpe.entity_id = cpsl.product_id',
|
115 |
+
array())
|
116 |
+
->joinInner(array('cpe_parent' => $this->getRes()->getTableName('catalog/product')),
|
117 |
+
'cpsl.parent_id = cpe_parent.entity_id',
|
118 |
+
array())
|
119 |
+
->where('cpe.sku', $sku)
|
120 |
+
->where('cpe_parent.type_id', $parent_type_id);
|
121 |
+
|
122 |
+
$result = $this->getConnRead()->fetchRow($query);
|
123 |
+
|
124 |
+
if ($result !== false)
|
125 |
+
{
|
126 |
+
$data = $result;
|
127 |
+
}
|
128 |
+
|
129 |
+
return $data;
|
130 |
+
}
|
131 |
+
|
132 |
+
public function getProductAttributeSelectValue($attribute, $valueId, $storeId = null, $strict = false, $debug = false)
|
133 |
+
{
|
134 |
+
if (is_null($storeId))
|
135 |
+
{
|
136 |
+
return $this->getProductAttributeSelectValue($attribute, $valueId, Mage_Core_Model_App::ADMIN_STORE_ID, true, $debug);
|
137 |
+
}
|
138 |
+
|
139 |
+
$attributeId = $attribute->getAttributeId();
|
140 |
+
|
141 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
142 |
+
$query = $conn->select()
|
143 |
+
->from($this->getRes()->getTableName('eav/attribute_option'),
|
144 |
+
array('opt'))
|
145 |
+
->where('opt.option_id = ?', $valueId)
|
146 |
+
->where('opt.attribute_id = ?', $attributeId)
|
147 |
+
->where('opt.store_id = ?', $storeId);
|
148 |
+
|
149 |
+
$value = $this->getConnRead()->fetchCol($query);
|
150 |
+
if (is_array($value) && @$value[0] === null)
|
151 |
+
$value = null;
|
152 |
+
elseif (is_array($value) && isset($value[0]))
|
153 |
+
$value = $value[0];
|
154 |
+
else if (is_array($value) && count($value) == 0)
|
155 |
+
$value = null;
|
156 |
+
|
157 |
+
if (is_null($value) && $storeId != Mage_Core_Model_App::ADMIN_STORE_ID && $strict === false)
|
158 |
+
{
|
159 |
+
return $this->getProductAttributeSelectValue($attribute, $valueId, Mage_Core_Model_App::ADMIN_STORE_ID, true, $debug);
|
160 |
+
}
|
161 |
+
|
162 |
+
return $value;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Get categories ids by product id.
|
167 |
+
*
|
168 |
+
* @param string $type_id
|
169 |
+
* @param string $sku
|
170 |
+
* @param string $parent_type_id
|
171 |
+
* @return array|false
|
172 |
+
*/
|
173 |
+
public function getCategoriesById($productId)
|
174 |
+
{
|
175 |
+
$data = false;
|
176 |
+
|
177 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
178 |
+
$query = $conn->select()
|
179 |
+
->from($this->getRes()->getTableName('catalog/category_product'),
|
180 |
+
array('category_id'))
|
181 |
+
->where('product_id = ?', $productId);
|
182 |
+
|
183 |
+
$result = $this->getConnRead()->fetchAll($query);
|
184 |
+
|
185 |
+
if ($result !== false)
|
186 |
+
{
|
187 |
+
$data = array();
|
188 |
+
foreach ($result as $k => $row)
|
189 |
+
$data[] = $row['category_id'];
|
190 |
+
}
|
191 |
+
return $data;
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Gets stores ids of product(s).
|
196 |
+
* @param int|array $productId
|
197 |
+
* @return array()
|
198 |
+
*/
|
199 |
+
public function getProductInStoresIds($productId)
|
200 |
+
{
|
201 |
+
|
202 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
203 |
+
|
204 |
+
if (is_array($productId))
|
205 |
+
{
|
206 |
+
$value = array();
|
207 |
+
foreach ($productId as $pid)
|
208 |
+
$value[$pid] = array();
|
209 |
+
|
210 |
+
$query = $conn->select()
|
211 |
+
->from(array('pw' => $this->getRes()->getTableName('catalog/product_website')),
|
212 |
+
array('product_id' => 'pw.product_id',
|
213 |
+
'store_id' => 's.store_id'))
|
214 |
+
->joinInner(array('s' => $this->getRes()->getTableName('core/store')),
|
215 |
+
's.website_id = pw.website_id',
|
216 |
+
array())
|
217 |
+
->where('pw.product_id IN (?)', $productId);
|
218 |
+
|
219 |
+
$rows = $this->getConnRead()->fetchAll($query);
|
220 |
+
foreach ($rows as $row)
|
221 |
+
{
|
222 |
+
if (!isset($value[$row['product_id']]))
|
223 |
+
$value[$row['product_id']] = array();
|
224 |
+
$value[$row['product_id']][] = $row['store_id'];
|
225 |
+
}
|
226 |
+
return $value;
|
227 |
+
}
|
228 |
+
|
229 |
+
$query = $conn->select()
|
230 |
+
->from(array('pw' => $this->getRes()->getTableName('catalog/product_website')),
|
231 |
+
's.store_id')
|
232 |
+
->joinInner(array('s' => $this->getRes()->getTableName('core/store')),
|
233 |
+
's.website_id = pw.website_id',
|
234 |
+
array())
|
235 |
+
->where('pw.product_id = ?', $productId);
|
236 |
+
|
237 |
+
$value = $this->getConnRead()->fetchCol($query);
|
238 |
+
|
239 |
+
return $value;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @param int $productId - parent product id
|
244 |
+
* @return array
|
245 |
+
*/
|
246 |
+
public function getChildsIds($productId)
|
247 |
+
{
|
248 |
+
$data = false;
|
249 |
+
|
250 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
251 |
+
$query = $conn->select()
|
252 |
+
->from(array('cpe' => $this->getRes()->getTableName('catalog/product')),
|
253 |
+
array('cpe.entity_id')
|
254 |
+
)
|
255 |
+
->joinInner(array('cpsl' => $this->getRes()->getTableName('catalog/product_super_link')),
|
256 |
+
'cpe.entity_id = cpsl.product_id',
|
257 |
+
array())
|
258 |
+
->joinInner(array('cpe_parent' => $this->getRes()->getTableName('catalog/product')),
|
259 |
+
'cpsl.parent_id = cpe_parent.entity_id',
|
260 |
+
array())
|
261 |
+
->where('cpe_parent.entity_id = ?', $productId);
|
262 |
+
|
263 |
+
$result = $this->getConnRead()->fetchAll($query);
|
264 |
+
|
265 |
+
if ($result !== false)
|
266 |
+
{
|
267 |
+
foreach ($result as $row)
|
268 |
+
{
|
269 |
+
$data[] = $row['entity_id'];
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
return $data;
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* @param int $productId - parent product id
|
278 |
+
* @return array
|
279 |
+
*/
|
280 |
+
public function getConfigurableAttributeCodes($productId)
|
281 |
+
{
|
282 |
+
$data = false;
|
283 |
+
|
284 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
285 |
+
$query = $conn->select()
|
286 |
+
->from(array('csa' => $this->getRes()->getTableName('catalog/product_super_attribute')),
|
287 |
+
array('eav.attribute_code'))
|
288 |
+
->joinInner(array('eav' => $this->getRes()->getTableName('eav/attribute')),
|
289 |
+
'eav.attribute_id = csa.attribute_code',
|
290 |
+
array())
|
291 |
+
->where('csa.product_id = ?', $productId);
|
292 |
+
|
293 |
+
$result = $this->getConnRead()->fetchAll($query);
|
294 |
+
|
295 |
+
if ($result !== false)
|
296 |
+
{
|
297 |
+
foreach ($result as $row)
|
298 |
+
{
|
299 |
+
$data[] = $row['attribute_code'];
|
300 |
+
}
|
301 |
+
}
|
302 |
+
|
303 |
+
return $data;
|
304 |
+
}
|
305 |
+
|
306 |
+
public function explodeMultiselectValue($value)
|
307 |
+
{
|
308 |
+
$arr = array();
|
309 |
+
if (!empty($value))
|
310 |
+
{
|
311 |
+
$arr = explode(',', $value);
|
312 |
+
foreach ($arr as $k => $v) $arr[$k] = trim($v);
|
313 |
+
}
|
314 |
+
return $arr;
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* @return Mage_Core_Model_Resource
|
319 |
+
*/
|
320 |
+
public function getRes()
|
321 |
+
{
|
322 |
+
if (is_null($this->_res))
|
323 |
+
{
|
324 |
+
$this->_res = Mage::getSingleton('core/resource');
|
325 |
+
}
|
326 |
+
return $this->_res;
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* @return Varien_Db_Adapter_Pdo_Mysql
|
331 |
+
*/
|
332 |
+
public function getConnRead()
|
333 |
+
{
|
334 |
+
if (is_null($this->_conn_read))
|
335 |
+
{
|
336 |
+
$this->_conn_read = $this->getRes()->getConnection('core_read');
|
337 |
+
}
|
338 |
+
return $this->_conn_read;
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* @return Varien_Db_Adapter_Pdo_Mysql
|
343 |
+
*/
|
344 |
+
public function getConnWrite()
|
345 |
+
{
|
346 |
+
if (is_null($this->_conn_write))
|
347 |
+
{
|
348 |
+
$this->_conn_write = $this->getRes()->getConnection('core_write');
|
349 |
+
}
|
350 |
+
return $this->_conn_write;
|
351 |
+
}
|
352 |
+
|
353 |
+
public function getMagentoEdition()
|
354 |
+
{
|
355 |
+
if (is_callable('Mage::getEdition'))
|
356 |
+
{
|
357 |
+
return Mage::getEdition();
|
358 |
+
}
|
359 |
+
else
|
360 |
+
{
|
361 |
+
$features = array('Enterprise_Enterprise', 'Enterprise_AdminGws',
|
362 |
+
'Enterprise_Checkout', 'Enterprise_Customer');
|
363 |
+
$editions = array(
|
364 |
+
'Enterprise' => array(true, true, true, true), // ALL features
|
365 |
+
'Professional' => array(true, false), // ONLY the first
|
366 |
+
'Community' => array(false), // NO features
|
367 |
+
);
|
368 |
+
|
369 |
+
foreach ($editions as $editionName => $featuresMap)
|
370 |
+
{
|
371 |
+
$match = true;
|
372 |
+
|
373 |
+
foreach($featuresMap as $i => $featureValue)
|
374 |
+
$match = $match && ($featureValue === (bool) Mage::getConfig()->getModuleConfig($features[$i]));
|
375 |
+
|
376 |
+
if ($match)
|
377 |
+
return $editionName;
|
378 |
+
}
|
379 |
+
|
380 |
+
return "Unknown";
|
381 |
+
}
|
382 |
+
}
|
383 |
+
}
|
app/code/community/Doofinder/Feed/Test/Controller/Index.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Doofinder_Feed_Test_Controller_Index extends EcomDev_PHPUnit_Test_Case_Controller
|
3 |
+
{
|
4 |
+
/**
|
5 |
+
* Index controller test
|
6 |
+
*
|
7 |
+
* @test
|
8 |
+
* @doNotIndexAll
|
9 |
+
*/
|
10 |
+
public function testIndex()
|
11 |
+
{
|
12 |
+
$this->dispatch('doofinder');
|
13 |
+
$this->assertRequestRoute('doofinder_feed/index/index');
|
14 |
+
}
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Feed controller test
|
18 |
+
*
|
19 |
+
* @test
|
20 |
+
* @loadFixture
|
21 |
+
* @doNotIndexAll
|
22 |
+
* @dataProvider dataProvider
|
23 |
+
*/
|
24 |
+
public function testFeed()
|
25 |
+
{
|
26 |
+
$this->dispatch('doofinder/feed');
|
27 |
+
$this->assertRequestRoute('doofinder_feed/feed/index');
|
28 |
+
// var_dump($this->getResponse());
|
29 |
+
// $this->reset;
|
30 |
+
// $this->assertRequestRoute('cms');
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Feed controller test
|
35 |
+
*
|
36 |
+
* @test
|
37 |
+
* @loadFixture
|
38 |
+
* @doNotIndexAll
|
39 |
+
* @dataProvider dataProvider
|
40 |
+
*/
|
41 |
+
public function testConfig()
|
42 |
+
{
|
43 |
+
$this->dispatch('doofinder/feed/config');
|
44 |
+
$this->assertRequestRoute('doofinder_feed/feed/config');
|
45 |
+
// $this->assertRequestRoute('cms');
|
46 |
+
}
|
47 |
+
}
|
app/code/community/Doofinder/Feed/Test/Controller/Index/fixtures/testConfig.yaml
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
-
|
app/code/community/Doofinder/Feed/Test/Controller/Index/fixtures/testFeed.yaml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<<<<<<< HEAD
|
2 |
+
-
|
3 |
+
=======
|
4 |
+
config:
|
5 |
+
default/catalog/price/scope: 1 # Set price scope to website
|
6 |
+
>>>>>>> bug/price-calculation
|
app/code/community/Doofinder/Feed/Test/Controller/Index/fixtures/testIndex.yaml
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
-
|
app/code/community/Doofinder/Feed/Test/Controller/Index/providers/testConfig.yaml
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
-
|
app/code/community/Doofinder/Feed/Test/Controller/Index/providers/testFeed.yaml
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
-
|
2 |
+
-
|
app/code/community/Doofinder/Feed/Test/Controller/Index/providers/testIndex.yaml
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
-
|
app/code/community/Doofinder/Feed/Test/Model/Product.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Doofinder_Feed_Test_Model_Product extends EcomDev_PHPUnit_Test_Case
|
3 |
+
{
|
4 |
+
/**
|
5 |
+
* Product price calculation test
|
6 |
+
*
|
7 |
+
* @test
|
8 |
+
* @loadFixture
|
9 |
+
* @doNotIndexAll
|
10 |
+
* @dataProvider dataProvider
|
11 |
+
*/
|
12 |
+
public function testGenerator($productId, $storeId)
|
13 |
+
{
|
14 |
+
$storeId = Mage::app()->getStore($storeId)->getId();
|
15 |
+
|
16 |
+
// var_dump(Mage::getConfig()->getNode('default/tax/calculation'));
|
17 |
+
|
18 |
+
$generator = Mage::helper('doofinder_feed');
|
19 |
+
|
20 |
+
/* @var $product Mage_Catalog_Model_Product */
|
21 |
+
$product = Mage::getModel('catalog/product')
|
22 |
+
->setStoreId($storeId)
|
23 |
+
->load($productId);
|
24 |
+
|
25 |
+
$prices = $generator->collectProductPrices(
|
26 |
+
$product,
|
27 |
+
$storeId,
|
28 |
+
false,
|
29 |
+
false,
|
30 |
+
true
|
31 |
+
);
|
32 |
+
|
33 |
+
$finalPriceInclTax = Mage::helper('tax')->getPrice($product, $product->getFinalPrice(), true);
|
34 |
+
$finalPriceExclTax = Mage::helper('tax')->getPrice($product, $product->getFinalPrice(), false);
|
35 |
+
|
36 |
+
// Check that prices w/ and without tax are different
|
37 |
+
// $this->assertNotEquals(
|
38 |
+
// $finalPriceInclTax,
|
39 |
+
// $finalPriceExclTax
|
40 |
+
// );
|
41 |
+
|
42 |
+
$expected = $this->expected('%s-%s', $productId, $storeId);
|
43 |
+
|
44 |
+
// Check that final price matches expected
|
45 |
+
if (isset($prices['sale_price']))
|
46 |
+
{
|
47 |
+
// With tax
|
48 |
+
$this->assertEquals(
|
49 |
+
Mage::helper('core')->currency($finalPriceInclTax, true, false),
|
50 |
+
Mage::helper('core')->currency($prices['sale_price']['including_tax'], true, false)
|
51 |
+
);
|
52 |
+
// Without tax
|
53 |
+
$this->assertEquals(
|
54 |
+
Mage::helper('core')->currency($finalPriceExclTax, true, false),
|
55 |
+
Mage::helper('core')->currency($prices['sale_price']['excluding_tax'], true, false)
|
56 |
+
);
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
app/code/community/Doofinder/Feed/Test/Model/Product/expectations/testGenerator.yaml
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
1-1:
|
2 |
+
final_price: 12.99
|
3 |
+
price: 12.99
|
4 |
+
1-2: # Product=Book Store=USA
|
5 |
+
final_price: 9.99
|
6 |
+
price: 12.99
|
7 |
+
1-3: # Product=Book Store=Canada
|
8 |
+
final_price: 12.99
|
9 |
+
price: 12.99
|
10 |
+
1-4: # Product=Book Store=Germany
|
11 |
+
final_price: 5.99
|
12 |
+
price: 9.99
|
app/code/community/Doofinder/Feed/Test/Model/Product/fixtures/testGenerator.yaml
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
scope:
|
2 |
+
website: # Initialize websites
|
3 |
+
- website_id: 2
|
4 |
+
code: usa_website
|
5 |
+
name: USA Website
|
6 |
+
default_group_id: 2
|
7 |
+
- website_id: 3
|
8 |
+
code: canada_website
|
9 |
+
name: Canada Website
|
10 |
+
default_group_id: 3
|
11 |
+
- website_id: 4
|
12 |
+
code: german_website
|
13 |
+
name: German Website
|
14 |
+
default_group_id: 4
|
15 |
+
group: # Initializes store groups
|
16 |
+
- group_id: 2
|
17 |
+
website_id: 2
|
18 |
+
name: USA Store Group
|
19 |
+
default_store_id: 2
|
20 |
+
root_category_id: 2 # Default Category
|
21 |
+
- group_id: 3
|
22 |
+
website_id: 3
|
23 |
+
name: Canada Store Group
|
24 |
+
default_store_id: 3
|
25 |
+
root_category_id: 2 # Default Category
|
26 |
+
- group_id: 4
|
27 |
+
website_id: 4
|
28 |
+
name: German Store Group
|
29 |
+
default_store_id: 4
|
30 |
+
root_category_id: 2 # Default Category
|
31 |
+
store: # Initializes store views
|
32 |
+
- store_id: 2
|
33 |
+
website_id: 2
|
34 |
+
group_id: 2
|
35 |
+
code: usa
|
36 |
+
name: USA Store
|
37 |
+
is_active: 1
|
38 |
+
- store_id: 3
|
39 |
+
website_id: 3
|
40 |
+
group_id: 3
|
41 |
+
code: canada
|
42 |
+
name: Canada Store
|
43 |
+
is_active: 1
|
44 |
+
- store_id: 4
|
45 |
+
website_id: 4
|
46 |
+
group_id: 4
|
47 |
+
code: germany
|
48 |
+
name: Germany Store
|
49 |
+
is_active: 1
|
50 |
+
config:
|
51 |
+
default/catalog/price/scope: 1 # Set price scope to website
|
52 |
+
default/tax/calculation/algorithm: UNIT_BASE_CACLULATION
|
53 |
+
default/tax/calculation/based_on: origin
|
54 |
+
default/tax/calculation/apply_tax_on: 0
|
55 |
+
default/tax/calculation/price_includes_tax: 0
|
56 |
+
default/tax/calculation/apply_after_discount: 1
|
57 |
+
default/tax/defaults/country: DE
|
58 |
+
default/tax/defaults/region: 0
|
59 |
+
default/tax/defaults/postcode: *
|
60 |
+
default/tax/display/type: 2
|
61 |
+
default/tax/sales_display/price: 1
|
62 |
+
tables:
|
63 |
+
tax/tax_class:
|
64 |
+
- class_id: 3
|
65 |
+
class_name: Retail Customer
|
66 |
+
class_type: CUSTOMER
|
67 |
+
tax/tax_calculation_rate:
|
68 |
+
- tax_calculation_rate_id: 3
|
69 |
+
tax_country_id: DE
|
70 |
+
tax_region_id: 12
|
71 |
+
tax_postcode: *
|
72 |
+
code: VAT
|
73 |
+
rate: 19
|
74 |
+
tax/tax_calculation_rule:
|
75 |
+
- tax_calculation_rule_id: 1
|
76 |
+
code: Retail Customer-VAT
|
77 |
+
priority: 1
|
78 |
+
position: 1
|
79 |
+
calculate_subtotal: 0
|
80 |
+
tax/tax_calculation:
|
81 |
+
- tax_calculation_id: 3
|
82 |
+
tax_calculation_rule_id: 1
|
83 |
+
tax_calculation_rate_id: 3
|
84 |
+
customer_tax_class_id: 3
|
85 |
+
product_tax_class_id: 3
|
86 |
+
customer/customer_group:
|
87 |
+
- customer_group_id: 1
|
88 |
+
customer_group_code: General
|
89 |
+
tax_class_id: 3
|
90 |
+
eav:
|
91 |
+
catalog_product:
|
92 |
+
- entity_id: 1
|
93 |
+
attribute_set_id: 4
|
94 |
+
type_id: simple
|
95 |
+
sku: book
|
96 |
+
name: Book
|
97 |
+
short_description: Book
|
98 |
+
description: Book
|
99 |
+
url_key: book
|
100 |
+
stock:
|
101 |
+
qty: 100.00
|
102 |
+
is_in_stock: 1
|
103 |
+
website_ids:
|
104 |
+
- usa_website
|
105 |
+
- canada_website
|
106 |
+
- german_website
|
107 |
+
category_ids:
|
108 |
+
- 2 # Default Category
|
109 |
+
price: 12.99
|
110 |
+
tax_class_id: 3 # Retail Customer-VAT
|
111 |
+
status: 1 # Enabled
|
112 |
+
visibility: 4 # Visible in Catalog & Search
|
113 |
+
/websites: # Set different prices per website
|
114 |
+
usa_website:
|
115 |
+
special_price: 9.99
|
116 |
+
german_website:
|
117 |
+
price: 9.99
|
118 |
+
special_price: 5.99
|
app/code/community/Doofinder/Feed/Test/Model/Product/providers/testGenerator.yaml
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-
|
2 |
+
- 1
|
3 |
+
- default
|
4 |
+
-
|
5 |
+
- 1
|
6 |
+
- usa
|
7 |
+
-
|
8 |
+
- 1
|
9 |
+
- canada
|
10 |
+
-
|
11 |
+
- 1
|
12 |
+
- germany
|
app/code/community/Doofinder/Feed/controllers/DoofinderFeedFeedController.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category controllers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_DoofinderFeedFeedController extends Mage_Adminhtml_Controller_Action
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* Generate feed action
|
16 |
+
*/
|
17 |
+
public function generateAction()
|
18 |
+
{
|
19 |
+
$storeCode = $this->getRequest()->getParam('store', false);
|
20 |
+
|
21 |
+
$codes = array();
|
22 |
+
|
23 |
+
// Create stores codes array
|
24 |
+
if ($storeCode) {
|
25 |
+
$codes[] = $storeCode;
|
26 |
+
} else {
|
27 |
+
$stores = Mage::app()->getStores();
|
28 |
+
foreach ($stores as $store) {
|
29 |
+
if ($store->getIsActive()) {
|
30 |
+
$codes[] = $store->getCode();
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
$scheduleObserver = Mage::getSingleton('doofinder_feed/observers_schedule');
|
36 |
+
|
37 |
+
foreach ($codes as $storeCode) {
|
38 |
+
$scheduleObserver->updateProcess($storeCode, true, true, true);
|
39 |
+
}
|
40 |
+
|
41 |
+
$this->getResponse()->setBody('Feed generation has been scheduled.');
|
42 |
+
}
|
43 |
+
}
|
app/code/community/Doofinder/Feed/controllers/DoofinderFeedLogController.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category controllers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
class Doofinder_Feed_DoofinderFeedLogController extends Mage_Adminhtml_Controller_Action
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* View log for specified process.
|
16 |
+
*/
|
17 |
+
public function viewAction()
|
18 |
+
{
|
19 |
+
$this->loadLayout();
|
20 |
+
$this->renderLayout();
|
21 |
+
}
|
22 |
+
}
|
app/code/community/Doofinder/Feed/controllers/FeedController.php
ADDED
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category controllers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Feed controller for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_FeedController extends Mage_Core_Controller_Front_Action
|
19 |
+
{
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Send JSON headers
|
23 |
+
*/
|
24 |
+
protected function _setJSONHeaders()
|
25 |
+
{
|
26 |
+
$this->getResponse()
|
27 |
+
->clearHeaders()
|
28 |
+
->setHeader('Content-type', 'application/json; charset="utf-8"', true);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Send XML headers
|
33 |
+
*/
|
34 |
+
protected function _setXMLHeaders()
|
35 |
+
{
|
36 |
+
$this->getResponse()
|
37 |
+
->clearHeaders()
|
38 |
+
->setHeader('Content-type', 'application/xml; charset="utf-8"', true);
|
39 |
+
}
|
40 |
+
|
41 |
+
public function indexAction()
|
42 |
+
{
|
43 |
+
$storeCode = $this->_getStoreCode();
|
44 |
+
$config = Mage::helper('doofinder_feed')->getStoreConfig($storeCode);
|
45 |
+
|
46 |
+
// Set options for cron generator
|
47 |
+
$options = array(
|
48 |
+
'_limit_' => $this->_getInteger('limit', null),
|
49 |
+
'_offset_' => 0,
|
50 |
+
'store_code' => $config['storeCode'],
|
51 |
+
'grouped' => (bool) $config['grouped'],
|
52 |
+
'display_price' => (bool) $config['display_price'],
|
53 |
+
'minimal_price' => $this->_getBoolean('minimal_price', false),
|
54 |
+
'customer_group_id' => 0,
|
55 |
+
'image_size' => $config['image_size'],
|
56 |
+
);
|
57 |
+
|
58 |
+
$generator = Mage::getSingleton('doofinder_feed/generator', $options);
|
59 |
+
|
60 |
+
// Convert offset to product id
|
61 |
+
$offset = $this->_getInteger('offset', 0);
|
62 |
+
|
63 |
+
if ($offset > 0) {
|
64 |
+
$collection = $generator->getProductCollection();
|
65 |
+
$collection->getSelect()->limit(1, $offset);
|
66 |
+
|
67 |
+
$item = $collection->fetchItem();
|
68 |
+
|
69 |
+
$offset = $item ? $item->getEntityId() - 1 : -1;
|
70 |
+
}
|
71 |
+
|
72 |
+
$response = '';
|
73 |
+
if ($offset >= 0) {
|
74 |
+
$generator->setData('_offset_', $offset);
|
75 |
+
$this->_setXMLHeaders();
|
76 |
+
|
77 |
+
$response = $generator->run();
|
78 |
+
|
79 |
+
ob_end_clean();
|
80 |
+
}
|
81 |
+
|
82 |
+
$this->getResponse()->setBody($response);
|
83 |
+
}
|
84 |
+
|
85 |
+
public function configAction()
|
86 |
+
{
|
87 |
+
$this->_setJSONHeaders();
|
88 |
+
|
89 |
+
$helper = Mage::helper('doofinder_feed');
|
90 |
+
|
91 |
+
$tools = Mage::getModel('doofinder_feed/tools');
|
92 |
+
|
93 |
+
$storeCodes = array_keys(Mage::app()->getStores(false, true));
|
94 |
+
$storesConfiguration = array();
|
95 |
+
|
96 |
+
// Get file spath
|
97 |
+
$filesUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA).'doofinder'.DS;
|
98 |
+
$filesPath = Mage::getBaseDir('media').DS.'doofinder'.DS;
|
99 |
+
|
100 |
+
foreach ($storeCodes as $code)
|
101 |
+
{
|
102 |
+
$settings = $helper->getStoreConfig($code);
|
103 |
+
|
104 |
+
if ($settings['enabled'])
|
105 |
+
{
|
106 |
+
$filepath = $filesPath.$settings['xmlName'];
|
107 |
+
$fileurl = $filesUrl.$settings['xmlName'];
|
108 |
+
$feedUrl = $filesUrl.$settings['xmlName'];
|
109 |
+
$feedExists = (bool) $this->_feedExists($filepath);
|
110 |
+
}
|
111 |
+
else
|
112 |
+
{
|
113 |
+
$feedUrl = Mage::getUrl('doofinder/feed', array('_store' => $code));
|
114 |
+
$feedExists = true;
|
115 |
+
}
|
116 |
+
|
117 |
+
$oStore = Mage::app()->getStore($code);
|
118 |
+
$L = Mage::getStoreConfig('general/locale/code', $oStore->getId());
|
119 |
+
$storesConfiguration[$code] = array(
|
120 |
+
'language' => strtoupper(substr($L, 0, 2)),
|
121 |
+
'currency' => $oStore->getCurrentCurrencyCode(),
|
122 |
+
'feed' => $feedUrl,
|
123 |
+
'feed_exists' => $feedExists,
|
124 |
+
);
|
125 |
+
}
|
126 |
+
|
127 |
+
$config = array(
|
128 |
+
'platform' => array(
|
129 |
+
'name' => 'Magento',
|
130 |
+
'edition' => $tools->getMagentoEdition(),
|
131 |
+
'version' => Mage::getVersion()
|
132 |
+
),
|
133 |
+
'module' => array(
|
134 |
+
'version' => $this->_getVersion(),
|
135 |
+
'feed' => Mage::getUrl('doofinder/feed'),
|
136 |
+
'options' => array(
|
137 |
+
'language' => $storeCodes,
|
138 |
+
),
|
139 |
+
'configuration' => $storesConfiguration,
|
140 |
+
),
|
141 |
+
);
|
142 |
+
|
143 |
+
$response = Mage::helper('core')->jsonEncode($config);
|
144 |
+
$this->getResponse()->setBody($response);
|
145 |
+
}
|
146 |
+
/**
|
147 |
+
* Check if feed on filepath exists.
|
148 |
+
* @param string $filepath
|
149 |
+
* @return bool
|
150 |
+
*/
|
151 |
+
protected function _feedExists($filepath = null) {
|
152 |
+
if (file_exists($filepath)) {
|
153 |
+
return true;
|
154 |
+
}
|
155 |
+
return false;
|
156 |
+
}
|
157 |
+
|
158 |
+
protected function _dumpMessage($s_level, $s_message, $a_extra=array())
|
159 |
+
{
|
160 |
+
$error = array('status' => $s_level, 'message' => $s_message);
|
161 |
+
|
162 |
+
if (is_array($a_extra) && count($a_extra))
|
163 |
+
$error = array_merge($error, $a_extra);
|
164 |
+
|
165 |
+
$this->_setJSONHeaders();
|
166 |
+
|
167 |
+
$response = Mage::helper('core')->jsonEncode($error);
|
168 |
+
$this->getResponse()->setBody($response);
|
169 |
+
}
|
170 |
+
|
171 |
+
protected function _getVersion()
|
172 |
+
{
|
173 |
+
return Mage::getConfig()
|
174 |
+
->getNode()
|
175 |
+
->modules
|
176 |
+
->Doofinder_Feed
|
177 |
+
->version
|
178 |
+
->asArray();
|
179 |
+
}
|
180 |
+
|
181 |
+
protected function _getStoreCode()
|
182 |
+
{
|
183 |
+
$storeCode = $this->getRequest()->getParam('language');
|
184 |
+
|
185 |
+
if (is_null($storeCode))
|
186 |
+
$storeCode = $this->getRequest()->getParam('store'); // Backwards...
|
187 |
+
|
188 |
+
if (is_null($storeCode))
|
189 |
+
$storeCode = Mage::app()->getStore()->getCode();
|
190 |
+
|
191 |
+
try
|
192 |
+
{
|
193 |
+
return Mage::app()->getStore($storeCode)->getCode();
|
194 |
+
}
|
195 |
+
catch(Mage_Core_Model_Store_Exception $e)
|
196 |
+
{
|
197 |
+
$this->_dumpMessage('error', 'Invalid <language> parameter.',
|
198 |
+
array('code' => 'INVALID_OPTIONS'));
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
protected function _getBoolean($param, $defaultValue = false)
|
203 |
+
{
|
204 |
+
$value = strtolower($this->getRequest()->getParam($param));
|
205 |
+
|
206 |
+
if ( is_numeric($value) )
|
207 |
+
return ((int)($value *= 1) > 0);
|
208 |
+
|
209 |
+
$yes = array('true', 'on', 'yes');
|
210 |
+
$no = array('false', 'off', 'no');
|
211 |
+
|
212 |
+
if ( in_array($value, $yes) )
|
213 |
+
return true;
|
214 |
+
|
215 |
+
if ( in_array($value, $no) )
|
216 |
+
return false;
|
217 |
+
|
218 |
+
return $defaultValue;
|
219 |
+
}
|
220 |
+
|
221 |
+
protected function _getInteger($param, $defaultValue)
|
222 |
+
{
|
223 |
+
$value = $this->getRequest()->getParam($param);
|
224 |
+
if ( is_numeric($value) )
|
225 |
+
return (int)($value *= 1);
|
226 |
+
return $defaultValue;
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Creates directory.
|
231 |
+
* @param string $dir
|
232 |
+
* @return bool
|
233 |
+
*/
|
234 |
+
protected function _createDirectory($dir = null) {
|
235 |
+
if (!$dir) return false;
|
236 |
+
|
237 |
+
if(!mkdir($dir, 0777, true)) {
|
238 |
+
Mage::throwException('Could not create directory: '.$dir);
|
239 |
+
}
|
240 |
+
|
241 |
+
return true;
|
242 |
+
}
|
243 |
+
|
244 |
+
/*
|
245 |
+
TEST TOOLS
|
246 |
+
*/
|
247 |
+
|
248 |
+
// public function testsAction()
|
249 |
+
// {
|
250 |
+
// if ( !in_array(Mage::helper('core/http')->getRemoteAddr(), array('127.0.0.1', '::1')) )
|
251 |
+
// {
|
252 |
+
// $this->norouteAction();
|
253 |
+
// return false;
|
254 |
+
// }
|
255 |
+
|
256 |
+
// $oStore = Mage::app()->getStore($this->_getStoreCode());
|
257 |
+
// $bGrouped = $this->_getBoolean('grouped', true);
|
258 |
+
// $bMinimalPrice = $this->_getBoolean('minimal_price', false);
|
259 |
+
// $bCurrencyConvert = $this->_getBoolean('convert_currency', true);
|
260 |
+
// $iCustomerGroupId = $this->_getInteger('customer_group', 0);
|
261 |
+
|
262 |
+
// $ids = array(
|
263 |
+
// 'simple' => array(166, 27),
|
264 |
+
// 'grouped' => 54,
|
265 |
+
// 'configurable' => 83,
|
266 |
+
// 'virtual' => 142,
|
267 |
+
// 'bundle' => 158,
|
268 |
+
// 'downloadable' => 167
|
269 |
+
// );
|
270 |
+
|
271 |
+
// $data = array(
|
272 |
+
// 'store' => array(
|
273 |
+
// 'store_id' => $oStore->getStoreId(),
|
274 |
+
// 'website_id' => $oStore->getWebsiteId(),
|
275 |
+
// 'base_currency' => $oStore->getBaseCurrencyCode(),
|
276 |
+
// 'current_currency' => $oStore->getCurrentCurrencyCode(),
|
277 |
+
// 'default_currency' => $oStore->getDefaultCurrencyCode(),
|
278 |
+
// ),
|
279 |
+
// 'products' => array(),
|
280 |
+
// );
|
281 |
+
|
282 |
+
// $rule = Mage::getModel('catalogrule/rule');
|
283 |
+
// $dataHelper = Mage::helper('doofinder_feed');
|
284 |
+
|
285 |
+
// foreach ($ids as $product_type => $ids)
|
286 |
+
// {
|
287 |
+
// foreach ((array) $ids as $id)
|
288 |
+
// {
|
289 |
+
// $product = Mage::getModel('catalog/product')
|
290 |
+
// ->setStoreId($oStore->getStoreId())
|
291 |
+
// ->setCustomerGroupId($iCustomerGroupId)
|
292 |
+
// ->load($id);
|
293 |
+
|
294 |
+
// $data['products'][$id] = array(
|
295 |
+
// 'product_type' => $product_type,
|
296 |
+
// 'name' => $product->getName(),
|
297 |
+
// );
|
298 |
+
|
299 |
+
// $data['products'][$id] = array_merge(
|
300 |
+
// $data['products'][$id],
|
301 |
+
// $dataHelper->collectProductPrices($product, $oStore, $bCurrencyConvert, $bMinimalPrice, $bGrouped)
|
302 |
+
// );
|
303 |
+
// }
|
304 |
+
// }
|
305 |
+
|
306 |
+
// $this->_setJSONHeaders();
|
307 |
+
|
308 |
+
// $response = Mage::helper('core')->jsonEncode($data);
|
309 |
+
// $this->getResponse()->setBody($response);
|
310 |
+
// }
|
311 |
+
}
|
app/code/community/Doofinder/Feed/controllers/IndexController.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* This file is part of Doofinder_Feed.
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @category controllers
|
8 |
+
* @package Doofinder_Feed
|
9 |
+
* @version 1.6.5
|
10 |
+
*/
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Index controller for Doofinder Feed
|
14 |
+
*
|
15 |
+
* @version 1.6.5
|
16 |
+
* @package Doofinder_Feed
|
17 |
+
*/
|
18 |
+
class Doofinder_Feed_IndexController extends Mage_Core_Controller_Front_Action
|
19 |
+
{
|
20 |
+
public function indexAction()
|
21 |
+
{
|
22 |
+
$this->_redirect('/');
|
23 |
+
}
|
24 |
+
|
25 |
+
public function testAction() {
|
26 |
+
$allStores = Mage::app()->getStores();
|
27 |
+
foreach ($allStores as $store) {
|
28 |
+
var_dump($store->getCode());
|
29 |
+
}
|
30 |
+
|
31 |
+
}
|
32 |
+
}
|
app/code/community/Doofinder/Feed/etc/config.xml
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<config>
|
3 |
+
|
4 |
+
<modules>
|
5 |
+
<Doofinder_Feed>
|
6 |
+
<version>1.6.5</version>
|
7 |
+
</Doofinder_Feed>
|
8 |
+
</modules>
|
9 |
+
<global>
|
10 |
+
<helpers>
|
11 |
+
<doofinder_feed>
|
12 |
+
<class>Doofinder_Feed_Helper</class>
|
13 |
+
</doofinder_feed>
|
14 |
+
</helpers>
|
15 |
+
<models>
|
16 |
+
<doofinder_feed>
|
17 |
+
<class>Doofinder_Feed_Model</class>
|
18 |
+
<resourceModel>doofinder_feed_mysql4</resourceModel>
|
19 |
+
</doofinder_feed>
|
20 |
+
<doofinder_feed_mysql4>
|
21 |
+
<class>Doofinder_Feed_Model_Mysql4</class>
|
22 |
+
<entities>
|
23 |
+
<cron>
|
24 |
+
<table>doofinder_feed</table>
|
25 |
+
</cron>
|
26 |
+
<log>
|
27 |
+
<table>doofinder_log</table>
|
28 |
+
</log>
|
29 |
+
</entities>
|
30 |
+
</doofinder_feed_mysql4>
|
31 |
+
<catalogsearch_resource>
|
32 |
+
<rewrite>
|
33 |
+
<fulltext>Doofinder_Feed_Model_CatalogSearch_Resource_Fulltext</fulltext>
|
34 |
+
</rewrite>
|
35 |
+
</catalogsearch_resource>
|
36 |
+
</models>
|
37 |
+
<resources>
|
38 |
+
<doofinder_feed_write>
|
39 |
+
<connection>
|
40 |
+
<use>core_write</use>
|
41 |
+
</connection>
|
42 |
+
</doofinder_feed_write>
|
43 |
+
<doofinder_feed_read>
|
44 |
+
<connection>
|
45 |
+
<use>core_read</use>
|
46 |
+
</connection>
|
47 |
+
</doofinder_feed_read>
|
48 |
+
<doofinder_feed_setup>
|
49 |
+
<setup>
|
50 |
+
<module>Doofinder_Feed</module>
|
51 |
+
<class>Doofinder_Feed_Model_Resource_Mysql4_Setup</class>
|
52 |
+
</setup>
|
53 |
+
<connection>
|
54 |
+
<use>core_setup</use>
|
55 |
+
</connection>
|
56 |
+
</doofinder_feed_setup>
|
57 |
+
</resources>
|
58 |
+
<blocks>
|
59 |
+
<doofinder_feed>
|
60 |
+
<class>Doofinder_Feed_Block</class>
|
61 |
+
</doofinder_feed>
|
62 |
+
</blocks>
|
63 |
+
<events>
|
64 |
+
<admin_system_config_changed_section_doofinder_cron>
|
65 |
+
<observers>
|
66 |
+
<doofinder_feed>
|
67 |
+
<type>singleton</type>
|
68 |
+
<class>doofinder_feed/observers_schedule</class>
|
69 |
+
<method>saveNewSchedule</method>
|
70 |
+
</doofinder_feed>
|
71 |
+
</observers>
|
72 |
+
</admin_system_config_changed_section_doofinder_cron>
|
73 |
+
<catalog_product_save_after>
|
74 |
+
<observers>
|
75 |
+
<doofinder_feed>
|
76 |
+
<class>doofinder_feed/observers_feed</class>
|
77 |
+
<method>updateSearchEngineIndexes</method>
|
78 |
+
</doofinder_feed>
|
79 |
+
</observers>
|
80 |
+
</catalog_product_save_after>
|
81 |
+
</events>
|
82 |
+
</global>
|
83 |
+
|
84 |
+
<adminhtml>
|
85 |
+
<layout>
|
86 |
+
<updates>
|
87 |
+
<doofinder_feed>
|
88 |
+
<file>doofinder.xml</file>
|
89 |
+
</doofinder_feed>
|
90 |
+
</updates>
|
91 |
+
</layout>
|
92 |
+
<acl>
|
93 |
+
<resources>
|
94 |
+
<admin>
|
95 |
+
<children>
|
96 |
+
<system>
|
97 |
+
<children>
|
98 |
+
<config>
|
99 |
+
<children>
|
100 |
+
<doofinder_cron>
|
101 |
+
<title>Cron Options</title>
|
102 |
+
</doofinder_cron>
|
103 |
+
<doofinder_search>
|
104 |
+
<title>Search Options</title>
|
105 |
+
</doofinder_search>
|
106 |
+
</children>
|
107 |
+
</config>
|
108 |
+
</children>
|
109 |
+
</system>
|
110 |
+
</children>
|
111 |
+
</admin>
|
112 |
+
</resources>
|
113 |
+
</acl>
|
114 |
+
</adminhtml>
|
115 |
+
|
116 |
+
<install></install>
|
117 |
+
|
118 |
+
<frontend>
|
119 |
+
<layout>
|
120 |
+
<updates>
|
121 |
+
<doofinder_feed>
|
122 |
+
<file>doofinder.xml</file>
|
123 |
+
</doofinder_feed>
|
124 |
+
</updates>
|
125 |
+
</layout>
|
126 |
+
<routers>
|
127 |
+
<doofinder_feed>
|
128 |
+
<use>standard</use>
|
129 |
+
<args>
|
130 |
+
<module>Doofinder_Feed</module>
|
131 |
+
<frontName>doofinder</frontName>
|
132 |
+
</args>
|
133 |
+
</doofinder_feed>
|
134 |
+
</routers>
|
135 |
+
</frontend>
|
136 |
+
|
137 |
+
<default>
|
138 |
+
<doofinder>
|
139 |
+
<feed>
|
140 |
+
|
141 |
+
<enabled>0</enabled>
|
142 |
+
<debug>1</debug>
|
143 |
+
|
144 |
+
<group_configurable_products>1</group_configurable_products>
|
145 |
+
|
146 |
+
<product_types><![CDATA[simple,virtual,bundle,configurable,downloadable,grouped]]></product_types>
|
147 |
+
<!--product_types><![CDATA[bundle]]></product_types-->
|
148 |
+
<excluded_attributes><![CDATA[gallery,image,small_image,special_price,special_from_date,special_to_date,price_view,url_key]]></excluded_attributes>
|
149 |
+
<additional_attributes><![CDATA[status]]></additional_attributes>
|
150 |
+
|
151 |
+
<directives>
|
152 |
+
<df_directive_id>
|
153 |
+
<label><![CDATA[Product Id]]></label>
|
154 |
+
</df_directive_id>
|
155 |
+
<df_directive_url>
|
156 |
+
<label><![CDATA[Product URL]]></label>
|
157 |
+
</df_directive_url>
|
158 |
+
<df_directive_image_link>
|
159 |
+
<label><![CDATA[Product Image URL (Base)]]></label>
|
160 |
+
</df_directive_image_link>
|
161 |
+
<df_directive_image_link_thumbnail>
|
162 |
+
<label><![CDATA[Product Image URL (Thumbnail)]]></label>
|
163 |
+
</df_directive_image_link_thumbnail>
|
164 |
+
<df_directive_image_link_small>
|
165 |
+
<label><![CDATA[Product Image URL (Small Image)]]></label>
|
166 |
+
</df_directive_image_link_small>
|
167 |
+
<df_directive_sale_price>
|
168 |
+
<label><![CDATA[Sale Price]]></label>
|
169 |
+
</df_directive_sale_price>
|
170 |
+
<df_directive_price>
|
171 |
+
<label><![CDATA[Price]]></label>
|
172 |
+
</df_directive_price>
|
173 |
+
<df_directive_currency>
|
174 |
+
<label><![CDATA[Store Currency]]></label>
|
175 |
+
</df_directive_currency>
|
176 |
+
<df_directive_availability>
|
177 |
+
<label><![CDATA[Availability]]></label>
|
178 |
+
</df_directive_availability>
|
179 |
+
<df_directive_condition>
|
180 |
+
<label><![CDATA[Product Condition]]></label>
|
181 |
+
</df_directive_condition>
|
182 |
+
</directives>
|
183 |
+
|
184 |
+
<fields>
|
185 |
+
<id>
|
186 |
+
<label>Unique Identifier</label>
|
187 |
+
</id>
|
188 |
+
<mpn>
|
189 |
+
<label>Product SKU</label>
|
190 |
+
</mpn>
|
191 |
+
<title>
|
192 |
+
<label>Title</label>
|
193 |
+
</title>
|
194 |
+
<description>
|
195 |
+
<label>Description</label>
|
196 |
+
</description>
|
197 |
+
<long_description>
|
198 |
+
<label>Long Description</label>
|
199 |
+
</long_description>
|
200 |
+
<meta_title>
|
201 |
+
<label>Meta Title</label>
|
202 |
+
</meta_title>
|
203 |
+
<meta_keyword>
|
204 |
+
<label>Meta Keywords</label>
|
205 |
+
</meta_keyword>
|
206 |
+
<meta_description>
|
207 |
+
<label>Meta Description</label>
|
208 |
+
</meta_description>
|
209 |
+
<brand>
|
210 |
+
<label>Brand</label>
|
211 |
+
</brand>
|
212 |
+
<link>
|
213 |
+
<label>Product Link</label>
|
214 |
+
</link>
|
215 |
+
<image_link>
|
216 |
+
<label>Image</label>
|
217 |
+
</image_link>
|
218 |
+
<price>
|
219 |
+
<label>Normal Price</label>
|
220 |
+
</price>
|
221 |
+
<sale_price>
|
222 |
+
<label>Sale Price</label>
|
223 |
+
</sale_price>
|
224 |
+
<availability>
|
225 |
+
<label>Availability</label>
|
226 |
+
</availability>
|
227 |
+
<currency>
|
228 |
+
<label>Currency</label>
|
229 |
+
</currency>
|
230 |
+
</fields>
|
231 |
+
|
232 |
+
</feed>
|
233 |
+
</doofinder>
|
234 |
+
<doofinder_cron>
|
235 |
+
<schedule_settings>
|
236 |
+
<enabled>1</enabled>
|
237 |
+
<name>doofinder-{store_code}.xml</name>
|
238 |
+
<time>0,0,0</time>
|
239 |
+
<frequency>D</frequency>
|
240 |
+
<delay>5</delay>
|
241 |
+
<step>1000</step>
|
242 |
+
<reset>0</reset>
|
243 |
+
</schedule_settings>
|
244 |
+
<feed_settings>
|
245 |
+
<display_price>1</display_price>
|
246 |
+
<grouped>0</grouped>
|
247 |
+
<image_size></image_size>
|
248 |
+
<atomic_updates_enabled>0</atomic_updates_enabled>
|
249 |
+
<categories_in_navigation>0</categories_in_navigation>
|
250 |
+
</feed_settings>
|
251 |
+
<attributes_mapping>
|
252 |
+
<id>df_directive_id</id>
|
253 |
+
<mpn>sku</mpn>
|
254 |
+
<title>name</title>
|
255 |
+
<description>short_description</description>
|
256 |
+
<brand>manufacturer</brand>
|
257 |
+
<link>df_directive_url</link>
|
258 |
+
<image_link>df_directive_image_link</image_link>
|
259 |
+
<price>df_directive_price</price>
|
260 |
+
<sale_price>df_directive_sale_price</sale_price>
|
261 |
+
<availability>df_directive_availability</availability>
|
262 |
+
<currency>df_directive_currency</currency>
|
263 |
+
<!--long_description>description</long_description>
|
264 |
+
<meta_title>meta_title</meta_title>
|
265 |
+
<meta_keyword>meta_keyword</meta_keyword>
|
266 |
+
<meta_description>meta_description</meta_description-->
|
267 |
+
</attributes_mapping>
|
268 |
+
</doofinder_cron>
|
269 |
+
<doofinder_search>
|
270 |
+
<layer_settings>
|
271 |
+
<enabled>0</enabled>
|
272 |
+
</layer_settings>
|
273 |
+
|
274 |
+
<internal_settings>
|
275 |
+
<request_limit>100</request_limit>
|
276 |
+
<total_limit>500</total_limit>
|
277 |
+
</internal_settings>
|
278 |
+
</doofinder_search>
|
279 |
+
</default>
|
280 |
+
|
281 |
+
<stores></stores>
|
282 |
+
|
283 |
+
<websites></websites>
|
284 |
+
|
285 |
+
<crontab>
|
286 |
+
<jobs>
|
287 |
+
<doofinder_feed_generate>
|
288 |
+
<schedule>
|
289 |
+
<cron_expr>* * * * *</cron_expr>
|
290 |
+
</schedule>
|
291 |
+
<run>
|
292 |
+
<model>doofinder_feed/observers_feed::generateFeed</model>
|
293 |
+
</run>
|
294 |
+
</doofinder_feed_generate>
|
295 |
+
<doofinder_feed_schedule>
|
296 |
+
<schedule>
|
297 |
+
<cron_expr>1 */12 * * *</cron_expr>
|
298 |
+
</schedule>
|
299 |
+
<run>
|
300 |
+
<model>doofinder_feed/observers_schedule::regenerateSchedule</model>
|
301 |
+
</run>
|
302 |
+
</doofinder_feed_schedule>
|
303 |
+
<doofinder_feed_clear_logs>
|
304 |
+
<schedule>
|
305 |
+
<cron_expr>30 2 * * *</cron_expr>
|
306 |
+
</schedule>
|
307 |
+
<run>
|
308 |
+
<model>doofinder_feed/observers_logs::clearLogs</model>
|
309 |
+
</run>
|
310 |
+
</doofinder_feed_clear_logs>
|
311 |
+
</jobs>
|
312 |
+
</crontab>
|
313 |
+
|
314 |
+
<phpunit>
|
315 |
+
<suite>
|
316 |
+
<modules>
|
317 |
+
<Doofinder_Feed />
|
318 |
+
</modules>
|
319 |
+
<groups>
|
320 |
+
<controller>Controller</controller>
|
321 |
+
</groups>
|
322 |
+
</suite>
|
323 |
+
</phpunit>
|
324 |
+
|
325 |
+
<admin>
|
326 |
+
<routers>
|
327 |
+
<adminhtml>
|
328 |
+
<args>
|
329 |
+
<modules>
|
330 |
+
<Doofinder_Feed after="Mage_Adminhtml">Doofinder_Feed</Doofinder_Feed>
|
331 |
+
</modules>
|
332 |
+
</args>
|
333 |
+
</adminhtml>
|
334 |
+
</routers>
|
335 |
+
</admin>
|
336 |
+
|
337 |
+
</config>
|
app/code/community/Doofinder/Feed/etc/system.xml
ADDED
@@ -0,0 +1,515 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<config>
|
2 |
+
<tabs>
|
3 |
+
<doofinder_config translate="label" module="doofinder_feed">
|
4 |
+
<label>Doofinder Search</label>
|
5 |
+
<sort_order>99999</sort_order>
|
6 |
+
</doofinder_config>
|
7 |
+
</tabs>
|
8 |
+
<sections>
|
9 |
+
<doofinder_cron translate="label" module="doofinder_feed">
|
10 |
+
<label>Product Data Feed</label>
|
11 |
+
<tab>doofinder_config</tab>
|
12 |
+
<frontend_type>text</frontend_type>
|
13 |
+
<sort_order>1000</sort_order>
|
14 |
+
<show_in_default>1</show_in_default>
|
15 |
+
<show_in_website>0</show_in_website>
|
16 |
+
<show_in_store>1</show_in_store>
|
17 |
+
<groups>
|
18 |
+
<attributes_mapping translate="label">
|
19 |
+
<label>Feed Attributes</label>
|
20 |
+
<expanded>false</expanded>
|
21 |
+
<frontend_type>text</frontend_type>
|
22 |
+
<sort_order>10</sort_order>
|
23 |
+
<show_in_default>1</show_in_default>
|
24 |
+
<show_in_website>0</show_in_website>
|
25 |
+
<show_in_store>1</show_in_store>
|
26 |
+
<fields>
|
27 |
+
<id translate="label">
|
28 |
+
<label>Id</label>
|
29 |
+
<frontend_type>select</frontend_type>
|
30 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
31 |
+
<sort_order>1</sort_order>
|
32 |
+
<show_in_default>1</show_in_default>
|
33 |
+
<show_in_website>0</show_in_website>
|
34 |
+
<show_in_store>1</show_in_store>
|
35 |
+
</id>
|
36 |
+
<title translate="label">
|
37 |
+
<label>Title</label>
|
38 |
+
<frontend_type>select</frontend_type>
|
39 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
40 |
+
<sort_order>2</sort_order>
|
41 |
+
<show_in_default>1</show_in_default>
|
42 |
+
<show_in_website>0</show_in_website>
|
43 |
+
<show_in_store>1</show_in_store>
|
44 |
+
</title>
|
45 |
+
<description translate="label">
|
46 |
+
<label>Description</label>
|
47 |
+
<frontend_type>select</frontend_type>
|
48 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
49 |
+
<sort_order>3</sort_order>
|
50 |
+
<show_in_default>1</show_in_default>
|
51 |
+
<show_in_website>0</show_in_website>
|
52 |
+
<show_in_store>1</show_in_store>
|
53 |
+
</description>
|
54 |
+
<brand translate="label">
|
55 |
+
<label>Brand</label>
|
56 |
+
<frontend_type>select</frontend_type>
|
57 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
58 |
+
<sort_order>4</sort_order>
|
59 |
+
<show_in_default>1</show_in_default>
|
60 |
+
<show_in_website>0</show_in_website>
|
61 |
+
<show_in_store>1</show_in_store>
|
62 |
+
</brand>
|
63 |
+
<link translate="label">
|
64 |
+
<label>Link</label>
|
65 |
+
<frontend_type>select</frontend_type>
|
66 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
67 |
+
<sort_order>5</sort_order>
|
68 |
+
<show_in_default>1</show_in_default>
|
69 |
+
<show_in_website>0</show_in_website>
|
70 |
+
<show_in_store>1</show_in_store>
|
71 |
+
</link>
|
72 |
+
<image_link translate="label">
|
73 |
+
<label>Image Link</label>
|
74 |
+
<frontend_type>select</frontend_type>
|
75 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
76 |
+
<sort_order>6</sort_order>
|
77 |
+
<show_in_default>1</show_in_default>
|
78 |
+
<show_in_website>0</show_in_website>
|
79 |
+
<show_in_store>1</show_in_store>
|
80 |
+
</image_link>
|
81 |
+
<price translate="label">
|
82 |
+
<label>Price</label>
|
83 |
+
<frontend_type>select</frontend_type>
|
84 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
85 |
+
<sort_order>7</sort_order>
|
86 |
+
<show_in_default>1</show_in_default>
|
87 |
+
<show_in_website>0</show_in_website>
|
88 |
+
<show_in_store>1</show_in_store>
|
89 |
+
</price>
|
90 |
+
<sale_price translate="label">
|
91 |
+
<label>Sale price</label>
|
92 |
+
<frontend_type>select</frontend_type>
|
93 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
94 |
+
<sort_order>8</sort_order>
|
95 |
+
<show_in_default>1</show_in_default>
|
96 |
+
<show_in_website>0</show_in_website>
|
97 |
+
<show_in_store>1</show_in_store>
|
98 |
+
</sale_price>
|
99 |
+
<mpn translate="label">
|
100 |
+
<label>MPN</label>
|
101 |
+
<frontend_type>select</frontend_type>
|
102 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
103 |
+
<sort_order>9</sort_order>
|
104 |
+
<show_in_default>1</show_in_default>
|
105 |
+
<show_in_website>0</show_in_website>
|
106 |
+
<show_in_store>1</show_in_store>
|
107 |
+
</mpn>
|
108 |
+
<availability translate="label">
|
109 |
+
<label>Availability</label>
|
110 |
+
<frontend_type>select</frontend_type>
|
111 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
112 |
+
<sort_order>10</sort_order>
|
113 |
+
<show_in_default>1</show_in_default>
|
114 |
+
<show_in_website>0</show_in_website>
|
115 |
+
<show_in_store>1</show_in_store>
|
116 |
+
</availability>
|
117 |
+
<currency translate="label">
|
118 |
+
<label>Currency</label>
|
119 |
+
<frontend_type>select</frontend_type>
|
120 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
121 |
+
<sort_order>11</sort_order>
|
122 |
+
<show_in_default>1</show_in_default>
|
123 |
+
<show_in_website>0</show_in_website>
|
124 |
+
<show_in_store>1</show_in_store>
|
125 |
+
</currency>
|
126 |
+
<additional translate="label">
|
127 |
+
<label>Additional Attributes</label>
|
128 |
+
<frontend_model>doofinder_feed/adminhtml_map_additional</frontend_model>
|
129 |
+
<backend_model>doofinder_feed/system_config_backend_map_additional</backend_model>
|
130 |
+
<sort_order>12</sort_order>
|
131 |
+
<show_in_default>1</show_in_default>
|
132 |
+
<show_in_website>0</show_in_website>
|
133 |
+
<show_in_store>1</show_in_store>
|
134 |
+
</additional>
|
135 |
+
<!--long_description translate="label">
|
136 |
+
<label>Long Description</label>
|
137 |
+
<frontend_type>select</frontend_type>
|
138 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
139 |
+
<sort_order>5</sort_order>
|
140 |
+
<show_in_default>1</show_in_default>
|
141 |
+
<show_in_website>0</show_in_website>
|
142 |
+
<show_in_store>1</show_in_store>
|
143 |
+
</long_description>
|
144 |
+
<meta_title translate="label">
|
145 |
+
<label>Meta Title</label>
|
146 |
+
<frontend_type>select</frontend_type>
|
147 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
148 |
+
<sort_order>6</sort_order>
|
149 |
+
<show_in_default>1</show_in_default>
|
150 |
+
<show_in_website>0</show_in_website>
|
151 |
+
<show_in_store>1</show_in_store>
|
152 |
+
</meta_title>
|
153 |
+
<meta_keyword translate="label">
|
154 |
+
<label>Meta Keyword</label>
|
155 |
+
<frontend_type>select</frontend_type>
|
156 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
157 |
+
<sort_order>7</sort_order>
|
158 |
+
<show_in_default>1</show_in_default>
|
159 |
+
<show_in_website>0</show_in_website>
|
160 |
+
<show_in_store>1</show_in_store>
|
161 |
+
</meta_keyword>
|
162 |
+
<meta_description translate="label">
|
163 |
+
<label>Meta Description</label>
|
164 |
+
<frontend_type>select</frontend_type>
|
165 |
+
<source_model>doofinder_feed/system_config_source_product_attributes</source_model>
|
166 |
+
<sort_order>8</sort_order>
|
167 |
+
<show_in_default>1</show_in_default>
|
168 |
+
<show_in_website>0</show_in_website>
|
169 |
+
<show_in_store>1</show_in_store>
|
170 |
+
</meta_description-->
|
171 |
+
</fields>
|
172 |
+
</attributes_mapping>
|
173 |
+
|
174 |
+
<feed_settings translate="label">
|
175 |
+
<label>Feed Settings</label>
|
176 |
+
<expanded>true</expanded>
|
177 |
+
<frontend_type>text</frontend_type>
|
178 |
+
<sort_order>15</sort_order>
|
179 |
+
<show_in_default>1</show_in_default>
|
180 |
+
<show_in_website>0</show_in_website>
|
181 |
+
<show_in_store>1</show_in_store>
|
182 |
+
<fields>
|
183 |
+
<display_price translate="label">
|
184 |
+
<label>Export Product Prices</label>
|
185 |
+
<frontend_type>select</frontend_type>
|
186 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
187 |
+
<sort_order>7</sort_order>
|
188 |
+
<show_in_default>1</show_in_default>
|
189 |
+
<show_in_website>0</show_in_website>
|
190 |
+
<show_in_store>1</show_in_store>
|
191 |
+
</display_price>
|
192 |
+
<grouped translate="label">
|
193 |
+
<label>Split Configurable Products</label>
|
194 |
+
<comment>Export each component of a configurable product separately, instead of exporting them as a single product.</comment>
|
195 |
+
<frontend_type>select</frontend_type>
|
196 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
197 |
+
<sort_order>8</sort_order>
|
198 |
+
<show_in_default>1</show_in_default>
|
199 |
+
<show_in_website>0</show_in_website>
|
200 |
+
<show_in_store>1</show_in_store>
|
201 |
+
</grouped>
|
202 |
+
<image_size translate="label">
|
203 |
+
<label>Image size</label>
|
204 |
+
<comment>Export product image with given width. Leave empty to use original size.</comment>
|
205 |
+
<frontend_type>text</frontend_type>
|
206 |
+
<sort_order>9</sort_order>
|
207 |
+
<show_in_default>1</show_in_default>
|
208 |
+
<show_in_website>0</show_in_website>
|
209 |
+
<show_in_store>1</show_in_store>
|
210 |
+
</image_size>
|
211 |
+
<atomic_updates_enabled translate="label">
|
212 |
+
<label>Use Doofinder API to update products on save</label>
|
213 |
+
<frontend_type>select</frontend_type>
|
214 |
+
<comment>Requires a Management API key configured in Search Configuration</comment>
|
215 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
216 |
+
<sort_order>1</sort_order>
|
217 |
+
<show_in_default>1</show_in_default>
|
218 |
+
<show_in_website>0</show_in_website>
|
219 |
+
<show_in_store>1</show_in_store>
|
220 |
+
</atomic_updates_enabled>
|
221 |
+
<categories_in_navigation translate="label">
|
222 |
+
<label>Export only categories present in navigation menus</label>
|
223 |
+
<frontend_type>select</frontend_type>
|
224 |
+
<comment>Whether this option is enabled or not, only active categories will be exported.</comment>
|
225 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
226 |
+
<sort_order>10</sort_order>
|
227 |
+
<show_in_default>1</show_in_default>
|
228 |
+
<show_in_website>0</show_in_website>
|
229 |
+
<show_in_store>1</show_in_store>
|
230 |
+
</categories_in_navigation>
|
231 |
+
</fields>
|
232 |
+
</feed_settings>
|
233 |
+
|
234 |
+
<schedule_settings translate="label">
|
235 |
+
<label>Schedule Settings (Beta!)</label>
|
236 |
+
<expanded>false</expanded>
|
237 |
+
<frontend_type>text</frontend_type>
|
238 |
+
<sort_order>20</sort_order>
|
239 |
+
<show_in_default>1</show_in_default>
|
240 |
+
<show_in_website>0</show_in_website>
|
241 |
+
<show_in_store>1</show_in_store>
|
242 |
+
<fields>
|
243 |
+
<instruction>
|
244 |
+
<frontend_model>doofinder_feed/settings_panel_crondescription</frontend_model>
|
245 |
+
<sort_order>1</sort_order>
|
246 |
+
<show_in_default>1</show_in_default>
|
247 |
+
<show_in_website>0</show_in_website>
|
248 |
+
<show_in_store>0</show_in_store>
|
249 |
+
</instruction>
|
250 |
+
<enabled>
|
251 |
+
<label>Enabled</label>
|
252 |
+
<frontend_type>select</frontend_type>
|
253 |
+
<comment>Activate/deactivate the feed for this store</comment>
|
254 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
255 |
+
<sort_order>1</sort_order>
|
256 |
+
<show_in_default>1</show_in_default>
|
257 |
+
<show_in_website>0</show_in_website>
|
258 |
+
<show_in_store>1</show_in_store>
|
259 |
+
</enabled>
|
260 |
+
<time translate="label">
|
261 |
+
<label>Feed generation start time</label>
|
262 |
+
<frontend_type>time</frontend_type>
|
263 |
+
<comment>The time at which the feed generation starts.</comment>
|
264 |
+
<sort_order>2</sort_order>
|
265 |
+
<show_in_default>1</show_in_default>
|
266 |
+
<show_in_website>0</show_in_website>
|
267 |
+
<show_in_store>1</show_in_store>
|
268 |
+
</time>
|
269 |
+
<frequency translate="label">
|
270 |
+
<label>Frequency</label>
|
271 |
+
<frontend_type>select</frontend_type>
|
272 |
+
<comment>Feed generating frequency (e.g. "monthly" means that the feed will be generated on the first day of each month at a given hour).</comment>
|
273 |
+
<source_model>adminhtml/system_config_source_cron_frequency</source_model>
|
274 |
+
<sort_order>3</sort_order>
|
275 |
+
<show_in_default>1</show_in_default>
|
276 |
+
<show_in_website>0</show_in_website>
|
277 |
+
<show_in_store>1</show_in_store>
|
278 |
+
</frequency>
|
279 |
+
<step translate="label">
|
280 |
+
<label>Step Size</label>
|
281 |
+
<frontend_type>text</frontend_type>
|
282 |
+
<comment>The maximum number of products added to the feed on each iteration of the generation process (maximum recommended value: 1000)</comment>
|
283 |
+
<sort_order>4</sort_order>
|
284 |
+
<show_in_default>1</show_in_default>
|
285 |
+
<show_in_website>0</show_in_website>
|
286 |
+
<show_in_store>1</show_in_store>
|
287 |
+
</step>
|
288 |
+
<delay translate="label">
|
289 |
+
<label>Step Delay</label>
|
290 |
+
<frontend_type>text</frontend_type>
|
291 |
+
<comment><![CDATA[The interval (in minutes) between subsequent stages of the feed generation process. It should not be smaller than the frequency of execution of your cron.sh file. (<a href="http://devdocs.magento.com/guides/v2.0/config-guide/cli/config-cli-subcommands-cron.html" target="step-delay">What's this?</a>)]]></comment>
|
292 |
+
<sort_order>5</sort_order>
|
293 |
+
<show_in_default>1</show_in_default>
|
294 |
+
<show_in_website>0</show_in_website>
|
295 |
+
<show_in_store>1</show_in_store>
|
296 |
+
</delay>
|
297 |
+
<generate_global translate="label">
|
298 |
+
<frontend_type>button</frontend_type>
|
299 |
+
<frontend_model>doofinder_feed/settings_buttons_generate</frontend_model>
|
300 |
+
<sort_order>20</sort_order>
|
301 |
+
<show_in_default>1</show_in_default>
|
302 |
+
<show_in_website>0</show_in_website>
|
303 |
+
<show_in_store>0</show_in_store>
|
304 |
+
</generate_global>
|
305 |
+
<generate translate="label">
|
306 |
+
<frontend_type>button</frontend_type>
|
307 |
+
<frontend_model>doofinder_feed/settings_buttons_generate</frontend_model>
|
308 |
+
<sort_order>20</sort_order>
|
309 |
+
<show_in_default>0</show_in_default>
|
310 |
+
<show_in_website>0</show_in_website>
|
311 |
+
<show_in_store>1</show_in_store>
|
312 |
+
</generate>
|
313 |
+
</fields>
|
314 |
+
</schedule_settings>
|
315 |
+
<panel translate="label">
|
316 |
+
<label>Feed Status</label>
|
317 |
+
<expanded>false</expanded>
|
318 |
+
<frontend_type>text</frontend_type>
|
319 |
+
<sort_order>30</sort_order>
|
320 |
+
<show_in_default>1</show_in_default>
|
321 |
+
<show_in_website>0</show_in_website>
|
322 |
+
<show_in_store>1</show_in_store>
|
323 |
+
<fields>
|
324 |
+
<cron_issue>
|
325 |
+
<label>Cron Issues</label>
|
326 |
+
<frontend_model>doofinder_feed/settings_panel_cron</frontend_model>
|
327 |
+
<sort_order>5</sort_order>
|
328 |
+
<show_in_default>1</show_in_default>
|
329 |
+
<show_in_website>1</show_in_website>
|
330 |
+
<show_in_store>1</show_in_store>
|
331 |
+
</cron_issue>
|
332 |
+
<status>
|
333 |
+
<label>Status</label>
|
334 |
+
<frontend_model>doofinder_feed/settings_panel_message</frontend_model>
|
335 |
+
<sort_order>10</sort_order>
|
336 |
+
<show_in_default>0</show_in_default>
|
337 |
+
<show_in_website>0</show_in_website>
|
338 |
+
<show_in_store>1</show_in_store>
|
339 |
+
</status>
|
340 |
+
<message>
|
341 |
+
<label>Message</label>
|
342 |
+
<frontend_model>doofinder_feed/settings_panel_message</frontend_model>
|
343 |
+
<sort_order>20</sort_order>
|
344 |
+
<show_in_default>0</show_in_default>
|
345 |
+
<show_in_website>0</show_in_website>
|
346 |
+
<show_in_store>1</show_in_store>
|
347 |
+
</message>
|
348 |
+
<complete>
|
349 |
+
<label>Complete</label>
|
350 |
+
<frontend_model>doofinder_feed/settings_panel_message</frontend_model>
|
351 |
+
<sort_order>30</sort_order>
|
352 |
+
<show_in_default>0</show_in_default>
|
353 |
+
<show_in_website>0</show_in_website>
|
354 |
+
<show_in_store>1</show_in_store>
|
355 |
+
</complete>
|
356 |
+
<next_run>
|
357 |
+
<label>Next Run</label>
|
358 |
+
<frontend_model>doofinder_feed/settings_panel_datetime</frontend_model>
|
359 |
+
<sort_order>40</sort_order>
|
360 |
+
<show_in_default>0</show_in_default>
|
361 |
+
<show_in_website>0</show_in_website>
|
362 |
+
<show_in_store>1</show_in_store>
|
363 |
+
</next_run>
|
364 |
+
<next_iteration>
|
365 |
+
<label>Next Iteration</label>
|
366 |
+
<frontend_model>doofinder_feed/settings_panel_datetime</frontend_model>
|
367 |
+
<sort_order>50</sort_order>
|
368 |
+
<show_in_default>0</show_in_default>
|
369 |
+
<show_in_website>0</show_in_website>
|
370 |
+
<show_in_store>1</show_in_store>
|
371 |
+
</next_iteration>
|
372 |
+
<last_file>
|
373 |
+
<label>Last generated feed</label>
|
374 |
+
<frontend_model>doofinder_feed/settings_panel_file</frontend_model>
|
375 |
+
<sort_order>60</sort_order>
|
376 |
+
<show_in_default>0</show_in_default>
|
377 |
+
<show_in_website>0</show_in_website>
|
378 |
+
<show_in_store>1</show_in_store>
|
379 |
+
</last_file>
|
380 |
+
<last_files>
|
381 |
+
<label>Last generated feeds</label>
|
382 |
+
<frontend_model>doofinder_feed/settings_panel_file</frontend_model>
|
383 |
+
<sort_order>60</sort_order>
|
384 |
+
<show_in_default>1</show_in_default>
|
385 |
+
<show_in_website>0</show_in_website>
|
386 |
+
<show_in_store>0</show_in_store>
|
387 |
+
</last_files>
|
388 |
+
<error_stack>
|
389 |
+
<label>Errors</label>
|
390 |
+
<frontend_model>doofinder_feed/settings_panel_message</frontend_model>
|
391 |
+
<sort_order>70</sort_order>
|
392 |
+
<show_in_default>0</show_in_default>
|
393 |
+
<show_in_website>0</show_in_website>
|
394 |
+
<show_in_store>1</show_in_store>
|
395 |
+
</error_stack>
|
396 |
+
<view_log>
|
397 |
+
<frontend_type>button</frontend_type>
|
398 |
+
<frontend_model>doofinder_feed/settings_buttons_viewLog</frontend_model>
|
399 |
+
<sort_order>80</sort_order>
|
400 |
+
<show_in_default>0</show_in_default>
|
401 |
+
<show_in_website>0</show_in_website>
|
402 |
+
<show_in_store>1</show_in_store>
|
403 |
+
</view_log>
|
404 |
+
</fields>
|
405 |
+
</panel>
|
406 |
+
</groups>
|
407 |
+
</doofinder_cron>
|
408 |
+
<doofinder_search translate="label" module="doofinder_feed">
|
409 |
+
<label>Search Configuration</label>
|
410 |
+
<tab>doofinder_config</tab>
|
411 |
+
<frontend_type>text</frontend_type>
|
412 |
+
<sort_order>1010</sort_order>
|
413 |
+
<show_in_default>1</show_in_default>
|
414 |
+
<show_in_website>0</show_in_website>
|
415 |
+
<show_in_store>1</show_in_store>
|
416 |
+
<groups>
|
417 |
+
<layer_settings translate="label">
|
418 |
+
<label>Doofinder Layer</label>
|
419 |
+
<expanded>true</expanded>
|
420 |
+
<frontend_type>text</frontend_type>
|
421 |
+
<sort_order>10</sort_order>
|
422 |
+
<show_in_default>1</show_in_default>
|
423 |
+
<show_in_website>0</show_in_website>
|
424 |
+
<show_in_store>1</show_in_store>
|
425 |
+
<fields>
|
426 |
+
<enabled>
|
427 |
+
<label>Enabled</label>
|
428 |
+
<frontend_type>select</frontend_type>
|
429 |
+
<comment>Activate/deactivate the search layer</comment>
|
430 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
431 |
+
<sort_order>0</sort_order>
|
432 |
+
<show_in_default>1</show_in_default>
|
433 |
+
<show_in_website>0</show_in_website>
|
434 |
+
<show_in_store>1</show_in_store>
|
435 |
+
</enabled>
|
436 |
+
<instruction>
|
437 |
+
<frontend_model>doofinder_feed/settings_panel_layerdescription</frontend_model>
|
438 |
+
<sort_order>1</sort_order>
|
439 |
+
<show_in_default>1</show_in_default>
|
440 |
+
<show_in_website>0</show_in_website>
|
441 |
+
<show_in_store>0</show_in_store>
|
442 |
+
</instruction>
|
443 |
+
<script translate="label">
|
444 |
+
<label>Script</label>
|
445 |
+
<frontend_type>textarea</frontend_type>
|
446 |
+
<comment>Paste your integration script here.</comment>
|
447 |
+
<sort_order>2</sort_order>
|
448 |
+
<show_in_default>0</show_in_default>
|
449 |
+
<show_in_website>0</show_in_website>
|
450 |
+
<show_in_store>1</show_in_store>
|
451 |
+
</script>
|
452 |
+
</fields>
|
453 |
+
</layer_settings>
|
454 |
+
<internal_settings translate="label">
|
455 |
+
<label>Internal Search</label>
|
456 |
+
<expanded>true</expanded>
|
457 |
+
<frontend_type>text</frontend_type>
|
458 |
+
<sort_order>20</sort_order>
|
459 |
+
<show_in_default>1</show_in_default>
|
460 |
+
<show_in_website>0</show_in_website>
|
461 |
+
<show_in_store>1</show_in_store>
|
462 |
+
<fields>
|
463 |
+
<enable translate="label">
|
464 |
+
<label>Enabled</label>
|
465 |
+
<frontend_type>select</frontend_type>
|
466 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
467 |
+
<comment>Use Doofinder Search instead of Magento default search.</comment>
|
468 |
+
<sort_order>1</sort_order>
|
469 |
+
<show_in_default>1</show_in_default>
|
470 |
+
<show_in_website>0</show_in_website>
|
471 |
+
<show_in_store>1</show_in_store>
|
472 |
+
</enable>
|
473 |
+
<api_key translate="label">
|
474 |
+
<label>API Key</label>
|
475 |
+
<frontend_type>text</frontend_type>
|
476 |
+
<sort_order>2</sort_order>
|
477 |
+
<show_in_default>1</show_in_default>
|
478 |
+
<show_in_website>0</show_in_website>
|
479 |
+
<show_in_store>0</show_in_store>
|
480 |
+
</api_key>
|
481 |
+
|
482 |
+
<hash_id translate="label">
|
483 |
+
<label>Hash ID</label>
|
484 |
+
<frontend_type>text</frontend_type>
|
485 |
+
<backend_model>doofinder_feed/adminhtml_system_config_validation_hashid</backend_model>
|
486 |
+
<sort_order>3</sort_order>
|
487 |
+
<show_in_default>0</show_in_default>
|
488 |
+
<show_in_website>0</show_in_website>
|
489 |
+
<show_in_store>1</show_in_store>
|
490 |
+
</hash_id>
|
491 |
+
<instruction>
|
492 |
+
<frontend_model>doofinder_feed/settings_panel_hashdescription</frontend_model>
|
493 |
+
<custom>hola</custom>
|
494 |
+
<sort_order>4</sort_order>
|
495 |
+
<show_in_default>1</show_in_default>
|
496 |
+
<show_in_website>0</show_in_website>
|
497 |
+
<show_in_store>0</show_in_store>
|
498 |
+
</instruction>
|
499 |
+
|
500 |
+
<total_limit translate="label">
|
501 |
+
<label>Total limit</label>
|
502 |
+
<frontend_type>text</frontend_type>
|
503 |
+
<backend_model>doofinder_feed/system_config_backend_total_limit</backend_model>
|
504 |
+
<comment>Number of results returned for search query.</comment>
|
505 |
+
<sort_order>6</sort_order>
|
506 |
+
<show_in_default>1</show_in_default>
|
507 |
+
<show_in_website>0</show_in_website>
|
508 |
+
<show_in_store>1</show_in_store>
|
509 |
+
</total_limit>
|
510 |
+
</fields>
|
511 |
+
</internal_settings>
|
512 |
+
</groups>
|
513 |
+
</doofinder_search>
|
514 |
+
</sections>
|
515 |
+
</config>
|
app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-install-1.5.4.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Cron table
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (version_compare(Mage::getVersion(), '1.6', '<'))
|
12 |
+
{
|
13 |
+
$installer->run("DROP TABLE IF EXISTS {$installer->getTable('doofinder_feed/cron')};");
|
14 |
+
}
|
15 |
+
else
|
16 |
+
{
|
17 |
+
$installer->getConnection()->dropTable( $installer->getTable('doofinder_feed/cron') );
|
18 |
+
}
|
19 |
+
|
20 |
+
$table = $installer->getConnection()
|
21 |
+
->newTable($installer->getTable('doofinder_feed/cron'))
|
22 |
+
->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
23 |
+
'nullable' => false,
|
24 |
+
'identity' => true,
|
25 |
+
'primary' => true,
|
26 |
+
), 'ID')
|
27 |
+
->addColumn('store_code', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
28 |
+
'nullable' => false,
|
29 |
+
), 'Store Code')
|
30 |
+
->addColumn('status', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
31 |
+
'length' => 255,
|
32 |
+
), 'Status')
|
33 |
+
->addColumn('message', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
34 |
+
), 'Message')
|
35 |
+
->addColumn('error_stack', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
36 |
+
'default' => 0,
|
37 |
+
), 'Error Stack')
|
38 |
+
->addColumn('complete', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
39 |
+
'length' => 12,
|
40 |
+
), 'Complete')
|
41 |
+
->addColumn('next_run', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
42 |
+
'length' => 255,
|
43 |
+
), 'Next Run')
|
44 |
+
->addColumn('next_iteration', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
45 |
+
'length' => 255,
|
46 |
+
), 'Next Iteration')
|
47 |
+
->addColumn('last_feed_name', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
48 |
+
'length' => 255,
|
49 |
+
), 'Last Feed Name')
|
50 |
+
->addColumn('offset', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
51 |
+
'default' => 0,
|
52 |
+
), 'Offset')
|
53 |
+
->addColumn('schedule_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
54 |
+
'default' => null,
|
55 |
+
), 'Schedule ID');
|
56 |
+
|
57 |
+
$installer->getConnection()->createTable($table);
|
58 |
+
|
59 |
+
$installer->endSetup();
|
app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-install-1.5.7.php
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
// 1.5
|
8 |
+
if ( version_compare(Mage::getVersion(), '1.6', '<') )
|
9 |
+
{
|
10 |
+
$installer->run("DROP TABLE IF EXISTS {$installer->getTable('doofinder_feed/cron')};");
|
11 |
+
$installer->run("DROP TABLE IF EXISTS {$installer->getTable('doofinder_feed/log')};");
|
12 |
+
}
|
13 |
+
// 1.6+
|
14 |
+
else
|
15 |
+
{
|
16 |
+
$installer->getConnection()->dropTable( $installer->getTable('doofinder_feed/cron') );
|
17 |
+
$installer->getConnection()->dropTable( $installer->getTable('doofinder_feed/log') );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Cron table
|
22 |
+
*/
|
23 |
+
|
24 |
+
$table = $installer->getConnection()
|
25 |
+
->newTable($installer->getTable('doofinder_feed/cron'))
|
26 |
+
->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
27 |
+
'nullable' => false,
|
28 |
+
'identity' => true,
|
29 |
+
'primary' => true,
|
30 |
+
), 'ID')
|
31 |
+
->addColumn('store_code', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
32 |
+
'nullable' => false,
|
33 |
+
), 'Store Code')
|
34 |
+
->addColumn('status', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
35 |
+
'length' => 255,
|
36 |
+
), 'Status')
|
37 |
+
->addColumn('message', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
38 |
+
), 'Message')
|
39 |
+
->addColumn('error_stack', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
40 |
+
'default' => 0,
|
41 |
+
), 'Error Stack')
|
42 |
+
->addColumn('complete', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
43 |
+
'length' => 12,
|
44 |
+
), 'Complete')
|
45 |
+
->addColumn('next_run', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
46 |
+
'length' => 255,
|
47 |
+
), 'Next Run')
|
48 |
+
->addColumn('next_iteration', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
49 |
+
'length' => 255,
|
50 |
+
), 'Next Iteration')
|
51 |
+
->addColumn('last_feed_name', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
52 |
+
'length' => 255,
|
53 |
+
), 'Last Feed Name')
|
54 |
+
->addColumn('offset', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
55 |
+
'default' => 0,
|
56 |
+
), 'Offset');
|
57 |
+
|
58 |
+
$installer->getConnection()->createTable($table);
|
59 |
+
|
60 |
+
// 1.5
|
61 |
+
if ( version_compare(Mage::getVersion(), '1.6', '<') )
|
62 |
+
{
|
63 |
+
$installer->run("
|
64 |
+
|
65 |
+
ALTER TABLE {$installer->getTable('doofinder_feed/cron')}
|
66 |
+
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
67 |
+
|
68 |
+
");
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Log table
|
73 |
+
*/
|
74 |
+
|
75 |
+
// 1.6+
|
76 |
+
if ( ! version_compare(Mage::getVersion(), '1.6', '<') )
|
77 |
+
{
|
78 |
+
// Add log table
|
79 |
+
$table = $installer->getConnection()
|
80 |
+
->newTable($installer->getTable('doofinder_feed/log'))
|
81 |
+
->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
82 |
+
'nullable' => false,
|
83 |
+
'identity' => true,
|
84 |
+
'primary' => true,
|
85 |
+
), 'ID')
|
86 |
+
->addColumn('process_id', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
87 |
+
'nullable' => false,
|
88 |
+
), 'Store Code')
|
89 |
+
->addColumn('type', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
90 |
+
'nullable' => false,
|
91 |
+
), 'Type')
|
92 |
+
->addColumn('time', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
|
93 |
+
'nullable' => false,
|
94 |
+
'default' => Varien_Db_Ddl_Table::TIMESTAMP_INIT,
|
95 |
+
), 'Type')
|
96 |
+
->addColumn('message', Varien_Db_Ddl_Table::TYPE_TEXT, null, array(
|
97 |
+
'nullable' => false,
|
98 |
+
), 'Message');
|
99 |
+
|
100 |
+
// Add indexes to log table
|
101 |
+
$table->addIndex(
|
102 |
+
$installer->getIdxName(
|
103 |
+
'doofinder_feed/log',
|
104 |
+
array(
|
105 |
+
'process_id',
|
106 |
+
'type',
|
107 |
+
),
|
108 |
+
Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX
|
109 |
+
),
|
110 |
+
array(
|
111 |
+
'process_id',
|
112 |
+
'type',
|
113 |
+
),
|
114 |
+
array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX)
|
115 |
+
);
|
116 |
+
$table->addIndex(
|
117 |
+
$installer->getIdxName(
|
118 |
+
'doofinder_feed/log',
|
119 |
+
array(
|
120 |
+
'time',
|
121 |
+
),
|
122 |
+
Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX
|
123 |
+
),
|
124 |
+
array(
|
125 |
+
'time',
|
126 |
+
),
|
127 |
+
array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX)
|
128 |
+
);
|
129 |
+
|
130 |
+
$installer->getConnection()->createTable($table);
|
131 |
+
}
|
132 |
+
// 1.5
|
133 |
+
else
|
134 |
+
{
|
135 |
+
$installer->run("
|
136 |
+
|
137 |
+
CREATE TABLE {$installer->getTable('doofinder_feed/log')} (
|
138 |
+
`id` int(11) NOT NULL COMMENT 'ID',
|
139 |
+
`process_id` varchar(255) NOT NULL COMMENT 'Store Code',
|
140 |
+
`type` varchar(255) NOT NULL COMMENT 'Type',
|
141 |
+
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Type',
|
142 |
+
`message` text NOT NULL COMMENT 'Message'
|
143 |
+
);
|
144 |
+
|
145 |
+
ALTER TABLE {$installer->getTable('doofinder_feed/log')}
|
146 |
+
ADD PRIMARY KEY (`id`), ADD KEY `IDX_DOOFINDER_LOG_PROCESS_ID_TYPE` (`process_id`,`type`), ADD KEY `IDX_DOOFINDER_LOG_TIME` (`time`);
|
147 |
+
|
148 |
+
ALTER TABLE {$installer->getTable('doofinder_feed/log')}
|
149 |
+
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
150 |
+
|
151 |
+
");
|
152 |
+
}
|
153 |
+
|
154 |
+
// /**
|
155 |
+
// * Trigger feed generation
|
156 |
+
// */
|
157 |
+
// $scheduleObserver = Mage::getSingleton('doofinder_feed/observers_schedule');
|
158 |
+
|
159 |
+
// foreach (Mage::getModel('core/store')->getCollection() as $store) {
|
160 |
+
// $scheduleObserver->updateProcess($store->getCode(), true, true);
|
161 |
+
// }
|
162 |
+
|
163 |
+
$installer->endSetup();
|
app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-upgrade-1.5.4-1.5.5.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
// 1.5
|
8 |
+
if ( version_compare(Mage::getVersion(), '1.6', '<') )
|
9 |
+
{
|
10 |
+
$installer->run("
|
11 |
+
|
12 |
+
ALTER TABLE {$installer->getTable('doofinder_feed/cron')}
|
13 |
+
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
14 |
+
|
15 |
+
");
|
16 |
+
}
|
17 |
+
|
18 |
+
$installer->endSetup();
|
app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-upgrade-1.5.5-1.5.6.php
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
// 1.5
|
8 |
+
if ( version_compare(Mage::getVersion(), '1.6', '<') )
|
9 |
+
{
|
10 |
+
$installer->run("DROP TABLE IF EXISTS {$installer->getTable('doofinder_feed/log')};");
|
11 |
+
}
|
12 |
+
// 1.6+
|
13 |
+
else
|
14 |
+
{
|
15 |
+
$installer->getConnection()->dropTable( $installer->getTable('doofinder_feed/log') );
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Log table
|
20 |
+
*/
|
21 |
+
|
22 |
+
// 1.6+
|
23 |
+
if ( ! version_compare(Mage::getVersion(), '1.6', '<') )
|
24 |
+
{
|
25 |
+
// Add log table
|
26 |
+
$table = $installer->getConnection()
|
27 |
+
->newTable($installer->getTable('doofinder_feed/log'))
|
28 |
+
->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
|
29 |
+
'nullable' => false,
|
30 |
+
'identity' => true,
|
31 |
+
'primary' => true,
|
32 |
+
), 'ID')
|
33 |
+
->addColumn('process_id', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
34 |
+
'nullable' => false,
|
35 |
+
), 'Store Code')
|
36 |
+
->addColumn('type', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
|
37 |
+
'nullable' => false,
|
38 |
+
), 'Type')
|
39 |
+
->addColumn('time', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
|
40 |
+
'nullable' => false,
|
41 |
+
'default' => Varien_Db_Ddl_Table::TIMESTAMP_INIT,
|
42 |
+
), 'Type')
|
43 |
+
->addColumn('message', Varien_Db_Ddl_Table::TYPE_TEXT, null, array(
|
44 |
+
'nullable' => false,
|
45 |
+
), 'Message');
|
46 |
+
|
47 |
+
// Add indexes to log table
|
48 |
+
$table->addIndex(
|
49 |
+
$installer->getIdxName(
|
50 |
+
'doofinder_feed/log',
|
51 |
+
array(
|
52 |
+
'process_id',
|
53 |
+
'type',
|
54 |
+
),
|
55 |
+
Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX
|
56 |
+
),
|
57 |
+
array(
|
58 |
+
'process_id',
|
59 |
+
'type',
|
60 |
+
),
|
61 |
+
array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX)
|
62 |
+
);
|
63 |
+
$table->addIndex(
|
64 |
+
$installer->getIdxName(
|
65 |
+
'doofinder_feed/log',
|
66 |
+
array(
|
67 |
+
'time',
|
68 |
+
),
|
69 |
+
Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX
|
70 |
+
),
|
71 |
+
array(
|
72 |
+
'time',
|
73 |
+
),
|
74 |
+
array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_INDEX)
|
75 |
+
);
|
76 |
+
|
77 |
+
$installer->getConnection()->createTable($table);
|
78 |
+
}
|
79 |
+
// 1.5
|
80 |
+
else
|
81 |
+
{
|
82 |
+
$installer->run("
|
83 |
+
|
84 |
+
CREATE TABLE {$installer->getTable('doofinder_feed/log')} (
|
85 |
+
`id` int(11) NOT NULL COMMENT 'ID',
|
86 |
+
`process_id` varchar(255) NOT NULL COMMENT 'Store Code',
|
87 |
+
`type` varchar(255) NOT NULL COMMENT 'Type',
|
88 |
+
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Type',
|
89 |
+
`message` text NOT NULL COMMENT 'Message'
|
90 |
+
);
|
91 |
+
|
92 |
+
ALTER TABLE {$installer->getTable('doofinder_feed/log')}
|
93 |
+
ADD PRIMARY KEY (`id`), ADD KEY `IDX_DOOFINDER_LOG_PROCESS_ID_TYPE` (`process_id`,`type`), ADD KEY `IDX_DOOFINDER_LOG_TIME` (`time`);
|
94 |
+
|
95 |
+
ALTER TABLE {$installer->getTable('doofinder_feed/log')}
|
96 |
+
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
97 |
+
|
98 |
+
");
|
99 |
+
}
|
100 |
+
|
101 |
+
$installer->endSetup();
|
app/code/community/Doofinder/Feed/sql/doofinder_feed_setup/mysql4-upgrade-1.5.6-1.5.7.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
// Drop schedule id column in doofinder_feed table
|
8 |
+
$installer->getConnection()
|
9 |
+
->dropColumn($installer->getTable('doofinder_feed/cron'), 'schedule_id');
|
10 |
+
|
11 |
+
// Drop all scheduled doofinder jobs
|
12 |
+
$collection = Mage::getModel('cron/schedule')->getCollection()
|
13 |
+
->addFieldToFilter('job_code', Doofinder_Feed_Helper_Data::JOB_CODE);
|
14 |
+
|
15 |
+
foreach ($collection->getItems() as $item) {
|
16 |
+
$item->delete();
|
17 |
+
}
|
18 |
+
|
19 |
+
$installer->endSetup();
|
app/design/adminhtml/default/default/layout/doofinder.xml
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<layout>
|
2 |
+
<default>
|
3 |
+
<reference name="head">
|
4 |
+
<action method="addCss"><name>doofinder/styles.css</name></action>
|
5 |
+
</reference>
|
6 |
+
<reference name="head">
|
7 |
+
<action method="addJs"><name>doofinder/admin.js</name></action>
|
8 |
+
</reference>
|
9 |
+
</default>
|
10 |
+
|
11 |
+
<adminhtml_doofinderfeedlog_view>
|
12 |
+
<reference name="content">
|
13 |
+
<block type="doofinder_feed/adminhtml_log_view" name="doofinder_log_view"></block>
|
14 |
+
</reference>
|
15 |
+
</adminhtml_doofinderfeedlog_view>
|
16 |
+
</layout>
|
app/design/frontend/base/default/layout/doofinder.xml
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<layout>
|
2 |
+
<default>
|
3 |
+
<reference name="head">
|
4 |
+
<block type="doofinder_feed/integration" name="doofinder.integration"></block>
|
5 |
+
</reference>
|
6 |
+
</default>
|
7 |
+
</layout>
|
app/etc/modules/Doofinder_Feed.xml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<Doofinder_Feed>
|
5 |
+
<active>true</active>
|
6 |
+
<codePool>community</codePool>
|
7 |
+
</Doofinder_Feed>
|
8 |
+
</modules>
|
9 |
+
</config>
|
js/doofinder/admin.js
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
;(function() {
|
2 |
+
'use strict';
|
3 |
+
|
4 |
+
/**
|
5 |
+
* This file is part of Doofinder_Feed.
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* @category Javascript
|
10 |
+
* @package Doofinder_Feed
|
11 |
+
* @version 1.5.9
|
12 |
+
*/
|
13 |
+
|
14 |
+
var add_message = function(message_type, text) {
|
15 |
+
var html = '' +
|
16 |
+
'<ul class="messages">' +
|
17 |
+
' <li class="' + message_type + '-msg">' + text + '</li>' +
|
18 |
+
'</ul>';
|
19 |
+
$('messages').insert(html);
|
20 |
+
};
|
21 |
+
|
22 |
+
document.observe("dom:loaded", function() {
|
23 |
+
try {
|
24 |
+
var $td = $('row_doofinder_cron_schedule_settings_time').select('td.value')[0];
|
25 |
+
$td.innerHTML = $td.innerHTML.replace(/ : /g, '<span class="df-separator"></span>');
|
26 |
+
$td.select('.df-separator')[1].hide();
|
27 |
+
$td.select('select')[2].hide();
|
28 |
+
} catch (e) {}
|
29 |
+
|
30 |
+
if ($('doofinder_cron_feed_settings')) {
|
31 |
+
var changed = false;
|
32 |
+
new Form.Observer('config_edit_form', 0.3, function(form, value) {
|
33 |
+
if (changed) return;
|
34 |
+
add_message('notice', 'Configuration has changed. The feed generation will be rescheduled after saving.');
|
35 |
+
form.insert('<input type="hidden" name="reset" value="1"/>');
|
36 |
+
changed = true;
|
37 |
+
});
|
38 |
+
}
|
39 |
+
});
|
40 |
+
|
41 |
+
})();
|
lib/Doofinder/doofinder_api.php
ADDED
@@ -0,0 +1,804 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Author:: JoeZ99 (<jzarate@gmail.com>). all credit to Gilles Devaux (<gilles.devaux@gmail.com>) (https://github.com/flaptor/indextank-php)
|
4 |
+
*
|
5 |
+
* License:: Apache License, Version 2.0
|
6 |
+
*
|
7 |
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
8 |
+
* not use this file except in compliance with the License. You may obtain
|
9 |
+
* a copy of the License at
|
10 |
+
*
|
11 |
+
* http://www.apache.org/licenses/LICENSE-2.0
|
12 |
+
*
|
13 |
+
* Unless required by applicable law or agreed to in writing, software
|
14 |
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
15 |
+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
16 |
+
* License for the specific language governing permissions and limitations
|
17 |
+
* under the License.
|
18 |
+
*/
|
19 |
+
|
20 |
+
|
21 |
+
class DoofinderApi{
|
22 |
+
/*
|
23 |
+
* Basic client for an account.
|
24 |
+
* It needs an API url to be constructed.
|
25 |
+
* Its only method is to query the doofinder search server
|
26 |
+
* Returns a DoofinderResults object
|
27 |
+
*/
|
28 |
+
|
29 |
+
const URL_SUFFIX = '-search.doofinder.com';
|
30 |
+
const DEFAULT_TIMEOUT = 10000;
|
31 |
+
const DEFAULT_RPP = 10;
|
32 |
+
const DEFAULT_PARAMS_PREFIX = 'dfParam_';
|
33 |
+
const DEFAULT_API_VERSION = '4';
|
34 |
+
const VERSION = '5.2.5';
|
35 |
+
|
36 |
+
private $api_key = null; // user API_KEY
|
37 |
+
private $hashid = null; // hashid of the doofinder account
|
38 |
+
|
39 |
+
private $apiVersion = null;
|
40 |
+
private $url = null;
|
41 |
+
private $results = null;
|
42 |
+
private $query = null;
|
43 |
+
private $search_options = array(); // assoc. array with doofinder options to be sent as request parameters
|
44 |
+
|
45 |
+
private $page = 1; // the page of the search results we're at
|
46 |
+
private $queryName = null; // the name of the last successfull query made
|
47 |
+
private $lastQuery = null; // the last successfull query made
|
48 |
+
private $total = null; // total number of results obtained
|
49 |
+
private $maxScore = null;
|
50 |
+
private $paramsPrefix = self::DEFAULT_PARAMS_PREFIX;
|
51 |
+
private $serializationArray = null;
|
52 |
+
private $queryParameter = 'query'; // the parameter used for querying
|
53 |
+
private $allowedParameters = array('page', 'rpp', 'timeout', 'types', 'filter', 'query_name', 'transformer'); // request parameters that doofinder handle
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Constructor. account's hashid and api version set here
|
57 |
+
*
|
58 |
+
* @param string $hashid the account's hashid
|
59 |
+
* @param boolean $fromParams if set, the object is unserialized from GET or POST params
|
60 |
+
* @param array $init_options. associative array with some options:
|
61 |
+
* -'prefix' (default: 'dfParam_')=> the prefix to use when serializing.
|
62 |
+
* -'queryParameter' (default: 'query') => the parameter used for querying
|
63 |
+
* -'apiVersion' (default: '4')=> the api of the search server to query
|
64 |
+
* -'restrictedRequest'(default: $_REQUEST): =>restrict request object
|
65 |
+
* to look for params when unserializing. either 'get' or 'post'
|
66 |
+
* @throws DoofinderException if $hashid is not a md5 hash or api is no 4, 3.0 or 1.0
|
67 |
+
*/
|
68 |
+
function __construct($hashid, $api_key, $fromParams=false, $init_options = array()){
|
69 |
+
$zone_key_array = explode('-', $api_key);
|
70 |
+
|
71 |
+
if(2 === count($zone_key_array)){
|
72 |
+
$this->api_key = $zone_key_array[1];
|
73 |
+
$this->zone = $zone_key_array[0];
|
74 |
+
$this->url = "https://" . $this->zone . self::URL_SUFFIX;
|
75 |
+
} else {
|
76 |
+
throw new DoofinderException("API Key is no properly set.");
|
77 |
+
}
|
78 |
+
|
79 |
+
if(array_key_exists('prefix', $init_options)){
|
80 |
+
$this->paramsPrefix = $init_options['prefix'];
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
$this->allowedParameters = array_map(array($this, 'addprefix'), $this->allowedParameters);
|
85 |
+
|
86 |
+
|
87 |
+
if(array_key_exists('queryParameter', $init_options)){
|
88 |
+
$this->queryParameter = $init_options['queryParameter'];
|
89 |
+
} else {
|
90 |
+
$this->queryParameter = $this->paramsPrefix.$this->queryParameter;
|
91 |
+
}
|
92 |
+
|
93 |
+
|
94 |
+
$this->apiVersion = array_key_exists('apiVersion', $init_options) ?
|
95 |
+
$init_options['apiVersion'] : self::DEFAULT_API_VERSION;
|
96 |
+
$this->serializationArray = $_REQUEST;
|
97 |
+
if(array_key_exists('restrictedRequest', $init_options))
|
98 |
+
{
|
99 |
+
switch(strtolower($init_options['restrictedRequest'])){
|
100 |
+
case 'get':
|
101 |
+
$this->serializationArray = $_GET;
|
102 |
+
break;
|
103 |
+
case 'post':
|
104 |
+
$this->serializationArray = $_POST;
|
105 |
+
break;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
$patt = '/^[0-9a-f]{32}$/i';
|
109 |
+
if(!preg_match($patt, $hashid))
|
110 |
+
{
|
111 |
+
throw new DoofinderException("Wrong hashid");
|
112 |
+
}
|
113 |
+
if(!in_array($this->apiVersion, array('5', '4', '3.0','1.0')))
|
114 |
+
{
|
115 |
+
throw new DoofinderException('Wrong API');
|
116 |
+
}
|
117 |
+
$this->hashid = $hashid;
|
118 |
+
if($fromParams)
|
119 |
+
{
|
120 |
+
$this->fromQuerystring();
|
121 |
+
}
|
122 |
+
|
123 |
+
}
|
124 |
+
|
125 |
+
private function addprefix($value){
|
126 |
+
return $this->paramsPrefix.$value;
|
127 |
+
}
|
128 |
+
|
129 |
+
/*
|
130 |
+
* translateFilter
|
131 |
+
*
|
132 |
+
* translates a range filter to the new ES format
|
133 |
+
* 'from'=>9, 'to'=>20 to 'gte'=>9, 'lte'=>20
|
134 |
+
*
|
135 |
+
* @param array $filter
|
136 |
+
* @return array the translated filter
|
137 |
+
*/
|
138 |
+
private function translateFilter($filter){
|
139 |
+
$new_filter = array();
|
140 |
+
foreach($filter as $key => $value){
|
141 |
+
if ($key === 'from') {
|
142 |
+
$new_filter['gte'] = $value;
|
143 |
+
} else if ($key === 'to') {
|
144 |
+
$new_filter['lte'] = $value;
|
145 |
+
} else {
|
146 |
+
$new_filter[$key] = $value;
|
147 |
+
}
|
148 |
+
}
|
149 |
+
return $new_filter;
|
150 |
+
}
|
151 |
+
|
152 |
+
private function reqHeaders(){
|
153 |
+
$headers = array();
|
154 |
+
$headers[] = 'Expect:'; //Fixes the HTTP/1.1 417 Expectation Failed
|
155 |
+
$authHeaderName = $this->apiVersion == '4' ? 'API Token: ' : 'authorization: ';
|
156 |
+
$headers[] = $authHeaderName . $this->api_key; //API Authorization
|
157 |
+
return $headers;
|
158 |
+
}
|
159 |
+
|
160 |
+
private function apiCall($entry_point='search', $params=array()){
|
161 |
+
$params['hashid'] = $this->hashid;
|
162 |
+
$args = http_build_query($this->sanitize($params)); // remove any null value from the array
|
163 |
+
|
164 |
+
$url = $this->url.'/'.$this->apiVersion.'/'.$entry_point.'?'.$args;
|
165 |
+
|
166 |
+
$session = curl_init($url);
|
167 |
+
curl_setopt($session, CURLOPT_CUSTOMREQUEST, 'GET');
|
168 |
+
curl_setopt($session, CURLOPT_HEADER, false); // Tell curl not to return headers
|
169 |
+
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Tell curl to return the response
|
170 |
+
curl_setopt($session, CURLOPT_HTTPHEADER, $this->reqHeaders()); // Adding request headers
|
171 |
+
$response = curl_exec($session);
|
172 |
+
$httpCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
|
173 |
+
curl_close($session);
|
174 |
+
|
175 |
+
if (floor($httpCode / 100) == 2) {
|
176 |
+
return $response;
|
177 |
+
}
|
178 |
+
throw new DoofinderException($httpCode.' - '.$response, $httpCode);
|
179 |
+
}
|
180 |
+
|
181 |
+
public function getOptions(){
|
182 |
+
return $this->apiCall('options/'.$this->hashid);
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* query. makes the query to the doofinder search server.
|
187 |
+
* also set several search parameters through it's $options argument
|
188 |
+
*
|
189 |
+
* @param string $query the search query
|
190 |
+
* @param int $page the page number or the results to show
|
191 |
+
* @param array $options query options:
|
192 |
+
* - 'rpp'=> number of results per page. default 10
|
193 |
+
* - 'timeout' => timeout after which the search server drops the conn.
|
194 |
+
* defaults to 10 seconds
|
195 |
+
* - 'types' => types of index to search at. default: all.
|
196 |
+
* - 'filter' => filter to apply. ['color'=>['red','blue'], 'price'=>['from'=>33]]
|
197 |
+
* - any other param will be sent as a request parameter
|
198 |
+
* @return DoofinderResults results
|
199 |
+
*/
|
200 |
+
public function query($query=null, $page=null, $options = array()){
|
201 |
+
if($query){
|
202 |
+
$this->search_options['query'] = $query;
|
203 |
+
}
|
204 |
+
if($page){
|
205 |
+
$this->search_options['page'] = (int)$page;
|
206 |
+
}
|
207 |
+
foreach($options as $optionName => $optionValue){
|
208 |
+
$this->search_options[$optionName] = $options[$optionName];
|
209 |
+
}
|
210 |
+
|
211 |
+
$params = $this->search_options;
|
212 |
+
|
213 |
+
// translate filters
|
214 |
+
if(!empty($params['filter']))
|
215 |
+
{
|
216 |
+
foreach($params['filter'] as $filterName => $filterValue){
|
217 |
+
$params['filter'][$filterName] = $this->translateFilter($filterValue);
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
// no query? then match all documents
|
222 |
+
if(!$this->optionExists('query') || !trim($this->search_options['query'])){
|
223 |
+
$params['query_name'] = 'match_all';
|
224 |
+
}
|
225 |
+
|
226 |
+
// if filters without query_name, pre-query first to obtain it.
|
227 |
+
if (empty($params['query_name']) && !empty($params['filter']))
|
228 |
+
{
|
229 |
+
$filter = $params['filter'];
|
230 |
+
unset($params['filter']);
|
231 |
+
$dfResults = new DoofinderResults($this->apiCall('search', $params));
|
232 |
+
$params['query_name'] = $dfResults->getProperty('query_name');
|
233 |
+
$params['filter'] = $filter;
|
234 |
+
}
|
235 |
+
$dfResults = new DoofinderResults($this->apiCall('search', $params));
|
236 |
+
$this->page = $dfResults->getProperty('page');
|
237 |
+
$this->total = $dfResults->getProperty('total');
|
238 |
+
$this->search_options['query'] = $dfResults->getProperty('query');
|
239 |
+
$this->maxScore = $dfResults->getProperty('max_score');
|
240 |
+
$this->queryName = $dfResults->getProperty('query_name');
|
241 |
+
$this->lastQuery = $dfResults->getProperty('query');
|
242 |
+
|
243 |
+
return $dfResults;
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* hasNext
|
248 |
+
*
|
249 |
+
* @return boolean true if there is another page of results
|
250 |
+
*/
|
251 |
+
public function hasNext(){
|
252 |
+
return $this->page*$this->getRpp() < $this->total;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* hasPrev
|
257 |
+
*
|
258 |
+
* @return true if there is a previous page of results
|
259 |
+
*/
|
260 |
+
public function hasPrev(){
|
261 |
+
return ($this->page-1)*$this->getRpp() > 0;
|
262 |
+
}
|
263 |
+
|
264 |
+
|
265 |
+
/**
|
266 |
+
* getPage
|
267 |
+
*
|
268 |
+
* obtain the current page number
|
269 |
+
* @return int the page number
|
270 |
+
*/
|
271 |
+
public function getPage(){
|
272 |
+
return $this->page;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* setFilter
|
277 |
+
*
|
278 |
+
* set a filter for the query
|
279 |
+
* @param string filterName the name of the filter to set
|
280 |
+
* @param array filter if simple array, terms filter assumed
|
281 |
+
* if 'from', 'to' in keys, range filter assumed
|
282 |
+
*/
|
283 |
+
public function setFilter($filterName, $filter){
|
284 |
+
if(!$this->optionExists('filter')){
|
285 |
+
$this->search_options['filter'] = array();
|
286 |
+
}
|
287 |
+
$this->search_options['filter'][$filterName] = $filter;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* getFilter
|
292 |
+
*
|
293 |
+
* get conditions for certain filter
|
294 |
+
* @param string filterName
|
295 |
+
* @return array filter conditions: - simple array if terms filter
|
296 |
+
* - 'from', 'to' assoc array if range f.
|
297 |
+
* @return false if no filter definition found
|
298 |
+
*/
|
299 |
+
public function getFilter($filterName){
|
300 |
+
if($this->optionExists('filter') && isset($this->search_options['filter'][$filterName])){
|
301 |
+
return $this->filter[$filterName];
|
302 |
+
}
|
303 |
+
return false;
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* getFilters
|
308 |
+
*
|
309 |
+
* get all filters and their configs
|
310 |
+
* @return array assoc array filterName => filterConditions
|
311 |
+
*/
|
312 |
+
public function getFilters(){
|
313 |
+
return $this->search_options['filter'];
|
314 |
+
}
|
315 |
+
|
316 |
+
|
317 |
+
/**
|
318 |
+
* addTerm
|
319 |
+
*
|
320 |
+
* add a term to a terms filter
|
321 |
+
* @param string filterName the filter to add the term to
|
322 |
+
* @param string term the term to add
|
323 |
+
*/
|
324 |
+
public function addTerm($filterName, $term){
|
325 |
+
if(!$this->optionExists('filter')){
|
326 |
+
$this->search_options['filter'] = array($filterName => array());
|
327 |
+
}
|
328 |
+
if(!isset($this->search_options['filter'][$filterName]))
|
329 |
+
{
|
330 |
+
$this->filter[$filterName] = array();
|
331 |
+
$this->search_options['filter'][$filterName] = array();
|
332 |
+
}
|
333 |
+
$this->filter[$filterName][] = $term;
|
334 |
+
$this->search_options['filter'][$filterName][] = $term;
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* removeTerm
|
339 |
+
*
|
340 |
+
* remove a term from a terms filter
|
341 |
+
* @param string filterName the filter to remove the term from
|
342 |
+
* @param string term the term to be removed
|
343 |
+
*/
|
344 |
+
public function removeTerm($filterName, $term){
|
345 |
+
if($this->optionExists('filter') && isset($this->search_options['filter'][$filterName]) &&
|
346 |
+
in_array($term, $this->search_options['filter'][$filterName]))
|
347 |
+
{
|
348 |
+
function filter_me($value){
|
349 |
+
global $term;
|
350 |
+
return $value != $term;
|
351 |
+
}
|
352 |
+
$this->search_options['filter'][$filterName] =
|
353 |
+
array_filter($this->search_options['filter'][$filterName], 'filter_me');
|
354 |
+
}
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* setRange
|
359 |
+
*
|
360 |
+
* set a range filter
|
361 |
+
* @param string filterName the filter to set
|
362 |
+
* @param int from the lower bound value. included
|
363 |
+
* @param int to the upper bound value. included
|
364 |
+
*/
|
365 |
+
public function setRange($filterName, $from=null, $to=null){
|
366 |
+
if(!$this->optionExists('filter')){
|
367 |
+
$this->search_options['filter'] = array($filterName=>array());
|
368 |
+
}
|
369 |
+
if(!isset($this->search_options['filter'][$filterName]))
|
370 |
+
{
|
371 |
+
$this->search_options['filter'][$filterName] = array();
|
372 |
+
}
|
373 |
+
if($from)
|
374 |
+
{
|
375 |
+
$this->search_options['filter'][$filterName]['from'] = $from;
|
376 |
+
}
|
377 |
+
if($to)
|
378 |
+
{
|
379 |
+
$this->search_options['filter'][$filterName]['to'] = $from;
|
380 |
+
}
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* toQuerystring
|
385 |
+
*
|
386 |
+
* 'serialize' the object's state to querystring params
|
387 |
+
* @param int $page the pagenumber. defaults to the current page
|
388 |
+
*/
|
389 |
+
public function toQuerystring($page=null){
|
390 |
+
|
391 |
+
foreach($this->search_options as $paramName => $paramValue){
|
392 |
+
if($paramName == 'query'){
|
393 |
+
$toParams[$this->queryParameter] = $paramValue;
|
394 |
+
} else {
|
395 |
+
$toParams[$this->paramsPrefix.$paramName] = $paramValue;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
if($page){
|
399 |
+
$toParams[$this->paramsPrefix.'page'] = $page;
|
400 |
+
}
|
401 |
+
return http_build_query($toParams);
|
402 |
+
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* fromQuerystring
|
406 |
+
*
|
407 |
+
* obtain object's state from querystring params
|
408 |
+
* @param string $params where to obtain params from:
|
409 |
+
* - 'GET' $_GET params (default)
|
410 |
+
* - 'POST' $_POST params
|
411 |
+
*/
|
412 |
+
public function fromQuerystring(){
|
413 |
+
$doofinderReqParams = array_filter(array_keys($this->serializationArray),
|
414 |
+
array($this, 'belongsToDoofinder'));
|
415 |
+
|
416 |
+
foreach($doofinderReqParams as $dfReqParam){
|
417 |
+
if($dfReqParam == $this->queryParameter){
|
418 |
+
$keey = 'query';
|
419 |
+
} else {
|
420 |
+
$keey = substr($dfReqParam, strlen($this->paramsPrefix));
|
421 |
+
}
|
422 |
+
$this->search_options[$keey] = $this->serializationArray[$dfReqParam];
|
423 |
+
}
|
424 |
+
}
|
425 |
+
|
426 |
+
/**
|
427 |
+
* sanitize
|
428 |
+
*
|
429 |
+
* Clean array of keys with empty values
|
430 |
+
*
|
431 |
+
* @param array $params array to be cleaned
|
432 |
+
* @return array array with no empty keys
|
433 |
+
*/
|
434 |
+
private function sanitize($params){
|
435 |
+
$result = array();
|
436 |
+
foreach($params as $name => $value){
|
437 |
+
if (is_array($value)){
|
438 |
+
$result[$name] = $this->sanitize($value);
|
439 |
+
} else if (trim($value)){
|
440 |
+
$result[$name] = $value;
|
441 |
+
}
|
442 |
+
}
|
443 |
+
return $result;
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* belongsToDoofinder
|
448 |
+
*
|
449 |
+
* to know if certain parameter name belongs to doofinder serialization parameters
|
450 |
+
*
|
451 |
+
* @param string $paramName name of the param
|
452 |
+
* @return boolean true or false.
|
453 |
+
*/
|
454 |
+
private function belongsToDoofinder($paramName){
|
455 |
+
if($pos = strpos($paramName, '[')){
|
456 |
+
$paramName = substr($paramName, 0, $pos);
|
457 |
+
}
|
458 |
+
return in_array($paramName, $this->allowedParameters) || $paramName == $this->queryParameter;
|
459 |
+
}
|
460 |
+
|
461 |
+
/**
|
462 |
+
* optionExists
|
463 |
+
*
|
464 |
+
* checks whether a search option is defined in $this->search_options
|
465 |
+
*
|
466 |
+
* @param string $optionName
|
467 |
+
* @return boolean
|
468 |
+
*/
|
469 |
+
private function optionExists($optionName){
|
470 |
+
return array_key_exists($optionName, $this->search_options);
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* nextPage
|
475 |
+
*
|
476 |
+
* obtain the results for the next page
|
477 |
+
* @return DoofinderResults if there are results.
|
478 |
+
* @return null otherwise
|
479 |
+
*/
|
480 |
+
public function nextPage(){
|
481 |
+
if($this->hasNext())
|
482 |
+
{
|
483 |
+
return $this->query($this->lastQuery, $this->page+1 );
|
484 |
+
}
|
485 |
+
return null;
|
486 |
+
}
|
487 |
+
|
488 |
+
|
489 |
+
/**
|
490 |
+
* prevPage
|
491 |
+
*
|
492 |
+
* obtain results for the previous page
|
493 |
+
* @return DoofinderResults
|
494 |
+
* @return null otherwise
|
495 |
+
*/
|
496 |
+
public function prevPage(){
|
497 |
+
if($this->hasPrev())
|
498 |
+
{
|
499 |
+
return $this->query($this->lastQuery, $this->page-1 );
|
500 |
+
}
|
501 |
+
return null;
|
502 |
+
}
|
503 |
+
|
504 |
+
/**
|
505 |
+
* numPages
|
506 |
+
*
|
507 |
+
* @return integer the number of pages
|
508 |
+
*/
|
509 |
+
public function numPages(){
|
510 |
+
return ceil($this->total / $this->getRpp());
|
511 |
+
}
|
512 |
+
|
513 |
+
public function getRpp(){
|
514 |
+
$rpp = $this->optionExists('rpp') ? $this->search_options['rpp'] : null;
|
515 |
+
$rpp = $rpp ? $rpp: self::DEFAULT_RPP;
|
516 |
+
return $rpp;
|
517 |
+
}
|
518 |
+
/**
|
519 |
+
* setApiVersion
|
520 |
+
*
|
521 |
+
* sets the api version to use.
|
522 |
+
* @param string $apiVersion the api version , '1.0' or '3.0' or '4'
|
523 |
+
*/
|
524 |
+
public function setApiVersion($apiVersion){
|
525 |
+
$this->apiVersion = $apiVersion;
|
526 |
+
}
|
527 |
+
|
528 |
+
/**
|
529 |
+
* setPrefix
|
530 |
+
*
|
531 |
+
* sets the prefix that will be used for serialization to querystring params
|
532 |
+
* @param string $prefix the prefix
|
533 |
+
*/
|
534 |
+
public function setPrefix($prefix){
|
535 |
+
$this->paramsPrefix = $prefix;
|
536 |
+
}
|
537 |
+
|
538 |
+
/**
|
539 |
+
* setQueryName
|
540 |
+
*
|
541 |
+
* sets query_name
|
542 |
+
* CAUTION: node will complain if this is wrong
|
543 |
+
*/
|
544 |
+
public function setQueryName($queryName){
|
545 |
+
$this->queryName = $queryName;
|
546 |
+
}
|
547 |
+
|
548 |
+
/**
|
549 |
+
* getFilterType
|
550 |
+
* obtain the filter type (i.e. 'terms' or 'numeric range' from its conditions)
|
551 |
+
* @param array filter conditions
|
552 |
+
* @return string 'terms' or 'numericrange' false otherwise
|
553 |
+
*/
|
554 |
+
private function getFilterType($filter){
|
555 |
+
if(!is_array($filter))
|
556 |
+
{
|
557 |
+
return false;
|
558 |
+
}
|
559 |
+
if(count(array_intersect(array('from', 'to'), array_keys($filter)))>0)
|
560 |
+
{
|
561 |
+
return 'numericrange';
|
562 |
+
}
|
563 |
+
return 'terms';
|
564 |
+
}
|
565 |
+
|
566 |
+
|
567 |
+
}
|
568 |
+
|
569 |
+
/**
|
570 |
+
* @author JoeZ99 <jzarate@gmail.com>
|
571 |
+
*
|
572 |
+
* DoofinderResults
|
573 |
+
*
|
574 |
+
* Very thin wrapper of the results obtained from the doofinder server
|
575 |
+
* it holds to accessor:
|
576 |
+
* - getProperty : get single property of the search results (rpp, page, etc....)
|
577 |
+
* - getResults: get an array with the results
|
578 |
+
*/
|
579 |
+
class DoofinderResults{
|
580 |
+
|
581 |
+
// doofinder status
|
582 |
+
const SUCCESS = 'success'; // everything ok
|
583 |
+
const NOTFOUND = 'notfound'; // no account with the provided hashid found
|
584 |
+
const EXHAUSTED = 'exhausted'; // the account has reached its query limit
|
585 |
+
|
586 |
+
private $properties = null;
|
587 |
+
private $results = null;
|
588 |
+
private $facets = null;
|
589 |
+
private $filter = null;
|
590 |
+
public $status = null;
|
591 |
+
|
592 |
+
/**
|
593 |
+
* Constructor
|
594 |
+
*
|
595 |
+
* @param string $jsonString stringified json returned by doofinder search server
|
596 |
+
*/
|
597 |
+
function __construct($jsonString){
|
598 |
+
$rawResults = json_decode($jsonString, true);
|
599 |
+
foreach($rawResults as $kkey => $vall){
|
600 |
+
if(!is_array($vall)){
|
601 |
+
$this->properties[$kkey] = $vall;
|
602 |
+
}
|
603 |
+
}
|
604 |
+
// doofinder status
|
605 |
+
$this->status = isset($this->properties['doofinder_status'])?
|
606 |
+
$this->properties['doofinder_status'] : self::SUCCESS;
|
607 |
+
|
608 |
+
// results
|
609 |
+
$this->results = array();
|
610 |
+
|
611 |
+
if(isset($rawResults['results']) && is_array($rawResults['results']))
|
612 |
+
{
|
613 |
+
$this->results = $rawResults['results'];
|
614 |
+
}
|
615 |
+
|
616 |
+
// build a friendly filter array
|
617 |
+
$this->filter = array();
|
618 |
+
// reorder filter, before assigning it to $this
|
619 |
+
if(isset($rawResults['filter']))
|
620 |
+
{
|
621 |
+
foreach($rawResults['filter'] as $filterType => $filters)
|
622 |
+
{
|
623 |
+
foreach($filters as $filterName => $filterProperties)
|
624 |
+
{
|
625 |
+
$this->filter[$filterName] = $filterProperties;
|
626 |
+
}
|
627 |
+
}
|
628 |
+
}
|
629 |
+
|
630 |
+
// facets
|
631 |
+
$this->facets = array();
|
632 |
+
if(isset($rawResults['facets']))
|
633 |
+
{
|
634 |
+
$this->facets = $rawResults['facets'];
|
635 |
+
|
636 |
+
// mark "selected" true or false according to filters presence
|
637 |
+
foreach($this->facets as $facetName => $facetProperties){
|
638 |
+
switch($facetProperties['_type']){
|
639 |
+
case 'terms':
|
640 |
+
foreach($facetProperties['terms'] as $pos => $term){
|
641 |
+
if(isset($this->filter[$facetName]) && in_array($term['term'], $this->filter[$facetName])){
|
642 |
+
$this->facets[$facetName]['terms'][$pos]['selected'] = true;
|
643 |
+
} else {
|
644 |
+
$this->facets[$facetName]['terms'][$pos]['selected'] = false;
|
645 |
+
}
|
646 |
+
}
|
647 |
+
break;
|
648 |
+
case 'range':
|
649 |
+
foreach($facetProperties['ranges'] as $pos => $range){
|
650 |
+
$this->facets[$facetName]['ranges'][$pos]['selected_from'] = false;
|
651 |
+
$this->facets[$facetName]['ranges'][$pos]['selected_to'] = false;
|
652 |
+
if(isset($this->filter[$facetName]) && isset($this->filter[$facetName]['gte'])){
|
653 |
+
$this->facets[$facetName]['ranges'][$pos]['selected_from'] = $this->filter[$facetName]['gte'];
|
654 |
+
}
|
655 |
+
if(isset($this->filter[$facetName]) && isset($this->filter[$facetName]['lte'])){
|
656 |
+
$this->facets[$facetName]['ranges'][$pos]['selected_to'] = $this->filter[$facetName]['lte'];
|
657 |
+
}
|
658 |
+
|
659 |
+
}
|
660 |
+
break;
|
661 |
+
}
|
662 |
+
}
|
663 |
+
}
|
664 |
+
}
|
665 |
+
|
666 |
+
/**
|
667 |
+
* getProperty
|
668 |
+
*
|
669 |
+
* get single property from the results
|
670 |
+
* @param string @propertyName: 'results_per_page', 'query', 'max_score', 'page', 'total', 'hashid'
|
671 |
+
* @return mixed the value of the property
|
672 |
+
*/
|
673 |
+
public function getProperty($propertyName){
|
674 |
+
return array_key_exists($propertyName, $this->properties) ?
|
675 |
+
$this->properties[$propertyName]: null;
|
676 |
+
}
|
677 |
+
|
678 |
+
/**
|
679 |
+
* getResults
|
680 |
+
*
|
681 |
+
* @return array search results. at the moment, only the 'cooked' version.
|
682 |
+
* Each result is of the form:
|
683 |
+
* array('header'=>...,
|
684 |
+
* 'body' => ..,
|
685 |
+
* 'price' => ..,
|
686 |
+
* 'href' => ...,
|
687 |
+
* 'image' => ...,
|
688 |
+
* 'type' => ...,
|
689 |
+
* 'id' => ..)
|
690 |
+
*/
|
691 |
+
public function getResults(){
|
692 |
+
return $this->results;
|
693 |
+
}
|
694 |
+
|
695 |
+
/**
|
696 |
+
*
|
697 |
+
* getFacetsNames
|
698 |
+
*
|
699 |
+
* @return array facets names.
|
700 |
+
*/
|
701 |
+
public function getFacetsNames(){
|
702 |
+
return array_keys($this->facets);
|
703 |
+
}
|
704 |
+
|
705 |
+
/**
|
706 |
+
* getFacet
|
707 |
+
*
|
708 |
+
* @param string name the facet name whose results are wanted
|
709 |
+
*
|
710 |
+
* @return array facet search data
|
711 |
+
* - for terms facets
|
712 |
+
* array(
|
713 |
+
* '_type'=> 'terms', // type of facet 'terms' or 'range'
|
714 |
+
* 'missing'=> 3, // # of elements with no value for this facet
|
715 |
+
* 'others'=> 2, // # of terms not present in the search response
|
716 |
+
* 'total'=> 6, // # number of possible terms for this facet
|
717 |
+
* 'terms'=> array(
|
718 |
+
* array('count'=>6, 'term'=>'Blue', 'selected'=>false), // in the response, there are 6 'blue' terms
|
719 |
+
* array('count'=>3, 'term': 'Red', 'selected'=>true), // if 'selected'=>true, that term has been selected as filter
|
720 |
+
* ...
|
721 |
+
* )
|
722 |
+
* )
|
723 |
+
* - for range facets
|
724 |
+
* array(
|
725 |
+
* '_type'=> 'range',
|
726 |
+
* 'ranges'=> array(
|
727 |
+
* array(
|
728 |
+
* 'count'=>6, // in the response, 6 elements within that range.
|
729 |
+
* 'from':0,
|
730 |
+
* 'min': 30
|
731 |
+
* 'max': 90,
|
732 |
+
* 'mean'=>33.2,
|
733 |
+
* 'total'=>432,
|
734 |
+
* 'total_count'=>6,
|
735 |
+
* 'selected_from'=> 34.3 // if present. this value has been used as filter. false otherwise
|
736 |
+
* 'selected_to'=> 99.3 // if present. this value has been used as filter. false otherwise
|
737 |
+
* ),
|
738 |
+
* ...
|
739 |
+
* )
|
740 |
+
* )
|
741 |
+
*
|
742 |
+
*
|
743 |
+
*/
|
744 |
+
public function getFacet($facetName){
|
745 |
+
return $this->facets[$facetName];
|
746 |
+
}
|
747 |
+
|
748 |
+
/**
|
749 |
+
* getFacets
|
750 |
+
*
|
751 |
+
* get the whole facets associative array:
|
752 |
+
* array('color'=>array(...), 'brand'=>array(...))
|
753 |
+
* each array is defined as in getFacet() docstring
|
754 |
+
*
|
755 |
+
* @return array facets assoc. array
|
756 |
+
*/
|
757 |
+
public function getFacets(){
|
758 |
+
return $this->facets;
|
759 |
+
}
|
760 |
+
|
761 |
+
/**
|
762 |
+
* getAppliedFilters
|
763 |
+
*
|
764 |
+
* get the filters the query has defined
|
765 |
+
* array('categories' => array( // filter name . same as facet name
|
766 |
+
* 'Sillas de paseo', // if simple array, it's a terms facet
|
767 |
+
* 'Sacos sillas de paseo'
|
768 |
+
* ),
|
769 |
+
* 'color' => array(
|
770 |
+
* 'red',
|
771 |
+
* 'blue'
|
772 |
+
* ),
|
773 |
+
* 'price' => array(
|
774 |
+
* 'include_upper'=>true, // if 'from' , 'to' keys, it's a range facet
|
775 |
+
* 'from'=>35.19,
|
776 |
+
* 'to'=>9999
|
777 |
+
* )
|
778 |
+
* )
|
779 |
+
* MEANING OF THE EXAMPLE FILTER:
|
780 |
+
* "FROM the query results, filter only results that have ('Sillas de paseo' OR 'Sacos sillas de paseo') categories
|
781 |
+
* AND ('red' OR 'blue') color AND price is BETWEEN 34.3 and 99.3"
|
782 |
+
|
783 |
+
*/
|
784 |
+
public function getAppliedFilters(){
|
785 |
+
return $this->filter;
|
786 |
+
}
|
787 |
+
|
788 |
+
/**
|
789 |
+
* isOk
|
790 |
+
*
|
791 |
+
* checks if all went well
|
792 |
+
* @return boolean true if the status is 'success'.
|
793 |
+
* false if the status is not.
|
794 |
+
*/
|
795 |
+
public function isOk(){
|
796 |
+
return $this->status == self::SUCCESS;
|
797 |
+
}
|
798 |
+
}
|
799 |
+
|
800 |
+
|
801 |
+
class DoofinderException extends Exception{
|
802 |
+
|
803 |
+
}
|
804 |
+
|
lib/Doofinder/doofinder_management_api.php
ADDED
@@ -0,0 +1,408 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Author:: JoeZ99 (<jzarate@gmail.com>).
|
4 |
+
*
|
5 |
+
* License:: Apache License, Version 2.0
|
6 |
+
*
|
7 |
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
8 |
+
* not use this file except in compliance with the License. You may obtain
|
9 |
+
* a copy of the License at
|
10 |
+
*
|
11 |
+
* http://www.apache.org/licenses/LICENSE-2.0
|
12 |
+
*
|
13 |
+
* Unless required by applicable law or agreed to in writing, software
|
14 |
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
15 |
+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
16 |
+
* License for the specific language governing permissions and limitations
|
17 |
+
* under the License.
|
18 |
+
*/
|
19 |
+
require_once dirname(__FILE__).'/errors.php';
|
20 |
+
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Class to manage the connection with the API servers.
|
24 |
+
*
|
25 |
+
* Needs APIKEY in initialization.
|
26 |
+
* Example: $dma = new DoofinderManagementApi('eu1-d531af87f10969f90792a4296e2784b089b8a875')
|
27 |
+
*/
|
28 |
+
class DoofinderManagementApi
|
29 |
+
{
|
30 |
+
const REMOTE_API_ENDPOINT = "https://%s-api.doofinder.com/v1";
|
31 |
+
const LOCAL_API_ENDPOINT = "http://localhost:8000/api/v1";
|
32 |
+
|
33 |
+
private $apiKey = null;
|
34 |
+
private $clusterRegion = 'eu1';
|
35 |
+
private $token = null;
|
36 |
+
private $baseManagementUrl = null;
|
37 |
+
|
38 |
+
function __construct($apiKey, $local = false){
|
39 |
+
$this->apiKey = $apiKey;
|
40 |
+
$clusterToken = explode('-', $apiKey);
|
41 |
+
$this->clusterRegion = $clusterToken[0];
|
42 |
+
$this->token = $clusterToken[1];
|
43 |
+
if ($local === true){
|
44 |
+
$this->baseManagementUrl = self::LOCAL_API_ENDPOINT;
|
45 |
+
} else {
|
46 |
+
$this->baseManagementUrl = sprintf(self::REMOTE_API_ENDPOINT, $this->clusterRegion);
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Makes the actual request to the API server and normalize response
|
52 |
+
*
|
53 |
+
* @param string $method The HTTP method to use. 'GET|PUT|POST|DELETE'
|
54 |
+
* @param string $entryPoint The path to use. '/<hashid>/items/product'
|
55 |
+
* @param array $params If any, url request parameters
|
56 |
+
* @param array $data If any, body request parameters
|
57 |
+
* @return array Array with both status code and response .
|
58 |
+
*/
|
59 |
+
function managementApiCall($method='GET', $entryPoint='', $params=null, $data=null){
|
60 |
+
$headers = array('Authorization: Token '.$this->token, // for Auth
|
61 |
+
'Content-Type: application/json',
|
62 |
+
'Expect:'); // Fixes the HTTP/1.1 417 Expectation Failed
|
63 |
+
|
64 |
+
$url = $this->baseManagementUrl.'/'.$entryPoint;
|
65 |
+
if (is_array($params) && sizeof($params) > 0){
|
66 |
+
$url .= '?'.http_build_query($params);
|
67 |
+
}
|
68 |
+
|
69 |
+
$session = curl_init($url);
|
70 |
+
curl_setopt($session, CURLOPT_CUSTOMREQUEST, $method);
|
71 |
+
curl_setopt($session, CURLOPT_HEADER, false);
|
72 |
+
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Tell curl to return the response
|
73 |
+
curl_setopt($session, CURLOPT_HTTPHEADER, $headers); // Adding request headers
|
74 |
+
|
75 |
+
if (in_array($method, array('POST', 'PUT'))){
|
76 |
+
curl_setopt($session, CURLOPT_POSTFIELDS, $data);
|
77 |
+
}
|
78 |
+
|
79 |
+
$response = curl_exec($session);
|
80 |
+
$httpCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
|
81 |
+
curl_close($session);
|
82 |
+
|
83 |
+
handleErrors($httpCode, $response);
|
84 |
+
|
85 |
+
$return = array('statusCode' => $httpCode);
|
86 |
+
$return['response'] = ($decoded = json_decode($response, true)) ? $decoded : $response;
|
87 |
+
|
88 |
+
return $return;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* To get info on all possible api entry points
|
93 |
+
* @return array An assoc. array with the different entry points
|
94 |
+
*/
|
95 |
+
function getApiRoot(){
|
96 |
+
$response = $this->managementApiCall();
|
97 |
+
return $response['response'];
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Obtain a list of SearchEngines objects, ready to interact with the API
|
102 |
+
* @return array list of searchEngines objects
|
103 |
+
*/
|
104 |
+
function getSearchEngines(){
|
105 |
+
$searchEngines = array();
|
106 |
+
$apiRoot = $this->getApiRoot();
|
107 |
+
unset($apiRoot['searchengines']);
|
108 |
+
|
109 |
+
foreach($apiRoot as $hashid => $props){
|
110 |
+
$searchEngines[] = new SearchEngine($this, $hashid, $props['name']);
|
111 |
+
}
|
112 |
+
|
113 |
+
return $searchEngines;
|
114 |
+
}
|
115 |
+
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Class with all the capabilities described in the API
|
120 |
+
*
|
121 |
+
* see http://www.doofinder.com/developer/topics/api/management-api
|
122 |
+
*/
|
123 |
+
class SearchEngine {
|
124 |
+
public $name = null;
|
125 |
+
public $hashid = null;
|
126 |
+
|
127 |
+
private $dma = null; // DoofinderManagementApi instance
|
128 |
+
|
129 |
+
function __construct($dma, $hashid, $name){
|
130 |
+
$this->name = $name;
|
131 |
+
$this->hashid = $hashid;
|
132 |
+
$this->dma = $dma;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Get a list of searchengine's types
|
137 |
+
*
|
138 |
+
* @return array list of types
|
139 |
+
*/
|
140 |
+
function getDatatypes(){
|
141 |
+
return $this->getTypes();
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Get a list of searchengine's types
|
146 |
+
*
|
147 |
+
* @return array list of types
|
148 |
+
*/
|
149 |
+
function getTypes(){
|
150 |
+
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/types");
|
151 |
+
return $result['response'];
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Add a type to the searchengine
|
156 |
+
*
|
157 |
+
* @param string $dType the type name
|
158 |
+
* @return new list of searchengine's types
|
159 |
+
*/
|
160 |
+
function addType($dType){
|
161 |
+
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/types", null,
|
162 |
+
json_encode(array('name' => $dType)));
|
163 |
+
return $result['response'];
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Delete a type and all its items. HANDLE WITH CARE
|
168 |
+
*
|
169 |
+
* @param string $dType the Type to delete. All items belonging
|
170 |
+
* to that type will be removed. mandatory
|
171 |
+
* @return boolean true on success
|
172 |
+
*/
|
173 |
+
function deleteType($dType){
|
174 |
+
$result = $this->dma->managementApiCall('DELETE', "{$this->hashid}/types/{$dType}");
|
175 |
+
return $result['statusCode'] == 204;
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Get paginated indexed items belonging to a searchengine's type
|
180 |
+
*
|
181 |
+
* It only paginates forward. Can't go backwards
|
182 |
+
* @param string $dType Type of the items to list
|
183 |
+
* @param string $scrollId identifier of the pagination set
|
184 |
+
* @return array Assoc array with scroll_id ,paginated results and total results.
|
185 |
+
*/
|
186 |
+
public function getScrolledItemsPage($dType, $scrollId = null){
|
187 |
+
$params = $scrollId ? array("scroll_id" => $scrollId) : null;
|
188 |
+
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/items/{$dType}", $params);
|
189 |
+
return array(
|
190 |
+
'scroll_id' => $result['response']['scroll_id'],
|
191 |
+
'results' => $result['response']['results'],
|
192 |
+
'total' => $result['response']['count'],
|
193 |
+
);
|
194 |
+
}
|
195 |
+
|
196 |
+
function items($dType){
|
197 |
+
return new ItemsRS($this, $dType);
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Get details of a specific item
|
202 |
+
*
|
203 |
+
* @param string $dType Type of the item.
|
204 |
+
* @param string $itemId the id of the item
|
205 |
+
* @return array Assoc array representing the item.
|
206 |
+
*/
|
207 |
+
function getItem($dType, $itemId){
|
208 |
+
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/items/{$dType}/{$itemId}");
|
209 |
+
return $result['response'];
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Add an item to the search engine
|
214 |
+
*
|
215 |
+
* - If the 'id' field is present, use that as item's id or overwrite an existing
|
216 |
+
* item with that id.
|
217 |
+
* - It the 'id' field is not present, create one.
|
218 |
+
*
|
219 |
+
* @param string $dType type of the. If not provided, first available type is used
|
220 |
+
* @param array $itemDescription Assoc array representation of the item
|
221 |
+
* @return string the id of the item just created
|
222 |
+
*/
|
223 |
+
function addItem($dType, $itemDescription){
|
224 |
+
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/items/{$dType}", null,
|
225 |
+
json_encode($itemDescription));
|
226 |
+
return $result['response']['id'];
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Add items in bulk to the search engine
|
231 |
+
*
|
232 |
+
* For each item:
|
233 |
+
* - If the 'id' field is present, use that as item's id or overwrite an existing
|
234 |
+
* item with that id.
|
235 |
+
* - It the 'id' field is not present, create one.
|
236 |
+
*
|
237 |
+
* @param string $dType type of the. If not provided, first available type is used
|
238 |
+
* @param array $itemsDescription List of Assoc array representation of the item
|
239 |
+
* @return array List of ids of the added items
|
240 |
+
*/
|
241 |
+
function addItems($dType, $itemsDescription){
|
242 |
+
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/items/{$dType}", null,
|
243 |
+
json_encode($itemsDescription));
|
244 |
+
|
245 |
+
function fetchId($item){
|
246 |
+
return $item['id'];
|
247 |
+
};
|
248 |
+
|
249 |
+
return array_map('fetchId', $result['response']);
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Update or create an item of the search engine
|
254 |
+
*
|
255 |
+
* In case of conflict between itemDescription's id or $itemId,
|
256 |
+
* the latter is used.
|
257 |
+
*
|
258 |
+
* @param string $dType type of the Item.
|
259 |
+
* @param string $itemId Id of the item to be updated/added
|
260 |
+
* @param array $itemDescription Assoc array representating the item.
|
261 |
+
* @return boolean true on success.
|
262 |
+
*/
|
263 |
+
function updateItem($dType, $itemId, $itemDescription){
|
264 |
+
$result = $this->dma->managementApiCall('PUT', "{$this->hashid}/items/{$dType}/{$itemId}", null,
|
265 |
+
json_encode($itemDescription));
|
266 |
+
return $result['statusCode'] == 200;
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* Bulk update of several items
|
271 |
+
*
|
272 |
+
* Each item description must contain the 'id' field
|
273 |
+
*
|
274 |
+
* @param string $dType type of the items.
|
275 |
+
* @param array $itemsDescription List of assoc array representing items
|
276 |
+
* @return boolean true on success
|
277 |
+
*/
|
278 |
+
function updateItems($dType, $itemsDescription){
|
279 |
+
$result = $this->dma->managementApiCall('PUT', "{$this->hashid}/items/{$dType}", null,
|
280 |
+
json_encode($itemsDescription));
|
281 |
+
return $result['statusCode'] == 200;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Delete an item
|
286 |
+
*
|
287 |
+
* @param string $dType type of the item
|
288 |
+
* @param string $itemId id of the item
|
289 |
+
* @return boolean true if success, false if failure
|
290 |
+
*/
|
291 |
+
function deleteItem($dType, $itemId){
|
292 |
+
$result = $this->dma->managementApiCall('DELETE', "{$this->hashid}/items/{$dType}/{$itemId}");
|
293 |
+
return $result['statusCode'] == 204 ;
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Ask the server to process the search engine's feeds
|
298 |
+
*
|
299 |
+
* @return array Assoc array with:
|
300 |
+
* - 'task_created': boolean true if a new task has been created
|
301 |
+
* - 'task_id': if task created, the id of the task.
|
302 |
+
*/
|
303 |
+
function process(){
|
304 |
+
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/tasks/process");
|
305 |
+
$taskCreated = ($result['statusCode'] == 201);
|
306 |
+
$taskId = $taskCreated ? obtainId($result['response']['link']) : null;
|
307 |
+
return array('task_created'=>$taskCreated, 'task_id' => $taskId);
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* Obtain info of the last processing task sent to the server
|
312 |
+
*
|
313 |
+
* @return array Assoc array with 'state' and 'message' indicating status of the
|
314 |
+
* last asked processing task
|
315 |
+
*/
|
316 |
+
function processInfo(){
|
317 |
+
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/tasks/process");
|
318 |
+
unset($result['response']['task_name']);
|
319 |
+
return $result['response'];
|
320 |
+
}
|
321 |
+
|
322 |
+
/**
|
323 |
+
* Obtain info about how a task is going or its result
|
324 |
+
*
|
325 |
+
* @return array Assoc array with 'state' and 'message' indicating the status
|
326 |
+
* of the task
|
327 |
+
*/
|
328 |
+
function taskInfo($taskId){
|
329 |
+
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/tasks/{$taskId}");
|
330 |
+
unset($result['response']['task_name']);
|
331 |
+
return $result['response'];
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Obtain logs of the latest feed processing tasks done
|
336 |
+
*
|
337 |
+
* @return array list of arrays representing the logs
|
338 |
+
*/
|
339 |
+
function logs(){
|
340 |
+
$result = $this->dma->managementApiCall("GET", "{$this->hashid}/logs");
|
341 |
+
return $result['response'];
|
342 |
+
}
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Helper class to iterate through the search engine's items
|
347 |
+
*
|
348 |
+
* Implemets Iterator interface so foreach() can work with ItemRS
|
349 |
+
*/
|
350 |
+
class ItemsRS implements Iterator {
|
351 |
+
|
352 |
+
private $searchEngine = null;
|
353 |
+
private $resultsPage = null;
|
354 |
+
private $scrollId = null;
|
355 |
+
private $position = 0;
|
356 |
+
private $total = null;
|
357 |
+
|
358 |
+
function __construct($searchEngine, $dType){
|
359 |
+
$this->dType = $dType;
|
360 |
+
$this->searchEngine = $searchEngine;
|
361 |
+
}
|
362 |
+
|
363 |
+
private function fetchResults(){
|
364 |
+
$apiResults = $this->searchEngine->getScrolledItemsPage($this->dType, $this->scrollId);
|
365 |
+
$this->total = $apiResults['total'];
|
366 |
+
$this->resultsPage = $apiResults['results'];
|
367 |
+
$this->scrollId = $apiResults['scroll_id'];
|
368 |
+
$this->currentItem = each($this->resultsPage);
|
369 |
+
}
|
370 |
+
|
371 |
+
function rewind(){
|
372 |
+
$this->scrollId = null;
|
373 |
+
$this->fetchResults();
|
374 |
+
}
|
375 |
+
|
376 |
+
function valid(){
|
377 |
+
return $this->position < $this->total;
|
378 |
+
}
|
379 |
+
|
380 |
+
function current(){
|
381 |
+
return $this->currentItem['value'];
|
382 |
+
}
|
383 |
+
|
384 |
+
function key(){
|
385 |
+
return $this->position;
|
386 |
+
}
|
387 |
+
|
388 |
+
function next(){
|
389 |
+
++$this->position;
|
390 |
+
$this->currentItem = each($this->resultsPage);
|
391 |
+
if (!$this->currentItem && $this->position < $this->total){
|
392 |
+
$this->fetchResults();
|
393 |
+
reset($this->resultsPage);
|
394 |
+
$this->currentItem = each($this->resultsPage);
|
395 |
+
}
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
/**
|
400 |
+
* Extracts identificator from an item or task url.
|
401 |
+
*
|
402 |
+
* @param string $url item or task resource locator
|
403 |
+
* @return the item identificator
|
404 |
+
*/
|
405 |
+
function obtainId($url){
|
406 |
+
preg_match('~/\w{32}/(items/\w+|tasks)/([\w-_]+)/?$~', $url, $matches);
|
407 |
+
return $matches[2];
|
408 |
+
}
|
lib/Doofinder/errors.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class NotAllowed extends Exception {}
|
4 |
+
|
5 |
+
class BadRequest extends Exception {}
|
6 |
+
|
7 |
+
class QuotaExhausted extends Exception {}
|
8 |
+
|
9 |
+
class WrongResponse extends Exception {}
|
10 |
+
|
11 |
+
class ThrottledResponse extends Exception {}
|
12 |
+
|
13 |
+
function readError($response) {
|
14 |
+
$error = json_decode($response, true);
|
15 |
+
$error = $error['detail'];
|
16 |
+
if (!isset($error)) {
|
17 |
+
$error = $response;
|
18 |
+
}
|
19 |
+
return $error;
|
20 |
+
}
|
21 |
+
|
22 |
+
function handleErrors($statusCode, $response){
|
23 |
+
switch($statusCode)
|
24 |
+
{
|
25 |
+
case 403:
|
26 |
+
throw new NotAllowed("The user does not have permissions to perform this operation: ".readError($response));
|
27 |
+
case 401:
|
28 |
+
throw new NotAllowed("The user hasn't provided valid authorization: ".readError($response));
|
29 |
+
case 404:
|
30 |
+
throw new BadRequest("Not Found: ".readError($response));
|
31 |
+
case 409: // trying to post with an already used id
|
32 |
+
throw new BadRequest("Request conflict: ".readError($response));
|
33 |
+
case 429:
|
34 |
+
if (stripos($response, 'throttled')) {
|
35 |
+
throw new ThrottledResponse(readError($response));
|
36 |
+
} else {
|
37 |
+
throw new QuotaExhausted("The query quota has been reached. No more queries can be requested right now");
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
if ($statusCode >= 500) {
|
42 |
+
throw new WrongResponse("Server error: ".readError($response));
|
43 |
+
}
|
44 |
+
|
45 |
+
if ($statusCode >= 400) {
|
46 |
+
throw new BadRequest("The client made a bad request: ".readError($response));
|
47 |
+
}
|
48 |
+
}
|
package.xml
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>Doofinder_Feed</name>
|
4 |
+
<version>1.6.5</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL 3.0)</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>This plugin allows you to integrate Doofinder in your store with almost no effort.</summary>
|
10 |
+
<description>Doofinder is the best on-site search engine service for Magento. This free extension allows you to populate the data feed file required to use the Doofinder search service for each store view in a URL (the data feed displays only public information of your products).
|
11 |
+

|
12 |
+
# Why use Doofinder?
|
13 |
+

|
14 |
+
Doofinder provides fast and accurate results based on your website content. Accurate and relevant results appear in your search box at an incredible speed as the user types. With Doofinder you are confident that your visitors are finding what they are looking for regardless of the number of products in your site. These are some advantages of using Doofinder in your site:
|
15 |
+

|
16 |
+
- Instant, relevant results.
|
17 |
+
- Tolerant of misspellings.
|
18 |
+
- Search filters.
|
19 |
+
- Increases the conversion rates.
|
20 |
+
- No technical knowledge are required.
|
21 |
+
- Allows the use of labels and synonyms.
|
22 |
+
- Installs in minutes.
|
23 |
+
- Provides statistical information.
|
24 |
+
- Doofinder brings back the control over the searches in your site to you.
|
25 |
+

|
26 |
+
A good search engine should provide relevant results. It is exactly what Doofinder does.
|
27 |
+

|
28 |
+
When the user starts typing in the search box, Doofinder displays the best results for her search. If the user makes a typo, our algorithms detect it and will perform the search as if the term is correctly typed.
|
29 |
+

|
30 |
+
Furthermore, Doofinder sorts the results displaying the most relevant first.
|
31 |
+

|
32 |
+
# Relevant Statistics
|
33 |
+

|
34 |
+
The Doofinder's backend also offers easy to understand statistics. They provide useful information about the usage, results and search trends of your users in the last 90 days.
|
35 |
+

|
36 |
+
With the knowledge of what are the most used terms by your users, you can provide guided results for their queries. You can link the results with your Google Analytics profile to better evaluate and improve the contents of your website.
|
37 |
+

|
38 |
+
# SaaS
|
39 |
+

|
40 |
+
With Doofinder you will benefit permanently of the service updates and improvements with no additional cost. All our plans provide full technical support.
|
41 |
+

|
42 |
+
If you have any question, please, contact us. Our technical support team will be happy to assist you.
|
43 |
+

|
44 |
+
# More information
|
45 |
+

|
46 |
+
Doofinder is fast and innovative. With no doubt, it is the best search engine for Magento. The Doofinder service is not free but it has a 30 day trial period.
|
47 |
+

|
48 |
+
You can get more info and create your account visiting the Doofinder site:
|
49 |
+

|
50 |
+
http://www.doofinder.com</description>
|
51 |
+
<notes>Correctly handle custom boost fields.</notes>
|
52 |
+
<authors><author><name>Carlos Escribano Rey</name><user>doofinder</user><email>carlos@doofinder.com</email></author></authors>
|
53 |
+
<date>2016-10-24</date>
|
54 |
+
<time>17:01:35</time>
|
55 |
+
<contents><target name="magecommunity"><dir name="Doofinder"><dir name="Feed"><dir name="Block"><dir name="Adminhtml"><dir name="Log"><file name="View.php" hash="a4208a50820e6b9df016314c1d4f07dc"/></dir><dir name="Map"><file name="Additional.php" hash="4b45c8bc6303097aeadc35c6d5ef81da"/></dir></dir><file name="Integration.php" hash="b42a26dce05e7b5ce6a1193b2920cc9d"/><dir name="Settings"><dir name="Buttons"><file name="Generate.php" hash="93dcc1911ca37d7f8937a3f70c4379be"/><file name="ViewLog.php" hash="ee27b184bba43f68c71f9b0b40a65b13"/></dir><dir name="Panel"><file name="Cron.php" hash="e93e0471544eef8d6cc1ea5c2a8037dd"/><file name="Crondescription.php" hash="cd421bc35644aa18df853c46e46d18fa"/><file name="Datetime.php" hash="fd91a4f4e0506ab83252eac5a3d5d502"/><file name="Description.php" hash="f0fd46fb11b5ccdbec98e68344aca3dc"/><file name="File.php" hash="addbf646a91200e9906d38fe60f51c9b"/><file name="Hashdescription.php" hash="707aa2f0eff7a1e170e6355787e09e84"/><file name="Layerdescription.php" hash="0d344a2bad97f047733fea1ad03ea214"/><file name="Message.php" hash="8026858e966d0cddd40b2b72a1bc5ab2"/></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="7ff426aac405f3c183ca0e838040ddc6"/><file name="Log.php" hash="08c6835c855a9947867113b8128643a4"/><file name="Search.php" hash="507d1fd5410cd57d4ac3437b9aff1d2f"/><file name="Tax.php" hash="b0ad35c35d1aad2cca660b573a511f96"/></dir><dir name="Model"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Backend"><file name="Cron.php" hash="e0c343224d9fdd3795d1d0a2b8e79da0"/></dir><dir name="Validation"><file name="Hashid.php" hash="5c84ddebe10442c48bc8bca05a9253d1"/></dir></dir></dir></dir><dir name="CatalogSearch"><dir name="Resource"><file name="Fulltext.php" hash="0e1394fcfbefb5d84d779fedc54d07f4"/></dir></dir><file name="Config.php" hash="0841738770f2981fbf8365822911d01b"/><file name="Cron.php" hash="2592919990941bcfcd51d1b82d710674"/><file name="Generator.php" hash="e244f1429862478aa2cef79f561e169e"/><file name="Log.php" hash="7119a7c51c0d60bdc4ce80ccee0fab56"/><dir name="Map"><dir name="Product"><file name="Abstract.php" hash="632a7d1656110384ad61433115826afd"/><file name="Associated.php" hash="788e01dda6cca448d4070cde193cf563"/><file name="Bundle.php" hash="372f233378744df9bf3b85c9c2280ded"/><file name="Configurable.php" hash="1539003b9da916420dbf59d35b048da6"/><file name="Downloadable.php" hash="7ae25026887f745c62e2649396456a2e"/><file name="Grouped.php" hash="aecf53b4541120404c14c3fb24ac4057"/><file name="Simple.php" hash="79ba6184149a5ed017bbf49b056d5ea4"/><file name="Virtual.php" hash="dfe0aefd4b4f6bd602bea798493491b9"/></dir></dir><dir name="Mysql4"><dir name="Cron"><file name="Collection.php" hash="e5ff5b9962152cb57eb195cc1d5e87ca"/></dir><file name="Cron.php" hash="a870fe6235fe359cf91993727e92dc67"/><dir name="Log"><file name="Collection.php" hash="85cd1039b5b14d7afc94ce0844859293"/></dir><file name="Log.php" hash="54a36a682eeffec64ea1ffe7b2e60c94"/></dir><dir name="Observers"><file name="Feed.php" hash="2aaf718ef2923ffb602a52a07440a642"/><file name="Logs.php" hash="c216bc6a29c5de41930e9614f7be22e7"/><file name="Schedule.php" hash="cbf364a83a577f871f77a838f1e3c471"/></dir><dir name="Resource"><dir name="Mysql4"><file name="Setup.php" hash="1d9cd49949d4ea44796b5df731af4d24"/></dir></dir><dir name="System"><dir name="Config"><dir name="Backend"><dir name="Map"><file name="Additional.php" hash="a4e383b9895eb7d121be8f4b5ea4e532"/></dir><dir name="Total"><file name="Limit.php" hash="a23092ea72cbe81e2779b086ad055bf6"/></dir></dir><file name="Reset.php" hash="1124da63d309e1b46aead687925b8c66"/><dir name="Source"><dir name="Product"><file name="Attributes.php" hash="bea37e6b9857dd0b8dbe7bb9c621b902"/></dir></dir></dir></dir><file name="Tools.php" hash="a13cd01a07c953f427d08f3e8f194cba"/></dir><dir name="Test"><dir name="Controller"><dir name="Index"><dir name="fixtures"><file name="testConfig.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/><file name="testFeed.yaml" hash="694cf25a35a9a301a8ae678866937909"/><file name="testIndex.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/></dir><dir name="providers"><file name="testConfig.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/><file name="testFeed.yaml" hash="1ea2f638be8fdcea22ef47767ed8d7db"/><file name="testIndex.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/></dir></dir><file name="Index.php" hash="2771de706303653d039818bd0f6590ea"/></dir><dir name="Model"><dir name="Product"><dir name="expectations"><file name="testGenerator.yaml" hash="232dda1f4fd88b8ef081393f08044731"/></dir><dir name="fixtures"><file name="testGenerator.yaml" hash="df25e3ca67fd98ab1b933c4951c599ef"/></dir><dir name="providers"><file name="testGenerator.yaml" hash="84779d5dcd8d92abdecf0cd5ee65cfb0"/></dir></dir><file name="Product.php" hash="6c45ae2b36c6cc721ef634855ed6d596"/></dir></dir><dir name="controllers"><file name="DoofinderFeedFeedController.php" hash="0e676cb464d4643befdbf0520a3ba08c"/><file name="DoofinderFeedLogController.php" hash="e31c02e2ebb2a5521a80fc952111fd42"/><file name="FeedController.php" hash="2fdc3fef31e28beeed37da732618f482"/><file name="IndexController.php" hash="d89e485848ddaf040c149898a60253f0"/></dir><dir name="etc"><file name="config.xml" hash="940d767a73891fceed5d2de577aa5608"/><file name="system.xml" hash="4c69b26ff91eedb582883e83a26b843d"/></dir><dir name="sql"><dir name="doofinder_feed_setup"><file name="mysql4-install-1.5.4.php" hash="9dc5ed4e10febbe75ab1911259a1c9fe"/><file name="mysql4-install-1.5.7.php" hash="85baa03d9c4d76f6b744ba107c21f8da"/><file name="mysql4-upgrade-1.5.4-1.5.5.php" hash="df7158f6d6cdded9bdfc5cb72c1dc8e3"/><file name="mysql4-upgrade-1.5.5-1.5.6.php" hash="0f3ca5263356a0bc83d9352b463944dc"/><file name="mysql4-upgrade-1.5.6-1.5.7.php" hash="b0180770655f36d6723483aa3bd1541f"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Doofinder_Feed.xml" hash="9d3b6fbbbec12708461c33260715451c"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="doofinder.xml" hash="a7b9105a4e613086340b042845793d9f"/></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="doofinder.xml" hash="48a8636096950914917461260416c355"/></dir></dir></dir></dir></target><target name="mage"><dir name="js"><dir name="doofinder"><file name="admin.js" hash="ca050b0527ae101c75532fbca1c4a274"/></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="doofinder"><file name="styles.css" hash="d6ec303c3199db3ab4dffa8d2491105e"/></dir></dir></dir></dir></target><target name="magelib"><dir name="Doofinder"><file name="doofinder_api.php" hash="3a27154c61f86990f58bb719f44ccd0f"/><file name="doofinder_management_api.php" hash="71ac700c6e0d04496fd2da0bc012b84e"/><file name="errors.php" hash="6aaf93010cee28c87a6ab4ab0e2606d1"/></dir></target></contents>
|
56 |
+
<compatible/>
|
57 |
+
<dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
|
58 |
+
</package>
|
skin/adminhtml/default/default/doofinder/styles.css
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* This file is part of Doofinder_Feed.
|
3 |
+
*/
|
4 |
+
|
5 |
+
/**
|
6 |
+
* @category CSS
|
7 |
+
* @package Doofinder_Feed
|
8 |
+
* @version 1.5.9
|
9 |
+
*/
|
10 |
+
|
11 |
+
#doofinder_feed_additional_mapping_container input {
|
12 |
+
max-width: 80px;
|
13 |
+
}
|
14 |
+
#doofinder_feed_additional_mapping_container select {
|
15 |
+
max-width: 180px;
|
16 |
+
}
|
17 |
+
|
18 |
+
#row_doofinder_cron_schedule_settings_time select {
|
19 |
+
width: 80px !important;
|
20 |
+
}
|
21 |
+
|
22 |
+
#row_doofinder_cron_schedule_settings_time .df-separator:before {
|
23 |
+
content: " : "
|
24 |
+
}
|
25 |
+
|
26 |
+
#row_doofinder_search_layer_settings_script textarea {
|
27 |
+
min-width: 370px;
|
28 |
+
height: 30em;
|
29 |
+
font-family: "Courier New", monospace;
|
30 |
+
font-size: 13px;
|
31 |
+
}
|
32 |
+
|
33 |
+
.doofinder-info,
|
34 |
+
.doofinder-warning {
|
35 |
+
padding: 10px;
|
36 |
+
border: 1px solid transparent;
|
37 |
+
font-size: .95em !important;
|
38 |
+
}
|
39 |
+
|
40 |
+
.doofinder-info {
|
41 |
+
border-color: #d6d6d6;
|
42 |
+
background-color: #fafafa;
|
43 |
+
color: #333;
|
44 |
+
}
|
45 |
+
|
46 |
+
.doofinder-warning {
|
47 |
+
border-color: #EEE2BE;
|
48 |
+
background-color: #FFF9E9;
|
49 |
+
color: #5A4421;
|
50 |
+
}
|