Hellodialog_Tracker - Version 2.0.0

Version Notes

This plugin connects your Magento shop to Hellodialog. Your customers will automatically be subscribed to your Hellodialog account.
Optionally use the Hellodialog eCommerce Plugin to target groups of customers based on their purchase history.

Release 2.0.0 was completely rebuilt from scratch. The most important changes:
- Made the importing of orders with customers optional
- Fieldmapping: specify which Magento fields to import into Hellodialog
- Synchronize history of all completed orders

For any questions or when you need help installing our extension, please contact us at support@hellodialog.com or call us +31 23 53 22 608.

Download this release

Release Info

Developer Maarten van Schalkwijk
Extension Hellodialog_Tracker
Version 2.0.0
Comparing to
See all releases


Version 2.0.0

app/code/community/Hellodialog/Tracker/Block/Cronprogress.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Hellodialog_Tracker_Block_Cronprogress extends Mage_Adminhtml_Block_System_Config_Form_Field
3
+ {
4
+
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ $total = Mage::getModel('sales/order')->getCollection()
8
+ ->addFieldToFilter('status', 'complete')
9
+ ->count();
10
+
11
+ $total_pages = ceil($total / Hellodialog_Tracker_Model_Cron::orders_per_job());
12
+
13
+ return "<a onclick=\"$('cron-progress-details').setStyle({display: 'block'});this.remove();\" style='cursor: pointer;'>View details</a><pre id='cron-progress-details' style='display: none; background: #eee; width: 258px; border: 1px solid #bbb; color: #888; padding: 5px 10px; margin: 10px 0 5px 0; font-size: 11px;'>"
14
+ ."order_total ".$total
15
+ ."\n"
16
+ ."orders_processed ".(Hellodialog_Tracker_Model_Cron::current_page() * Hellodialog_Tracker_Model_Cron::orders_per_job())
17
+ ."\n"
18
+ ."current_page ".Hellodialog_Tracker_Model_Cron::current_page()." / ".$total_pages
19
+ ."\n"
20
+ ."cron_started ".date('d-m-Y, H:i:s', Mage::getStoreConfig('hellodialog/synchronize_history/cron_started'))
21
+ ."\n"
22
+ ."cron_started_unix ".Mage::getStoreConfig('hellodialog/synchronize_history/cron_started')
23
+ ."\n"
24
+ ."cron_lastrun ".date('d-m-Y, H:i:s', Mage::getStoreConfig('hellodialog/synchronize_history/cron_lastrun'))
25
+ ."\n"
26
+ ."cron_lastrun_unix ".Mage::getStoreConfig('hellodialog/synchronize_history/cron_lastrun')
27
+ ."\n"
28
+ ."assume_done_force_disable ".(Hellodialog_Tracker_Model_Cron::current_page() >= $total_pages ? 'true' : 'false')
29
+ ."</pre>";
30
+ }
31
+ }
app/code/community/Hellodialog/Tracker/Block/Fieldmapping.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Hellodialog_Tracker_Block_Fieldmapping extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract
3
+ {
4
+
5
+ private $hellodialog_options;
6
+ private $magento_options;
7
+
8
+ public function __construct()
9
+ {
10
+ // $o = new HelloDialog_Tracker_Model_Observer();
11
+ // $o->test();
12
+ // Hellodialog_Tracker_Model_Cron::sync();
13
+
14
+ $this->addColumn('magento', array(
15
+ 'label' => 'Customer',
16
+ 'style' => 'width:130px',
17
+ ));
18
+ $this->addColumn('hellodialog', array(
19
+ 'label' => 'into Hellodialog',
20
+ 'style' => 'width:130px',
21
+ ));
22
+ $this->_addAfter = false;
23
+ $this->_addButtonLabel = 'Add';
24
+ parent::__construct();
25
+ }
26
+
27
+ protected function _renderCellTemplate($columnName)
28
+ {
29
+ if ($columnName == 'hellodialog') {
30
+ $html = '<select style="width: 130px;" name="'.$this->getElement()->getName() . '[#{_id}][' . $columnName . ']" data-value="#{' . $columnName . '}" class="dropdown-hellodialog-fieldmappings">';
31
+ foreach ($this->_getHellodialogOptions() as $value => $description) {
32
+ $html .= '<option value="'.$value.'">'.$description.'</option>';
33
+ }
34
+ $html .= '</select>';
35
+ return $html;
36
+ }
37
+
38
+ if ($columnName == 'magento') {
39
+ $html = '<select style="width: 130px;" name="'.$this->getElement()->getName() . '[#{_id}][' . $columnName . ']" data-value="#{' . $columnName . '}" class="dropdown-hellodialog-fieldmappings">';
40
+ foreach ($this->_getMagentoOptions() as $value => $description) {
41
+ $html .= '<option value="'.$value.'">'.$description.'</option>';
42
+ }
43
+ $html .= '</select>';
44
+ return $html;
45
+ }
46
+
47
+ return parent::_renderCellTemplate($columnName);
48
+ }
49
+
50
+ protected function _getHellodialogOptions() {
51
+ if (!is_null($this->hellodialog_options)) {
52
+ return $this->hellodialog_options;
53
+ }
54
+
55
+ $key = Mage::getStoreConfig('hellodialog/general/apikey');
56
+
57
+ if (!class_exists('HDApi', false)) {
58
+ require_once dirname(__FILE__).'/../Model/HDApi.php';
59
+ }
60
+
61
+ $this->hellodialog_options = array();
62
+ HDApi::setToken($key);
63
+
64
+ try {
65
+ $api = new HDApi('fields');
66
+ $fields = (array)$api->get();
67
+
68
+
69
+ // error accessing API
70
+ if (isset($fields['result']) && isset($fields['result']->message)) {
71
+ $this->hellodialog_options[] = htmlentities($fields['result']->message, ENT_QUOTES);
72
+ return $this->hellodialog_options;
73
+ }
74
+
75
+ // read fields from response
76
+ foreach ($fields as $field) {
77
+ $field = (array)$field;
78
+ // only present 'flat' fields as possible targets
79
+ if (is_array($field['columns']) && count($field['columns']) == 1 && $field['columns'][0] != 'email') {
80
+ $this->hellodialog_options[$field['columns'][0]] = $field['name'];
81
+ }
82
+ }
83
+ } catch (Exception $e) {
84
+ // no API token yet, don't crash
85
+ }
86
+
87
+
88
+ return $this->hellodialog_options;
89
+ }
90
+
91
+ protected function _getMagentoOptions() {
92
+ if (!is_null($this->magento_options)) {
93
+ return $this->magento_options;
94
+ }
95
+
96
+ $this->magento_options = array();
97
+
98
+ $this->magento_options['firstname'] = "Firstname";
99
+ $this->magento_options['lastname'] = "Lastname";
100
+ $this->magento_options['name'] = "Name (Firstname + Lastname)";
101
+ $this->magento_options['company'] = "Company";
102
+ $this->magento_options['address'] = "Address";
103
+ $this->magento_options['zipcode'] = "Postcode";
104
+ $this->magento_options['city'] = "City";
105
+ $this->magento_options['country'] = "Country";
106
+ $this->magento_options['telephone'] = "Telephone";
107
+
108
+ return $this->magento_options;
109
+ }
110
+ }
app/code/community/Hellodialog/Tracker/Block/Syncstatus.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Hellodialog_Tracker_Block_Syncstatus extends Mage_Adminhtml_Block_System_Config_Form_Field
3
+ {
4
+
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ if (!class_exists('HDApi', false)) {
8
+ require_once dirname(__FILE__).'/../Model/HDApi.php';
9
+ }
10
+
11
+ $key = Mage::getStoreConfig('hellodialog/general/apikey');
12
+ HDApi::setToken($key);
13
+
14
+ try {
15
+ $api = new HDApi('orders');
16
+ $result = $api->addParam('count', 1)->get();
17
+
18
+ if (is_object($result) && isset($result->result) && isset($result->result->data)) {
19
+ $processed = (int) $result->result->data->count;
20
+ } else {
21
+ $processed = 0;
22
+ }
23
+
24
+ $total = Mage::getModel('sales/order')->getCollection()
25
+ ->addFieldToFilter('status', 'complete')
26
+ ->count();
27
+
28
+ $perc = round(100 * $processed / $total);
29
+
30
+ $progress_color = (Mage::getStoreConfig('hellodialog/synchronize_history/enabled') || $perc == 100) ? array('#6ada64', '#296426') : array('#c4c4c4', '#666666');
31
+
32
+ return "<div style='background: #e4e4e4; border: 1px solid #aaa; margin-right: 10px; width: 180px; height: 15px; border-radius: 3px; display: inline-block; overflow: hidden; box-shadow: inset 0 0 7px rgba(0,0,0,0.2);'>".
33
+ "<div style='background: ".$progress_color[0]."; height: 100%; border-right: 1px solid #aaa; text-align: center; color: ".$progress_color[1]."; line-height: 15px; font-size: 10px; width: ".$perc."%;'>".$perc."%</div></div>".number_format($processed).' / '.number_format($total);
34
+ } catch (Exception $e) {
35
+ return "<span style='font-style: italic;'>Please fill out your API-Key first.</span>";
36
+ }
37
+ }
38
+ }
app/code/community/Hellodialog/Tracker/Block/Validator.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Hellodialog_Tracker_Block_Validator extends Mage_Adminhtml_Block_System_Config_Form_Field
3
+ {
4
+
5
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
6
+ {
7
+ $this->setElement($element);
8
+
9
+ $key = Mage::getStoreConfig('hellodialog/general/apikey');
10
+ require_once dirname(__FILE__).'/../Model/APIKeyValidator.php';
11
+ $status = APIKeyValidator::validate($key);
12
+
13
+ $output = "<div style='background: #eee; width: 258px; border: 1px solid #bbb; color: #888; padding: 5px 10px; margin: 10px 0 20px 0; font-size: 11px;' id='hellodialog_apikey_validation_output'>".$status."</div>";
14
+
15
+ $html = $this->getLayout()->createBlock('adminhtml/widget_button')
16
+ ->setType('button')
17
+ ->setClass('')
18
+ ->setLabel('Validate API Key')
19
+ ->setOnClick($this->onClick())
20
+ ->toHtml();
21
+
22
+ return $output.$html."<br/><br/><br/>";
23
+ }
24
+
25
+ protected function onClick() {
26
+ return "
27
+ (function() {
28
+
29
+ var button = $$('#row_hellodialog_general_validate button')[0];
30
+ var api_key = $('hellodialog_general_apikey').value;
31
+ var url = '".Mage::helper('adminhtml')->getUrl('adminhtml/adminhtml_form/check')."';
32
+
33
+ button.toggleClassName('loading');
34
+ $('hellodialog_apikey_validation_output').innerHTML = 'Validating...';
35
+ new Ajax.Request(url, {
36
+ 'method': 'post',
37
+ 'parameters': {
38
+ 'api_key': api_key
39
+ },
40
+ 'onSuccess': function(response) {
41
+ $('hellodialog_apikey_validation_output').innerHTML = response.responseText;
42
+ },
43
+ 'onFailure': function(response) {
44
+ $('hellodialog_apikey_validation_output').innerHTML = 'Could not process validation request due to an error with the Hellodialog Magento Plugin [' + response.status + '].';
45
+ },
46
+ 'onComplete': function() {
47
+ button.toggleClassName('loading');
48
+ }
49
+ });
50
+ })();
51
+ ";
52
+ }
53
+ }
54
+ ?>
app/code/community/Hellodialog/Tracker/Model/APIKeyValidator.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class APIKeyValidator {
3
+
4
+ public static function validate($key) {
5
+ $key = trim($key);
6
+ if ($key == '') {
7
+ return self::render("Please provide an API Key.", "warning");
8
+ }
9
+
10
+ if (!class_exists('HDApi', false)) {
11
+ require_once dirname(__FILE__).'/HDApi.php';
12
+ }
13
+
14
+ HDApi::setToken($key);
15
+ $ping = new HDApi('ping');
16
+ $result = $ping->get();
17
+
18
+ if (!is_object($result)) {
19
+ return self::render("Could not validate API settings, unexpected response from API to 'ping'-command.", "warning");
20
+ }
21
+
22
+ if (isset($result->result) && is_object($result->result) && isset($result->result->message)) {
23
+ return self::render("Unexpected result from API:", "warning")."<pre>".$result->result->message."</pre>";
24
+ }
25
+
26
+ if (isset($result->access) && is_object($result->access)) {
27
+ $access = array();
28
+
29
+ if (isset($result->access->all) && is_array($result->access->all) && in_array('*', $result->access->all)) {
30
+ $access[] = self::render("Access OK (Full access)", "ok");
31
+ } else {
32
+ // separately validate access to "contacts" and "orders"
33
+
34
+ $orders = (isset($result->access->orders) && is_array($result->access->orders) && (in_array('*', $result->access->orders) || in_array('POST', $result->access->orders)));
35
+ $contacts = (isset($result->access->contacts) && is_array($result->access->contacts) && (in_array('*', $result->access->contacts) || (in_array('GET', $result->access->contacts) && in_array('POST', $result->access->contacts) && in_array('PUT', $result->access->contacts))));
36
+ $fields = (isset($result->access->fields) && is_array($result->access->fields) && (in_array('*', $result->access->fields) || in_array('GET', $result->access->fields)));
37
+
38
+ if ($orders && $contacts && $fields) {
39
+ $access[] = self::render("Access OK", "ok");
40
+ } else {
41
+ if ($orders) {
42
+ $access[] = self::render("API has access to eCommerce", "ok");
43
+ } else {
44
+ $access[] = self::render("API can't access eCommerce", "warning");
45
+ }
46
+
47
+ if ($contacts) {
48
+ $access[] = self::render("API has access to Contacts", "ok");
49
+ } else {
50
+ $access[] = self::render("API can't access Contacts", "warning");
51
+ }
52
+
53
+ if ($fields) {
54
+ $access[] = self::render("API has access to Fields", "ok");
55
+ } else {
56
+ $access[] = self::render("API can't access Fields", "warning");
57
+ }
58
+ }
59
+ }
60
+
61
+ if (is_array($result->modules)) {
62
+ if (!in_array("ECOMMERCE", $result->modules)) {
63
+ $access[] = self::render("Plan without eCommerce Plugin", "warning");
64
+ }
65
+ } else {
66
+ $access[] = self::render("Error checking plan features", "warning");
67
+ }
68
+
69
+ return "<table cellspacing='0' cellpadding='10' border='0' style='line-height: 20px;'>
70
+ <tr><td>Status</td><td>".implode("<br/>", $access)."</td></tr>
71
+ <tr><td style='width: 60px;'>Account</td><td>".$result->account."</td></tr>
72
+ <tr><td>Plan</td><td>".$result->plan."</td></tr>
73
+ </table>";
74
+ }
75
+
76
+ return self::render("Unexpected response format from API.<br/>Please contact our <a style='color: blue;' href='mailto:support@hellodialog.com'>support-team</a>.", "warning");
77
+ }
78
+
79
+ protected static function render($txt, $status) {
80
+ switch ($status) {
81
+ case 'notice':
82
+ $color = 'blue'; break;
83
+ case 'warning':
84
+ case 'error':
85
+ case 'problem':
86
+ $color = 'red'; break;
87
+ case 'ok':
88
+ case 'success':
89
+ $color = 'green'; break;
90
+ }
91
+
92
+ return "<span style='color: ".$color.";'>".$txt."</span>";
93
+ }
94
+ }
app/code/community/Hellodialog/Tracker/Model/Cron.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Hellodialog_Tracker_Model_Cron {
3
+
4
+ // 2.400 per day (job every 15 minutes), this ensures we stay within Google's gecode limits (2500/24hrs)
5
+ private static $orders_per_job = 25;
6
+
7
+ public static function orders_per_job() {
8
+ return self::$orders_per_job;
9
+ }
10
+
11
+ public static function current_page() {
12
+ return (int)Mage::getStoreConfig('hellodialog/synchronize_history/current_page');
13
+ }
14
+
15
+ public static function sync() {
16
+
17
+ if (!class_exists("HelloDialog_Tracker_Model_Observer", false)) {
18
+ require_once "Observer.php";
19
+ }
20
+
21
+ $configurator = new Mage_Core_Model_Config();
22
+ $configurator->saveConfig('hellodialog/synchronize_history/cron_lastrun', time(), 'default', 0);
23
+
24
+ // check if this job is enabled in the first place
25
+ if (!Mage::getStoreConfig('hellodialog/synchronize_history/enabled')) {
26
+ return;
27
+ }
28
+
29
+ // configuration
30
+ $orders_per_job = self::orders_per_job();
31
+ $current_page = self::current_page();
32
+ $hd_observer = new HelloDialog_Tracker_Model_Observer();
33
+ $total = $orders = Mage::getModel('sales/order')->getCollection()
34
+ ->addFieldToFilter('status', 'complete')
35
+ ->count();
36
+
37
+ // mark start
38
+ if ($current_page == 0) {
39
+ $configurator->saveConfig('hellodialog/synchronize_history/cron_started', time(), 'default', 0);
40
+ }
41
+
42
+ // check if done
43
+ if (self::check_if_done($configurator, $total, $current_page)) {
44
+ return;
45
+ }
46
+
47
+ // process orders
48
+ self::log('==============================================');
49
+ self::log('Hellodialog Order Sync '.date('d-m-Y @ H:i:s'));
50
+ self::log('Processing orders (LIMIT '.($current_page * self::orders_per_job()).', '.self::orders_per_job().')');
51
+
52
+ $orders = Mage::getModel('sales/order')->getCollection()
53
+ ->addFieldToFilter('status', 'complete') // only import completed orders
54
+ ->addAttributeToSort('increment_id', 'DESC') // import most recent orders first
55
+ ->setCurPage($current_page + 1) // magento starts numbering pages at 1 (instead of 0)
56
+ ->setPageSize(self::orders_per_job());
57
+
58
+ $current_page++;
59
+
60
+ foreach ($orders as $order) {
61
+ self::log("- processing order ".$order->getIncrementId()." (".$order->getCustomerName().")");
62
+ $hd_observer->sync_order($order);
63
+ }
64
+
65
+ // update current page in config
66
+ if (self::check_if_done($configurator, $total, $current_page)) {
67
+ return;
68
+ }
69
+ $configurator->saveConfig('hellodialog/synchronize_history/current_page', $current_page, 'default', 0);
70
+ }
71
+
72
+ private static function log($l) {
73
+ Mage::log($l, null, 'hellodialog.log');
74
+ }
75
+
76
+ private static function check_if_done($configurator, $total, $page) {
77
+ if ($total <= $page * self::orders_per_job()) {
78
+ self::log("Hellodialog Order Sync >> DONE <<");
79
+ $configurator->saveConfig('hellodialog/synchronize_history/enabled', 0, 'default', 0);
80
+ return true;
81
+ }
82
+ return false;
83
+ }
84
+ }
app/code/community/Hellodialog/Tracker/Model/HDApi.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class HDApi {
3
+
4
+ protected static $_token = null;
5
+
6
+ protected $_url = null;
7
+ protected $_conditions = array();
8
+ protected $_data = array();
9
+ protected $_params = array();
10
+
11
+ public static function setToken($token){
12
+ self::$_token = $token;
13
+ }
14
+
15
+ public function __construct($url){
16
+ $this->_url = $url;
17
+ return $this;
18
+ }
19
+
20
+ public function clear(){
21
+ $this->_conditions = array();
22
+ $this->_data = array();
23
+ $this->_params = array();
24
+ return $this;
25
+ }
26
+
27
+ public function data($data){
28
+ $this->_data = $data;
29
+ return $this;
30
+ }
31
+
32
+ protected $_validConditions = array(
33
+ 'equals',
34
+ 'equals-any',
35
+ 'not-equals',
36
+ 'greater-than',
37
+ 'less-than',
38
+ 'contains',
39
+ 'not-contains',
40
+ 'starts-with',
41
+ 'ends-with',
42
+ 'before',
43
+ 'after',
44
+ 'contains-any',
45
+ 'contains-all',
46
+ 'contains-exactly',
47
+ 'not-contains-any',
48
+ 'not-contains-all'
49
+ );
50
+
51
+ public function condition($key, $value, $condition = 'equals'){
52
+ if(!in_array($condition, $this->_validConditions)){
53
+ throw new Exception("'".$condition."' is not a valid condition");
54
+ }
55
+ $this->_conditions[$key] = array(
56
+ 'value' => $value,
57
+ 'condition' => $condition
58
+ );
59
+ return $this;
60
+ }
61
+
62
+ public function addParam($key, $value) {
63
+ $this->_params[$key] = $value;
64
+ return $this;
65
+ }
66
+
67
+ public function put($id = null){
68
+ return $this->_request('PUT', $id);
69
+ }
70
+
71
+ public function delete($id){
72
+ return $this->_request('DELETE', $id);
73
+ }
74
+
75
+ public function get($id = null){
76
+ return $this->_request('GET', $id);
77
+ }
78
+
79
+ public function post(){
80
+ return $this->_request('POST');
81
+ }
82
+
83
+ protected function _request($method, $id = null){
84
+ if(is_null($this->_url)){
85
+ throw new Exception("No url specified");
86
+ }
87
+
88
+ if(is_null(self::$_token)){
89
+ throw new Exception("API token is required");
90
+ }
91
+
92
+ $curl = curl_init();
93
+
94
+ $requestUrl = 'https://app.klantenbinder2.nl/api/'.$this->_url;
95
+
96
+ if(!is_null($id)){
97
+ $requestUrl .= '/'.$id;
98
+ }
99
+
100
+ $requestUrl .= '?token='.self::$_token;
101
+
102
+ foreach($this->_conditions as $key => $data){
103
+ $requestUrl .= '&condition['.$key.']='.$data['condition'].'&values['.$key.']='.urlencode($data['value']);
104
+ }
105
+
106
+ foreach ($this->_params as $key => $value) {
107
+ $requestUrl .= '&'.urlencode($key).'='.urlencode($value);
108
+ }
109
+
110
+ if($this->_data){
111
+ curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($this->_data));
112
+ }
113
+
114
+ curl_setopt($curl, CURLOPT_URL, $requestUrl);
115
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
116
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
117
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
118
+
119
+ $response = curl_exec($curl);
120
+ curl_close($curl);
121
+
122
+ return json_decode($response);
123
+ }
124
+
125
+ }
126
+
127
+ ?>
app/code/community/Hellodialog/Tracker/Model/HDEcommerce.php ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class HDEcommerce {
3
+
4
+ protected $_orders = array();
5
+ protected $_contacts = array();
6
+ protected $_contactData = array();
7
+ protected $_unsentOrders = array();
8
+
9
+ protected $_contactsProcessed = false;
10
+ protected $_ordersProcessed = false;
11
+
12
+ protected $_strict = true;
13
+ protected $_orderRequired = array('order_number', 'created_on', 'price');
14
+ protected $_productRequired = array('product_code', 'price', 'name');
15
+
16
+ public function __construct($strict = true){
17
+ // Strict defines whether unknown fields in order/product data throw an Exception or are simply ignored
18
+ $this->_strict = $strict;
19
+ }
20
+
21
+ public function addOrder(Array $order, $contactEmail){
22
+ $contactEmail = strtolower(trim($contactEmail));
23
+
24
+ $this->_checkRequired($this->_orderRequired, $order);
25
+
26
+ // Validate email
27
+ if(!filter_var($contactEmail, FILTER_VALIDATE_EMAIL)){
28
+ throw new Exception('Invalid contactEmail ('.$contactEmail.'), must be a valid e-mail address');
29
+ }
30
+
31
+ $order = $this->_validateAndParseOrder($order);
32
+
33
+ // Set working variables
34
+ $order['contact_email'] = $contactEmail;
35
+ $this->_orders[$order['order_number']] = $order;
36
+ $this->_contactEmails[] = $contactEmail;
37
+ $this->_ordersProcessed = false;
38
+ }
39
+
40
+ protected function _validateAndParseOrder(Array $order){
41
+ $data = array();
42
+
43
+ // Loop through, validate and parse order fields
44
+ foreach($order as $field => $value){
45
+ switch($field){
46
+ case 'order_number':
47
+ case 'payment_method':
48
+ case 'zip_code':
49
+ case 'coupon':
50
+ $data[$field] = $value;
51
+ break;
52
+
53
+ case 'price':
54
+ case 'discount':
55
+ case 'lat':
56
+ case 'lng':
57
+ if(!is_null($value) && !is_numeric($value)){
58
+ throw new Exception('Invalid '.$field.' ('.$value.'), must be numeric');
59
+ }
60
+ $data[$field] = $value;
61
+ break;
62
+
63
+ case 'payment_status':
64
+ if(is_null($value)){
65
+ $data[$field] = null;
66
+ break;
67
+ }
68
+
69
+ if(!in_array(strtoupper($value), array('PAID','ERROR','REFUND','CANCELLED','PENDING', 'DENIED'))){
70
+ throw new Exception('Invalid payment status ('.$value.'), accepted: [PAID, ERROR, REFUND, CANCELLED, PENDING, DENIED]');
71
+ }
72
+
73
+ $data[$field] = strtoupper($value);
74
+ break;
75
+
76
+ case 'created_on':
77
+ if(!is_null($value) && !is_numeric($value)){
78
+ throw new Exception('Invalid created_on ('.$value.'), must be a timestamp');
79
+ }
80
+ $data[$field] = $value;
81
+ break;
82
+
83
+ case 'country':
84
+ if(is_null($value)){
85
+ $data[$field] = null;
86
+ break;
87
+ }
88
+
89
+ if(strlen($value) !== 2){
90
+ throw new Exception('Invalid country ('.$value.'), needs to be ISO 3166-1 Alpha-2 compatible');
91
+ }
92
+ $data[$field] = strtoupper($value);
93
+ break;
94
+
95
+ case 'language':
96
+ if(is_null($value)){
97
+ $data[$field] = null;
98
+ break;
99
+ }
100
+
101
+ if(strlen($value) !== 2){
102
+ throw new Exception('Invalid language ('.$value.'), needs to be ISO 639-1 Alpha-2 compatible');
103
+ }
104
+ $data[$field] = strtoupper($value);
105
+ break;
106
+
107
+ default:
108
+ if($this->_strict){
109
+ throw new Exception('Unknown field "'.$field.'"');
110
+ }
111
+ break;
112
+ }
113
+
114
+ }
115
+
116
+ // Check if order number has been used before in this cycle
117
+ if(!empty($this->_orders[$data['order_number']])){
118
+ throw new Exception('Invalid order_number ('.$data['order_number'].'), must be unique');
119
+ }
120
+
121
+ return $data;
122
+ }
123
+
124
+ public function addProduct(Array $product, $orderNumber){
125
+ $this->_checkRequired($this->_productRequired, $product);
126
+
127
+ // Set working variables
128
+ $product = $this->_validateAndParseProduct($product);
129
+ $this->_orders[$orderNumber]['products'][] = $product;
130
+ $this->_ordersProcessed = false;
131
+ }
132
+
133
+ protected function _validateAndParseProduct(Array $product){
134
+ $data = array();
135
+
136
+ // Loop through, validate and parse product fields
137
+ foreach($product as $field => $value){
138
+ switch($field){
139
+ case 'product_code':
140
+ case 'name':
141
+ case 'category':
142
+ case 'subcategory':
143
+ $data[$field] = $value;
144
+ break;
145
+
146
+ case 'price':
147
+ case 'discount':
148
+ if(!is_numeric($value)){
149
+ throw new Exception('Invalid '.$field.' ('.$value.'), must be numeric');
150
+ }
151
+ $data[$field] = $value;
152
+ break;
153
+
154
+ case 'quantity':
155
+ if(is_null($value)){
156
+ $data[$field] = null;
157
+ break;
158
+ }
159
+
160
+ if(!is_int($value)){
161
+ throw new Exception('Invalid quantity ('.$value.'), must be integer');
162
+ }
163
+ $data[$field] = $value;
164
+ break;
165
+
166
+ default:
167
+ if($this->_strict){
168
+ throw new Exception('Unknown field "'.$field.'"');
169
+ }
170
+ break;
171
+ }
172
+ }
173
+
174
+ return $data;
175
+ }
176
+
177
+ public function setContactData(Array $data, $contactEmail, $overwriteData = false){
178
+ $this->_contactData[$contactEmail] = array(
179
+ 'overwrite' => $overwriteData,
180
+ 'data' => $data
181
+ );
182
+ $this->_contactsProcessed = false;
183
+ }
184
+
185
+ protected function _checkRequired(Array $requiredFields, Array $data){
186
+ foreach($requiredFields as $field){
187
+ if(empty($data[$field])){
188
+ throw new Exception($field.' is required');
189
+ }
190
+ }
191
+ }
192
+
193
+ protected function _processContacts(){
194
+ $this->_retrieveContacts();
195
+ $this->_decorateContacts();
196
+
197
+ $this->_contactsProcessed = true;
198
+ }
199
+
200
+ protected function _processOrders(){
201
+ $this->_linkContactsWithOrders();
202
+
203
+ $this->_ordersProcessed = true;
204
+ }
205
+
206
+ protected function _retrieveContacts(){
207
+ if(empty($this->_contactEmails)){
208
+ // No contacts added
209
+ return;
210
+ }
211
+
212
+ // Retrieve contacts in chunks via HDApi
213
+ $hdContacts = new HDApi('contacts');
214
+
215
+ foreach(array_chunk($this->_contactEmails, 10) as $contactChunk){
216
+ $contactsResult = $hdContacts->condition('email', implode(",", $contactChunk), 'equals-any')->get();
217
+ if(!empty($contactsResult)){
218
+ foreach($contactsResult as $contact){
219
+ $this->_contacts[$contact->email] = $contact;
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ protected function _decorateContacts(){
226
+ // Decorate retrieved contacts with order data
227
+ if(!empty($this->_contactData)){
228
+ foreach($this->_contactData as $contactEmail => $data){
229
+ if(!isset($this->_contacts[$contactEmail])){
230
+ continue;
231
+ }
232
+ $contact = $this->_contacts[$contactEmail];
233
+ foreach($data['data'] as $field => $value){
234
+ if($data['overwrite'] || !isset($contact->{$field})){
235
+ $contact->{$field} = $value;
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+
242
+ protected function _linkContactsWithOrders(){
243
+ if(empty($this->_orders)){
244
+ // No orders found
245
+ return;
246
+ }elseif(empty($this->_contacts)){
247
+ // No contacts found, no orders will be sent
248
+ $this->_unsentOrders = $this->_orders;
249
+ return;
250
+ }
251
+
252
+ foreach($this->_orders as $key => &$order){
253
+ // If there's no contact for this order, skip and remove it
254
+ if(!($contact = $this->_contacts[$order['contact_email']])){
255
+ $this->_unsentOrders[] = $order;
256
+ unset($this->_orders[$key]);
257
+ continue;
258
+ }
259
+
260
+ // Link contact to order and unset email from order
261
+ $order['contact'] = $contact->id;
262
+ unset($order['contact_email']);
263
+ }
264
+ }
265
+
266
+ public function putContacts(){
267
+ if(!$this->_contactsProcessed){
268
+ $this->_processContacts();
269
+ }
270
+
271
+ if(empty($this->_contacts)) return;
272
+
273
+ // PUT contacts to HDApi
274
+ $hdContacts = new HDApi('contacts');
275
+ $result = $hdContacts->data(array_values($this->_contacts))->put();
276
+
277
+ return $result;
278
+ }
279
+
280
+ public function postOrders(){
281
+ if(!$this->_contactsProcessed){
282
+ $this->_processContacts();
283
+ }
284
+ if(!$this->_ordersProcessed){
285
+ $this->_processOrders();
286
+ }
287
+
288
+ if(empty($this->_orders) || empty($this->_contacts)) return;
289
+
290
+ // POST orders to HDApi
291
+ $hdOrders = new HDApi('orders');
292
+ $result = $hdOrders->data($this->_orders)->post();
293
+
294
+ return $result;
295
+ }
296
+
297
+ public function getUnsentOrders(){
298
+ return $this->_unsentOrders;
299
+ }
300
+
301
+ }
302
+
303
+ ?>
app/code/community/Hellodialog/Tracker/Model/Observer.php ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class HelloDialog_Tracker_Model_Observer {
3
+
4
+ private $_data;
5
+ private $_order;
6
+ private $_value_location = array(
7
+ 'id' => 'getIncrementId',
8
+ 'firstname' => 'getCustomerFirstname',
9
+ 'lastname' => 'getCustomerLastname',
10
+ 'name' => 'getCustomerName',
11
+ 'company' => 'getBillingAddress/getCompany',
12
+ 'address' => 'getBillingAddress/getStreetFull',
13
+ 'zipcode' => 'getBillingAddress/getPostcode',
14
+ 'city' => 'getBillingAddress/getCity',
15
+ 'countrycode' => 'getBillingAddress/getCountry',
16
+ 'country' => 'getBillingAddress/getCountry',
17
+ 'telephone' => 'getBillingAddress/getTelephone',
18
+ 'email' => 'getCustomerEmail',
19
+ 'price' => 'getBaseGrandTotal',
20
+ 'discount' => 'getBaseDiscountAmount',
21
+ 'coupon' => 'getDiscountDescription',
22
+ 'timestamp' => 'getCreatedAt',
23
+ 'status' => 'getStatus',
24
+ 'status_raw' => 'getStatus',
25
+ 'payment' => 'getPayment/getMethodInstance/getTitle',
26
+ );
27
+
28
+ public function test() {
29
+ $this->_set_order(Mage::getModel('sales/order')->loadByIncrementId('145000018'));
30
+ $this->_sync_order();
31
+ $this->_update_order();
32
+ }
33
+
34
+ public function sync_order($order) {
35
+ $this->_set_order($order);
36
+ $this->_sync_order();
37
+ }
38
+
39
+ public function trackOrder($observer) {
40
+ $this->_set_order($observer->getEvent()->getOrder());
41
+ $this->_sync_order();
42
+ }
43
+
44
+ public function updateOrder($observer) {
45
+ $order_id = $observer->getData('order_ids');
46
+ $this->_set_order(Mage::getModel('sales/order')->load($order_id));
47
+ $this->_update_order();
48
+ }
49
+
50
+ private function _set_order($order) {
51
+ $this->_order = $order;
52
+ $this->_data = null;
53
+ }
54
+
55
+ private function _load_dependencies() {
56
+ if (!class_exists('HDApi', false)) {
57
+ require_once 'HDApi.php';
58
+ }
59
+
60
+ if (!class_exists('HDEcommerce', false)) {
61
+ require_once 'HDEcommerce.php';
62
+ }
63
+ }
64
+
65
+ private function _createHellodialogContact() {
66
+ $contact = array();
67
+ $contact['_state'] = 'Contact';
68
+
69
+ // load fieldmapping from config, and ...
70
+ $mapping = unserialize(Mage::getStoreConfig('hellodialog/general/fieldmapping'));
71
+
72
+ if (is_array($mapping)) {
73
+ // ... add hardcoded required field: email
74
+ $mapping[] = array('magento' => 'email', 'hellodialog' => 'email');
75
+ foreach ($mapping as $map) {
76
+ $contact[$map['hellodialog']] = $this->_value($map['magento']);
77
+ }
78
+ }
79
+
80
+ return $contact;
81
+ }
82
+
83
+ private function _createHellodialogOrder() {
84
+ $order = array(
85
+ 'order_number' => $this->_value('id'),
86
+ 'created_on' => $this->_value('timestamp'),
87
+ 'payment_status' => $this->_value('status'),
88
+ 'payment_method' => $this->_value('payment'),
89
+ 'price' => $this->_value('price'),
90
+ 'discount' => $this->_value('discount'),
91
+ 'coupon' => $this->_value('coupon'),
92
+ 'country' => $this->_value('countrycode'),
93
+ 'zip_code' => $this->_value('zipcode'),
94
+ );
95
+
96
+ if (Mage::getStoreConfig('hellodialog/general/geocode')) {
97
+ list($lat, $lng) = $this->_get_geocode();
98
+ $order['lat'] = $lat;
99
+ $order['lng'] = $lng;
100
+ }
101
+
102
+ return $order;
103
+ }
104
+
105
+ private function _get_geocode() {
106
+ $address = array(
107
+ $this->_value('address'),
108
+ $this->_value('zipcode'),
109
+ $this->_value('city'),
110
+ $this->_value('country'),
111
+ );
112
+
113
+ $url = 'http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=';
114
+ $url .= urlencode(implode(", ", array_filter($address)));
115
+
116
+ $result = json_decode($this->_get_url($url));
117
+
118
+ if (is_object($result) && isset($result->status) && $result->status == 'OK') {
119
+ $lat = $result->results[0]->geometry->location->lat ?: null;
120
+ $lng = $result->results[0]->geometry->location->lng ?: null;
121
+ return array($lat, $lng);
122
+ }
123
+
124
+ return array(0,0);
125
+ }
126
+
127
+ private function _get_url($url) {
128
+ $ch = curl_init();
129
+ curl_setopt($ch, CURLOPT_URL, $url);
130
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
131
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
132
+ $data = curl_exec($ch);
133
+ curl_close($ch);
134
+ return $data;
135
+ }
136
+
137
+ private function _value($key) {
138
+ if (isset($this->_data[$key])) {
139
+ return $this->_data[$key];
140
+ }
141
+
142
+ if (!isset($this->_value_location[$key])) {
143
+ return '';
144
+ }
145
+
146
+ $target = $this->_order;
147
+ $calls = explode("/", $this->_value_location[$key]);
148
+
149
+ foreach ($calls as $call) {
150
+ $target = $target->{$call}();
151
+ }
152
+
153
+ // postprocessing
154
+ switch ($key) {
155
+ case 'countrycode':
156
+ $target = strtolower($target);
157
+ break;
158
+
159
+ case 'country':
160
+ $target = Mage::getModel('directory/country')->loadByCode($target)->getName();
161
+ break;
162
+
163
+ case 'timestamp':
164
+ $target = strtotime($target);
165
+ break;
166
+
167
+ case 'status':
168
+ $target = strtoupper($target);
169
+
170
+ switch ($target) {
171
+ case 'CANCELED':
172
+ case 'CANCEL_OGONE':
173
+ case 'PAYPAL_CANCELED_REVERSAL':
174
+ case 'PAYPAL_REVERSED':
175
+ $target = 'CANCELLED';
176
+ break;
177
+
178
+ case 'DECLINE_OGONE':
179
+ case 'FRAUD':
180
+ $target = 'DENIED';
181
+ break;
182
+
183
+ case 'CLOSED':
184
+ $target = 'REFUND';
185
+ break;
186
+
187
+ case 'COMPLETE':
188
+ $target = 'PAID';
189
+ break;
190
+
191
+ case 'HOLDED':
192
+ case 'PROCESSED_OGONE':
193
+ case 'PAYMENT_REVIEW':
194
+ case 'PENDING':
195
+ case 'PENDING_OGONE':
196
+ case 'PENDING_PAYMENT':
197
+ case 'PENDING_PAYPAL':
198
+ case 'PROCESSING':
199
+ case 'PROCESSING_OGONE':
200
+ case 'WAITING_AUTHOROZATION':
201
+ $target = 'PENDING';
202
+ break;
203
+ }
204
+
205
+ if (!in_array($target, array('PAID', 'ERROR', 'REFUND', 'CANCELLED', 'PENDING', 'DENIED'))) {
206
+ $target = 'PENDING';
207
+ }
208
+ break;
209
+ }
210
+
211
+ // cache
212
+ $this->_data[$key] = $target;
213
+
214
+ return $target;
215
+ }
216
+
217
+ private function _sync_order() {
218
+
219
+ $this->_load_dependencies();
220
+
221
+ HDApi::setToken(Mage::getStoreConfig('hellodialog/general/apikey'));
222
+
223
+ // Create or Update contact in Hellodialog
224
+ Mage::log(">>> HelloDialog_Tracker_Model_Observer->_sync_order()", null, 'hellodialog.log');
225
+ Mage::log("Asserting existance of contact with email: ".$this->_value('email'), null, 'hellodialog.log');
226
+ $contact = $this->_createHellodialogContact();
227
+ $api = new HDApi('contacts');
228
+ $result = $api->data($contact)->post();
229
+
230
+ // if duplicate, update:
231
+ if ($result->result->code == 612) {
232
+ $result = $api->data($contact)->put($result->result->data->id_of_duplicate);
233
+ Mage::log("... UPDATED (Contact already existed, updated information)", null, 'hellodialog.log');
234
+ } else {
235
+ Mage::log("... OK (Contact created)", null, 'hellodialog.log');
236
+ }
237
+
238
+
239
+ // eCommerce plugin
240
+ // ---------------------------------
241
+ // Only synch the actual order when
242
+ // eCommerce plugin is enabled.
243
+
244
+ if (Mage::getStoreConfig('hellodialog/general/ecommerce')) {
245
+
246
+ //HD Ecommerce
247
+ $HDEcommerce = new HDEcommerce();
248
+ $HDEcommerce->addOrder($this->_createHellodialogOrder(), $this->_value('email'));
249
+ $HDEcommerce->setContactData($contact, $this->_value('email'), true);
250
+
251
+ // retrieve all items in this order
252
+ $items = $this->_order->getAllVisibleItems();
253
+
254
+ foreach ($items as $item){
255
+ $HDEcommerce->addProduct(array(
256
+ 'product_code' => $item->getSku(),
257
+ 'name' => $item->getName(),
258
+ 'quantity' => $item->getQty(),
259
+ 'price' => $item->getPrice(),
260
+ 'discount' => $item->getDiscountAmount(),
261
+ ), $this->_value('id'));
262
+ }
263
+
264
+ $success = true;
265
+ try {
266
+ Mage::log("eCommerce plugin enabled: posting order to Hellodialog", null, 'hellodialog.log');
267
+ $ordersResult = $HDEcommerce->postOrders();
268
+ } catch (Exception $e) {
269
+ $success = false;
270
+ Mage::log("... FAILED (error while creating order: duplicate or other error)", null, 'hellodialog.log');
271
+ }
272
+
273
+ if ($success) {
274
+ if (is_object($ordersResult) && isset($ordersResult->result) && is_object($ordersResult->result) && isset($ordersResult->result->code)) {
275
+ if ($ordersResult->result->code == '200') {
276
+ Mage::log("... OK (Orders created)", null, 'hellodialog.log');
277
+ } else {
278
+ Mage::log("... FAILED (".$ordersResult->result->data->errors[0]->message.")", null, 'hellodialog.log');
279
+ }
280
+ } else {
281
+ Mage::log("... FAILED (unexpected response)", null, 'hellodialog.log');
282
+ }
283
+ }
284
+
285
+ } else {
286
+ Mage::log("eCommerce plugin disabled: not posting order to Hellodialog", null, 'hellodialog.log');
287
+ }
288
+ }
289
+
290
+ private function _update_order() {
291
+
292
+ $this->_load_dependencies();
293
+
294
+ HDApi::setToken(Mage::getStoreConfig('hellodialog/general/apikey'));
295
+
296
+ $api = new HDApi('orders');
297
+ $hd_order = $api->addParam('order_number', $this->_value('id'))->get();
298
+
299
+ Mage::log("Trying to update payment status for order: ".$this->_value('id')." ('".$this->_value('status_raw')."', interpreted as '".$this->_value('status')."')", null, 'hellodialog.log');
300
+
301
+ if (is_object($hd_order) && isset($hd_order->id)) {
302
+ $response = $api->clear()->data(array(
303
+ 'payment_status' => $this->_value('status'),
304
+ ))->put($hd_order->id);
305
+ Mage::log("... OK (Updated status)", null, 'hellodialog.log');
306
+ } else {
307
+ Mage::log("... FAILED (Unknown order or other error)", null, 'hellodialog.log');
308
+ }
309
+ }
310
+ }
app/code/community/Hellodialog/Tracker/Model/SubscribeOptions.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Hellodialog_Tracker_Model_SubscribeOptions
3
+ {
4
+ public function toOptionArray()
5
+ {
6
+ return array(
7
+ array('value' => 'enabled_on', 'label'=>Mage::helper('adminhtml')->__('Enabled - checked by default')),
8
+ array('value' => 'enabled_off', 'label'=>Mage::helper('adminhtml')->__('Enabled - unchecked by default')),
9
+ array('value' => 'disabled', 'label'=>Mage::helper('adminhtml')->__('Disabled')),
10
+ );
11
+ }
12
+ }
app/code/community/Hellodialog/Tracker/controllers/Adminhtml/FormController.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Hellodialog_Tracker_Adminhtml_FormController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ public function checkAction()
6
+ {
7
+ require_once dirname(__FILE__).'/../../Model/APIKeyValidator.php';
8
+ Mage::app()->getResponse()->setBody(APIKeyValidator::validate($_POST['api_key']));
9
+ }
10
+ }
app/code/community/Hellodialog/Tracker/etc/adminhtml.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <all>
6
+ <title>Allow Everything</title>
7
+ </all>
8
+ <admin>
9
+ <children>
10
+ <system>
11
+ <children>
12
+ <config>
13
+ <children>
14
+ <hellodialog translate="title" module="hellodialog_tracker">
15
+ <title>Hellodialog</title>
16
+ <sort_order>999</sort_order>
17
+ </hellodialog>
18
+ </children>
19
+ </config>
20
+ </children>
21
+ </system>
22
+ </children>
23
+ </admin>
24
+ </resources>
25
+ </acl>
26
+ </config>
app/code/community/Hellodialog/Tracker/etc/config.xml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <config>
3
+
4
+ <modules>
5
+ <Hellodialog_Tracker>
6
+ <version>2.0.0</version>
7
+ </Hellodialog_Tracker>
8
+ </modules>
9
+
10
+ <crontab>
11
+ <jobs>
12
+ <hellodialog_tracker_sync_cron>
13
+ <schedule>
14
+ <cron_expr>*/15 * * * *</cron_expr>
15
+ </schedule>
16
+ <run>
17
+ <model>hellodialogtrackermodel/Cron::sync</model>
18
+ </run>
19
+ </hellodialog_tracker_sync_cron>
20
+ </jobs>
21
+ </crontab>
22
+
23
+ <global>
24
+ <models>
25
+ <hellodialogtrackermodel>
26
+ <class>Hellodialog_Tracker_Model</class>
27
+ </hellodialogtrackermodel>
28
+ </models>
29
+ <events>
30
+ <sales_order_place_after>
31
+ <observers>
32
+ <hellodialog_tracker_after_order_handler>
33
+ <type>singleton</type>
34
+ <class>hellodialogtrackermodel/Observer</class>
35
+ <method>trackOrder</method>
36
+ </hellodialog_tracker_after_order_handler>
37
+ </observers>
38
+ </sales_order_place_after>
39
+ <checkout_onepage_controller_success_action>
40
+ <observers>
41
+ <hellodialog_tracker_after_payment_handler>
42
+ <type>singleton</type>
43
+ <class>hellodialogtrackermodel/Observer</class>
44
+ <method>updateOrder</method>
45
+ </hellodialog_tracker_after_payment_handler>
46
+ </observers>
47
+ </checkout_onepage_controller_success_action>
48
+ </events>
49
+ </global>
50
+
51
+ <admin>
52
+ <routers>
53
+ <adminhtml>
54
+ <args>
55
+ <modules>
56
+ <hellodialogtracker after="Mage_Adminhtml">Hellodialog_Tracker</hellodialogtracker>
57
+ </modules>
58
+ </args>
59
+ </adminhtml>
60
+ </routers>
61
+ </admin>
62
+
63
+ </config>
app/code/community/Hellodialog/Tracker/etc/system.xml ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <config>
3
+ <tabs>
4
+ <hellodialog>
5
+ <label><![CDATA[<img style="margin-right: 5px; height: 10px;" src="http://www.hellodialog.com/img/magento_plugin_assets/logo.png" alt="" border="0" />HelloDialog]]></label>
6
+ <sort_order>100</sort_order>
7
+ </hellodialog>
8
+ </tabs>
9
+ <sections>
10
+ <hellodialog>
11
+ <label>Configuration</label>
12
+ <tab>hellodialog</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>1000</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <general>
20
+ <label>Hellodialog Magento plugin v2.0.0</label>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>1</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>1</show_in_website>
25
+ <show_in_store>1</show_in_store>
26
+ <expanded>1</expanded>
27
+ <comment>
28
+ <![CDATA[
29
+ <img src='http://www.hellodialog.com/img/magento_plugin_assets/logo.png' style='float: left; width: 100px;'/>
30
+ <div style='margin-left: 130px; padding-top: 6px; line-height: 25px;'>
31
+ <strong>Hellodialog Magento Plugin v2.0.0</strong><br/>
32
+ This plugin connects your Magento shop to Hellodialog. Your customers will automatically be subscribed to your Hellodialog account.
33
+ <br/>
34
+ Optionally use the Hellodialog eCommerce Plugin to target groups of customers based on their purchase history.
35
+ <br/>
36
+ Need help or have feedback? Please check our <a style='color: #ea7601; text-decoration: underline; margin-left: 0;' target='_blank' href='http://www.hellodialog.com/en/online-support-email-marketing'>support page</a> or contact us at <a style='color: #ea7601; text-decoration: underline; margin-left: 0;' href='mailto:support@hellodialog.com?subject=Hellodialog Magento plugin'>support@hellodialog.com</a>.
37
+ </div>
38
+ <div style='clear: both; padding-bottom: 20px; border-bottom: 1px solid #ddd; margin-bottom: 20px;'></div>
39
+ ]]>
40
+ </comment>
41
+ <fields>
42
+ <apikey>
43
+ <label>API Key</label>
44
+ <comment>
45
+ <![CDATA[Please enter your Hellodialog API Key.<br/><a target='_blank' href='https://app.klantenbinder2.nl/settings/api'>Where do I find my API key?</a>]]>
46
+ </comment>
47
+ <frontend_type>text</frontend_type>
48
+ <sort_order>2</sort_order>
49
+ <show_in_default>1</show_in_default>
50
+ <show_in_website>1</show_in_website>
51
+ <show_in_store>1</show_in_store>
52
+ </apikey>
53
+ <validate>
54
+ <label></label>
55
+ <frontend_type>button</frontend_type>
56
+ <frontend_model>hellodialog_tracker_block_validator</frontend_model>
57
+ <sort_order>3</sort_order>
58
+ <show_in_default>1</show_in_default>
59
+ <show_in_website>1</show_in_website>
60
+ <show_in_store>1</show_in_store>
61
+ </validate>
62
+ <fieldmapping translate="label comment">
63
+ <label>Fields</label>
64
+ <frontend_type>button</frontend_type>
65
+ <frontend_model>hellodialog_tracker_block_fieldmapping</frontend_model>
66
+ <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
67
+ <sort_order>5</sort_order>
68
+ <show_in_default>1</show_in_default>
69
+ <show_in_website>1</show_in_website>
70
+ <show_in_store>1</show_in_store>
71
+ <comment><![CDATA[
72
+ Configure additional customer data to be added to Hellodialog. Select a customer attribute on the left, and select the target field in Hellodialog on the right.
73
+ <script type='text/javascript'>
74
+ document.observe("dom:loaded", function() {
75
+ $$('.dropdown-hellodialog-fieldmappings').each(function(select) {
76
+ select.value = select.readAttribute('data-value');
77
+ });
78
+ });
79
+ </script>
80
+ ]]>
81
+ </comment>
82
+ </fieldmapping>
83
+ <ecommerce>
84
+ <label>Enable eCommerce Plugin</label>
85
+ <comment>Optionally enrich your subscribers in Hellodialog. This enables you to target groups of customers based on their purchase history.</comment>
86
+ <frontend_type>select</frontend_type>
87
+ <source_model>adminhtml/system_config_source_yesno</source_model>
88
+ <sort_order>7</sort_order>
89
+ <show_in_default>1</show_in_default>
90
+ <show_in_website>1</show_in_website>
91
+ <show_in_store>1</show_in_store>
92
+ </ecommerce>
93
+ <geocode>
94
+ <label>Geocode addresses</label>
95
+ <comment>Use Google's geocode API to automatically send Lat/Lng coordinates to Hellodialog. This enables detailed location filtering.</comment>
96
+ <frontend_type>select</frontend_type>
97
+ <source_model>adminhtml/system_config_source_yesno</source_model>
98
+ <sort_order>9</sort_order>
99
+ <show_in_default>1</show_in_default>
100
+ <show_in_website>1</show_in_website>
101
+ <show_in_store>1</show_in_store>
102
+ </geocode>
103
+ </fields>
104
+ </general>
105
+ <synchronize_history>
106
+ <label>Synchronize Order history</label>
107
+ <frontend_type>text</frontend_type>
108
+ <sort_order>2</sort_order>
109
+ <show_in_default>1</show_in_default>
110
+ <show_in_website>1</show_in_website>
111
+ <show_in_store>1</show_in_store>
112
+ <expanded>1</expanded>
113
+ <comment>
114
+ <![CDATA[
115
+ <img src='http://www.hellodialog.com/img/magento_plugin_assets/sync.png' style='width: 100px; float: left;'/>
116
+ <div style='margin-left: 130px; line-height: 25px;'>
117
+ <strong>Synchronize Order history</strong><br/>
118
+ Enable the synchronization to start importing all your completed orders into Hellodialog.<br/>
119
+ This job will start running in the background, and takes time to complete (about 2.400 orders will be processed per 24 hours).<br/>
120
+ <span style='color: red; font-style: italic;'>Requires Magento cronjobs to be up and running.</span>
121
+ </div>
122
+ <div style='clear: both; padding-bottom: 20px; border-bottom: 1px solid #ddd; margin-bottom: 20px;'></div>
123
+ ]]>
124
+ </comment>
125
+ <fields>
126
+ <enabled>
127
+ <label>Synchronization enabled</label>
128
+ <comment>Start importing orders by toggling this on.</comment>
129
+ <frontend_type>select</frontend_type>
130
+ <source_model>adminhtml/system_config_source_yesno</source_model>
131
+ <sort_order>1</sort_order>
132
+ <show_in_default>1</show_in_default>
133
+ <show_in_website>1</show_in_website>
134
+ <show_in_store>1</show_in_store>
135
+ </enabled>
136
+ <coverage>
137
+ <label>Coverage</label>
138
+ <comment>
139
+ <![CDATA[This represents the number of orders in Magento that have been synchronized to Hellodialog.]]>
140
+ </comment>
141
+ <frontend_type>button</frontend_type>
142
+ <frontend_model>hellodialog_tracker_block_syncstatus</frontend_model>
143
+ <sort_order>2</sort_order>
144
+ <show_in_default>1</show_in_default>
145
+ <show_in_website>1</show_in_website>
146
+ <show_in_store>1</show_in_store>
147
+ </coverage>
148
+ <status>
149
+ <label>Cron Progress</label>
150
+ <comment>
151
+ <![CDATA[Whenever you encounter problems during your sync, these numbers might help our team to identify the issue.]]>
152
+ </comment>
153
+ <frontend_type>button</frontend_type>
154
+ <frontend_model>hellodialog_tracker_block_cronprogress</frontend_model>
155
+ <sort_order>3</sort_order>
156
+ <show_in_default>1</show_in_default>
157
+ <show_in_website>1</show_in_website>
158
+ <show_in_store>1</show_in_store>
159
+ </status>
160
+ </fields>
161
+ </synchronize_history>
162
+ </groups>
163
+ </hellodialog>
164
+ </sections>
165
+ </config>
app/etc/modules/Hellodialog_Tracker.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <config>
4
+ <modules>
5
+ <Hellodialog_Tracker>
6
+ <active>true</active>
7
+ <codePool>community</codePool>
8
+ </Hellodialog_Tracker>
9
+ </modules>
10
+ </config>
package.xml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Hellodialog_Tracker</name>
4
+ <version>2.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://www.gnu.org/copyleft/gpl.html">GNU General Public License (GPL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Connect your Magento shop to Hellodialog.</summary>
10
+ <description>This plugin connects your Magento shop to Hellodialog. Your customers will automatically be subscribed to your Hellodialog account.&#xD;
11
+ &#xD;
12
+ Optionally use the Hellodialog eCommerce Plugin to target groups of customers based on their purchase history.</description>
13
+ <notes>This plugin connects your Magento shop to Hellodialog. Your customers will automatically be subscribed to your Hellodialog account.&#xD;
14
+ Optionally use the Hellodialog eCommerce Plugin to target groups of customers based on their purchase history.&#xD;
15
+ &#xD;
16
+ Release 2.0.0 was completely rebuilt from scratch. The most important changes:&#xD;
17
+ - Made the importing of orders with customers optional&#xD;
18
+ - Fieldmapping: specify which Magento fields to import into Hellodialog&#xD;
19
+ - Synchronize history of all completed orders&#xD;
20
+ &#xD;
21
+ For any questions or when you need help installing our extension, please contact us at support@hellodialog.com or call us +31 23 53 22 608.</notes>
22
+ <authors><author><name>Maarten van Schalkwijk</name><user>Hellodialog</user><email>maarten@hellodialog.com</email></author></authors>
23
+ <date>2015-03-06</date>
24
+ <time>14:02:41</time>
25
+ <contents><target name="magecommunity"><dir name="Hellodialog"><dir name="Tracker"><dir name="Block"><file name="Cronprogress.php" hash="48122f9d7a8b3f01224a0750b68ff67e"/><file name="Fieldmapping.php" hash="1cd6e6e1b7e473cf8649f567f5e04f90"/><file name="Syncstatus.php" hash="88c1fd5d5e4dc4a7cb0c226a2ba9c958"/><file name="Validator.php" hash="beb1c17c3817b44e1078b1bec0d8ceef"/></dir><dir name="Model"><file name="APIKeyValidator.php" hash="ef82d2001f97ce4203d5717c770ddd82"/><file name="Cron.php" hash="b80aaa48269070e2d35738c80a95d2ee"/><file name="HDApi.php" hash="b65fb0c133b6e44b4732851f58d00beb"/><file name="HDEcommerce.php" hash="cb58ce227b74924968f81945fe4a2c82"/><file name="Observer.php" hash="3e8aee9152e074503b3dd281546dd6ef"/><file name="SubscribeOptions.php" hash="dc700ecb157f1ead9300eb441e0d190d"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="FormController.php" hash="74e25b46ad18817d305550bb82aacc26"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="12c5102eab80f7c17d0032a50cf96478"/><file name="config.xml" hash="37def834bedbf222dbb3d97f8aaaa889"/><file name="system.xml" hash="32aba8e356d77ccfb249e593b641f32e"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Hellodialog_Tracker.xml" hash="7d32b9c31228e1c1709f05642bed18d2"/></dir></target></contents>
26
+ <compatible/>
27
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php><extension><name>curl</name><min/><max/></extension></required></dependencies>
28
+ </package>