HawkSearch_Datafeed - Version 1.1.1.6

Version Notes

Adding multisite capabilities, including image cache generation.

1.1.1.3: Fixed issue with singletons stored in registry resulting in attributes from wrong store being returned in attributes.txt file.

1.1.1.4: Fixed issue with configurable product relationships in items.txt file. Moved "brand" attribute from items.txt to attributes.txt (if exists).

1.1.1.5: Fixed issue with image cache and items.txt not using "small_image". Explicit check to ensure 'unique id' is set in attributes.txt.

Download this release

Release Info

Developer Hawksearch Inc.
Extension HawkSearch_Datafeed
Version 1.1.1.6
Comparing to
See all releases


Code changes from version 1.0.3.1 to 1.1.1.6

Files changed (23) hide show
  1. app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Cron.php +13 -0
  2. app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Cron/Js.php +35 -0
  3. app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Generate.php +4 -3
  4. app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Generate/Js.php +9 -2
  5. app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Generateimagecache/Js.php +11 -1
  6. app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Next.php +33 -31
  7. app/code/community/Hawksearch/Datafeed/Helper/Data.php +7 -3
  8. app/code/community/Hawksearch/Datafeed/Helper/Feed.php +5 -3
  9. app/code/community/Hawksearch/Datafeed/Helper/runfeed.php +1 -1
  10. app/code/community/Hawksearch/Datafeed/Model/Cron.php +113 -24
  11. app/code/community/Hawksearch/Datafeed/Model/Feed.php +313 -663
  12. app/code/community/Hawksearch/Datafeed/Model/System/Config/Backend/Cron.php +19 -61
  13. app/code/community/Hawksearch/Datafeed/Model/System/Config/Source/Store.php +28 -0
  14. app/code/community/Hawksearch/Datafeed/controllers/Adminhtml/HawkdatagenerateController.php +79 -0
  15. app/code/community/Hawksearch/Datafeed/controllers/SearchController.php +19 -75
  16. app/code/community/Hawksearch/Datafeed/etc/config.xml +95 -79
  17. app/code/community/Hawksearch/Datafeed/etc/system.xml +120 -112
  18. app/code/community/Hawksearch/Datafeed/sql/hawksearch_datafeed_setup/install-1.0.0.0.php +9 -7
  19. app/code/community/Hawksearch/Datafeed/sql/hawksearch_datafeed_setup/upgrade-1.0.0.0-1.1.1.0.php +27 -0
  20. app/design/adminhtml/default/default/template/hawksearch/datafeed/generate/js.phtml +20 -1
  21. app/design/adminhtml/default/default/template/hawksearch/datafeed/validate.phtml +30 -0
  22. hawksearch/.htaccess.sample +3 -0
  23. package.xml +11 -5
app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Cron.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Cron extends Mage_Adminhtml_Block_System_Config_Form_Field {
4
+
5
+ protected $_buttonId = "generate_feed_button";
6
+
7
+ protected function _prepareLayout() {
8
+ $block = $this->getLayout()->createBlock("hawksearch_datafeed/system_config_frontend_feed_cron_js");
9
+
10
+ $this->getLayout()->getBlock('js')->append($block);
11
+ return parent::_prepareLayout();
12
+ }
13
+ }
app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Cron/Js.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
4
+ *
5
+ * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
6
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
7
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
8
+ * PARTICULAR PURPOSE.
9
+ */
10
+
11
+ class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Cron_Js extends Mage_Adminhtml_Block_Template {
12
+
13
+ /**
14
+ * Sets javascript template to be included in the adminhtml js text_list block
15
+ */
16
+ protected function _construct() {
17
+ parent::_construct();
18
+ $this->setTemplate('hawksearch/datafeed/validate.phtml');
19
+ }
20
+
21
+ /**
22
+ * Returns the cron string validation url
23
+ *
24
+ * @return string
25
+ */
26
+ public function getValidateUrl() {
27
+ return Mage::getModel('adminhtml/url')->getUrl(
28
+ 'adminhtml/hawkdatagenerate/validateCronString',
29
+ array(
30
+ '_secure' => true,
31
+ '_store' => Mage_Core_Model_App::ADMIN_STORE_ID
32
+ )
33
+ );
34
+ }
35
+ }
app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Generate.php CHANGED
@@ -55,12 +55,13 @@ class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Generate extends Mag
55
  'label' => $this->helper('hawksearch_datafeed')->__('Generate Feeds'),
56
  'onclick' => 'javascript:hawkSearchFeed.generateFeed(); return false;'
57
  ));
58
-
59
  if ($this->_feedGenIsLocked()) {
60
- $button->setData('class', 'disabled');
 
61
  }
62
 
63
- return $button->toHtml();
64
  }
65
 
66
  /**
55
  'label' => $this->helper('hawksearch_datafeed')->__('Generate Feeds'),
56
  'onclick' => 'javascript:hawkSearchFeed.generateFeed(); return false;'
57
  ));
58
+ $force = '';
59
  if ($this->_feedGenIsLocked()) {
60
+ $button->setDisabled(true);
61
+ $force = ' <input type="checkbox" value="feedforce" id="feedforce" name="feedforce" onclick="hawkSearchFeed.forceFeed();"> force';
62
  }
63
 
64
+ return $button->toHtml() . $force;
65
  }
66
 
67
  /**
app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Generate/Js.php CHANGED
@@ -24,10 +24,17 @@ class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Generate_Js extends
24
  * @return string
25
  */
26
  public function getGenerateUrl() {
27
- return Mage::getUrl('hawksearch_datafeed/search/runFeedGeneration/',
 
 
 
 
 
 
28
  array(
29
  '_secure' => true,
30
  '_store' => Mage_Core_Model_App::ADMIN_STORE_ID
31
- ));
 
32
  }
33
  }
24
  * @return string
25
  */
26
  public function getGenerateUrl() {
27
+ // return Mage::getUrl('hawksearch_datafeed/search/runFeedGeneration/',
28
+ // array(
29
+ // '_secure' => true,
30
+ // '_store' => Mage_Core_Model_App::ADMIN_STORE_ID
31
+ // ));
32
+ return Mage::getModel('adminhtml/url')->getUrl(
33
+ 'adminhtml/hawkdatagenerate/runFeedGeneration',
34
  array(
35
  '_secure' => true,
36
  '_store' => Mage_Core_Model_App::ADMIN_STORE_ID
37
+ )
38
+ );
39
  }
40
  }
app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Generateimagecache/Js.php CHANGED
@@ -23,7 +23,17 @@ class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Generateimagecache_J
23
  *
24
  * @return string
25
  */
26
- public function getGenerateUrl() {
 
 
 
 
 
 
 
 
 
 
27
  return Mage::getUrl('hawksearch_datafeed/search/runImageCacheGeneration/',
28
  array(
29
  '_secure' => true,
23
  *
24
  * @return string
25
  */
26
+ public function getGenerateUrl() {
27
+ return Mage::getModel('adminhtml/url')->getUrl(
28
+ 'adminhtml/hawkdatagenerate/runImageCacheGeneration',
29
+ array(
30
+ '_secure' => true,
31
+ '_store' => Mage_Core_Model_App::ADMIN_STORE_ID
32
+ )
33
+ );
34
+ }
35
+
36
+ public function getGenerateUrlold() {
37
  return Mage::getUrl('hawksearch_datafeed/search/runImageCacheGeneration/',
38
  array(
39
  '_secure' => true,
app/code/community/Hawksearch/Datafeed/Block/System/Config/Frontend/Feed/Next.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
4
  *
@@ -7,39 +8,40 @@
7
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
8
  * PARTICULAR PURPOSE.
9
  */
10
-
11
  class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Next extends Mage_Adminhtml_Block_System_Config_Form_Field {
12
 
13
- /**
14
- * Returns the config xml set job code for the cron job
15
- *
16
- * @return string
17
- */
18
- protected function _getHawkSearchCronJobCode() {
19
-
20
- $jobCode = Mage::getConfig()->getNode('crontab/hawksearch_datafeed/job_code');
21
-
22
- if (!$jobCode) {
23
- if(Mage::helper('hawksearch_datafeed/data')->isLoggingEnabled()) {
24
- Mage::log("No cron job code set for hawksearch_datafeed cron job in config xml.", null,'hawksearch_errors.log');
25
  }
26
- Mage::throwException("No cron job code set for hawksearch_datafeed cron job in config xml.");
27
- }
28
- return $jobCode;
29
- }
30
-
31
- /**
32
- * Renders the next scheduled cron time.
33
- *
34
- * @param Varien_Data_Form_Element_Abstract $element
35
- * @return <type>
36
- */
37
- protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
38
- $helper = Mage::helper('hawksearch_datafeed');
39
- $scheduledAt = $helper->getNextRunDateFromCronTime();
40
-
41
- return $scheduledAt;
42
- }
43
-
 
 
44
 
45
  }
1
  <?php
2
+
3
  /**
4
  * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
5
  *
8
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9
  * PARTICULAR PURPOSE.
10
  */
 
11
  class Hawksearch_Datafeed_Block_System_Config_Frontend_Feed_Next extends Mage_Adminhtml_Block_System_Config_Form_Field {
12
 
13
+ /**
14
+ * Returns the config xml set job code for the cron job
15
+ *
16
+ * @return string
17
+ */
18
+ protected function _getHawkSearchCronJobCode() {
19
+
20
+ $jobCode = Mage::getConfig()->getNode('crontab/hawksearch_datafeed/job_code');
21
+
22
+ if (!$jobCode) {
23
+ if (Mage::helper('hawksearch_datafeed/data')->isLoggingEnabled()) {
24
+ Mage::log("No cron job code set for hawksearch_datafeed cron job in config xml.", null, 'hawksearch_errors.log');
25
  }
26
+ Mage::throwException("No cron job code set for hawksearch_datafeed cron job in config xml.");
27
+ }
28
+ return $jobCode;
29
+ }
30
+
31
+ /**
32
+ * Renders the next scheduled cron time.
33
+ *
34
+ * @param Varien_Data_Form_Element_Abstract $element
35
+ * @return string
36
+ */
37
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
38
+ // $helper = Mage::helper('hawksearch_datafeed');
39
+ // $scheduledAt = $helper->getNextRunDateFromCronTime();
40
+
41
+ $scheduledAt = Mage::getSingleton('hawksearch_datafeed/system_config_backend_cron')->getNextRuntime();
42
+
43
+ return sprintf('<span id="%s">%s</span>', $element->getHtmlId(), $scheduledAt);
44
+ }
45
+
46
 
47
  }
app/code/community/Hawksearch/Datafeed/Helper/Data.php CHANGED
@@ -66,7 +66,7 @@ class Hawksearch_Datafeed_Helper_Data extends Mage_Core_Helper_Abstract {
66
  * @return string
67
  */
68
  public function getImageWidth() {
69
- return Mage::getStoreConfig(self::SECTION . self::FEED_GROUP . "image_width");
70
  }
71
 
72
  /**
@@ -75,7 +75,7 @@ class Hawksearch_Datafeed_Helper_Data extends Mage_Core_Helper_Abstract {
75
  * @return string
76
  */
77
  public function getImageHeight() {
78
- return Mage::getStoreConfig(self::SECTION . self::FEED_GROUP . "image_height");
79
  }
80
 
81
  /**
@@ -164,7 +164,8 @@ class Hawksearch_Datafeed_Helper_Data extends Mage_Core_Helper_Abstract {
164
  *
165
  * @return Zend_Date
166
  */
167
- public function getNextRunDateFromCronTime() {
 
168
  $now = Mage::app()->getLocale()->date();
169
  $frequency = $this->getCronFrequency();
170
  list($hours, $minutes, $seconds) = explode(',', $this->getCronTime());
@@ -221,5 +222,8 @@ class Hawksearch_Datafeed_Helper_Data extends Mage_Core_Helper_Abstract {
221
  return ((float) $usec + (float) $sec);
222
  }
223
 
 
 
 
224
 
225
  }
66
  * @return string
67
  */
68
  public function getImageWidth() {
69
+ return Mage::getStoreConfig('hawksearch_datafeed/imagecache/image_width');
70
  }
71
 
72
  /**
75
  * @return string
76
  */
77
  public function getImageHeight() {
78
+ return Mage::getStoreConfig('hawksearch_datafeed/imagecache/image_height');
79
  }
80
 
81
  /**
164
  *
165
  * @return Zend_Date
166
  */
167
+
168
+ public function getNextRunDateFromCronTimedead() {
169
  $now = Mage::app()->getLocale()->date();
170
  $frequency = $this->getCronFrequency();
171
  list($hours, $minutes, $seconds) = explode(',', $this->getCronTime());
222
  return ((float) $usec + (float) $sec);
223
  }
224
 
225
+ public function getSelectedStores(){
226
+ return explode(',', Mage::getStoreConfig('hawksearch_datafeed/feed/stores'));
227
+ }
228
 
229
  }
app/code/community/Hawksearch/Datafeed/Helper/Feed.php CHANGED
@@ -12,7 +12,7 @@ class Hawksearch_Datafeed_Helper_Feed {
12
 
13
  protected $_feedFilePath = null;
14
 
15
- public function refreshImageCache($storeid) {
16
  $tmppath = sys_get_temp_dir();
17
  $tmpfile = tempnam($tmppath, 'hawkfeed_');
18
 
@@ -26,8 +26,9 @@ class Hawksearch_Datafeed_Helper_Feed {
26
  fwrite($f, '#!/bin/sh' . "\n");
27
  $phpbin = PHP_BINDIR . DIRECTORY_SEPARATOR . "php";
28
 
29
- fwrite($f, "$phpbin $runfile -i $storeid -r $root -t $tmpfile\n");
30
 
 
31
  shell_exec("/bin/sh $tmpfile > /dev/null 2>&1 &");
32
 
33
  }
@@ -50,6 +51,7 @@ class Hawksearch_Datafeed_Helper_Feed {
50
 
51
  fwrite($f, "$phpbin $runfile -r $root -t $tmpfile\n");
52
 
 
53
  shell_exec("/bin/sh $tmpfile > /dev/null 2>&1 &");
54
 
55
  }
@@ -73,7 +75,7 @@ class Hawksearch_Datafeed_Helper_Feed {
73
  * @return string
74
  */
75
  public function makeVarPath($directories) {
76
- $path = Mage::getBaseDir('var');
77
  foreach ($directories as $dir) {
78
  $path .= DS . $dir;
79
  if (!is_dir($path)) {
12
 
13
  protected $_feedFilePath = null;
14
 
15
+ public function refreshImageCache() {
16
  $tmppath = sys_get_temp_dir();
17
  $tmpfile = tempnam($tmppath, 'hawkfeed_');
18
 
26
  fwrite($f, '#!/bin/sh' . "\n");
27
  $phpbin = PHP_BINDIR . DIRECTORY_SEPARATOR . "php";
28
 
29
+ fwrite($f, "$phpbin $runfile -i true -r $root -t $tmpfile\n");
30
 
31
+ // to debug, change '/dev/null' to some file path writable by the webserver
32
  shell_exec("/bin/sh $tmpfile > /dev/null 2>&1 &");
33
 
34
  }
51
 
52
  fwrite($f, "$phpbin $runfile -r $root -t $tmpfile\n");
53
 
54
+ // to debug, change '/dev/null' to some file path writable by the webserver
55
  shell_exec("/bin/sh $tmpfile > /dev/null 2>&1 &");
56
 
57
  }
75
  * @return string
76
  */
77
  public function makeVarPath($directories) {
78
+ $path = Mage::getBaseDir();
79
  foreach ($directories as $dir) {
80
  $path .= DS . $dir;
81
  if (!is_dir($path)) {
app/code/community/Hawksearch/Datafeed/Helper/runfeed.php CHANGED
@@ -24,7 +24,7 @@ if ($helper->thereAreFeedLocks()) {
24
  }
25
  if ($helper->CreateFeedLocks()) {
26
  if (isset($opts['i'])) {
27
- $feed->refreshImageCache($opts['i']);
28
  } else {
29
  $feed->generateFeed();
30
  }
24
  }
25
  if ($helper->CreateFeedLocks()) {
26
  if (isset($opts['i'])) {
27
+ $feed->refreshImageCache();
28
  } else {
29
  $feed->generateFeed();
30
  }
app/code/community/Hawksearch/Datafeed/Model/Cron.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  /**
3
  * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
4
  *
@@ -7,31 +8,119 @@
7
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
8
  * PARTICULAR PURPOSE.
9
  */
10
-
11
  class Hawksearch_Datafeed_Model_Cron {
12
 
13
- /**
14
- * Generates the feeds and sends email of status when done
15
- */
16
-
17
- public function generateFeeds() {
18
- if(!Mage::getStoreConfig('hawksearch_datafeed/cron/disabled'))
19
- {
20
- try {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  Mage::getModel('hawksearch_datafeed/feed')->generateFeed();
22
- $msg = "HawkSeach Feed Generated!";
23
- }
24
- catch (Exception $e) {
25
- $msg = $e->getMessage();
26
- }
27
- $this->_sendEmail($msg);
28
- }
29
- }
30
-
31
- /**
32
- * If there is a system config email set, send out the cron notification email.
33
- */
34
- protected function _sendEmail($msg) {
35
- Mage::getModel('hawksearch_datafeed/email')->setData('msg', $msg)->send();
36
- }
37
  }
1
  <?php
2
+
3
  /**
4
  * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
5
  *
8
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9
  * PARTICULAR PURPOSE.
10
  */
 
11
  class Hawksearch_Datafeed_Model_Cron {
12
 
13
+ /**
14
+ * Generates the feeds and sends email of status when done
15
+ */
16
+
17
+
18
+ public function isValidCronString($string) {
19
+ $e = preg_split('#\s+#', $string, null, PREG_SPLIT_NO_EMPTY);
20
+ if (sizeof($e) < 5 || sizeof($e) > 6) {
21
+ return false;
22
+ }
23
+ $isValid = $this->testCronPartSimple(0, $e)
24
+ && $this->testCronPartSimple(1, $e)
25
+ && $this->testCronPartSimple(2, $e)
26
+ && $this->testCronPartSimple(3, $e)
27
+ && $this->testCronPartSimple(4, $e);
28
+
29
+ if (!$isValid) {
30
+ return false;
31
+ }
32
+ return true;
33
+ }
34
+
35
+ public function testCronPartSimple($p, $e) {
36
+ if ($p === 0) {
37
+ // we only accept a single numeric value for the minute and it must be in range
38
+ if (!ctype_digit($e[$p])) {
39
+ return false;
40
+ }
41
+ if ($e[0] < 0 || $e[0] > 59) {
42
+ return false;
43
+ }
44
+ return true;
45
+ }
46
+ return $this->testCronPart($p, $e);
47
+ }
48
+
49
+ public function testCronPart($p, $e) {
50
+
51
+ if ($e[$p] === '*') {
52
+ return true;
53
+ }
54
+
55
+ foreach (explode(',', $e[$p]) as $v) {
56
+ if (!$this->isValidCronRange($p, $v)) {
57
+ return false;
58
+ }
59
+ }
60
+ return true;
61
+ }
62
+
63
+ private function isValidCronRange($p, $v) {
64
+ static $range = array(array(0, 59), array(0, 23), array(1, 31), array(1, 12), array(0, 6));
65
+ //$n = Mage::getSingleton('cron/schedule')->getNumeric($v);
66
+
67
+ // steps can be used with ranges
68
+ if (strpos($v, '/') !== false) {
69
+ $ops = explode('/', $v);
70
+ if (count($ops) !== 2) {
71
+ return false;
72
+ }
73
+ // step must be digit
74
+ if (!ctype_digit($ops[1])) {
75
+ return false;
76
+ }
77
+ $v = $ops[0];
78
+ }
79
+ if (strpos($v, '-') !== false) {
80
+ $ops = explode('-', $v);
81
+ if(count($ops) !== 2){
82
+ return false;
83
+ }
84
+ if ($ops[0] > $ops[1] || $ops[0] < $range[$p][0] || $ops[0] > $range[$p][1] || $ops[1] < $range[$p][0] || $ops[1] > $range[$p][1]) {
85
+ return false;
86
+ }
87
+ } else {
88
+ $a = Mage::getSingleton('cron/schedule')->getNumeric($v);
89
+ if($a < $range[$p][0] || $a > $range[$p][1]){
90
+ return false;
91
+ }
92
+ }
93
+ return true;
94
+ }
95
+
96
+ public function generateImagecache() {
97
+ if(Mage::getStoreConfigFlag('hawksearch_datafeed/imagecache/cron_enable')) {
98
+ try{
99
+ Mage::getModel('hawksearch_datafeed/feed')->refreshImageCache();
100
+ $msg = "Hawksearch Image cache generated!";
101
+ } catch (Exception $e) {
102
+ $msg = "Hawksearch Image cache generation exception: " . $e->getMessage();
103
+ }
104
+ $this->_sendEmail($msg);
105
+ }
106
+ }
107
+
108
+ public function generateFeeds() {
109
+ if (Mage::getStoreConfigFlag('hawksearch_datafeed/feed/cron_enable')) {
110
+ try {
111
  Mage::getModel('hawksearch_datafeed/feed')->generateFeed();
112
+ $msg = "HawkSeach Feed Generated!";
113
+ } catch (Exception $e) {
114
+ $msg = $e->getMessage();
115
+ }
116
+ $this->_sendEmail($msg);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * If there is a system config email set, send out the cron notification email.
122
+ */
123
+ protected function _sendEmail($msg) {
124
+ Mage::getModel('hawksearch_datafeed/email')->setData('msg', $msg)->send();
125
+ }
 
126
  }
app/code/community/Hawksearch/Datafeed/Model/Feed.php CHANGED
@@ -27,18 +27,14 @@ class Hawksearch_Datafeed_Model_Feed extends Mage_Core_Model_Abstract {
27
  private $countryMap;
28
  private $outputFileDelimiter;
29
  private $bufferSize;
 
30
  private $outputFileExtension;
31
- protected $multiSelectValues;
32
 
33
  /**
34
  * Constructor
35
  */
36
  function __construct() {
37
-
38
- // Ignore user aborts and allow the script
39
- // to run forever
40
- ignore_user_abort(true); // If Varnish decides that the results timed out. Browsers are better behaved.
41
- set_time_limit(0); // Even with DB calls not counting towards execution time, it's still a long running script.
42
  /** @var $helper Hawksearch_Datafeed_Helper_Data */
43
  $helper = Mage::helper('hawksearch_datafeed/data');
44
 
@@ -52,6 +48,7 @@ class Hawksearch_Datafeed_Model_Feed extends Mage_Core_Model_Abstract {
52
 
53
  $this->_feedPath = $helper->getFeedFilePath();
54
  if (empty($this->_feedPath)) {
 
55
  $this->_feedPath = Mage::getBaseDir('base') . DS . 'var/hawksearch/feeds';
56
  }
57
 
@@ -72,6 +69,9 @@ class Hawksearch_Datafeed_Model_Feed extends Mage_Core_Model_Abstract {
72
  $this->outputFileExtension = $helper->getOutputFileExtension();
73
  $this->_storeId = 0;
74
 
 
 
 
75
  parent::__construct();
76
  }
77
 
@@ -88,372 +88,143 @@ class Hawksearch_Datafeed_Model_Feed extends Mage_Core_Model_Abstract {
88
  }
89
  }
90
 
91
- /**
92
- * Retrieves the values of select/multiselect fields, to be dereferenced into values useful to Hawk
93
- *
94
- * @return array
95
- */
96
- protected function getMultiSelectValues() {
97
- if (!empty($this->multiSelectValues)) {
98
- return $this->multiSelectValues;
99
- }
100
-
101
- $eavTable = $this->_tablePrefix . 'eav_attribute';
102
- $eavAttributeTable = $this->_tablePrefix . 'eav_attribute';
103
- $eavOptionValueTable = $this->_tablePrefix . 'eav_attribute_option_value';
104
- $catalogProdEntityInt = $this->_tablePrefix . 'catalog_product_entity_int';
105
- $catalogProdEntityVarchar = $this->_tablePrefix . 'catalog_product_entity_varchar';
106
-
107
- $write = $this->_getConnection();
108
- $entity_type_id = $this->_entityTypeId;
109
- $attributeIds = array();
110
- $idList = "";
111
-
112
-
113
- $brandAttribute = Mage::helper('hawksearch_datafeed/data')->getBrandAttribute();
114
- if (!empty($brandAttribute)) {
115
- $attributeIds[$brandAttribute] = "";
116
- }
117
-
118
- // Get the list of attribute IDs to dereference and the attribute_codes that they match
119
- $sql = <<<EOSQL
120
- SELECT
121
- v.value, a.attribute_code
122
- FROM
123
- $catalogProdEntityVarchar v
124
- LEFT JOIN
125
- $eavAttributeTable a ON v.attribute_id = a.attribute_id
126
- WHERE
127
- v.entity_type_id = $this->_entityTypeId
128
- AND
129
- a.frontend_input IN ('select', 'multiselect')
130
- AND a.attribute_code != 'msrp_enabled' AND a.attribute_code != 'msrp_display_actual_price_type' AND a.attribute_code != 'is_recurring' AND a.attribute_code != 'enable_googlecheckout' AND a.attribute_code != 'tax_class_id' AND a.attribute_code != 'visibility' AND a.attribute_code != 'status'
131
- EOSQL;
132
-
133
- // Prepare the array of attribute codes and compile a unique list of IDs to dereference
134
-
135
- if ($rows = $write->fetchAll($sql)) {
136
- foreach ($rows as $row) {
137
- $opts = explode(',', $row['value']);
138
- foreach ($opts as $opt) {
139
- #print_r($opt);
140
- if (isset($attributeIds[$row['attribute_code']])) {
141
- if ((is_numeric($opt)) && !in_array($opt, $attributeIds[$row['attribute_code']])) {
142
- $attributeIds[$row['attribute_code']][$opt] = $opt;
143
- $attributeIds['ids'][$opt] = $opt;
144
- }
145
- }
146
- }
147
- }
148
- if (!empty($attributeIds['ids'])) {
149
- $idList = "'" . implode("', '", $attributeIds['ids']) . "'";
150
- }
151
- }
152
-
153
- // Get the list of attribute IDs to dereference and the attribute_codes that they match
154
- $sql = <<<EOSQL
155
- SELECT
156
- v.value, a.attribute_code
157
- FROM
158
- $catalogProdEntityInt v
159
- LEFT JOIN
160
- $eavAttributeTable a ON v.attribute_id = a.attribute_id
161
- WHERE
162
- v.entity_type_id = $this->_entityTypeId
163
- AND
164
- a.frontend_input IN ('select', 'multiselect')
165
- AND a.attribute_code != 'msrp_enabled' AND a.attribute_code != 'msrp_display_actual_price_type' AND a.attribute_code != 'is_recurring' AND a.attribute_code != 'enable_googlecheckout' AND a.attribute_code != 'tax_class_id' AND a.attribute_code != 'visibility' AND a.attribute_code != 'status'
166
- EOSQL;
167
-
168
- // Prepare the array of attribute codes and compile a unique list of IDs to dereference
169
- if ($rows = $write->fetchAll($sql)) {
170
- foreach ($rows as $row) {
171
- $opts = explode(',', $row['value']);
172
- foreach ($opts as $opt) {
173
- #print_r($opt);
174
- if (isset($attributeIds[$row['attribute_code']])) {
175
- if ((is_numeric($opt)) && !empty($attributeIds['ids']) && !in_array($opt, $attributeIds[$row['attribute_code']])) {
176
- $attributeIds[$row['attribute_code']][$opt] = $opt;
177
- $attributeIds['ids'][$opt] = $opt;
178
- }
179
- }
180
- }
181
- }
182
-
183
- if (!empty($attributeIds['ids'])) {
184
- $idList = "'" . implode("', '", $attributeIds['ids']) . "'";
185
- }
186
- }
187
-
188
 
189
- if (!empty($idList)) {
190
- // Get the dereferenced values
191
- $sql = <<<EOSQL
192
- SELECT
193
- value,
194
- option_id
195
- FROM
196
- $eavOptionValueTable
197
- WHERE
198
- option_id IN ($idList)
199
- EOSQL;
200
-
201
- // Replace the IDs with their values
202
- if ($rows = $write->fetchAll($sql)) {
203
- foreach ($rows as $row) {
204
- foreach ($attributeIds as &$attrGroup) {
205
- if (!empty($attrGroup[$row['option_id']])) {
206
- $attrGroup[$row['option_id']] = $row['value'];
207
- }
208
- }
209
- }
210
- }
211
  }
212
 
213
- // Cheating, I know, but it saves a tick.
214
- return $this->multiSelectValues = $attributeIds;
215
- }
216
-
217
- /**
218
- * Generate feed based on store and returns success
219
- *
220
- * @return boolean
221
- */
222
- protected function _getAttributeData() {
223
- $this->log('starting _getAttributeData');
224
- $filename = $this->_feedPath . DS . "attributes" . '.' . $this->outputFileExtension;
225
- $arrayExcludeFields = explode(",", $this->_excludedFields);
226
-
227
- $eavAttributeTable = $this->_tablePrefix . 'eav_attribute';
228
- $productEntityTable = $this->_tablePrefix . 'catalog_product_entity';
229
- $eavOptionValueTable = $this->_tablePrefix . 'eav_attribute_option_value';
230
- $categoryProductTable = $this->_tablePrefix . 'catalog_category_product';
231
- $productEntityValueTable = $this->_tablePrefix . 'catalog_product_entity_';
232
-
233
- $tables = array(
234
- 'int',
235
- 'text',
236
- 'varchar',
237
- 'decimal',
238
- 'datetime',
239
- );
240
-
241
- $write = $this->_getConnection();
242
-
243
- $excludeFields = implode("', '", $arrayExcludeFields);
244
- if (!empty($excludeFields)) {
245
- $this->log('adding exclude fields');
246
- $excludeFields = " AND attribute_code NOT IN ('" . $excludeFields . "')";
247
  }
248
 
249
  $this->log(sprintf('going to open feed file %s', $filename));
250
  $output = new CsvWriter($filename, $this->outputFileDelimiter, $this->bufferSize);
251
  $this->log('feed file open, appending header');
252
  $output->appendRow(array('unique_id', 'key', 'value'));
253
- $this->log('header appended');
254
-
255
- foreach ($tables as $table) {
256
- $this->log(sprintf('starting to pull from attribute table %s', $table));
257
-
258
- $done = false;
259
- $offset = 0;
260
- $valueTable = $productEntityValueTable . $table;
261
- if ($table == "catalog_category_product") {
262
- $this->log('creating query for catalog_category_product');
263
-
264
- $selectQry = <<<EOSQL
265
- SELECT
266
- e.entity_id,
267
- e.sku,
268
- a.attribute_code,
269
- a.source_model,
270
- a.frontend_input,
271
- a.attribute_id,
272
- a.attribute_code As value
273
- FROM
274
- $productEntityTable e
275
- LEFT JOIN
276
- $eavAttributeTable a ON e.entity_type_id = a.entity_type_id
277
- WHERE
278
- e.entity_type_id = $this->_entityTypeId AND a.attribute_code = 'category_ids'
279
- ORDER BY e.entity_id ASC
280
- EOSQL;
281
-
282
- } else {
283
- $valColumn = "v.value";
284
- $eaovTable = '';
285
- if ($table == 'int') {
286
- $valColumn = "case when a.frontend_input = 'select' and (a.source_model = 'eav/entity_attribute_source_table' or a.source_model = '' or a.source_model IS NULL) then ov.value else v.value end AS value";
287
- $eaovTable = "LEFT JOIN $eavOptionValueTable ov ON ov.option_id = v.value and ov.store_id = 0";
288
- } elseif ($table == 'varchar') {
289
- $valColumn = "case a.frontend_input when 'multiselect' then ov.value else v.value end AS value";
290
- $eaovTable = "LEFT JOIN $eavOptionValueTable ov ON ov.option_id = v.value and ov.store_id = 0";
291
- }
292
- $selectQry = <<<EOSQL
293
- SELECT
294
- e.entity_id,
295
- e.sku,
296
- a.attribute_code,
297
- a.source_model,
298
- a.frontend_input,
299
- a.attribute_id,
300
- $valColumn
301
- FROM
302
- $productEntityTable e
303
- LEFT JOIN
304
- $valueTable v ON e.entity_id = v.entity_id and v.store_id = 0
305
- LEFT JOIN
306
- $eavAttributeTable a ON v.attribute_id = a.attribute_id
307
- $eaovTable
308
- WHERE
309
- e.entity_type_id = $this->_entityTypeId
310
- $excludeFields
311
- ORDER BY e.entity_id ASC
312
- EOSQL;
313
- Mage::log($selectQry);
314
 
315
- }
 
 
316
 
317
- while (!$done) {
318
- $this->log(sprintf('querying data for table %s at offset %d', $table, $offset));
319
- try {
320
- if (($rows = $write->fetchAll($selectQry . ' LIMIT ' . $offset . ', ' . $this->_batchSize)) && (count($rows) > 0)) {
321
- $this->log(sprintf('fetch all succeeded for att type %s, processing %d rows', $table, count($rows)));
322
- foreach ($rows as $row) {
323
- if ($row['frontend_input'] == 'multiselect') {
324
- $values = explode(',', $row['value']);
325
- foreach ($values as $val) {
326
- if ($table == 'int' && !empty($row['source_model'])) {
327
- $source = Mage::getSingleton($row['source_model']);
328
- if ($row['source_model'] == 'eav/entity_attribute_source_table') {
329
- $attribute = Mage::getModel('eav/entity_attribute')->load($row['attribute_id']);
330
- $source->setAttribute($attribute);
331
- }
332
- $output->appendRow(array($row['sku'], $row['attribute_code'], $source->getOptionText($row['value'])));
333
- } else {
334
- $output->appendRow(array($row['sku'], $row['attribute_code'], $val));
335
- }
336
- }
337
- } else if ($row['attribute_code'] == 'category_ids' && $row['value'] == "category_ids") {
338
- $select_qry = 'SELECT category_id FROM ' . $categoryProductTable . ' WHERE product_id = "' . $row['entity_id'] . '"';
339
- $rows1 = $write->fetchAll($select_qry);
340
- foreach ($rows1 as $category_data) {
341
- $output->appendRow(array($row['sku'], 'category_id', $category_data['category_id']));
342
- }
343
- } elseif ($row['attribute_code'] == 'country_of_manufacture') {
344
- $output->appendRow(array($row['sku'], $row['attribute_code'], $this->getCountryName($row['value'])));
345
-
346
- } elseif ($table == 'int' && !empty($row['source_model']) && $row['source_model'] != 'eav/entity_attribute_source_table') {
347
- try {
348
- $source = Mage::getSingleton($row['source_model']);
349
- $attribute = Mage::getModel('eav/entity_attribute')->load($row['attribute_id']);
350
- $source->setAttribute($attribute);
351
- $output->appendRow(array($row['sku'], $row['attribute_code'], $source->getOptionText($row['value'])));
352
- } catch (Exception $e) {
353
- $this->log(sprintf('%s - Caught Exception on line %d or %s: %s', date('c'), $e->getLine(), $e->getFile(), $e->getTraceAsString()));
354
- $output->appendRow(array($row['sku'], $row['attribute_code'], $row['value']));
355
- }
356
- } else {
357
- $output->appendRow(array($row['sku'], $row['attribute_code'], $row['value']));
358
- }
359
- }
360
- $offset += $this->_batchSize;
361
  } else {
362
- $this->log(sprintf('done with table %s', $table));
363
- $done = true;
 
 
 
364
  }
365
- } catch (Exception $e) {
366
- // remove lock
367
- Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
368
-
369
- $this->log(sprintf('%s - Exception thrown on line %d or %s: %s', date('c'), $e->getLine(), $e->getFile(), $e->getTraceAsString()));
370
- $this->log('exiting function _getAttributeData() due to exception');
371
- return false;
372
  }
373
- }
374
- }
375
- $this->log('going to get product collection for category id selection');
376
- /* custom attribute code/category_id */
377
- $collection = Mage::getModel('catalog/product')->getCollection();
378
- $collection->addAttributeToSelect('sku');
379
- //$collection->addAttributeToFilter('visibility', array('neq' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE));
380
- if (!Mage::helper('hawksearch_datafeed/data')->getAllowDisabledAttribute()) {
381
- $this->log('adding status filter');
382
- $collection->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
383
- }
384
- if (!Mage::helper('hawksearch_datafeed/data')->isIncludeOutOfStockItems()) {
385
- $this->log('adding out of stock filter');
386
- /** @var Mage_CatalogInventory_Model_Stock $stockfilter */
387
- $stockfilter = Mage::getSingleton('cataloginventory/stock');
388
- $stockfilter->addInStockFilterToCollection($collection);
389
- }
390
-
391
- $this->log(sprintf('setting page size to %d', $this->_batchSize));
392
- $collection->setPageSize($this->_batchSize);
393
- $totalPages = $collection->getLastPageNumber();
394
- $this->log(sprintf('retrieved %d total pages', $totalPages));
395
-
396
- $currentPage = 1;
397
- do {
398
- $this->log(sprintf('setting current page to %d', $currentPage));
399
- $collection->setCurPage($currentPage);
400
- $collection->clear();
401
- $this->log(sprintf('going to process %d products on page %d', $collection->count(), $currentPage));
402
-
403
- /*
404
- * add ratings information
405
- */
406
- /** @var Mage_Review_Model_Review $review */
407
- $review = Mage::getSingleton('review/review');
408
- $review->appendSummary($collection);
409
-
410
- /** @var Mage_Catalog_Model_Product $product */
411
- foreach ($collection as $product) {
412
  foreach ($product->getCategoryIds() as $id) {
413
  $output->appendRow(array($product->getSku(), 'category_id', $id));
414
  }
415
- if(($rs = $product->getRatingSummary()) && $rs->getReviewsCount() > 0){
416
  $output->appendRow(array($product->getSku(), 'rating_summary', $rs->getRatingSummary()));
417
  $output->appendRow(array($product->getSku(), 'reviews_count', $rs->getReviewsCount()));
418
  }
419
- /* Example of custom attribute generation */
420
- // $product->load('media_gallery');
421
- // /** @var Varien_Data_Collection $images */
422
- // $images = $product->getMediaGalleryImages();
423
- // $image = $images->getLastItem();
424
- // $imagePath = $image->getFile();
425
- // $output->appendRow(array($product->getSku(), 'rollover_image', $imagePath));
426
-
427
- /* Gallery images should come out sorted,
428
- but if not, try this instead of getLastItem(): */
429
- // $pos = 0;
430
- // foreach ($images as $image) {
431
- // if ($image->getPosition() >= $pos) {
432
- // $pos = $image->getPosition();
433
- // $imagePath = $image->getFile();
434
- // }
435
- // }
436
  }
 
 
437
  $currentPage++;
438
- } while ($currentPage <= $totalPages);
 
439
 
440
- $this->log('done processing attributes');
441
- return true;
 
 
 
 
 
 
442
  }
443
 
444
  /**
445
  * @return bool
446
  * @throws Mage_Core_Exception
447
  */
448
- protected function _getCategoryData() {
449
  $this->log('starting _getCategoryData()');
450
- $filename = $this->_feedPath . DS . "hierarchy" . '.' . $this->outputFileExtension;
451
 
452
- $collection = Mage::getModel('catalog/category')->getCollection();
 
 
 
453
  $collection->addAttributeToSelect(array('name', 'is_active', 'parent_id', 'position'));
454
  $collection->addAttributeToFilter('is_active', array('eq' => '1'));
455
  $collection->addAttributeToSort('entity_id')->addAttributeToSort('parent_id')->addAttributeToSort('position');
456
 
 
457
  $collection->setPageSize($this->_batchSize);
458
  $pages = $collection->getLastPageNumber();
459
  $currentPage = 1;
@@ -464,21 +235,21 @@ EOSQL;
464
  $output->appendRow(array('category_id', 'category_name', 'parent_category_id', 'sort_order', 'is_active', 'category_url'));
465
  $output->appendRow(array('1', 'Root', '0', '0', '1', '/'));
466
  $this->log('header and root appended');
467
- $base = Mage::app()->getStore()->getBaseUrl();
468
 
469
  $cats = array();
470
  do {
471
- $this->log(sprintf('getting category page %d', $currentPage));
472
  $collection->setCurPage($currentPage);
473
  $collection->clear();
474
  $collection->load();
475
  foreach ($collection as $cat) {
476
  $fullUrl = Mage::helper('catalog/category')->getCategoryUrl($cat);
477
  $category_url = substr($fullUrl, strlen($base));
478
- if(substr($category_url, 0, 1) != '/'){
479
  $category_url = '/' . $category_url;
480
  }
481
- $this->log(sprintf("got full category url: %s, returning relative url %s", $fullUrl, $category_url));
482
  $cats[] = array(
483
  'id' => $cat->getId(),
484
  'name' => $cat->getName(),
@@ -491,10 +262,10 @@ EOSQL;
491
  $currentPage++;
492
  } while ($currentPage <= $pages);
493
 
494
- $rcid = Mage::app()->getStore()->getRootCategoryId();
495
  $myCategories = array();
496
- foreach($cats as $storecat){
497
- if($storecat['id'] == $rcid){
498
  $myCategories[] = $storecat;
499
  }
500
  }
@@ -502,7 +273,7 @@ EOSQL;
502
  $this->log("using root category id: $rcid");
503
  $this->r_find($rcid, $cats, $myCategories);
504
 
505
- foreach($myCategories as $final) {
506
  $output->appendRow(array(
507
  $final['id'],
508
  $final['name'],
@@ -526,145 +297,36 @@ EOSQL;
526
  * @param $tree
527
  */
528
  private function r_find($pid, &$all, &$tree) {
529
- foreach($all as $item) {
530
- if($item['pid'] == $pid) {
531
  $tree[] = $item;
532
  $this->r_find($item['id'], $all, $tree);
533
  }
534
  }
535
  }
536
 
537
- protected function _getProductData() {
538
- $this->log('starting _getProductData()');
539
- /** @var Mage_Catalog_Helper_Product $helper */
540
- $helper = Mage::helper('catalog/product');
541
- $urlSuffix = $helper->getProductUrlSuffix(0);
542
- if (!empty($urlSuffix)) {
543
- if (substr($urlSuffix, 0, 1) != '.') {
544
- $urlSuffix = '.' . $urlSuffix;
545
- }
546
- }
547
- $this->log(sprintf("using '%s' for the product url suffix", $urlSuffix));
548
-
549
- $done = false;
550
- $offset = 0;
551
- $filename = $this->_feedPath . DS . "items" . '.' . $this->outputFileExtension;
552
- $brand_sql = "";
553
- $attrCodes = array();
554
- $brand_select = "";
555
- $entity_type_id = $this->_entityTypeId;
556
-
557
- $this->_batchSize = 10000;
558
-
559
- $eavTable = $this->_tablePrefix . 'eav_attribute';
560
- $productEntityTable = $this->_tablePrefix . 'catalog_product_entity';
561
- $eavOptionValueTable = $this->_tablePrefix . 'eav_attribute_option_value';
562
- $productRelationTable = $this->_tablePrefix . 'catalog_product_relation';
563
- $productEntityIntTable = $this->_tablePrefix . 'catalog_product_entity_int';
564
- $productEntityTextTable = $this->_tablePrefix . 'catalog_product_entity_text';
565
- $productEntityVarCharTable = $this->_tablePrefix . 'catalog_product_entity_varchar';
566
- $productEntityDecimalTable = $this->_tablePrefix . 'catalog_product_entity_decimal';
567
- $productEntityDateTimeTable = $this->_tablePrefix . 'catalog_product_entity_datetime';
568
- $catalogInventoryStockTable = $this->_tablePrefix . 'cataloginventory_stock_item';
569
- $productEntityUrlTable = $this->_tablePrefix . 'catalog_product_entity_url_key';
570
-
571
- //$baseurl = Mage::getUrl();
572
- //$mediaurl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA);
573
- $brandAttribute = Mage::helper('hawksearch_datafeed/data')->getBrandAttribute();
574
- $allowDisabled = Mage::helper('hawksearch_datafeed/data')->getAllowDisabledAttribute();
575
-
576
- $write = $this->_getConnection();
577
-
578
- $attrCodesQuery = <<<EOSQL
579
- SELECT attribute_id, attribute_code
580
- FROM $eavTable
581
- WHERE attribute_code IN
582
- (
583
- 'name',
584
- 'url_key',
585
- 'image',
586
- 'description',
587
- 'short_description',
588
- 'meta_keyword',
589
- 'visibility',
590
- 'price',
591
- 'special_from_date',
592
- 'special_to_date',
593
- 'msrp',
594
- 'special_price',
595
- 'status',
596
- 'url_key',
597
- '$brandAttribute'
598
- )
599
- AND entity_type_id = '$entity_type_id'
600
- EOSQL;
601
-
602
- $this->log('fetching attribute codes');
603
- if ($codeRows = $write->fetchAll($attrCodesQuery)) {
604
- foreach ($codeRows as $row) {
605
- $attrCodes[$row['attribute_code']] = $row['attribute_id'];
606
- }
607
- }
608
- $this->log(sprintf('found %d codes: %s', count($attrCodes), implode("', '", array_keys($attrCodes))));
609
-
610
- $disabled_sql = '';
611
- $CONN = ' WHERE ';
612
- if (!$allowDisabled) {
613
- $disabled_sql = "LEFT JOIN $productEntityIntTable AS T6 ON P.entity_id = T6.entity_id AND T6.attribute_id = '" . $attrCodes['status'] . "' AND T6.store_id = '" . $this->_storeId . "'";
614
- }
615
 
616
- if (!empty($brandAttribute)) {
617
- $brand_select = ", B5.value AS Brand";
618
- $brand_sql = "LEFT JOIN " . $productEntityIntTable . " AS B5 ON P.entity_id = B5.entity_id AND B5.attribute_id = '" . $attrCodes[$brandAttribute] . "' AND B5.store_id = '" . $this->_storeId . "'";
619
- }
620
- $urlfield = ", CONCAT(V1.value, '" . $urlSuffix . "') AS Link";
621
- $urljoin = "LEFT JOIN $productEntityUrlTable AS V1 ON P.entity_id = V1.entity_id AND V1.attribute_id = '" . $attrCodes['url_key'] . "' AND V1.store_id = '" . $this->_storeId . "'";
622
- if (!Mage::getSingleton('core/resource')->getConnection('core_write')->isTableExists('catalog_product_entity_url_key')) {
623
- $this->log('using catalog_product_entity_url_table');
624
- $urljoin = ' LEFT JOIN ' . $productEntityVarCharTable . " AS V1 ON P.entity_id = V1.entity_id AND V1.attribute_id = '" . $attrCodes['url_key'] . "' AND V1.store_id = '" . $this->_storeId . "'";
625
  }
626
 
627
- $select_qry = "SELECT P.attribute_set_id, P.entity_id AS ProductID, P.type_id, P.sku, P.has_options, V.value AS Name, T1.value AS ProdDesc, T2.value AS ShortDesc, T3.value AS MetaKeyword, T5.value AS visibility, D.value AS Price, S.value AS Special_Price, SDF.value As Special_Date_From, SDT.value As Special_Date_To, ST.qty, ST.is_in_stock AS IsInStock, X.value AS Msrp" . $brand_select . ", R.parent_id AS GroupID $urlfield,
628
- CASE
629
- WHEN V2.Value IS NULL
630
- THEN '/no-image.jpg'
631
- ELSE CONCAT('', V2.value)
632
- END AS Image
633
- FROM $productEntityTable AS P
634
- INNER JOIN " . $productEntityVarCharTable . " AS V ON P.entity_id = V.entity_id AND V.attribute_id = '" . $attrCodes['name'] . "' AND V.store_id = '" . $this->_storeId . "'
635
- INNER JOIN " . $catalogInventoryStockTable . " AS ST ON P.entity_id = ST.product_id
636
- INNER JOIN " . $productEntityIntTable . " AS VIS ON VIS.entity_id = P.entity_id AND VIS.value != 1
637
- " . $urljoin . "
638
- LEFT JOIN " . $productEntityVarCharTable . " AS V2 ON P.entity_id = V2.entity_id AND V2.attribute_id = '" . $attrCodes['image'] . "' AND V2.store_id = '" . $this->_storeId . "'
639
- LEFT JOIN " . $productEntityTextTable . " AS T1 ON P.entity_id = T1.entity_id AND T1.attribute_id = '" . $attrCodes['description'] . "' AND T1.store_id = '" . $this->_storeId . "'
640
- LEFT JOIN " . $productEntityTextTable . " AS T2 ON P.entity_id = T2.entity_id AND T2.attribute_id = '" . $attrCodes['short_description'] . "' AND T2.store_id = '" . $this->_storeId . "'
641
- LEFT JOIN " . $productEntityTextTable . " AS T3 ON P.entity_id = T3.entity_id AND T3.attribute_id = '" . $attrCodes['meta_keyword'] . "' AND T3 .store_id = '" . $this->_storeId . "'
642
- LEFT JOIN " . $productEntityIntTable . " AS T5 ON P.entity_id = T5.entity_id AND T5.attribute_id = '" . $attrCodes['visibility'] . "' AND T5.store_id = '" . $this->_storeId . "'
643
- " . $disabled_sql . "
644
- LEFT JOIN " . $productEntityDecimalTable . " AS D ON P.entity_id = D.entity_id AND D.attribute_id = '" . $attrCodes['price'] . "' AND D.store_id = '" . $this->_storeId . "'
645
- LEFT JOIN " . $productEntityDateTimeTable . " AS SDF ON P.entity_id = SDF.entity_id AND SDF.attribute_id = '" . $attrCodes['special_from_date'] . "' AND SDF.store_id = '" . $this->_storeId . "'
646
- LEFT JOIN " . $productEntityDateTimeTable . " AS SDT ON P.entity_id = SDT.entity_id AND SDT.attribute_id = '" . $attrCodes['special_to_date'] . "' AND SDT.store_id = '" . $this->_storeId . "'
647
- " . $brand_sql . "
648
- LEFT JOIN " . $productEntityDecimalTable . " AS X ON P.entity_id = X.entity_id AND X.attribute_id = '" . $attrCodes['msrp'] . "' AND X.store_id = '" . $this->_storeId . "'
649
- LEFT JOIN " . $productRelationTable . " AS R ON P.entity_id = R.parent_id OR P.entity_id = R.child_id
650
- LEFT JOIN " . $productEntityDecimalTable . " AS S ON P.entity_id = S.entity_id AND S.attribute_id = '" . $attrCodes['special_price'] . "' AND S.store_id = '" . $this->_storeId . "'";
651
-
652
  if (!Mage::helper('hawksearch_datafeed/data')->isIncludeOutOfStockItems()) {
653
- $this->log('filtering for in-stock items');
654
- $select_qry .= " $CONN ST.is_in_stock = 1";
655
- $CONN = ' AND ';
656
- }
657
- if (!$allowDisabled) {
658
- $this->log('filtering disabled items');
659
- $select_qry .= " $CONN T6.value = 1 ";
660
- $CONN = ' AND ';
661
  }
662
 
663
- $select_qry .= " GROUP BY P.entity_id ";
664
-
665
- $this->log(sprintf('going to open feed file %s', $filename));
666
  $output = new CsvWriter($filename, $this->outputFileDelimiter, $this->bufferSize);
667
- $this->log('appending feed header');
668
  $output->appendRow(array(
669
  'product_id',
670
  'unique_id',
@@ -687,128 +349,85 @@ EOSQL;
687
  'is_new',
688
  'is_on_sale',
689
  'keyword',
690
- 'metric_inventory'));
691
-
692
- $full_query = 'not yet set';
693
- while (!$done) {
694
- try {
695
- $full_query = $select_qry . ' LIMIT ' . $offset . ', ' . $this->_batchSize;
696
- $this->log(sprintf('going to fetch %d products starting at offset %d', $this->_batchSize, $offset));
697
- if ($rows = $write->fetchAll($full_query)) {
698
- $this->log(sprintf('query returned %d rows', count($rows)));
699
- if (count($rows) > 0) {
700
- foreach ($rows as $row) {
701
- //$name = empty($row['Name']) ? $row['Name'] : str_replace("\"", "\"\"", $row['Name']);
702
- //$description = empty($row['ProdDesc']) ? $row['ProdDesc'] : str_replace("\"", "\"\"", $row['ProdDesc']);
703
- //$shortdescription = empty($row['ShortDesc']) ? $row['ShortDesc'] : str_replace("\"", "\"\"", $row['ShortDesc']);
704
- //$metakeyword = empty($row['MetaKeyword']) ? $row['MetaKeyword'] : str_replace("\"", "\"\"", $row['MetaKeyword']);
705
-
706
- $data_is_on_sale = empty($row['Special_Price']) ? 0 : 1;
707
- if (isset($row['Brand'])) {
708
- $select_brand_qry = $write->query("SELECT value FROM " . $eavOptionValueTable . " WHERE `option_id`=\"" . $row['Brand'] . "\" AND store_id ='0'");
709
- $brandRow = $select_brand_qry->fetch();
710
- $brand_text = $brandRow['value'];
711
- } else {
712
- $brand_text = "";
713
- }
714
- $output->appendRow(array(
715
- $row['ProductID'],
716
- $row['sku'],
717
- $row['Name'],
718
- $row['Link'],
719
- $row['Image'],
720
- $row['Msrp'],
721
- $row['Price'],
722
- $row['Special_Price'],
723
- $row['Special_Date_From'],
724
- $row['Special_Date_To'],
725
- $row['GroupID'],
726
- $row['ShortDesc'],
727
- $row['ProdDesc'],
728
- $brand_text,
729
- $row['sku'],
730
- 0, // sort_default
731
- 0, // sort_rating
732
- 0, // is_free_shipping
733
- 0, // is_new
734
- $data_is_on_sale,
735
- $row['MetaKeyword'],
736
- $row['qty']));
737
 
738
- }
739
- $offset += $this->_batchSize;
740
- }
741
- } else {
742
- $this->log('done fetching products');
743
- $done = true;
744
- }
745
- } catch (Exception $e) {
746
- // remove lock
747
- Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
748
 
749
- $this->log(sprintf('%s - Exception thrown on line %d or %s: %s', date('c'), $e->getLine(), $e->getFile(), $e->getTraceAsString()));
750
- $this->log(sprintf("\nSQL was:\n%s", $full_query));
751
- $this->log('exiting function _getProductData() due to exception');
752
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
  }
754
- } // end while
 
 
 
755
  $this->log('done with _getProductData()');
756
- return true;
757
  }
758
 
759
- protected function _getContentData() {
 
 
 
 
 
 
 
 
 
 
760
  $this->log('starting _getContentData()');
761
- $done = false;
762
- $offset = 0;
763
- $baseurl = Mage::getUrl();
764
- //$filename = $this->_feedPath . "/content.txt";
765
- $filename = $this->_feedPath . DS . "content" . '.' . $this->outputFileExtension;
766
-
767
- //$firstRecord = true;
768
- $cmsPageTable = $this->_tablePrefix . 'cms_page';
769
-
770
-
771
- $select_qry = "SELECT page_id, title, CONCAT('" . $baseurl . "', identifier) AS Link, content_heading, content, creation_time, is_active FROM " . $cmsPageTable . "";
772
 
773
- $write = $this->_getConnection();
774
-
775
- $this->log(sprintf('going to open feed file %s', $filename));
776
- $output = new CsvWriter($filename, $this->outputFileDelimiter, $this->bufferSize);
777
- $this->log('feed file open, going to append header');
778
  $output->appendRow(array('unique_id', 'name', 'url_detail', 'description_short', 'created_date'));
779
- $this->log('header appended, going to fetch data');
780
-
781
- $full_query = 'not yet set';
782
- while (!$done) {
783
- try {
784
- $this->log(sprintf('going to fetch %d rows at offset %d', $this->_batchSize, $offset));
785
- $full_query = $select_qry . ' LIMIT ' . $offset . ', ' . $this->_batchSize;
786
- if ($rows = $write->fetchAll($full_query)) {
787
- if (($numRows = count($rows)) && $numRows > 0) {
788
- foreach ($rows as $row) {
789
- //$content .= $row['page_id'] . "\t" . $row['title'] . "\t" . $row['Link'] . "\t" . $row['content_heading'] . "\t" . $row['creation_time'] . "\n";
790
- $output->appendRow(array($row['page_id'], $row['title'], $row['Link'], $row['content_heading'], $row['creation_time']));
791
- }
792
 
793
- $offset += $this->_batchSize;
794
- } else {
795
- $done = true;
796
- }
797
- } else {
798
- $done = true;
799
- }
800
- } catch (Exception $e) {
801
- // remove lock
802
- Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
803
-
804
- $this->log(sprintf('%s - Exception thrown on line %d or %s: %s', date('c'), $e->getLine(), $e->getFile(), $e->getTraceAsString()));
805
- $this->log(sprintf("\nSQL was:\n%s", $full_query));
806
- $this->log('exiting function _getContentData() due to exception');
807
- return false;
808
- }
809
- } // end while
810
- $this->log('done fetching content data');
811
- return true;
812
  }
813
 
814
  public function getCountryName($code) {
@@ -827,99 +446,127 @@ EOSQL;
827
  }
828
 
829
  public function generateFeed() {
830
- try {
831
- $sid = Mage::app()->getDefaultStoreView()->getId();
832
- // for working with proxy module
833
-
834
- // $code = Mage::getStoreConfig('hawksearch_proxy/proxy/store_code');
835
- // if(!empty($code)) {
836
- // /** @var Mage_Core_Model_Resource_Store_Collection $store */
837
- // $store = Mage::getModel('core/store')->getCollection();
838
- // $sid = $store->addFieldToFilter('code', $code)->getFirstItem()->getId();
839
- // }
840
-
841
- // end proxy module connection
842
-
843
- $this->log('starting environment for store id: ' . $sid);
844
- /** @var Mage_Core_Model_App_Emulation $appEmulation */
845
- $appEmulation = Mage::getSingleton('core/app_emulation');
846
- $initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($sid);
847
-
848
- //exports Category Data
849
- $this->_getCategoryData();
850
-
851
- //exports Product Data
852
- $this->_getProductData();
853
-
854
- //exports Attribute Data
855
- $this->_getAttributeData();
856
-
857
- //exports CMS / Content Data
858
- $this->_getContentData();
859
-
860
- //refresh image cache no longer part of feed generation
861
- //$this->refreshImageCache();
862
- $this->log('done generating data feed files, going to remove lock files.');
863
- // remove locks
864
- Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
865
- $appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
866
- } catch (Exception $e) {
867
- // remove lock
868
- Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
869
- $this->log(sprintf("General Exception %s at generateFeed() line %d, stack:\n%s", $e->getMessage(), $e->getLine(), $e->getTraceAsString()));
 
870
  }
871
- $this->log('done with generateFeed()');
 
 
 
 
 
872
  }
873
 
874
- public function refreshImageCache($storeid = null) {
875
- $this->log('starting refreshImageCache()');
876
- // $sid = Mage::app()->getWebsite()->getDefaultGroup()->getDefaultStoreId();
877
- // $h = Mage::helper("hawksearch_proxy");
878
- // if (!empty($h)) {
879
- // $sid = $h->getCategoryStoreId();
880
- // }
881
- $sid = Mage::app()->getDefaultStoreView()->getId();
882
 
883
- $this->log('starting environment for store id: ' . $sid);
 
 
 
 
 
 
884
 
885
- $appEmulation = Mage::getSingleton('core/app_emulation');
886
- $initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($sid);
887
 
888
- $products = Mage::getModel('catalog/product')
889
- ->getCollection()->addAttributeToSelect(array('image', 'small_image'));
890
- $products->setPageSize(100);
891
- $pages = $products->getLastPageNumber();
892
 
893
- $currentPage = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
 
895
- do {
896
- $this->log(sprintf('going to page %d of images', $currentPage));
897
- $products->clear();
898
- $products->setCurPage($currentPage);
899
- $products->load();
900
 
901
- foreach ($products as $product) {
902
- if (empty($this->_imageHeight)) {
903
- $this->log(
904
- sprintf('going to resize image for url: %s',
905
- Mage::helper('catalog/image')->init($product, 'small_image')->resize($this->_imageWidth
906
- )));
907
- } else {
908
- $this->log(
909
- sprintf('going to resize image for url: %s',
910
- Mage::helper('catalog/image')->init($product, 'small_image')->resize($this->_imageWidth, $this->_imageHeight
911
- )));
912
- }
913
- }
914
 
915
- $currentPage++;
 
916
 
917
- //clear collection and free memory
 
 
918
 
919
- } while ($currentPage <= $pages);
920
- $this->log('done generating image cache, going to remove locks.');
921
  Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
922
- $appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
923
  }
924
 
925
  /**
@@ -994,6 +641,9 @@ class CsvWriter {
994
  if (!$this->outputOpen) {
995
  $this->openOutput();
996
  }
 
 
 
997
  if (false === fputcsv($this->outputFile, $fields, $this->delimiter)) {
998
  throw new Exception("CsvWriter: failed to write row.");
999
  }
27
  private $countryMap;
28
  private $outputFileDelimiter;
29
  private $bufferSize;
30
+ private $feedSummary;
31
  private $outputFileExtension;
32
+ private $productAttributes;
33
 
34
  /**
35
  * Constructor
36
  */
37
  function __construct() {
 
 
 
 
 
38
  /** @var $helper Hawksearch_Datafeed_Helper_Data */
39
  $helper = Mage::helper('hawksearch_datafeed/data');
40
 
48
 
49
  $this->_feedPath = $helper->getFeedFilePath();
50
  if (empty($this->_feedPath)) {
51
+ $this->log(sprintf('WARNING: feed path not configured, dumping feeds into var/hawksearch/feeds/'));
52
  $this->_feedPath = Mage::getBaseDir('base') . DS . 'var/hawksearch/feeds';
53
  }
54
 
69
  $this->outputFileExtension = $helper->getOutputFileExtension();
70
  $this->_storeId = 0;
71
 
72
+ $this->feedSummary = new stdClass();
73
+ $this->productAttributes = array('entity_id', 'sku', 'name', 'url', 'small_image', 'msrp', 'price', 'special_price', 'special_from_date', 'special_to_date', 'short_description', 'description', 'meta_keyword', 'qty');
74
+
75
  parent::__construct();
76
  }
77
 
88
  }
89
  }
90
 
91
+ protected function _getAttributeData(Mage_Core_Model_Store $store) {
92
+ $this->log('starting _getAttributeData');
93
+ $filename = $this->getPathForFile('attributes');
94
+ $labelFilename = $this->getPathForFile('labels');
95
+
96
+ $this->log(sprintf('exporting attribute labels for store %s', $store->getName()));
97
+ $start = time();
98
+ /** @var Mage_Catalog_Model_Resource_Product_Attribute_Collection $pac */
99
+ $pac = Mage::getResourceModel('catalog/product_attribute_collection');
100
+ $pac->addSearchableAttributeFilter();
101
+ $pac->addStoreLabel($store->getId());
102
+ $attributes = array();
103
+
104
+ $labels = new CsvWriter($labelFilename, $this->outputFileDelimiter, $this->bufferSize);
105
+ $labels->appendRow(array('key', 'store_label'));
106
+ /** @var Mage_Catalog_Model_Resource_Eav_Attribute $att */
107
+ foreach ($pac as $att) {
108
+ $attributes[$att->getAttributeCode()] = $att;
109
+ $labels->appendRow(array($att->getAttributeCode(), $att->getStoreLabel()));
110
+ }
111
+ $labels->closeOutput();
112
+ $this->log(sprintf('Label export took %d seconds', time() - $start));
113
+
114
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $products */
115
+ $products = Mage::getModel('catalog/product')->getCollection();
116
+ $feedCodes = array_diff(array_keys($attributes), $this->productAttributes);
117
+ if(!in_array('sku', $feedCodes)) {
118
+ array_push($feedCodes, 'sku');
119
+ }
120
+ $this->log(sprintf('searchable atts: %s', implode(', ', array_keys($attributes))));
121
+ $this->log(sprintf('adding attributes to select: %s', implode(', ', $feedCodes)));
122
+ $products->addAttributeToSelect($feedCodes);
123
+ // $products->addAttributeToFilter('visibility', array('neq' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE));
124
+
125
+ $products->addStoreFilter($store);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ if (!Mage::helper('hawksearch_datafeed/data')->getAllowDisabledAttribute()) {
128
+ $this->log('adding status filter');
129
+ $products->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  }
131
 
132
+ if (!Mage::helper('hawksearch_datafeed/data')->isIncludeOutOfStockItems()) {
133
+ $this->log('adding out of stock filter');
134
+ /** @var Mage_CatalogInventory_Model_Stock $stockfilter */
135
+ $stockfilter = Mage::getSingleton('cataloginventory/stock');
136
+ $stockfilter->addInStockFilterToCollection($products);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  }
138
 
139
  $this->log(sprintf('going to open feed file %s', $filename));
140
  $output = new CsvWriter($filename, $this->outputFileDelimiter, $this->bufferSize);
141
  $this->log('feed file open, appending header');
142
  $output->appendRow(array('unique_id', 'key', 'value'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
+ $products->setPageSize($this->_batchSize);
145
+ $pages = $products->getLastPageNumber();
146
+ $currentPage = 1;
147
 
148
+ /** @var Mage_Review_Model_Review $review */
149
+ $review = Mage::getSingleton('review/review');
150
+
151
+ do{
152
+ $this->log(sprintf('starting attribute export for page %d', $currentPage));
153
+ $start = time();
154
+ $products->setCurPage($currentPage);
155
+ $products->clear();
156
+ $review->appendSummary($products);
157
+ $products->load();
158
+ foreach ($products as $product) {
159
+ foreach ($feedCodes as $attcode) {
160
+ if($product->getData($attcode) === null) {
161
+ continue;
162
+ }
163
+ $source = $attributes[$attcode]->getSource();
164
+ if($source instanceof Mage_Eav_Model_Entity_Attribute_Source_Table){
165
+ // TODO: These table based items need to be broken into separate line items
166
+ $output->appendRow(array(
167
+ $product->getSku(),
168
+ $attcode,
169
+ $product->getResource()->getAttribute($attcode)->getFrontend()->getValue($product)
170
+ ));
171
+ } elseif($source instanceof Mage_Catalog_Model_Product_Visibility
172
+ || $source instanceof Mage_Tax_Model_Class_Source_Product
173
+ || $source instanceof Mage_Catalog_Model_Product_Status ) {
174
+ $output->appendRow(array(
175
+ $product->getSku(),
176
+ $attcode,
177
+ $source->getOptionText($product->getData($attcode))
178
+ ));
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  } else {
180
+ $output->appendRow(array(
181
+ $product->getSku(),
182
+ $attcode,
183
+ $product->getData($attcode)
184
+ ));
185
  }
 
 
 
 
 
 
 
186
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  foreach ($product->getCategoryIds() as $id) {
188
  $output->appendRow(array($product->getSku(), 'category_id', $id));
189
  }
190
+ if (($rs = $product->getRatingSummary()) && $rs->getReviewsCount() > 0) {
191
  $output->appendRow(array($product->getSku(), 'rating_summary', $rs->getRatingSummary()));
192
  $output->appendRow(array($product->getSku(), 'reviews_count', $rs->getReviewsCount()));
193
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  }
195
+
196
+ $this->log(sprintf('page %d took %d seconds to export', $currentPage, time() - $start));
197
  $currentPage++;
198
+ } while($currentPage <= $pages);
199
+ }
200
 
201
+ private function getPathForFile($basename) {
202
+ //$filename = $this->_feedPath . DS . "hierarchy" . '.' . $this->outputFileExtension;
203
+ $dir = sprintf('%s/%s', $this->_feedPath, end($this->feedSummary->stores));
204
+ $this->log(sprintf('checking for dir: %s', $dir));
205
+ if (!is_dir($dir)) {
206
+ mkdir($dir, 0777, true);
207
+ }
208
+ return sprintf('%s/%s.%s', $dir, $basename, $this->outputFileExtension);
209
  }
210
 
211
  /**
212
  * @return bool
213
  * @throws Mage_Core_Exception
214
  */
215
+ protected function _getCategoryData(Mage_Core_Model_Store $store) {
216
  $this->log('starting _getCategoryData()');
217
+ $filename = $this->getPathForFile('hierarchy');
218
 
219
+ /** @var Mage_Catalog_Model_Resource_Category_Collection $collection */
220
+ /** @var Mage_Catalog_Model_Category $categoryModel */
221
+ $categoryModel = Mage::getModel('catalog/category');
222
+ $collection = $categoryModel->getCollection();
223
  $collection->addAttributeToSelect(array('name', 'is_active', 'parent_id', 'position'));
224
  $collection->addAttributeToFilter('is_active', array('eq' => '1'));
225
  $collection->addAttributeToSort('entity_id')->addAttributeToSort('parent_id')->addAttributeToSort('position');
226
 
227
+
228
  $collection->setPageSize($this->_batchSize);
229
  $pages = $collection->getLastPageNumber();
230
  $currentPage = 1;
235
  $output->appendRow(array('category_id', 'category_name', 'parent_category_id', 'sort_order', 'is_active', 'category_url'));
236
  $output->appendRow(array('1', 'Root', '0', '0', '1', '/'));
237
  $this->log('header and root appended');
238
+ $base = $store->getBaseUrl();
239
 
240
  $cats = array();
241
  do {
242
+ //$this->log(sprintf('getting category page %d', $currentPage));
243
  $collection->setCurPage($currentPage);
244
  $collection->clear();
245
  $collection->load();
246
  foreach ($collection as $cat) {
247
  $fullUrl = Mage::helper('catalog/category')->getCategoryUrl($cat);
248
  $category_url = substr($fullUrl, strlen($base));
249
+ if (substr($category_url, 0, 1) != '/') {
250
  $category_url = '/' . $category_url;
251
  }
252
+ //$this->log(sprintf("got full category url: %s, returning relative url %s", $fullUrl, $category_url));
253
  $cats[] = array(
254
  'id' => $cat->getId(),
255
  'name' => $cat->getName(),
262
  $currentPage++;
263
  } while ($currentPage <= $pages);
264
 
265
+ $rcid = $store->getRootCategoryId();
266
  $myCategories = array();
267
+ foreach ($cats as $storecat) {
268
+ if ($storecat['id'] == $rcid) {
269
  $myCategories[] = $storecat;
270
  }
271
  }
273
  $this->log("using root category id: $rcid");
274
  $this->r_find($rcid, $cats, $myCategories);
275
 
276
+ foreach ($myCategories as $final) {
277
  $output->appendRow(array(
278
  $final['id'],
279
  $final['name'],
297
  * @param $tree
298
  */
299
  private function r_find($pid, &$all, &$tree) {
300
+ foreach ($all as $item) {
301
+ if ($item['pid'] == $pid) {
302
  $tree[] = $item;
303
  $this->r_find($item['id'], $all, $tree);
304
  }
305
  }
306
  }
307
 
308
+ protected function _getProductData(Mage_Core_Model_Store $store) {
309
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $products */
310
+ $products = Mage::getModel('catalog/product')->getCollection();
311
+ $products->addAttributeToSelect($this->productAttributes);
312
+ //$products->addAttributeToFilter('visibility', array('neq' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE));
313
+ $products->addMinimalPrice();
314
+ $products->addStoreFilter($store);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
 
316
+ if (!Mage::helper('hawksearch_datafeed/data')->getAllowDisabledAttribute()) {
317
+ $this->log('adding status filter');
318
+ $products->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
 
 
 
 
 
 
319
  }
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  if (!Mage::helper('hawksearch_datafeed/data')->isIncludeOutOfStockItems()) {
322
+ $this->log('adding out of stock filter');
323
+ /** @var Mage_CatalogInventory_Model_Stock $stockfilter */
324
+ $stockfilter = Mage::getSingleton('cataloginventory/stock');
325
+ $stockfilter->addInStockFilterToCollection($products);
 
 
 
 
326
  }
327
 
328
+ $filename = $this->getPathForFile('items');
 
 
329
  $output = new CsvWriter($filename, $this->outputFileDelimiter, $this->bufferSize);
 
330
  $output->appendRow(array(
331
  'product_id',
332
  'unique_id',
349
  'is_new',
350
  'is_on_sale',
351
  'keyword',
352
+ 'metric_inventory',
353
+ 'minimal_price'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
+ $products->setPageSize($this->_batchSize);
356
+ $pages = $products->getLastPageNumber();
357
+ $currentPage = 1;
 
 
 
 
 
 
 
358
 
359
+ do {
360
+ $this->log(sprintf('Starting product page %d', $currentPage));
361
+ $products->setCurPage($currentPage);
362
+ $products->clear();
363
+ $start = time();
364
+ $products->load();
365
+ $seconds = time() - $start;
366
+ $this->log(sprintf('it took %d seconds to load product page %d', $seconds, $currentPage));
367
+ $start = time();
368
+ /** @var Mage_Catalog_Model_Product $product */
369
+ foreach ($products as $product) {
370
+ $output->appendRow(array(
371
+ $product->getId(),
372
+ $product->getSku(),
373
+ $product->getName(),
374
+ substr($product->getProductUrl(1), strlen($store->getBaseUrl())),
375
+ $product->getSmallImage(),
376
+ $product->getMsrp(),
377
+ $product->getPrice(),
378
+ $product->getSpecialPrice(),
379
+ $product->getSpecialFromDate(),
380
+ $product->getSpecialToDate(),
381
+ $this->getGroupId($product),
382
+ $product->getShortDescription(),
383
+ $product->getDescription(),
384
+ '',
385
+ $product->getSku(),
386
+ '',
387
+ '',
388
+ '',
389
+ '',
390
+ $product->getSpecialPrice() ? 1 : 0,
391
+ $product->getMetaKeyword(),
392
+ $product->getQty(),
393
+ $product->getMinimalPrice()
394
+ ));
395
  }
396
+ $this->log(sprintf('it took %d seconds to export page %d', time() - $start, $currentPage));
397
+ $currentPage++;
398
+ } while ($currentPage <= $pages);
399
+
400
  $this->log('done with _getProductData()');
 
401
  }
402
 
403
+ private function getGroupId(Mage_Catalog_Model_Product $product) {
404
+ if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_SIMPLE) {
405
+ $vals =implode(",", Mage::getResourceSingleton('catalog/product_type_configurable')
406
+ ->getParentIdsByChild($product->getId()));
407
+ if(!empty($vals)){
408
+ return $vals;
409
+ }
410
+ }
411
+ return $product->getId();
412
+ }
413
+ protected function _getContentData(Mage_Core_Model_Store $store){
414
  $this->log('starting _getContentData()');
415
+ $collection = Mage::getModel('cms/page')->getCollection();
416
+ $collection->addStoreFilter($store->getId());
 
 
 
 
 
 
 
 
 
417
 
418
+ $output = new CsvWriter($this->getPathForFile('content'),$this->outputFileDelimiter, $this->bufferSize);
 
 
 
 
419
  $output->appendRow(array('unique_id', 'name', 'url_detail', 'description_short', 'created_date'));
 
 
 
 
 
 
 
 
 
 
 
 
 
420
 
421
+ foreach ($collection as $page) {
422
+ $output->appendRow(array(
423
+ $page->getPageId(),
424
+ $page->getTitle(),
425
+ sprintf('%s%s',$store->getBaseUrl(), $page->getIdentifier()),
426
+ $page->getContentHeading(),
427
+ $page->getCreationTime()
428
+ ));
429
+ }
430
+ $this->log('done with getting content data');
 
 
 
 
 
 
 
 
 
431
  }
432
 
433
  public function getCountryName($code) {
446
  }
447
 
448
  public function generateFeed() {
449
+ /*
450
+ * ok, alternate multi store plan
451
+ * Start by looping through the list of
452
+ * selected stores:
453
+ * */
454
+ $selectedStores = Mage::helper('hawksearch_datafeed')->getSelectedStores();
455
+ /** @var Mage_Core_Model_Resource_Store_Collection $stores */
456
+ $stores = Mage::getModel('core/store')->getCollection();
457
+ $stores->addIdFilter($selectedStores);
458
+ /** @var Mage_Core_Model_Store $store */
459
+ foreach ($stores as $store) {
460
+ try {
461
+ Mage::reset();
462
+ Mage::app();
463
+
464
+ $this->log(sprintf('Starting environment for store %s', $store->getName()));
465
+ $appEmulation = Mage::getModel('core/app_emulation');
466
+ $initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($store->getId());
467
+
468
+ $this->log(sprintf('Setting feed folder for store_code %s', $store->getCode()));
469
+ $this->setFeedFolder($store);
470
+
471
+ //exports Category Data
472
+ $this->_getCategoryData($store);
473
+
474
+ //exports Product Data
475
+ $this->_getProductData($store);
476
+
477
+ //exports Attribute Data
478
+ $this->_getAttributeData($store);
479
+
480
+ //exports CMS / Content Data
481
+ $this->_getContentData($store);
482
+
483
+ // end emulation
484
+ $appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
485
+
486
+ } catch (Exception $e) {
487
+ $this->log(sprintf("General Exception %s at generateFeed() line %d, stack:\n%s", $e->getMessage(), $e->getLine(), $e->getTraceAsString()));
488
+ }
489
+
490
  }
491
+ $this->log(sprintf('going to write summary file %s', $this->getSummaryFilename()));
492
+ file_put_contents($this->getSummaryFilename(), json_encode($this->feedSummary));
493
+ $this->log('done generating data feed files, going to remove lock files.');
494
+ Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
495
+ $this->log('all done, goodbye');
496
+
497
  }
498
 
499
+ public function getSummaryFilename() {
500
+ return sprintf('%s%s%s', Mage::helper('hawksearch_datafeed/feed')->getFeedFilePath(), DS, "hawksearchFeedSummary.json");
501
+ }
 
 
 
 
 
502
 
503
+ public function setFeedFolder(Mage_Core_Model_Store $store) {
504
+ /*
505
+ * here we need to set up the environment for generating the feed files in the proper folder,
506
+ * as well as preparing the json data file. the folder name will be named with the store code
507
+ * the json data file will contain only basic data about the subfolders (just the name for now.)
508
+ */
509
+ $this->feedSummary->stores[] = $store->getCode();
510
 
511
+ }
 
512
 
513
+ public function refreshImageCache() {
514
+ $this->log('starting refreshImageCache()');
 
 
515
 
516
+ $selectedStores = Mage::helper('hawksearch_datafeed')->getSelectedStores();
517
+ /** @var Mage_Core_Model_Resource_Store_Collection $stores */
518
+ $stores = Mage::getModel('core/store')->getCollection();
519
+ $stores->addIdFilter($selectedStores);
520
+ /** @var Mage_Core_Model_Store $store */
521
+ foreach ($stores as $store) {
522
+ try {
523
+ $this->log(sprintf('Starting environment for store %s', $store->getName()));
524
+ $appEmulation = Mage::getSingleton('core/app_emulation');
525
+ $initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($store->getId());
526
+
527
+ $products = Mage::getModel('catalog/product')
528
+ ->getCollection()
529
+ ->addAttributeToSelect(array('small_image'))
530
+ ->addStoreFilter($store);
531
+ $products->setPageSize($this->_batchSize);
532
+ $pages = $products->getLastPageNumber();
533
+
534
+ $currentPage = 1;
535
+
536
+ do {
537
+ $this->log(sprintf('going to page %d of images', $currentPage));
538
+ $products->clear();
539
+ $products->setCurPage($currentPage);
540
+ $products->load();
541
+
542
+ foreach ($products as $product) {
543
+ if (empty($this->_imageHeight)) {
544
+ $this->log(
545
+ sprintf('going to resize image for url: %s',
546
+ Mage::helper('catalog/image')->init($product, 'small_image')->resize($this->_imageWidth
547
+ )));
548
+ } else {
549
+ $this->log(
550
+ sprintf('going to resize image for url: %s',
551
+ Mage::helper('catalog/image')->init($product, 'small_image')->resize($this->_imageWidth, $this->_imageHeight
552
+ )));
553
+ }
554
+ }
555
 
556
+ $currentPage++;
 
 
 
 
557
 
558
+ } while ($currentPage <= $pages);
 
 
 
 
 
 
 
 
 
 
 
 
559
 
560
+ // end emulation
561
+ $appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
562
 
563
+ } catch (Exception $e) {
564
+ $this->log(sprintf("General Exception %s at generateFeed() line %d, stack:\n%s", $e->getMessage(), $e->getLine(), $e->getTraceAsString()));
565
+ }
566
 
567
+ }
 
568
  Mage::helper('hawksearch_datafeed/feed')->RemoveFeedLocks();
569
+ $this->log('Done generating image cache for selected stores, goodbye');
570
  }
571
 
572
  /**
641
  if (!$this->outputOpen) {
642
  $this->openOutput();
643
  }
644
+ foreach ($fields as $k => $f) {
645
+ $fields[$k] = strtr($f, array('\"' => '"'));
646
+ }
647
  if (false === fputcsv($this->outputFile, $fields, $this->delimiter)) {
648
  throw new Exception("CsvWriter: failed to write row.");
649
  }
app/code/community/Hawksearch/Datafeed/Model/System/Config/Backend/Cron.php CHANGED
@@ -13,65 +13,23 @@ class Hawksearch_Datafeed_Model_System_Config_Backend_Cron extends Mage_Core_Mod
13
  const CRON_STRING_PATH = 'crontab/jobs/hawksearch_datafeed/schedule/cron_expr';
14
  const CRON_MODEL_PATH = 'crontab/jobs/hawksearch_datafeed/run/model';
15
 
16
- const XML_PATH_CRON_DISABLED = 'groups/cron/fields/disabled/value';
17
- const XML_PATH_CRON_TIME = 'groups/cron/fields/time/value';
18
- const XML_PATH_CRON_FREQUENCY = 'groups/cron/fields/frequency/value';
19
-
20
- /**
21
- * When frequency system configuration saves, save the values from the frequency and time as a cron string to a parsable path that the crontab will pick up
22
- */
23
- protected function _afterSave()
24
- {
25
- $disabled = $this->getData(self::XML_PATH_CRON_DISABLED);
26
- $frequncy = $this->getData(self::XML_PATH_CRON_FREQUENCY);
27
- $time = $this->getData(self::XML_PATH_CRON_TIME);
28
-
29
- $frequencyDaily = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_DAILY;
30
- $frequencyWeekly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_WEEKLY;
31
- $frequencyMonthly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_MONTHLY;
32
- $cronDayOfWeek = date('N');
33
-
34
- $cronExprArray = array(
35
- intval($time[1]), # Minute
36
- intval($time[0]), # Hour
37
- ($frequncy == $frequencyMonthly) ? '1' : '*', # Day of the Month
38
- '*', # Month of the Year
39
- ($frequncy == $frequencyWeekly) ? '1' : '*', # Day of the Week
40
- );
41
-
42
- $cronExprString = join(' ', $cronExprArray);
43
-
44
- try {
45
- Mage::getModel('core/config_data')
46
- ->load(self::CRON_STRING_PATH, 'path')
47
- ->setValue($cronExprString)
48
- ->setPath(self::CRON_STRING_PATH)
49
- ->save();
50
- Mage::getModel('core/config_data')
51
- ->load(self::CRON_MODEL_PATH, 'path')
52
- ->setValue((string) Mage::getConfig()->getNode(self::CRON_MODEL_PATH))
53
- ->setPath(self::CRON_MODEL_PATH)
54
- ->save();
55
- } catch (Exception $e) {
56
- throw new Exception(Mage::helper('cron')->__('Unable to save the cron expression.'));
57
- }
58
- }
59
-
60
-
61
- /**
62
- * Saves the necessary core config data entries for the cron to pull them from the database
63
- */
64
- public function saveCronTab($cronTab) {
65
- Mage::getModel('core/config_data')
66
- ->load(self::CRON_STRING_PATH, 'path')
67
- ->setValue($cronTab)
68
- ->setPath(self::CRON_STRING_PATH)
69
- ->save();
70
- Mage::getModel('core/config_data')
71
- ->load(self::CRON_MODEL_PATH, 'path')
72
- ->setValue((string) Mage::getConfig()->getNode(self::CRON_MODEL_PATH))
73
- ->setPath(self::CRON_MODEL_PATH)
74
- ->save();
75
- }
76
-
77
  }
13
  const CRON_STRING_PATH = 'crontab/jobs/hawksearch_datafeed/schedule/cron_expr';
14
  const CRON_MODEL_PATH = 'crontab/jobs/hawksearch_datafeed/run/model';
15
 
16
+ protected function _afterSave(){
17
+ try {
18
+ // hawksearch_datafeed/feed/cron_string
19
+ // hawksearch_datafeed/imagecache/cron_string
20
+ Mage::getModel('core/config_data')
21
+ ->load(self::CRON_STRING_PATH, 'path')
22
+ ->setValue(trim($this->getValue()))
23
+ ->setPath(self::CRON_STRING_PATH)
24
+ ->save();
25
+ Mage::getModel('core/config_data')
26
+ ->load(self::CRON_MODEL_PATH, 'path')
27
+ ->setValue((string) Mage::getConfig()->getNode(self::CRON_MODEL_PATH))
28
+ ->setPath(self::CRON_MODEL_PATH)
29
+ ->save();
30
+ } catch (Exception $e) {
31
+ throw new Exception(Mage::helper('cron')->__('Unable to save the cron expression.'));
32
+ }
33
+
34
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
app/code/community/Hawksearch/Datafeed/Model/System/Config/Source/Store.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ /**
5
+ * Used in creating options for Yes|No config value selection
6
+ *
7
+ */
8
+ //class Mage_Adminhtml_Model_System_Config_Source_Yesno
9
+ class Hawksearch_Datafeed_Model_System_Config_Source_Store
10
+ {
11
+
12
+ /**
13
+ * Options getter
14
+ *
15
+ * @return array
16
+ */
17
+ public function toOptionArray()
18
+ {
19
+ return Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(false, false);
20
+ }
21
+
22
+ /**
23
+ * Get options in "key-value" format
24
+ *
25
+ * @return array
26
+ */
27
+
28
+ }
app/code/community/Hawksearch/Datafeed/controllers/Adminhtml/HawkdatagenerateController.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: magentouser
5
+ * Date: 3/11/15
6
+ * Time: 1:47 PM
7
+ */
8
+
9
+ class Hawksearch_Datafeed_Adminhtml_HawkdatagenerateController
10
+ extends Mage_Adminhtml_Controller_Action {
11
+
12
+ public function validateCronStringAction() {
13
+ $response = array('valid' => false);
14
+
15
+ $cron = Mage::getSingleton('hawksearch_datafeed/cron');
16
+ if($cron->isValidCronString($this->getRequest()->getParam('cronString'))){
17
+ $response['valid'] = true;
18
+ }
19
+ $this->getResponse()
20
+ ->setHeader('Content-Type', 'application/json')
21
+ ->setBody(json_encode($response));
22
+ }
23
+
24
+ public function runFeedGenerationAction() {
25
+ $response = array('error' => false);
26
+ try {
27
+ $disabledFuncs = explode(',', ini_get('disable_functions'));
28
+ $isShellDisabled = is_array($disabledFuncs) ? in_array('shell_exec', $disabledFuncs) : true;
29
+ $isShellDisabled = (stripos(PHP_OS, 'win') === false) ? $isShellDisabled : true;
30
+
31
+ if($isShellDisabled) {
32
+ $response['error'] = 'This installation cannot run one off feed generation because the PHP function "shell_exec" has been disabled. Please use cron.';
33
+ } else {
34
+ $helper = Mage::helper('hawksearch_datafeed/feed');
35
+ if(strtolower($this->getRequest()->getParam('force')) == 'true') {
36
+ $helper->RemoveFeedLocks();
37
+ }
38
+ $helper->generateFeedsForAllStores();
39
+ }
40
+ }
41
+ catch (Exception $e) {
42
+ Mage::logException($e);
43
+ $response['error'] = "An unknown error occurred.";
44
+ }
45
+ $this->getResponse()
46
+ ->setHeader("Content-Type", "application/json")
47
+ ->setBody(json_encode($response));
48
+ }
49
+ /**
50
+ * Refreshes image cache.
51
+ */
52
+ public function runImageCacheGenerationAction() {
53
+ $response = array("error" => false);
54
+ try {
55
+ $disabledFuncs = explode(',', ini_get('disable_functions'));
56
+ $isShellDisabled = is_array($disabledFuncs) ? in_array('shell_exec', $disabledFuncs) : true;
57
+ $isShellDisabled = (stripos(PHP_OS, 'win') === false) ? $isShellDisabled : true;
58
+
59
+ if($isShellDisabled) {
60
+ $response['error'] = 'This installation cannot run one-off cache generations. Must use cron.';
61
+ } else {
62
+ $helper = Mage::helper('hawksearch_datafeed/feed');
63
+ if(strtolower($this->getRequest()->getParam('force')) == 'true') {
64
+ $helper->RemoveFeedLocks();
65
+ }
66
+ $helper->refreshImageCache();
67
+ }
68
+ }
69
+ catch (Exception $e) {
70
+ Mage::logException($e);
71
+ $response['error'] = "An unknown error occurred.";
72
+ }
73
+ $this->getResponse()
74
+ ->setHeader("Content-Type", "application/json")
75
+ ->setBody(json_encode($response));
76
+ }
77
+
78
+ }
79
+
app/code/community/Hawksearch/Datafeed/controllers/SearchController.php CHANGED
@@ -9,18 +9,18 @@
9
  */
10
 
11
  class Hawksearch_Datafeed_SearchController extends Mage_Core_Controller_Front_Action {
12
-
13
- public function templateAction() {
14
- $this->loadLayout();
15
- $this->renderLayout();
16
- }
17
-
18
  /**
19
- * API Call for Image CacheKey to get images from cache on auto resize.
20
- */
21
- public function getCacheKeyAction() {
22
  $response = array("error" => false);
23
- try {
24
  /** @var Mage_Catalog_Model_Resource_Product_Collection $coll */
25
  $coll = Mage::getModel('catalog/product')->getCollection();
26
  $coll->addAttributeToSelect('small_image');
@@ -29,79 +29,23 @@ class Hawksearch_Datafeed_SearchController extends Mage_Core_Controller_Front_Ac
29
  ));
30
  $coll->getSelect()->limit(100);
31
  $item = $coll->getLastItem();
32
- $path = (string) Mage::helper('catalog/image')->init($item, 'small_image');
33
  $imageArray = explode("/", $path);
34
  $cache_key = "";
35
- foreach($imageArray as $part) {
36
- if(preg_match('/[0-9a-fA-F]{32}/', $part)) {
37
  $cache_key = $part;
38
  }
39
  }
40
-
41
  $response['cache_key'] = $cache_key;
42
  $response['date_time'] = date('Y-m-d H:i:s');
43
- }
44
- catch (Exception $e) {
45
- $response['error'] = $e->getMessage();
46
- }
47
- $this->getResponse()
48
- ->setHeader("Content-Type", "application/json")
49
- ->setBody(json_encode($response));
50
- }
51
-
52
- /**
53
- * Asynchronous posting to feed generation url for each store.
54
- */
55
- public function runFeedGenerationAction() {
56
- $response = array("error" => false);
57
-
58
- try {
59
- $disabledFuncs = explode(',', ini_get('disable_functions'));
60
- $isShellDisabled = is_array($disabledFuncs) ? in_array('shell_exec', $disabledFuncs) : true;
61
- $isShellDisabled = (stripos(PHP_OS, 'win') === false) ? $isShellDisabled : true;
62
-
63
- if($isShellDisabled) {
64
- $response['error'] = 'This installation cannot run one off feed generations. Must use cron.';
65
- } else {
66
- Mage::helper('hawksearch_datafeed/feed')->generateFeedsForAllStores();
67
- }
68
- }
69
- catch (Exception $e) {
70
- Mage::logException($e);
71
- $response['error'] = "An unknown error occurred.";
72
- }
73
- $this->getResponse()
74
- ->setHeader("Content-Type", "application/json")
75
- ->setBody(json_encode($response));
76
- }
77
-
78
- /**
79
- * Refreshes image cache based on passed in store id. Defaults store id to default store
80
- */
81
- public function runImageCacheGenerationAction() {
82
- $response = array("error" => false);
83
- try {
84
- $storeId = $this->getRequest()->getParam("storeId");
85
-
86
- if (!$storeId) {
87
- $storeId = Mage::app()->getDefaultStoreView()->getId();
88
- }
89
- $disabledFuncs = explode(',', ini_get('disable_functions'));
90
- $isShellDisabled = is_array($disabledFuncs) ? in_array('shell_exec', $disabledFuncs) : true;
91
- $isShellDisabled = (stripos(PHP_OS, 'win') === false) ? $isShellDisabled : true;
92
-
93
- if($isShellDisabled) {
94
- $response['error'] = 'This installation cannot run one-off cache generations. Must use cron.';
95
- } else {
96
- Mage::helper('hawksearch_datafeed/feed')->refreshImageCache($storeId);
97
- }
98
- }
99
- catch (Exception $e) {
100
- Mage::logException($e);
101
- $response['error'] = "An unknown error occurred.";
102
- }
103
  $this->getResponse()
104
  ->setHeader("Content-Type", "application/json")
105
  ->setBody(json_encode($response));
106
- }
 
107
  }
9
  */
10
 
11
  class Hawksearch_Datafeed_SearchController extends Mage_Core_Controller_Front_Action {
12
+
13
+ public function templateAction() {
14
+ $this->loadLayout();
15
+ $this->renderLayout();
16
+ }
17
+
18
  /**
19
+ * API Call for Image CacheKey to get images from cache on auto resize.
20
+ */
21
+ public function getCacheKeyAction() {
22
  $response = array("error" => false);
23
+ try {
24
  /** @var Mage_Catalog_Model_Resource_Product_Collection $coll */
25
  $coll = Mage::getModel('catalog/product')->getCollection();
26
  $coll->addAttributeToSelect('small_image');
29
  ));
30
  $coll->getSelect()->limit(100);
31
  $item = $coll->getLastItem();
32
+ $path = (string)Mage::helper('catalog/image')->init($item, 'small_image');
33
  $imageArray = explode("/", $path);
34
  $cache_key = "";
35
+ foreach ($imageArray as $part) {
36
+ if (preg_match('/[0-9a-fA-F]{32}/', $part)) {
37
  $cache_key = $part;
38
  }
39
  }
40
+
41
  $response['cache_key'] = $cache_key;
42
  $response['date_time'] = date('Y-m-d H:i:s');
43
+ } catch (Exception $e) {
44
+ $response['error'] = $e->getMessage();
45
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  $this->getResponse()
47
  ->setHeader("Content-Type", "application/json")
48
  ->setBody(json_encode($response));
49
+ }
50
+
51
  }
app/code/community/Hawksearch/Datafeed/etc/config.xml CHANGED
@@ -1,86 +1,102 @@
1
  <?xml version="1.0"?>
2
- <!--
3
  /**
4
- * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
5
- *
6
- * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
7
- * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
8
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9
- * PARTICULAR PURPOSE.
10
- */
11
- -->
12
  <config>
13
- <modules>
14
- <Hawksearch_Datafeed>
15
- <version>1.0.3.1</version>
16
- </Hawksearch_Datafeed>
17
- </modules>
18
- <global>
19
- <blocks>
20
- <hawksearch_datafeed>
21
- <class>Hawksearch_Datafeed_Block</class>
22
- </hawksearch_datafeed>
23
- </blocks>
24
- <helpers>
25
- <hawksearch_datafeed>
26
- <class>Hawksearch_Datafeed_Helper</class>
27
- </hawksearch_datafeed>
28
- </helpers>
29
- <models>
30
- <hawksearch_datafeed>
31
- <class>Hawksearch_Datafeed_Model</class>
32
- </hawksearch_datafeed>
33
- </models>
34
- <resources>
35
- <hawksearch_datafeed_setup>
36
- <setup>
37
- <module>Hawksearch_Datafeed</module>
38
- </setup>
39
- </hawksearch_datafeed_setup>
40
- </resources>
41
- </global>
42
- <frontend>
43
- <routers>
44
- <hawksearch_datafeed>
45
- <use>standard</use>
46
- <args>
47
- <module>Hawksearch_Datafeed</module>
48
- <frontName>hawksearch</frontName>
49
- </args>
50
- </hawksearch_datafeed>
51
- </routers>
52
- </frontend>
53
- <default>
54
- <hawksearch_datafeed>
55
- <general>
56
- <enabled>1</enabled>
57
- <logging_enabled>1</logging_enabled>
58
- </general>
59
- <feed>
60
- <batch_limit>10000</batch_limit>
61
- <image_width>135</image_width>
62
- <exclude_fields>description,short_description,options_container,gift_message_available,visibility,status,tax_class_id,required_options,msrp_display_actual_price_type,enable_googlecheckout,created_at,updated_at,sku</exclude_fields>
63
- <stockstatus>0</stockstatus>
64
- <itemstatus>0</itemstatus>
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  <delimiter>tab</delimiter>
66
  <buffer_size>65536</buffer_size>
67
  <output_file_ext>txt</output_file_ext>
68
- <feed_path>var/hawksearch/feeds</feed_path>
69
- </feed>
70
- <cron>
71
- <frequency backend_model="adminhtml/system_config_backend_product_alert_cron">every_5min</frequency>
72
- <disabled>0</disabled>
73
- <time>02,00,00</time>
74
- </cron>
75
- </hawksearch_datafeed>
76
- </default>
77
- <crontab>
78
- <jobs>
79
- <hawksearch_datafeed>
80
- <run>
81
- <model>hawksearch_datafeed/cron::generateFeeds</model>
82
- </run>
83
- </hawksearch_datafeed>
84
- </jobs>
85
- </crontab>
 
 
 
86
  </config>
1
  <?xml version="1.0"?>
2
+ <!--
3
  /**
4
+ * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
5
+ *
6
+ * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
7
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
8
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9
+ * PARTICULAR PURPOSE.
10
+ */
11
+ -->
12
  <config>
13
+ <modules>
14
+ <Hawksearch_Datafeed>
15
+ <version>1.1.1.6</version>
16
+ </Hawksearch_Datafeed>
17
+ </modules>
18
+ <global>
19
+ <blocks>
20
+ <hawksearch_datafeed>
21
+ <class>Hawksearch_Datafeed_Block</class>
22
+ </hawksearch_datafeed>
23
+ </blocks>
24
+ <helpers>
25
+ <hawksearch_datafeed>
26
+ <class>Hawksearch_Datafeed_Helper</class>
27
+ </hawksearch_datafeed>
28
+ </helpers>
29
+ <models>
30
+ <hawksearch_datafeed>
31
+ <class>Hawksearch_Datafeed_Model</class>
32
+ </hawksearch_datafeed>
33
+ </models>
34
+ <resources>
35
+ <hawksearch_datafeed_setup>
36
+ <setup>
37
+ <module>Hawksearch_Datafeed</module>
38
+ </setup>
39
+ </hawksearch_datafeed_setup>
40
+ </resources>
41
+ </global>
42
+ <admin>
43
+ <routers>
44
+ <adminhtml>
45
+ <args>
46
+ <modules>
47
+ <hawksearch_datafeed after="Mage_Adminhtml">Hawksearch_Datafeed_Adminhtml</hawksearch_datafeed>
48
+ </modules>
49
+ </args>
50
+ </adminhtml>
51
+ </routers>
52
+ </admin>
53
+ <!--the frontend router is used for the image cache key -->
54
+ <frontend>
55
+ <routers>
56
+ <hawksearch_datafeed>
57
+ <use>standard</use>
58
+ <args>
59
+ <module>Hawksearch_Datafeed</module>
60
+ <frontName>hawksearch</frontName>
61
+ </args>
62
+ </hawksearch_datafeed>
63
+ </routers>
64
+ </frontend>
65
+ <default>
66
+ <hawksearch_datafeed>
67
+ <general>
68
+ <enabled>1</enabled>
69
+ <logging_enabled>1</logging_enabled>
70
+ </general>
71
+ <feed>
72
+ <batch_limit>10000</batch_limit>
73
+ <exclude_fields>
74
+ description,short_description,options_container,gift_message_available,status,tax_class_id,required_options,msrp_display_actual_price_type,enable_googlecheckout,created_at,updated_at,sku
75
+ </exclude_fields>
76
+ <stockstatus>0</stockstatus>
77
+ <itemstatus>0</itemstatus>
78
  <delimiter>tab</delimiter>
79
  <buffer_size>65536</buffer_size>
80
  <output_file_ext>txt</output_file_ext>
81
+ <feed_path>hawksearch/feeds</feed_path>
82
+ </feed>
83
+ <imagecache>
84
+ <image_width>135</image_width>
85
+ </imagecache>
86
+ </hawksearch_datafeed>
87
+ <crontab>
88
+ <jobs>
89
+ <hawksearch_datafeed_feed>
90
+ <run>
91
+ <model>hawksearch_datafeed/cron::generateFeeds</model>
92
+ </run>
93
+ </hawksearch_datafeed_feed>
94
+ <hawksearch_datafeed_imagecache>
95
+ <run>
96
+ <model>hawksearch_datafeed/cron::generateImagecache</model>
97
+ </run>
98
+ </hawksearch_datafeed_imagecache>
99
+ </jobs>
100
+ </crontab>
101
+ </default>
102
  </config>
app/code/community/Hawksearch/Datafeed/etc/system.xml CHANGED
@@ -40,27 +40,27 @@
40
  <source_model>adminhtml/system_config_source_yesno</source_model>
41
  <sort_order>100</sort_order>
42
  <show_in_default>1</show_in_default>
43
- <show_in_website>1</show_in_website>
44
- <show_in_store>1</show_in_store>
45
- </enabled>
46
  <logging_enabled>
47
- <label>Enabled Logging</label>
48
  <frontend_type>select</frontend_type>
49
  <source_model>adminhtml/system_config_source_yesno</source_model>
50
- <sort_order>200</sort_order>
51
  <show_in_default>1</show_in_default>
52
- <show_in_website>1</show_in_website>
53
- <show_in_store>1</show_in_store>
54
- </logging_enabled>
55
  <version translate="label">
56
  <label>Version</label>
57
  <frontend_type>label</frontend_type>
58
  <frontend_model>hawksearch_datafeed/system_config_form_field_version</frontend_model>
59
- <sort_order>300</sort_order>
60
  <show_in_default>1</show_in_default>
61
- <show_in_website>1</show_in_website>
62
- <show_in_store>1</show_in_store>
63
- </version>
64
  </fields>
65
  </general>
66
  <feed translate="label" module="hawksearch_datafeed">
@@ -81,154 +81,162 @@
81
  <show_in_website>0</show_in_website>
82
  <show_in_store>0</show_in_store>
83
  </generate>
84
- <generateimagecache translate="label">
85
- <label>Generate Image Cache</label>
86
- <frontend_type>button</frontend_type>
87
- <frontend_model>hawksearch_datafeed/system_config_frontend_feed_generateimagecache</frontend_model>
88
- <comment>Manually generates image cache.</comment>
89
- <sort_order>150</sort_order>
90
- <show_in_default>1</show_in_default>
91
- <show_in_website>0</show_in_website>
92
- <show_in_store>0</show_in_store>
93
- </generateimagecache>
94
- <submittal translate="label">
95
- <label>Next Automatic Feed Generation</label>
96
- <frontend_type>text</frontend_type>
97
- <frontend_model>hawksearch_datafeed/system_config_frontend_feed_next</frontend_model>
98
- <sort_order>200</sort_order>
99
  <show_in_default>1</show_in_default>
100
  <show_in_website>0</show_in_website>
101
  <show_in_store>0</show_in_store>
102
- </submittal>
103
  <batch_limit translate="label">
104
  <label>Batch Limit</label>
105
  <frontend_type>text</frontend_type>
106
  <comment>Set the block of blocks to batch at once</comment>
107
- <sort_order>300</sort_order>
108
  <show_in_default>1</show_in_default>
109
  <show_in_website>0</show_in_website>
110
  <show_in_store>0</show_in_store>
111
  </batch_limit>
112
- <image_width translate="label">
113
- <label>Image Width</label>
114
- <frontend_type>text</frontend_type>
115
- <comment>Set the width of the small image saved in the cache</comment>
116
- <sort_order>350</sort_order>
117
- <show_in_default>1</show_in_default>
118
- <show_in_website>0</show_in_website>
119
- <show_in_store>0</show_in_store>
120
- </image_width>
121
- <image_height translate="label">
122
- <label>Image Height</label>
123
- <frontend_type>text</frontend_type>
124
- <comment>Set the height of the small image saved in the cache</comment>
125
- <sort_order>375</sort_order>
126
- <show_in_default>1</show_in_default>
127
- <show_in_website>0</show_in_website>
128
- <show_in_store>0</show_in_store>
129
- </image_height>
130
- <brand_attribute translate="label">
131
- <label>Brand Attribute Value</label>
132
- <frontend_type>text</frontend_type>
133
- <comment>Set attribute_code value for the Brand attribute</comment>
134
- <sort_order>400</sort_order>
135
- <show_in_default>1</show_in_default>
136
- <show_in_website>0</show_in_website>
137
- <show_in_store>0</show_in_store>
138
- </brand_attribute>
139
- <exclude_fields translate="label">
140
- <label>Attributes to Exclude</label>
141
- <frontend_type>textarea</frontend_type>
142
- <comment>Comma Delimited List of attribute_code values to exclude</comment>
143
- <sort_order>500</sort_order>
144
- <show_in_default>1</show_in_default>
145
- <show_in_website>0</show_in_website>
146
- <show_in_store>0</show_in_store>
147
- </exclude_fields>
148
  <stockstatus translate="label">
149
  <label>Allow Out of Stock Items</label>
150
  <frontend_type>select</frontend_type>
151
  <source_model>adminhtml/system_config_source_yesno</source_model>
152
- <sort_order>600</sort_order>
153
  <show_in_default>1</show_in_default>
154
- <show_in_website>1</show_in_website>
155
- <show_in_store>1</show_in_store>
156
  </stockstatus>
157
  <itemstatus>
158
  <label>Allow Disabled Items</label>
159
  <frontend_type>select</frontend_type>
160
  <source_model>adminhtml/system_config_source_yesno</source_model>
161
- <sort_order>700</sort_order>
162
  <show_in_default>1</show_in_default>
163
- <show_in_website>1</show_in_website>
164
- <show_in_store>1</show_in_store>
165
  </itemstatus>
166
- <optional_htaccess_user translate="label">
167
- <label>HTACCESS Username</label>
 
 
 
 
 
 
 
 
 
168
  <frontend_type>text</frontend_type>
169
- <comment>Optional Username setting for sites with .htaccess enabled. Leave blank if not in use</comment>
170
- <sort_order>800</sort_order>
 
 
 
171
  <show_in_default>1</show_in_default>
172
  <show_in_website>0</show_in_website>
173
  <show_in_store>0</show_in_store>
174
- </optional_htaccess_user>
175
- <optional_htaccess_password translate="label">
176
- <label>HTACCESS Password</label>
 
 
 
177
  <frontend_type>text</frontend_type>
178
- <comment>Optional Username setting for sites with .htaccess enabled. Leave blank if not in use</comment>
179
- <sort_order>900</sort_order>
180
  <show_in_default>1</show_in_default>
181
  <show_in_website>0</show_in_website>
182
  <show_in_store>0</show_in_store>
183
- </optional_htaccess_password>
 
 
 
184
  </fields>
185
  </feed>
186
- <cron translate="label" module="hawksearch_datafeed">
187
- <label>Cron Settings</label>
188
  <frontend_type>text</frontend_type>
189
- <sort_order>200</sort_order>
190
  <show_in_default>1</show_in_default>
191
  <show_in_website>0</show_in_website>
192
  <show_in_store>0</show_in_store>
193
  <fields>
194
- <email translate="label">
195
- <label>Cron Email</label>
196
- <comment>When cron runs, email to send notification to.</comment>
197
- <frontend_type>text</frontend_type>
 
 
198
  <sort_order>100</sort_order>
199
  <show_in_default>1</show_in_default>
200
- <show_in_website>1</show_in_website>
201
- <show_in_store>1</show_in_store>
202
- </email>
203
- <frequency translate="label">
204
- <label>Frequency</label>
205
- <frontend_type>select</frontend_type>
206
- <source_model>adminhtml/system_config_source_cron_frequency</source_model>
207
- <backend_model>hawksearch_datafeed/system_config_backend_cron</backend_model>
208
- <sort_order>200</sort_order>
209
  <show_in_default>1</show_in_default>
210
- <show_in_website>1</show_in_website>
211
- <show_in_store>1</show_in_store>
212
- </frequency>
213
- <time translate="label">
214
- <label>Start Time</label>
215
- <frontend_type>time</frontend_type>
216
- <sort_order>300</sort_order>
 
217
  <show_in_default>1</show_in_default>
218
- <show_in_website>1</show_in_website>
219
- <show_in_store>1</show_in_store>
220
- </time>
221
- <disabled translate="label">
222
- <label>Disabled</label>
223
  <frontend_type>select</frontend_type>
224
  <source_model>adminhtml/system_config_source_yesno</source_model>
225
- <sort_order>400</sort_order>
 
 
 
 
 
 
 
 
 
 
 
 
226
  <show_in_default>1</show_in_default>
227
  <show_in_website>1</show_in_website>
228
  <show_in_store>1</show_in_store>
229
- </disabled>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </fields>
231
- </cron>
232
  </groups>
233
  </hawksearch_datafeed>
234
  </sections>
40
  <source_model>adminhtml/system_config_source_yesno</source_model>
41
  <sort_order>100</sort_order>
42
  <show_in_default>1</show_in_default>
43
+ <show_in_website>0</show_in_website>
44
+ <show_in_store>0</show_in_store>
45
+ </enabled>
46
  <logging_enabled>
47
+ <label>Enable Logging</label>
48
  <frontend_type>select</frontend_type>
49
  <source_model>adminhtml/system_config_source_yesno</source_model>
50
+ <sort_order>110</sort_order>
51
  <show_in_default>1</show_in_default>
52
+ <show_in_website>0</show_in_website>
53
+ <show_in_store>0</show_in_store>
54
+ </logging_enabled>
55
  <version translate="label">
56
  <label>Version</label>
57
  <frontend_type>label</frontend_type>
58
  <frontend_model>hawksearch_datafeed/system_config_form_field_version</frontend_model>
59
+ <sort_order>130</sort_order>
60
  <show_in_default>1</show_in_default>
61
+ <show_in_website>0</show_in_website>
62
+ <show_in_store>0</show_in_store>
63
+ </version>
64
  </fields>
65
  </general>
66
  <feed translate="label" module="hawksearch_datafeed">
81
  <show_in_website>0</show_in_website>
82
  <show_in_store>0</show_in_store>
83
  </generate>
84
+ <stores>
85
+ <label>Feed Stores</label>
86
+ <frontend_type>multiselect</frontend_type>
87
+ <source_model>hawksearch_datafeed/system_config_source_store</source_model>
88
+ <sort_order>110</sort_order>
 
 
 
 
 
 
 
 
 
 
89
  <show_in_default>1</show_in_default>
90
  <show_in_website>0</show_in_website>
91
  <show_in_store>0</show_in_store>
92
+ </stores>
93
  <batch_limit translate="label">
94
  <label>Batch Limit</label>
95
  <frontend_type>text</frontend_type>
96
  <comment>Set the block of blocks to batch at once</comment>
97
+ <sort_order>120</sort_order>
98
  <show_in_default>1</show_in_default>
99
  <show_in_website>0</show_in_website>
100
  <show_in_store>0</show_in_store>
101
  </batch_limit>
102
+ <!--<exclude_fields translate="label">-->
103
+ <!--<label>Attributes to Exclude</label>-->
104
+ <!--<frontend_type>textarea</frontend_type>-->
105
+ <!--<comment>Comma Delimited List of attribute_code values to exclude</comment>-->
106
+ <!--<sort_order>130</sort_order>-->
107
+ <!--<show_in_default>1</show_in_default>-->
108
+ <!--<show_in_website>0</show_in_website>-->
109
+ <!--<show_in_store>0</show_in_store>-->
110
+ <!--</exclude_fields>-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  <stockstatus translate="label">
112
  <label>Allow Out of Stock Items</label>
113
  <frontend_type>select</frontend_type>
114
  <source_model>adminhtml/system_config_source_yesno</source_model>
115
+ <sort_order>140</sort_order>
116
  <show_in_default>1</show_in_default>
117
+ <show_in_website>0</show_in_website>
118
+ <show_in_store>0</show_in_store>
119
  </stockstatus>
120
  <itemstatus>
121
  <label>Allow Disabled Items</label>
122
  <frontend_type>select</frontend_type>
123
  <source_model>adminhtml/system_config_source_yesno</source_model>
124
+ <sort_order>150</sort_order>
125
  <show_in_default>1</show_in_default>
126
+ <show_in_website>0</show_in_website>
127
+ <show_in_store>0</show_in_store>
128
  </itemstatus>
129
+ <cron_enable>
130
+ <label>Enable Datafeed Cron task</label>
131
+ <frontend_type>select</frontend_type>
132
+ <source_model>adminhtml/system_config_source_yesno</source_model>
133
+ <sort_order>160</sort_order>
134
+ <show_in_default>1</show_in_default>
135
+ <show_in_website>0</show_in_website>
136
+ <show_in_store>0</show_in_store>
137
+ </cron_enable>
138
+ <cron_string translate="label">
139
+ <label>Cron expression</label>
140
  <frontend_type>text</frontend_type>
141
+ <frontend_model>hawksearch_datafeed/system_config_frontend_feed_cron</frontend_model>
142
+ <backend_model>hawksearch_datafeed/system_config_backend_cron</backend_model>
143
+ <comment>Max run interval is hourly, first value must be between 0 and 59</comment>
144
+ <validate>validate-cron-ajax</validate>
145
+ <sort_order>170</sort_order>
146
  <show_in_default>1</show_in_default>
147
  <show_in_website>0</show_in_website>
148
  <show_in_store>0</show_in_store>
149
+ <depends>
150
+ <cron_enable>1</cron_enable>
151
+ </depends>
152
+ </cron_string>
153
+ <cron_email>
154
+ <label>Feed Cron Email</label>
155
  <frontend_type>text</frontend_type>
156
+ <sort_order>180</sort_order>
 
157
  <show_in_default>1</show_in_default>
158
  <show_in_website>0</show_in_website>
159
  <show_in_store>0</show_in_store>
160
+ <depends>
161
+ <cron_enable>1</cron_enable>
162
+ </depends>
163
+ </cron_email>
164
  </fields>
165
  </feed>
166
+ <imagecache translate="label" module="hawksearch_datafeed">
167
+ <label>Image Cache</label>
168
  <frontend_type>text</frontend_type>
169
+ <sort_order>300</sort_order>
170
  <show_in_default>1</show_in_default>
171
  <show_in_website>0</show_in_website>
172
  <show_in_store>0</show_in_store>
173
  <fields>
174
+ <generateimagecache translate="label">
175
+ <label>Generate Image Cache</label>
176
+ <frontend_type>button</frontend_type>
177
+ <frontend_model>hawksearch_datafeed/system_config_frontend_feed_generateimagecache
178
+ </frontend_model>
179
+ <comment>Manually generates image cache.</comment>
180
  <sort_order>100</sort_order>
181
  <show_in_default>1</show_in_default>
182
+ <show_in_website>0</show_in_website>
183
+ <show_in_store>0</show_in_store>
184
+ </generateimagecache>
185
+ <image_width translate="label">
186
+ <label>Image Width</label>
187
+ <frontend_type>text</frontend_type>
188
+ <comment>Set the width of the small image saved in the cache</comment>
189
+ <sort_order>110</sort_order>
 
190
  <show_in_default>1</show_in_default>
191
+ <show_in_website>0</show_in_website>
192
+ <show_in_store>0</show_in_store>
193
+ </image_width>
194
+ <image_height translate="label">
195
+ <label>Image Height</label>
196
+ <frontend_type>text</frontend_type>
197
+ <comment>Set the height of the small image saved in the cache</comment>
198
+ <sort_order>120</sort_order>
199
  <show_in_default>1</show_in_default>
200
+ <show_in_website>0</show_in_website>
201
+ <show_in_store>0</show_in_store>
202
+ </image_height>
203
+ <cron_enable>
204
+ <label>Enable Imagecache Cron task</label>
205
  <frontend_type>select</frontend_type>
206
  <source_model>adminhtml/system_config_source_yesno</source_model>
207
+ <sort_order>130</sort_order>
208
+ <show_in_default>1</show_in_default>
209
+ <show_in_website>0</show_in_website>
210
+ <show_in_store>0</show_in_store>
211
+ </cron_enable>
212
+ <cron_string translate="label">
213
+ <label>Cron expression</label>
214
+ <frontend_type>text</frontend_type>
215
+ <frontend_model>hawksearch_datafeed/system_config_frontend_feed_cron</frontend_model>
216
+ <backend_model>hawksearch_datafeed/system_config_backend_cron</backend_model>
217
+ <comment>Max run interval is hourly, first value must be between 0 and 59</comment>
218
+ <validate>validate-cron-ajax</validate>
219
+ <sort_order>140</sort_order>
220
  <show_in_default>1</show_in_default>
221
  <show_in_website>1</show_in_website>
222
  <show_in_store>1</show_in_store>
223
+ <depends>
224
+ <cron_enable>1</cron_enable>
225
+ </depends>
226
+ </cron_string>
227
+ <cron_email>
228
+ <label>Imagecache Cron Email</label>
229
+ <frontend_type>text</frontend_type>
230
+ <sort_order>150</sort_order>
231
+ <show_in_default>1</show_in_default>
232
+ <show_in_website>0</show_in_website>
233
+ <show_in_store>0</show_in_store>
234
+ <depends>
235
+ <cron_enable>1</cron_enable>
236
+ </depends>
237
+ </cron_email>
238
  </fields>
239
+ </imagecache>
240
  </groups>
241
  </hawksearch_datafeed>
242
  </sections>
app/code/community/Hawksearch/Datafeed/sql/hawksearch_datafeed_setup/install-1.0.0.0.php CHANGED
@@ -9,11 +9,13 @@
9
  */
10
 
11
  //Save cron job to the core config data table
 
 
12
 
13
- $frequency = Mage::getConfig()->getNode('default/hawksearch_datafeed/cron/frequency');
14
-
15
- $time = explode(",", Mage::getConfig()->getNode('default/hawksearch_datafeed/cron/time'));
16
-
17
- $cronTab = Mage::helper('hawksearch_datafeed')->getCronTimeAsCrontab($frequency, $time);
18
-
19
- Mage::getModel("hawksearch_datafeed/system_config_backend_cron")->saveCronTab($cronTab);
9
  */
10
 
11
  //Save cron job to the core config data table
12
+ // the user should configure this. switched to a cron string for scheduling to allow
13
+ // multiple runs per day.
14
 
15
+ //$frequency = Mage::getConfig()->getNode('default/hawksearch_datafeed/cron/frequency');
16
+ //
17
+ //$time = explode(",", Mage::getConfig()->getNode('default/hawksearch_datafeed/cron/time'));
18
+ //
19
+ //$cronTab = Mage::helper('hawksearch_datafeed')->getCronTimeAsCrontab($frequency, $time);
20
+ //
21
+ //Mage::getModel("hawksearch_datafeed/system_config_backend_cron")->saveCronTab($cronTab);
app/code/community/Hawksearch/Datafeed/sql/hawksearch_datafeed_setup/upgrade-1.0.0.0-1.1.1.0.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: magentouser
5
+ * Date: 2/17/15
6
+ * Time: 2:01 PM
7
+ */
8
+
9
+ $installer = $this;
10
+
11
+ $installer->startSetup();
12
+
13
+ //$installer->getConnection()->modifyColumn(
14
+ // $this->getTable('table/name'), 'datetime', 'DATETIME'
15
+ //);
16
+ //
17
+ //
18
+ //
19
+ //$cfg = Mage::getModel('core/config_data')
20
+ // ->load('path/to/var', 'path');
21
+ //if($cfg->getId()) {
22
+ // $cfg->setPath('path/to/var')
23
+ // ->save();
24
+ //}
25
+
26
+
27
+ $installer->endSetup();
app/design/adminhtml/default/default/template/hawksearch/datafeed/generate/js.phtml CHANGED
@@ -16,15 +16,21 @@
16
  buttonId: null,
17
  init: function () {
18
  this.url = '<?php echo $this->getGenerateUrl() ?>';
19
- this.buttonId = '<?php echo $this->getButtonId()?>';
20
  this.displayId = "hawksearch_display_msg";
21
  this.isInit = true;
 
22
  },
23
  generateFeed: function () {
24
  if (!this.isInit) {
25
  this.init();
26
  }
 
 
 
 
27
  new Ajax.Request(this.url, {
 
28
  onSuccess: function (transport) {
29
  var response = transport.responseText.evalJSON();
30
  this.displayResults(response);
@@ -49,6 +55,19 @@
49
  $(this.buttonId).disabled = true;
50
  $(this.buttonId).addClassName("disabled");
51
  return responseEl.innerHTML = "<?php echo Mage::getModel('hawksearch_datafeed/feed')->getAjaxNotice(); ?>";
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }
53
  }
54
 
16
  buttonId: null,
17
  init: function () {
18
  this.url = '<?php echo $this->getGenerateUrl() ?>';
19
+ this.buttonId = '<?php echo $this->getButtonId() ?>';
20
  this.displayId = "hawksearch_display_msg";
21
  this.isInit = true;
22
+ this.forceId = 'feedforce';
23
  },
24
  generateFeed: function () {
25
  if (!this.isInit) {
26
  this.init();
27
  }
28
+ var isForce = false;
29
+ if($(this.forceId)){
30
+ isForce = $(this.forceId).checked;
31
+ }
32
  new Ajax.Request(this.url, {
33
+ parameters: {force: isForce},
34
  onSuccess: function (transport) {
35
  var response = transport.responseText.evalJSON();
36
  this.displayResults(response);
55
  $(this.buttonId).disabled = true;
56
  $(this.buttonId).addClassName("disabled");
57
  return responseEl.innerHTML = "<?php echo Mage::getModel('hawksearch_datafeed/feed')->getAjaxNotice(); ?>";
58
+ },
59
+ forceFeed: function() {
60
+ if (!this.isInit) {
61
+ this.init();
62
+ }
63
+ if($(this.forceId).checked && $(this.buttonId).disabled){
64
+ $(this.buttonId).disabled = false;
65
+ $(this.buttonId).removeClassName("disabled");
66
+ } else {
67
+ $(this.buttonId).disabled = true;
68
+ $(this.buttonId).addClassName("disabled");
69
+ }
70
+
71
  }
72
  }
73
 
app/design/adminhtml/default/default/template/hawksearch/datafeed/validate.phtml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 Hawksearch (www.hawksearch.com) - All Rights Reserved
4
+ *
5
+ * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
6
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
7
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
8
+ * PARTICULAR PURPOSE.
9
+ */
10
+ ?>
11
+ <script type="text/javascript">
12
+ //<![CDATA[
13
+ Validation.add('validate-cron-ajax', 'Cron string is not valid', function(v){
14
+ var status = false;
15
+ new Ajax.Request('<?php echo $this->getValidateUrl() ?>', {
16
+ asynchronous: false,
17
+ parameters: {cronString: v},
18
+ onSuccess: function(t) {
19
+ var res = t.responseText.evalJSON();
20
+ status = res.valid;
21
+ },
22
+ onFailure: function(t) {
23
+ alert('Unable to validate cron string, please refresh page and try again.');
24
+ status = false;
25
+ }
26
+ });
27
+ return status;
28
+ });
29
+ //]]>
30
+ </script>
hawksearch/.htaccess.sample ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ Order deny,allow
2
+ Deny from all
3
+ Allow from 127.0.0.1 68.72.70ZZ 12.133 76.227.222 12.198.148
package.xml CHANGED
@@ -1,18 +1,24 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>HawkSearch_Datafeed</name>
4
- <version>1.0.3.1</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/OSL-3.0">Open Software License (OSL)</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>This package generates data feeds in the required format and can be consumed by Hawk Search.</summary>
10
  <description>This package generates data feeds in the required format and can be consumed by Hawk Search.</description>
11
- <notes>Added debug logging, category url to hierarchy file.</notes>
 
 
 
 
 
 
12
  <authors><author><name>Hawksearch Inc.</name><user>hawksearch</user><email>mmunoz@thanxmedia.com</email></author></authors>
13
- <date>2015-02-20</date>
14
- <time>23:02:25</time>
15
- <contents><target name="magecommunity"><dir name="Hawksearch"><dir name="Datafeed"><dir name="Block"><dir name="System"><dir name="Config"><dir name="Form"><dir name="Field"><file name="Version.php" hash="7f72ce91b67d40e68424454a449f1841"/></dir></dir><file name="Form.php" hash="1f6eaf265d3b8c78d3efd93cf93c7f2d"/><dir name="Frontend"><dir name="Feed"><dir name="Clearts"><file name="Js.php" hash="220b75f831af835d97be401798ec9461"/></dir><dir name="Generate"><file name="Js.php" hash="7e3f96c837b6602759160d4bb92635bf"/></dir><file name="Generate.php" hash="c73b0b2bd965b5c4e4cded455f23baa1"/><dir name="Generateimagecache"><file name="Js.php" hash="25ff246f556d518bcb05198101ec0d7f"/></dir><file name="Generateimagecache.php" hash="05a3585804163326b33d3a46ab7717b5"/><file name="Next.php" hash="92a25b140bc4d0f3b0c7b497fdf0ca6d"/><file name="Timestamp.php" hash="b12e57c577b7c86b346e0221256de97c"/></dir></dir></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="38cddf31605b88ea36cc83fa0d3e1f5f"/><file name="Feed.php" hash="07044195f931f3c59895b3b9b4059607"/><file name="runfeed.php" hash="aa1d25bd6e86ba401a03b0b54885be7e"/></dir><dir name="Model"><file name="Cron.php" hash="ca8d435d3c2f99b09758cb7644a5d141"/><file name="Email.php" hash="f671df12afac28ae10713cd1972bef2c"/><file name="Feed.php" hash="1187bc430a764f6a8f7a2a23e75ab7ba"/><dir name="System"><dir name="Config"><dir name="Backend"><file name="Cron.php" hash="2919385264c00276cbf09c105418d5ed"/></dir></dir></dir></dir><dir name="controllers"><file name="SearchController.php" hash="624b4a3ed9baa851174a8708cb15305b"/></dir><dir name="etc"><file name="adminhtml.xml" hash="96daebeddbd749dfe0341818fe2eeabb"/><file name="config.xml" hash="5eec41febdddcd12be08659feea85bf2"/><file name="system.xml" hash="3b763bc3073ce575eedb1632efc988a3"/></dir><dir name="sql"><dir name="hawksearch_datafeed_setup"><file name="install-1.0.0.0.php" hash="768e75fd75bbd9532c92859346d78ef2"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="hawksearch"><dir name="datafeed"><dir name="clearts"><file name="js.phtml" hash="e277cac474bb3e0d84b47c82c76448ca"/></dir><dir name="generate"><file name="js.phtml" hash="bb03ad1f14206b2e8e5ee228ac0cb6bc"/></dir><dir name="generateimagecache"><file name="js.phtml" hash="aba0594893460ae5763ae243866e7e65"/></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Hawksearch_Datafeed.xml" hash="06b94625c6b60d1269fe61c4127d2146"/></dir></target></contents>
16
  <compatible/>
17
  <dependencies><required><php><min>5.1.0</min><max>8.1.0</max></php></required></dependencies>
18
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>HawkSearch_Datafeed</name>
4
+ <version>1.1.1.6</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/OSL-3.0">Open Software License (OSL)</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>This package generates data feeds in the required format and can be consumed by Hawk Search.</summary>
10
  <description>This package generates data feeds in the required format and can be consumed by Hawk Search.</description>
11
+ <notes>Adding multisite capabilities, including image cache generation.&#xD;
12
+ &#xD;
13
+ 1.1.1.3: Fixed issue with singletons stored in registry resulting in attributes from wrong store being returned in attributes.txt file.&#xD;
14
+ &#xD;
15
+ 1.1.1.4: Fixed issue with configurable product relationships in items.txt file. Moved "brand" attribute from items.txt to attributes.txt (if exists). &#xD;
16
+ &#xD;
17
+ 1.1.1.5: Fixed issue with image cache and items.txt not using "small_image". Explicit check to ensure 'unique id' is set in attributes.txt. </notes>
18
  <authors><author><name>Hawksearch Inc.</name><user>hawksearch</user><email>mmunoz@thanxmedia.com</email></author></authors>
19
+ <date>2015-08-20</date>
20
+ <time>21:43:58</time>
21
+ <contents><target name="magecommunity"><dir name="Hawksearch"><dir name="Datafeed"><dir name="Block"><dir name="System"><dir name="Config"><dir name="Form"><dir name="Field"><file name="Version.php" hash="7f72ce91b67d40e68424454a449f1841"/></dir></dir><file name="Form.php" hash="1f6eaf265d3b8c78d3efd93cf93c7f2d"/><dir name="Frontend"><dir name="Feed"><dir name="Clearts"><file name="Js.php" hash="220b75f831af835d97be401798ec9461"/></dir><dir name="Cron"><file name="Js.php" hash="55b49d830aca37d5c5ae11d96ec81cc1"/></dir><file name="Cron.php" hash="828841ead180fccb3c0af4fb5e7f5482"/><dir name="Generate"><file name="Js.php" hash="562dc37edc3260ff16fa07e773d17b4e"/></dir><file name="Generate.php" hash="cf9a8be5b02361c0eb22bd6300e48884"/><dir name="Generateimagecache"><file name="Js.php" hash="fff68a4778f41d6fde39cc4c62b55833"/></dir><file name="Generateimagecache.php" hash="05a3585804163326b33d3a46ab7717b5"/><file name="Next.php" hash="b325b32c2338835a3d05b07cb04966f5"/><file name="Timestamp.php" hash="b12e57c577b7c86b346e0221256de97c"/></dir></dir></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="7ed312d82b25b202dcc2b109ec071495"/><file name="Feed.php" hash="8759e6b295d8fc8f6a55bc5c71379288"/><file name="runfeed.php" hash="58151e345c8749d26bd41b6c61ba84ea"/></dir><dir name="Model"><file name="Cron.php" hash="6115476d7240750803094e6c32d39e27"/><file name="Email.php" hash="f671df12afac28ae10713cd1972bef2c"/><file name="Feed.php" hash="18e74277ba1ba3f2673fb62eb3bc427d"/><dir name="System"><dir name="Config"><dir name="Backend"><file name="Cron.php" hash="03dd40965da119312e5b6eac10f54e75"/></dir><dir name="Source"><file name="Store.php" hash="e72e5e9b13edc13545cdc14e909ffd40"/></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="HawkdatagenerateController.php" hash="57b6b83d727796b9dfc4ca2a6c056ee7"/></dir><file name="SearchController.php" hash="89dc3d5c56282a865aedc435a6f61405"/></dir><dir name="etc"><file name="adminhtml.xml" hash="96daebeddbd749dfe0341818fe2eeabb"/><file name="config.xml" hash="6cc2bd7485c767678c344e704f881360"/><file name="system.xml" hash="c33a25e888174cef61d5b412115a0f3d"/></dir><dir name="sql"><dir name="hawksearch_datafeed_setup"><file name="install-1.0.0.0.php" hash="22abdf6ab3bda0df41795ac5b22f3347"/><file name="upgrade-1.0.0.0-1.1.1.0.php" hash="a39dd9bb5a4c3b4a5e5e45fe99ac4f85"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="hawksearch"><dir name="datafeed"><dir name="clearts"><file name="js.phtml" hash="e277cac474bb3e0d84b47c82c76448ca"/></dir><dir name="generate"><file name="js.phtml" hash="be53e356516c4c39521aaeee89bac1f4"/></dir><dir name="generateimagecache"><file name="js.phtml" hash="aba0594893460ae5763ae243866e7e65"/></dir><file name="validate.phtml" hash="4a7241fe598dfbf2b4abff50ded761bc"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Hawksearch_Datafeed.xml" hash="06b94625c6b60d1269fe61c4127d2146"/></dir></target><target name="mageweb"><dir><dir name="hawksearch"><file name=".htaccess.sample" hash="d664f8a18ccd0e491376a78fa63ce6f2"/></dir></dir></target></contents>
22
  <compatible/>
23
  <dependencies><required><php><min>5.1.0</min><max>8.1.0</max></php></required></dependencies>
24
  </package>