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 +31 -0
- app/code/community/Hellodialog/Tracker/Block/Fieldmapping.php +110 -0
- app/code/community/Hellodialog/Tracker/Block/Syncstatus.php +38 -0
- app/code/community/Hellodialog/Tracker/Block/Validator.php +54 -0
- app/code/community/Hellodialog/Tracker/Model/APIKeyValidator.php +94 -0
- app/code/community/Hellodialog/Tracker/Model/Cron.php +84 -0
- app/code/community/Hellodialog/Tracker/Model/HDApi.php +127 -0
- app/code/community/Hellodialog/Tracker/Model/HDEcommerce.php +303 -0
- app/code/community/Hellodialog/Tracker/Model/Observer.php +310 -0
- app/code/community/Hellodialog/Tracker/Model/SubscribeOptions.php +12 -0
- app/code/community/Hellodialog/Tracker/controllers/Adminhtml/FormController.php +10 -0
- app/code/community/Hellodialog/Tracker/etc/adminhtml.xml +26 -0
- app/code/community/Hellodialog/Tracker/etc/config.xml +63 -0
- app/code/community/Hellodialog/Tracker/etc/system.xml +165 -0
- app/etc/modules/Hellodialog_Tracker.xml +10 -0
- package.xml +28 -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.
|
11 |
+

|
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.
|
14 |
+
Optionally use the Hellodialog eCommerce Plugin to target groups of customers based on their purchase history.
|
15 |
+

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

|
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>
|