Version Notes
First version - Track abandoned carts & show a funnel report to show statistics
Download this release
Release Info
Developer | Anatta Design |
Extension | AnattaDesign_AbandonedCarts |
Version | 1.0.0 |
Comparing to | |
See all releases |
Version 1.0.0
- app/code/local/AnattaDesign/AbandonedCarts/Block/Widget.php +5 -0
- app/code/local/AnattaDesign/AbandonedCarts/Helper/Data.php +127 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Checkout/Onepage/Observer.php +112 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Opstatistics.php +9 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Opstatistics/Collection.php +9 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Statistics.php +9 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Statistics/Collection.php +9 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Observer.php +68 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Opstatistics.php +9 -0
- app/code/local/AnattaDesign/AbandonedCarts/Model/Statistics.php +75 -0
- app/code/local/AnattaDesign/AbandonedCarts/controllers/WidgetController.php +9 -0
- app/code/local/AnattaDesign/AbandonedCarts/etc/config.xml +177 -0
- app/code/local/AnattaDesign/AbandonedCarts/sql/anattadesign_abandonedcarts_setup/mysql4-install-1.0.php +39 -0
- app/design/adminhtml/default/default/layout/anattadesign_abandonedcarts.xml +16 -0
- app/design/adminhtml/default/default/template/anattadesign/abandonedcarts/widget.phtml +188 -0
- app/etc/modules/AnattaDesign_AbandonedCarts.xml +9 -0
- package.xml +18 -0
- skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/ar-lt.jpg +0 -0
- skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/ar-rt.jpg +0 -0
- skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/logo.jpg +0 -0
- skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/notifications-btm.gif +0 -0
- skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/notifications.gif +0 -0
- skin/adminhtml/default/default/anattadesign/abandonedcarts/css/style.css +46 -0
app/code/local/AnattaDesign/AbandonedCarts/Block/Widget.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Block_Widget extends Mage_Core_Block_Template {
|
4 |
+
|
5 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Helper/Data.php
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Helper_Data extends Mage_Core_Helper_Abstract {
|
4 |
+
|
5 |
+
public function getStatisticsModel() {
|
6 |
+
if ( $this->isAwesomeCheckoutActive() ) {
|
7 |
+
return Mage::getModel( 'anattadesign_abandonedcarts/statistics' );
|
8 |
+
} else {
|
9 |
+
return Mage::getModel( 'anattadesign_abandonedcarts/opstatistics' );
|
10 |
+
}
|
11 |
+
}
|
12 |
+
|
13 |
+
public function isAwesomeCheckoutActive() {
|
14 |
+
return Mage::getConfig()->getModuleConfig( 'AnattaDesign_AwesomeCheckout' )->is( 'active', 'true' );
|
15 |
+
}
|
16 |
+
|
17 |
+
public function getMessage() {
|
18 |
+
|
19 |
+
$cache = Mage::getSingleton( 'core/cache' );
|
20 |
+
$payload = $cache->load( 'abandonedcart_payload' );
|
21 |
+
|
22 |
+
if ( $payload === false ) {
|
23 |
+
|
24 |
+
$payload = file_get_contents( 'http://api.anattadesign.com/abandonedcart/1alpha/fetch/payload' );
|
25 |
+
$contents = json_decode( $payload, true );
|
26 |
+
|
27 |
+
if ( $contents['status'] == 'success' ) {
|
28 |
+
$message = $this->isAwesomeCheckoutActive() ? $contents['data']['ac'] : $contents['data']['non-ac'];
|
29 |
+
// cache data for 2 days
|
30 |
+
$cache->save( $payload, 'abandonedcart_payload', array( 'abandonedcart' ), 2 * 24 * 60 * 60 );
|
31 |
+
} else {
|
32 |
+
$message = false;
|
33 |
+
}
|
34 |
+
} else {
|
35 |
+
$contents = json_decode( $payload, true );
|
36 |
+
$message = $this->isAwesomeCheckoutActive() ? $contents['data']['ac'] : $contents['data']['non-ac'];
|
37 |
+
}
|
38 |
+
|
39 |
+
return $message;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function getCurrencySymbol() {
|
43 |
+
return Mage::app()->getLocale()->currency( Mage::app()->getStore()->getCurrentCurrencyCode() )->getSymbol();
|
44 |
+
}
|
45 |
+
|
46 |
+
public function getSalesVolume( $year, $month ) {
|
47 |
+
|
48 |
+
$cache = Mage::getSingleton( 'core/cache' );
|
49 |
+
$sales_volume = $cache->load( 'abandonedcart_sales_volume_' . $year . '_' . $month );
|
50 |
+
|
51 |
+
if ( $sales_volume === false ) {
|
52 |
+
// recalculate
|
53 |
+
$orderTotals = Mage::getModel( 'sales/order' )->getCollection()
|
54 |
+
->addAttributeToFilter( 'status', Mage_Sales_Model_Order::STATE_COMPLETE )
|
55 |
+
->addAttributeToFilter( 'created_at', array( 'from' => date( 'Y-m-01' ) ) )
|
56 |
+
->addAttributeToSelect( 'grand_total' )
|
57 |
+
->getColumnValues( 'grand_total' )
|
58 |
+
;
|
59 |
+
|
60 |
+
$sales_volume = array_sum( $orderTotals );
|
61 |
+
|
62 |
+
// cache it
|
63 |
+
$cache->save( $sales_volume, 'abandonedcart_sales_volume_' . $year . '_' . $month, array( 'abandonedcart' ), 24 * 60 * 60 );
|
64 |
+
}
|
65 |
+
|
66 |
+
return $sales_volume;
|
67 |
+
}
|
68 |
+
|
69 |
+
public function trailingslashit( $string ) {
|
70 |
+
return $this->untrailingslashit( $string ) . '/';
|
71 |
+
}
|
72 |
+
|
73 |
+
public function untrailingslashit( $string ) {
|
74 |
+
return rtrim( $string, '/' );
|
75 |
+
}
|
76 |
+
|
77 |
+
public function ping() {
|
78 |
+
|
79 |
+
// Get current version of the extension
|
80 |
+
$connection = Mage::getSingleton( 'core/resource' )->getConnection( 'core_read' );
|
81 |
+
$stmt = $connection->query( "SELECT version FROM core_resource WHERE code='anattadesign_abandonedcarts_setup';" );
|
82 |
+
$data = $stmt->fetch();
|
83 |
+
$version = $data['version'];
|
84 |
+
|
85 |
+
$ping = array(
|
86 |
+
'version' => $version,
|
87 |
+
'site_name' => Mage::getStoreConfig( 'general/store_information/name' ),
|
88 |
+
'url' => 'http://' . str_replace( array( 'http://', '/index.php/', '/index.php' ), '', Mage::getUrl() ) // making sure the url is in format - http://domain.com/
|
89 |
+
);
|
90 |
+
|
91 |
+
$ping['url'] = Mage::helper( 'anattadesign_abandonedcarts' )->trailingslashit( $ping['url'] );
|
92 |
+
|
93 |
+
// make call
|
94 |
+
$client = new Varien_Http_Client( 'http://api.anattadesign.com/abandonedcart/1alpha/collect/ping' );
|
95 |
+
$client->setMethod( Varien_Http_Client::POST );
|
96 |
+
$client->setParameterPost( 'ping', $ping );
|
97 |
+
|
98 |
+
try {
|
99 |
+
$response = $client->request();
|
100 |
+
if ( $response->isSuccessful() ) {
|
101 |
+
$json_response = json_decode( $response->getBody(), true );
|
102 |
+
$ping_success = $json_response['status'] == 'success' ? true : false;
|
103 |
+
}
|
104 |
+
} catch ( Exception $e ) {
|
105 |
+
$ping_success = false;
|
106 |
+
}
|
107 |
+
|
108 |
+
if ( $ping_success ) {
|
109 |
+
// make sure ping is not rescheduled anymore
|
110 |
+
Mage::getModel( 'core/config' )->deleteConfig( 'anattadesign_abandonedcarts_ping_rescheduled' );
|
111 |
+
} else {
|
112 |
+
// reschedule ping, increment counts if its already scheduled, so that we can see how many times it has failed
|
113 |
+
// $ping_rescheduled = Mage::getStoreConfig( 'anattadesign_abandonedcarts_ping_rescheduled' );
|
114 |
+
// Fetch directly from database to bypass Magento config cache.
|
115 |
+
// Its better to bypass cache and make a sql query in favor of performance, sql query is not gonna run up on frontend side, except when all the cache is refreshed & extension is upgraded
|
116 |
+
$stmt = $connection->query( "SELECT value FROM core_config_data WHERE path='anattadesign_abandonedcarts_ping_rescheduled' AND scope = 'default' AND scope_id = 0 LIMIT 1;" );
|
117 |
+
$data = $stmt->fetch();
|
118 |
+
if ( $data === false )
|
119 |
+
$ping_rescheduled = 1;
|
120 |
+
else
|
121 |
+
$ping_rescheduled = intval( $data['value'] ) + 1;
|
122 |
+
|
123 |
+
Mage::getModel( 'core/config' )->saveConfig( 'anattadesign_abandonedcarts_ping_rescheduled', $ping_rescheduled );
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Checkout/Onepage/Observer.php
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer {
|
4 |
+
|
5 |
+
protected $_model;
|
6 |
+
|
7 |
+
public function __construct() {
|
8 |
+
$this->_model = Mage::helper( 'anattadesign_abandonedcarts' )->getStatisticsModel();
|
9 |
+
}
|
10 |
+
|
11 |
+
public function getModel() {
|
12 |
+
return $this->_model;
|
13 |
+
}
|
14 |
+
|
15 |
+
public function getQuoteId() {
|
16 |
+
return Mage::getModel( 'checkout/cart' )->getQuote()->getId();
|
17 |
+
}
|
18 |
+
|
19 |
+
public function index( $observer ) {
|
20 |
+
$statistics = $this->getModel();
|
21 |
+
|
22 |
+
if ( !$this->getQuoteId() ) {
|
23 |
+
return;
|
24 |
+
}
|
25 |
+
|
26 |
+
if ( Mage::helper( 'anattadesign_abandonedcarts' )->isAwesomeCheckoutActive() ) {
|
27 |
+
$statistics->saveStepReached( 'shipping', $this->getQuoteId() );
|
28 |
+
} else {
|
29 |
+
if ( Mage::getSingleton( 'customer/session' )->isLoggedIn() ) {
|
30 |
+
$statistics->saveStepReached( 'billing', $this->getQuoteId() );
|
31 |
+
} else {
|
32 |
+
$statistics->saveStepReached( 'login', $this->getQuoteId() );
|
33 |
+
}
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
public function saveMethod( $observer ) {
|
38 |
+
$statistics = $this->getModel();
|
39 |
+
$statistics->saveStepMoved( 'login', $this->getQuoteId() );
|
40 |
+
$statistics->saveStepReached( 'billing', $this->getQuoteId() );
|
41 |
+
}
|
42 |
+
|
43 |
+
public function loginPost( $observer ) {
|
44 |
+
$location = Mage::getModel( 'customer/session' )->getBeforeAuthUrl();
|
45 |
+
if ( $location ) {
|
46 |
+
if ( strpos( $location, 'checkout/onepage' ) > 0 ) {
|
47 |
+
$statistics = $this->getModel();
|
48 |
+
$statistics->saveStepMoved( 'login', $this->getQuoteId() );
|
49 |
+
}
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
public function saveBilling( $observer ) {
|
54 |
+
$response = $observer->getEvent()->getData( 'controller_action' )->getResponse();
|
55 |
+
if ( $this->_isMovedToNextStep( $response ) ) {
|
56 |
+
$statistics = $this->getModel();
|
57 |
+
$statistics->saveStepMoved( 'billing', $this->getQuoteId() );
|
58 |
+
$statistics->saveStepReached( $this->_getNextStep( $response ), $this->getQuoteId() );
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
public function saveShipping( $observer ) {
|
63 |
+
$response = $observer->getEvent()->getData( 'controller_action' )->getResponse();
|
64 |
+
if ( $this->_isMovedToNextStep( $response ) ) {
|
65 |
+
$statistics = $this->getModel();
|
66 |
+
$statistics->saveStepMoved( 'shipping', $this->getQuoteId() );
|
67 |
+
$statistics->saveStepReached( $this->_getNextStep( $response ), $this->getQuoteId() );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
public function saveShippingMethod( $observer ) {
|
72 |
+
$response = $observer->getEvent()->getData( 'controller_action' )->getResponse();
|
73 |
+
if ( $this->_isMovedToNextStep( $response ) ) {
|
74 |
+
$statistics = $this->getModel();
|
75 |
+
$statistics->saveStepMoved( 'shipping_method', $this->getQuoteId() );
|
76 |
+
$statistics->saveStepReached( $this->_getNextStep( $response ), $this->getQuoteId() );
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
public function savePayment( $observer ) {
|
81 |
+
$response = $observer->getEvent()->getData( 'controller_action' )->getResponse();
|
82 |
+
if ( $this->_isMovedToNextStep( $response ) ) {
|
83 |
+
$statistics = $this->getModel();
|
84 |
+
$statistics->saveStepMoved( 'payment', $this->getQuoteId() );
|
85 |
+
$statistics->saveStepReached( $this->_getNextStep( $response ), $this->getQuoteId() );
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
public function saveOrder( $observer ) {
|
90 |
+
$response = $observer->getEvent()->getData( 'controller_action' )->getResponse();
|
91 |
+
if ( $this->_isMovedToNextStep( $response ) ) {
|
92 |
+
$statistics = $this->getModel();
|
93 |
+
$statistics->saveStepMoved( 'review', $this->getQuoteId() );
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
protected function _getNextStep( Mage_Core_Controller_Response_Http $response ) {
|
98 |
+
$body = json_decode( $response->getBody() );
|
99 |
+
return $body->goto_section;
|
100 |
+
}
|
101 |
+
|
102 |
+
protected function _isMovedToNextStep( Mage_Core_Controller_Response_Http $response ) {
|
103 |
+
if ( $response->getHttpResponseCode() == 200 ) {
|
104 |
+
$body = json_decode( $response->getBody() );
|
105 |
+
if ( empty( $body->error ) ) {
|
106 |
+
return true;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
return false;
|
110 |
+
}
|
111 |
+
|
112 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Opstatistics.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Mysql4_Opstatistics extends Mage_Core_Model_Mysql4_Abstract {
|
4 |
+
|
5 |
+
protected function _construct() {
|
6 |
+
$this->_init( 'anattadesign_abandonedcarts/opstatistics', 'statistics_id' );
|
7 |
+
}
|
8 |
+
|
9 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Opstatistics/Collection.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Mysql4_Opstatistics_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
4 |
+
|
5 |
+
protected function _construct() {
|
6 |
+
$this->_init( 'anattadesign_abandonedcarts/opstatistics' );
|
7 |
+
}
|
8 |
+
|
9 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Statistics.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Mysql4_Statistics extends Mage_Core_Model_Mysql4_Abstract {
|
4 |
+
|
5 |
+
protected function _construct() {
|
6 |
+
$this->_init( 'anattadesign_abandonedcarts/statistics', 'statistics_id' );
|
7 |
+
}
|
8 |
+
|
9 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Mysql4/Statistics/Collection.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Mysql4_Statistics_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
4 |
+
|
5 |
+
protected function _construct() {
|
6 |
+
$this->_init( 'anattadesign_abandonedcarts/statistics' );
|
7 |
+
}
|
8 |
+
|
9 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Observer.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Observer {
|
4 |
+
|
5 |
+
public function uponAdminLogin() {
|
6 |
+
$this->ping();
|
7 |
+
$this->checkLatestVersion();
|
8 |
+
}
|
9 |
+
|
10 |
+
public function ping() {
|
11 |
+
// Instead of using getStoreConfig make a direct sql query to bypass magento cache
|
12 |
+
// $is_ping_rescheduled = Mage::getStoreConfig( 'anattadesign_abandonedcarts_ping_rescheduled' );
|
13 |
+
$connection = Mage::getSingleton( 'core/resource' )->getConnection( 'core_read' );
|
14 |
+
$stmt = $connection->query( "SELECT value FROM core_config_data WHERE path='anattadesign_abandonedcarts_ping_rescheduled' AND scope = 'default' AND scope_id = 0 LIMIT 1;" );
|
15 |
+
$data = $stmt->fetch();
|
16 |
+
// If $data is false, then that means there is no row in the table, and no ping has been rescheduled
|
17 |
+
if ( $data !== false )
|
18 |
+
Mage::helper( 'anattadesign_abandonedcarts' )->ping();
|
19 |
+
}
|
20 |
+
|
21 |
+
public function checkLatestVersion() {
|
22 |
+
$contents = file_get_contents( 'http://api.anattadesign.com/abandonedcart/1alpha/status/latestVersion' );
|
23 |
+
$latest = json_decode( $contents );
|
24 |
+
|
25 |
+
if ( $latest->status == "success" ) {
|
26 |
+
|
27 |
+
if ( $latest->latestVersion == Mage::getStoreConfig( 'anattadesign_abandonedcart_latest_checked_version' ) )
|
28 |
+
return;
|
29 |
+
|
30 |
+
$connection = Mage::getSingleton( 'core/resource' )->getConnection( 'core_read' );
|
31 |
+
$stmt = $connection->query( "SELECT version FROM core_resource WHERE code='anattadesign_abandonedcarts_setup'" );
|
32 |
+
$data = $stmt->fetch();
|
33 |
+
$version = $data['version'];
|
34 |
+
|
35 |
+
if ( $latest->latestVersion != $version ) {
|
36 |
+
Mage::getModel( 'adminnotification/inbox' )
|
37 |
+
->setSeverity( Mage_AdminNotification_Model_Inbox::SEVERITY_NOTICE )
|
38 |
+
->setTitle( "My Abandoned Cart {$latest->latestVersion} is now available" )
|
39 |
+
->setDateAdded( gmdate( 'Y-m-d H:i:s' ) )
|
40 |
+
->setUrl( 'http://www.myabandonedcarts.com/' )
|
41 |
+
->setDescription( 'Your version of My Abandoned Cart is currently not up-to-date. Please <a href="http://www.myabandonedcarts.com/">click here</a> to get the latest version.' )
|
42 |
+
->save();
|
43 |
+
Mage::getModel( 'core/config' )->saveConfig( 'anattadesign_abandonedcart_latest_checked_version', $latest->latestVersion );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
public function addJavascriptBlock( $observer ) {
|
49 |
+
|
50 |
+
$controller = $observer->getAction();
|
51 |
+
|
52 |
+
if ( !$controller instanceof Mage_Adminhtml_DashboardController )
|
53 |
+
return;
|
54 |
+
|
55 |
+
$layout = $controller->getLayout();
|
56 |
+
$block = $layout->createBlock( 'core/text' );
|
57 |
+
$block->setText(
|
58 |
+
'<script type="text/javascript">
|
59 |
+
var anattadesign_abandonedcarts = {
|
60 |
+
url: "' . Mage::helper( 'adminhtml' )->getUrl( 'abandonedcarts/widget/render/' ) . '"
|
61 |
+
};
|
62 |
+
</script>'
|
63 |
+
);
|
64 |
+
|
65 |
+
$layout->getBlock( 'js' )->append( $block );
|
66 |
+
}
|
67 |
+
|
68 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Opstatistics.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Opstatistics extends AnattaDesign_AbandonedCarts_Model_Statistics {
|
4 |
+
|
5 |
+
protected function _construct() {
|
6 |
+
$this->_init( 'anattadesign_abandonedcarts/opstatistics' );
|
7 |
+
}
|
8 |
+
|
9 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/Model/Statistics.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_Model_Statistics extends Mage_Core_Model_Abstract {
|
4 |
+
|
5 |
+
protected function _construct() {
|
6 |
+
$this->_init( 'anattadesign_abandonedcarts/statistics' );
|
7 |
+
}
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Save moved to step for quote.
|
11 |
+
*
|
12 |
+
* @param string $step
|
13 |
+
* @param int $quoteId
|
14 |
+
* @return AnattaDesign_AbandonedCarts_Model_Statistics
|
15 |
+
*/
|
16 |
+
public function saveStepMoved( $step, $quoteId ) {
|
17 |
+
return $this->_saveStepData( array( 'step' => $step, 'quoteId' => $quoteId, 'moved' => 1 ) );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Save reached step for quote.
|
22 |
+
*
|
23 |
+
* @param string $step
|
24 |
+
* @param int $quoteId
|
25 |
+
* @return AnattaDesign_AbandonedCarts_Model_Statistics
|
26 |
+
*/
|
27 |
+
public function saveStepReached( $step, $quoteId ) {
|
28 |
+
$data = array(
|
29 |
+
'step' => $step,
|
30 |
+
'quoteId' => $quoteId,
|
31 |
+
'reached' => 1,
|
32 |
+
'year' => date( 'Y' ),
|
33 |
+
'month' => date( 'n' ),
|
34 |
+
'date' => date( 'Y-m-d h:i:s' )
|
35 |
+
);
|
36 |
+
return $this->_saveStepData( $data );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Save step data.
|
41 |
+
* Step and quote id are required.
|
42 |
+
*
|
43 |
+
* @param array $data
|
44 |
+
* @throws Exception
|
45 |
+
* @return AnattaDesign_AbandonedCarts_Model_Statistics
|
46 |
+
*/
|
47 |
+
protected function _saveStepData( array $data ) {
|
48 |
+
if ( !array_key_exists( 'step', $data ) ) {
|
49 |
+
throw new Exception( 'Step key is required' );
|
50 |
+
}
|
51 |
+
|
52 |
+
// Prepare data
|
53 |
+
$data = array_merge( $data, array(
|
54 |
+
'sales_flat_quote_id' => $data['quoteId']
|
55 |
+
)
|
56 |
+
);
|
57 |
+
|
58 |
+
// Get existing statistics for this quote and step
|
59 |
+
$collection = $this->getCollection();
|
60 |
+
$collection->addFieldToFilter( 'sales_flat_quote_id', $data['quoteId'] );
|
61 |
+
$collection->addFieldToFilter( 'step', $data['step'] );
|
62 |
+
|
63 |
+
// TODO: Add date field to filter < 1 month
|
64 |
+
|
65 |
+
$collection->addOrder( 'statistics_id' );
|
66 |
+
$collection->load();
|
67 |
+
|
68 |
+
if ( count( $collection ) ) {
|
69 |
+
return $collection->fetchItem()->addData( $data )->save();
|
70 |
+
} else {
|
71 |
+
return $this->addData( $data )->save();
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/controllers/WidgetController.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class AnattaDesign_AbandonedCarts_WidgetController extends Mage_Adminhtml_Controller_Action {
|
4 |
+
|
5 |
+
public function renderAction() {
|
6 |
+
$html = $this->getLayout()->createBlock( 'anattadesign_abandonedcarts/widget', 'root' )->setTemplate( 'anattadesign/abandonedcarts/widget.phtml' )->toHtml();
|
7 |
+
$this->getResponse()->setBody( $html );
|
8 |
+
}
|
9 |
+
}
|
app/code/local/AnattaDesign/AbandonedCarts/etc/config.xml
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<AnattaDesign_AbandonedCarts>
|
5 |
+
<version>1.0.0</version>
|
6 |
+
</AnattaDesign_AbandonedCarts>
|
7 |
+
</modules>
|
8 |
+
<global>
|
9 |
+
<models>
|
10 |
+
<anattadesign_abandonedcarts>
|
11 |
+
<class>AnattaDesign_AbandonedCarts_Model</class>
|
12 |
+
<resourceModel>anattadesign_abandonedcarts_mysql4</resourceModel>
|
13 |
+
</anattadesign_abandonedcarts>
|
14 |
+
<anattadesign_abandonedcarts_mysql4>
|
15 |
+
<class>AnattaDesign_AbandonedCarts_Model_Mysql4</class>
|
16 |
+
<entities>
|
17 |
+
<statistics>
|
18 |
+
<table>anattadesign_abandonedcarts_statistics</table>
|
19 |
+
</statistics>
|
20 |
+
<opstatistics>
|
21 |
+
<table>anattadesign_abandonedcarts_opstatistics</table>
|
22 |
+
</opstatistics>
|
23 |
+
</entities>
|
24 |
+
</anattadesign_abandonedcarts_mysql4>
|
25 |
+
</models>
|
26 |
+
<blocks>
|
27 |
+
<anattadesign_abandonedcarts>
|
28 |
+
<class>AnattaDesign_AbandonedCarts_Block</class>
|
29 |
+
</anattadesign_abandonedcarts>
|
30 |
+
</blocks>
|
31 |
+
<resources>
|
32 |
+
<anattadesign_abandonedcarts_setup>
|
33 |
+
<setup>
|
34 |
+
<module>AnattaDesign_AbandonedCarts</module>
|
35 |
+
<class>Mage_Eav_Model_Entity_Setup</class>
|
36 |
+
</setup>
|
37 |
+
<connection>
|
38 |
+
<use>core_setup</use>
|
39 |
+
</connection>
|
40 |
+
</anattadesign_abandonedcarts_setup>
|
41 |
+
</resources>
|
42 |
+
<helpers>
|
43 |
+
<anattadesign_abandonedcarts>
|
44 |
+
<class>AnattaDesign_AbandonedCarts_Helper</class>
|
45 |
+
</anattadesign_abandonedcarts>
|
46 |
+
</helpers>
|
47 |
+
<translate>
|
48 |
+
<modules>
|
49 |
+
<AnattaDesign_AbandonedCarts>
|
50 |
+
<files>
|
51 |
+
<default>AnattaDesign_AbandonedCarts.csv</default>
|
52 |
+
</files>
|
53 |
+
</AnattaDesign_AbandonedCarts>
|
54 |
+
</modules>
|
55 |
+
</translate>
|
56 |
+
<events>
|
57 |
+
<admin_session_user_login_success>
|
58 |
+
<observers>
|
59 |
+
<anattadesign_abandonedcarts_model_observer>
|
60 |
+
<class>AnattaDesign_AbandonedCarts_Model_Observer</class>
|
61 |
+
<method>uponAdminLogin</method>
|
62 |
+
</anattadesign_abandonedcarts_model_observer>
|
63 |
+
</observers>
|
64 |
+
</admin_session_user_login_success>
|
65 |
+
<controller_action_layout_generate_blocks_after>
|
66 |
+
<observers>
|
67 |
+
<anattadesign_awesomecheckout_addjs>
|
68 |
+
<!--<type>singleton</type>-->
|
69 |
+
<class>AnattaDesign_AbandonedCarts_Model_Observer</class>
|
70 |
+
<method>addJavascriptBlock</method>
|
71 |
+
</anattadesign_awesomecheckout_addjs>
|
72 |
+
</observers>
|
73 |
+
</controller_action_layout_generate_blocks_after>
|
74 |
+
<controller_action_postdispatch_checkout_onepage_index>
|
75 |
+
<observers>
|
76 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
77 |
+
<type>singleton</type>
|
78 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
79 |
+
<method>index</method>
|
80 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
81 |
+
</observers>
|
82 |
+
</controller_action_postdispatch_checkout_onepage_index>
|
83 |
+
<controller_action_postdispatch_checkout_onepage_saveMethod>
|
84 |
+
<observers>
|
85 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
86 |
+
<type>singleton</type>
|
87 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
88 |
+
<method>saveMethod</method>
|
89 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
90 |
+
</observers>
|
91 |
+
</controller_action_postdispatch_checkout_onepage_saveMethod>
|
92 |
+
<controller_action_predispatch_customer_account_loginPost>
|
93 |
+
<observers>
|
94 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
95 |
+
<type>singleton</type>
|
96 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
97 |
+
<method>loginPost</method>
|
98 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
99 |
+
</observers>
|
100 |
+
</controller_action_predispatch_customer_account_loginPost>
|
101 |
+
<controller_action_postdispatch_checkout_onepage_saveBilling>
|
102 |
+
<observers>
|
103 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
104 |
+
<type>singleton</type>
|
105 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
106 |
+
<method>saveBilling</method>
|
107 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
108 |
+
</observers>
|
109 |
+
</controller_action_postdispatch_checkout_onepage_saveBilling>
|
110 |
+
<controller_action_postdispatch_checkout_onepage_saveShipping>
|
111 |
+
<observers>
|
112 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
113 |
+
<type>singleton</type>
|
114 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
115 |
+
<method>saveShipping</method>
|
116 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
117 |
+
</observers>
|
118 |
+
</controller_action_postdispatch_checkout_onepage_saveShipping>
|
119 |
+
<controller_action_postdispatch_checkout_onepage_saveShippingMethod>
|
120 |
+
<observers>
|
121 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
122 |
+
<type>singleton</type>
|
123 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
124 |
+
<method>saveShippingMethod</method>
|
125 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
126 |
+
</observers>
|
127 |
+
</controller_action_postdispatch_checkout_onepage_saveShippingMethod>
|
128 |
+
<controller_action_postdispatch_checkout_onepage_savePayment>
|
129 |
+
<observers>
|
130 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
131 |
+
<type>singleton</type>
|
132 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
133 |
+
<method>savePayment</method>
|
134 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
135 |
+
</observers>
|
136 |
+
</controller_action_postdispatch_checkout_onepage_savePayment>
|
137 |
+
<controller_action_postdispatch_checkout_onepage_saveOrder>
|
138 |
+
<observers>
|
139 |
+
<anattadesign_abandonedcarts_checkout_observer>
|
140 |
+
<type>singleton</type>
|
141 |
+
<class>AnattaDesign_AbandonedCarts_Model_Checkout_Onepage_Observer</class>
|
142 |
+
<method>saveOrder</method>
|
143 |
+
</anattadesign_abandonedcarts_checkout_observer>
|
144 |
+
</observers>
|
145 |
+
</controller_action_postdispatch_checkout_onepage_saveOrder>
|
146 |
+
</events>
|
147 |
+
</global>
|
148 |
+
<admin>
|
149 |
+
<routers>
|
150 |
+
<abandonedcarts>
|
151 |
+
<use>admin</use>
|
152 |
+
<args>
|
153 |
+
<module>AnattaDesign_AbandonedCarts</module>
|
154 |
+
<frontName>abandonedcarts</frontName>
|
155 |
+
</args>
|
156 |
+
</abandonedcarts>
|
157 |
+
</routers>
|
158 |
+
</admin>
|
159 |
+
<adminhtml>
|
160 |
+
<translate>
|
161 |
+
<modules>
|
162 |
+
<AnattaDesign_AbandonedCarts>
|
163 |
+
<files>
|
164 |
+
<default>AnattaDesign_AbandonedCarts.csv</default>
|
165 |
+
</files>
|
166 |
+
</AnattaDesign_AbandonedCarts>
|
167 |
+
</modules>
|
168 |
+
</translate>
|
169 |
+
<layout>
|
170 |
+
<updates>
|
171 |
+
<anattadesign_abandonedcarts>
|
172 |
+
<file>anattadesign_abandonedcarts.xml</file>
|
173 |
+
</anattadesign_abandonedcarts>
|
174 |
+
</updates>
|
175 |
+
</layout>
|
176 |
+
</adminhtml>
|
177 |
+
</config>
|
app/code/local/AnattaDesign/AbandonedCarts/sql/anattadesign_abandonedcarts_setup/mysql4-install-1.0.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
$installer->run(
|
8 |
+
"CREATE TABLE `" . $installer->getTable( 'anattadesign_abandonedcarts/opstatistics' ) . "` (
|
9 |
+
`statistics_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
10 |
+
`sales_flat_quote_id` int(10) unsigned NOT NULL,
|
11 |
+
`step` varchar(32) NOT NULL,
|
12 |
+
`reached` tinyint(4) NOT NULL DEFAULT '0',
|
13 |
+
`moved` tinyint(4) NOT NULL DEFAULT '0',
|
14 |
+
`year` smallint(6) NOT NULL,
|
15 |
+
`month` tinyint(4) NOT NULL,
|
16 |
+
`date` datetime NOT NULL,
|
17 |
+
PRIMARY KEY (`statistics_id`),
|
18 |
+
KEY `dashboard_tab` (`year`,`month`,`step`,`reached`,`moved`)
|
19 |
+
) ENGINE=InnoDB"
|
20 |
+
);
|
21 |
+
|
22 |
+
$installer->run(
|
23 |
+
"CREATE TABLE `" . $installer->getTable( 'anattadesign_abandonedcarts/statistics' ) . "` (
|
24 |
+
`statistics_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
25 |
+
`sales_flat_quote_id` int(10) unsigned NOT NULL,
|
26 |
+
`step` varchar(32) NOT NULL,
|
27 |
+
`reached` tinyint(4) NOT NULL DEFAULT '0',
|
28 |
+
`moved` tinyint(4) NOT NULL DEFAULT '0',
|
29 |
+
`year` smallint(6) NOT NULL,
|
30 |
+
`month` tinyint(4) NOT NULL,
|
31 |
+
`date` datetime NOT NULL,
|
32 |
+
PRIMARY KEY (`statistics_id`),
|
33 |
+
KEY `dashboard_tab` (`year`,`month`,`step`,`reached`,`moved`)
|
34 |
+
) ENGINE=InnoDB"
|
35 |
+
);
|
36 |
+
|
37 |
+
$installer->endSetup();
|
38 |
+
|
39 |
+
Mage::helper( 'anattadesign_abandonedcarts' )->ping();
|
app/design/adminhtml/default/default/layout/anattadesign_abandonedcarts.xml
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
2 |
+
<layout>
|
3 |
+
<adminhtml_dashboard_index>
|
4 |
+
<reference name="head">
|
5 |
+
<action method="addCss">
|
6 |
+
<stylesheet>anattadesign/abandonedcarts/css/style.css</stylesheet>
|
7 |
+
</action>
|
8 |
+
<action method="addJs">
|
9 |
+
<script>anattadesign/abandonedcarts/zepto.js</script>
|
10 |
+
</action>
|
11 |
+
<action method="addJs">
|
12 |
+
<script>anattadesign/abandonedcarts/adminhack.js</script>
|
13 |
+
</action>
|
14 |
+
</reference>
|
15 |
+
</adminhtml_dashboard_index>
|
16 |
+
</layout>
|
app/design/adminhtml/default/default/template/anattadesign/abandonedcarts/widget.phtml
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( !Mage::helper( 'core' )->isModuleEnabled( 'AnattaDesign_AbandonedCarts' ) ) {
|
3 |
+
die();
|
4 |
+
}
|
5 |
+
?>
|
6 |
+
|
7 |
+
<link href='http://fonts.googleapis.com/css?family=Exo:600' rel='stylesheet' type='text/css'>
|
8 |
+
<link href='http://fonts.googleapis.com/css?family=Exo:100' rel='stylesheet' type='text/css'>
|
9 |
+
<link href='http://fonts.googleapis.com/css?family=Arvo' rel='stylesheet' type='text/css'>
|
10 |
+
<link href='http://fonts.googleapis.com/css?family=Ubuntu:400,500' rel='stylesheet' type='text/css'>
|
11 |
+
|
12 |
+
<?php
|
13 |
+
$display_month = array(
|
14 |
+
1 => 'January',
|
15 |
+
2 => 'February',
|
16 |
+
3 => 'March',
|
17 |
+
4 => 'April',
|
18 |
+
5 => 'May',
|
19 |
+
6 => 'June',
|
20 |
+
7 => 'July',
|
21 |
+
8 => 'August',
|
22 |
+
9 => 'September',
|
23 |
+
10 => 'October',
|
24 |
+
11 => 'November',
|
25 |
+
12 => 'December'
|
26 |
+
);
|
27 |
+
|
28 |
+
$current_year = date( 'Y' );
|
29 |
+
|
30 |
+
if ( isset( $_GET['month'] ) && intval( abs( $_GET['month'] ) ) != 0 ) {
|
31 |
+
$queried_month = intval( abs( $_GET['month'] ) );
|
32 |
+
} else {
|
33 |
+
$queried_month = date( 'm' );
|
34 |
+
}
|
35 |
+
|
36 |
+
if ( isset( $_GET['year'] ) && intval( abs( $_GET['year'] ) ) != 0 ) {
|
37 |
+
$queried_year = intval( abs( $_GET['year'] ) );
|
38 |
+
} else {
|
39 |
+
$queried_year = $current_year;
|
40 |
+
}
|
41 |
+
|
42 |
+
// Select table name where data is stored
|
43 |
+
if ( Mage::helper( 'anattadesign_abandonedcarts' )->isAwesomeCheckoutActive() ) {
|
44 |
+
|
45 |
+
$steps = array(
|
46 |
+
0 => array(
|
47 |
+
'step' => 'shipping'
|
48 |
+
),
|
49 |
+
1 => array(
|
50 |
+
'step' => 'shipping_method'
|
51 |
+
),
|
52 |
+
2 => array(
|
53 |
+
'step' => 'payment'
|
54 |
+
),
|
55 |
+
3 => array(
|
56 |
+
'step' => 'review'
|
57 |
+
)
|
58 |
+
);
|
59 |
+
$table = 'anattadesign_abandonedcarts_statistics';
|
60 |
+
} else {
|
61 |
+
|
62 |
+
$steps = array(
|
63 |
+
0 => array(
|
64 |
+
'step' => 'login'
|
65 |
+
),
|
66 |
+
1 => array(
|
67 |
+
'step' => 'billing'
|
68 |
+
),
|
69 |
+
2 => array(
|
70 |
+
'step' => 'shipping'
|
71 |
+
),
|
72 |
+
3 => array(
|
73 |
+
'step' => 'shipping_method'
|
74 |
+
),
|
75 |
+
4 => array(
|
76 |
+
'step' => 'payment'
|
77 |
+
),
|
78 |
+
5 => array(
|
79 |
+
'step' => 'review'
|
80 |
+
)
|
81 |
+
);
|
82 |
+
|
83 |
+
$table = 'anattadesign_abandonedcarts_opstatistics';
|
84 |
+
}
|
85 |
+
|
86 |
+
// Add reached & moved values in array
|
87 |
+
$connection = Mage::getSingleton( 'core/resource' )->getConnection( 'core_read' );
|
88 |
+
foreach ( $steps as &$step ) {
|
89 |
+
$query = "SELECT sum(reached) as reached, sum(moved) as moved FROM $table WHERE month = $queried_month AND year = $queried_year AND step = '" . $step['step'] . "';";
|
90 |
+
$resource = $connection->query( $query );
|
91 |
+
$fetched_data = $resource->fetch();
|
92 |
+
$step['reached'] = intval( $fetched_data['reached'] );
|
93 |
+
$step['moved'] = intval( $fetched_data['moved'] );
|
94 |
+
$step['loss'] = $step['reached'] == $step['moved'] ? 0 : ( $step['reached'] - $step['moved'] ) * 100 / $step['reached'];
|
95 |
+
unset( $step );
|
96 |
+
}
|
97 |
+
|
98 |
+
$step_no = 1;
|
99 |
+
$step_names = array(
|
100 |
+
'login' => 'Login',
|
101 |
+
'billing' => 'Billing',
|
102 |
+
'shipping' => 'Shipping',
|
103 |
+
'shipping_method' => 'Shipping Method',
|
104 |
+
'payment' => 'Payment',
|
105 |
+
'review' => 'Review'
|
106 |
+
);
|
107 |
+
|
108 |
+
// adjustments to data
|
109 |
+
if ( Mage::helper( 'anattadesign_abandonedcarts' )->isAwesomeCheckoutActive() ) {
|
110 |
+
|
111 |
+
} else {
|
112 |
+
// make sure login[reached] is not smaller than billing[reached] (for the sake of funnel)
|
113 |
+
$steps[0]['reached'] = max( $steps[0]['reached'], $steps[1]['reached'] );
|
114 |
+
// make sure shipping step which is optional only depicts the loss, so add padding here (for the sake of funnel)
|
115 |
+
$diff = $steps[1]['moved'] - $steps[2]['reached'];
|
116 |
+
$steps[2]['reached']+= $diff;
|
117 |
+
$steps[2]['moved']+= $diff;
|
118 |
+
}
|
119 |
+
|
120 |
+
// calculate abandonment rate & loss for the queried month-year
|
121 |
+
$abandonedment_rate = $steps[0]['reached'] == $steps[count( $steps ) - 1]['moved'] ? 0 : round( ( $steps[0]['reached'] - $steps[count( $steps ) - 1]['moved'] ) * 100 / $steps[0]['reached'], 2 );
|
122 |
+
$approx_loss = ( Mage::helper( 'anattadesign_abandonedcarts' )->getSalesVolume( $queried_year, $queried_month ) * ( $abandonedment_rate / 100 ) ) / ( 1 - ( $abandonedment_rate / 100 ) );
|
123 |
+
$approx_loss = Mage::helper( 'core' )->currency( $approx_loss, true, false );
|
124 |
+
|
125 |
+
// Get message to display user
|
126 |
+
$message = Mage::helper( 'anattadesign_abandonedcarts' )->getMessage();
|
127 |
+
?>
|
128 |
+
|
129 |
+
<div id="ac-wrapper">
|
130 |
+
<!--header-->
|
131 |
+
<header id="ac-header">
|
132 |
+
<img class="left" src="<?php echo $this->getSkinUrl( 'anattadesign/abandonedcarts/css/images/logo.jpg' ); ?>" alt="Abandoned Cart"><!--logo-->
|
133 |
+
<!--calender-->
|
134 |
+
<div id="cal-widget" data-month="<?php echo $queried_month; ?>" data-year="<?php echo $queried_year; ?>" class="right">
|
135 |
+
<a class="previous-month" href="#"></a>
|
136 |
+
<span><?php echo $display_month[$queried_month] . ' ' . $queried_year; ?></span>
|
137 |
+
<a class="next-month" href="#"></a>
|
138 |
+
</div>
|
139 |
+
<!--/calender-->
|
140 |
+
</header>
|
141 |
+
<!--/header-->
|
142 |
+
<div id="ac-content">
|
143 |
+
<?php if ( $steps[0]['reached'] == 0 ) { ?>
|
144 |
+
<ul>
|
145 |
+
<li><p class="out-of-data">You will see some data here as soon as we have it.</p></li>
|
146 |
+
</ul>
|
147 |
+
<?php } else { ?>
|
148 |
+
<!--stats list-->
|
149 |
+
<ul>
|
150 |
+
<?php foreach ( $steps as $key => $step ) { ?>
|
151 |
+
<li>
|
152 |
+
<div class="ac-steps left"><span><?php echo $key + 1; ?> </span><?php echo $step_names[$step['step']]; ?></div>
|
153 |
+
<div class="ac-graph left">
|
154 |
+
<center><?php echo $step['reached']; ?></center>
|
155 |
+
<div class="ac-usr-in" style="width:<?php echo $steps[0]['reached'] == 0 ? 0 : round( $step['reached'] * 100 / $steps[0]['reached'], 0 ); ?>px;">
|
156 |
+
<div class="ac-usr-out" style="height:<?php echo round( $step['loss'], 0 ); ?>%"></div>
|
157 |
+
</div>
|
158 |
+
<?php
|
159 |
+
// if on last step, show last 'moved' value i.e. tail of funnel
|
160 |
+
if ( $key == count( $steps ) - 1 )
|
161 |
+
echo '<center>' . $step['moved'] . '<center>';
|
162 |
+
?>
|
163 |
+
</div>
|
164 |
+
<div class="ac-stats left"><div> <?php echo round( $step['loss'], 2 ); ?> %</div></div>
|
165 |
+
</li>
|
166 |
+
<?php } ?>
|
167 |
+
</ul>
|
168 |
+
<!--stats list-->
|
169 |
+
<?php } ?>
|
170 |
+
<!--sidebar-->
|
171 |
+
<div id="ac-sidebar">
|
172 |
+
<div class="ac-notif">
|
173 |
+
<div>
|
174 |
+
<big>NOTIFICATION!</big>
|
175 |
+
<?php echo $message; ?>
|
176 |
+
</div>
|
177 |
+
</div>
|
178 |
+
<div class="ac-side-content">
|
179 |
+
<span>>APPX. LOSS</span>
|
180 |
+
<p>*based on total monthly sales</p>
|
181 |
+
<big><?php echo $approx_loss; ?></big>
|
182 |
+
<p class="btm">TOTAL PERCENTAGE <br/>OF ABANDONMENT</p>
|
183 |
+
<big class="btm"><?php echo $abandonedment_rate; ?>%</big>
|
184 |
+
</div>
|
185 |
+
</div>
|
186 |
+
<!--sidebar-->
|
187 |
+
</div>
|
188 |
+
</div>
|
app/etc/modules/AnattaDesign_AbandonedCarts.xml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<AnattaDesign_AbandonedCarts>
|
5 |
+
<active>true</active>
|
6 |
+
<codePool>local</codePool>
|
7 |
+
</AnattaDesign_AbandonedCarts>
|
8 |
+
</modules>
|
9 |
+
</config>
|
package.xml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>AnattaDesign_AbandonedCarts</name>
|
4 |
+
<version>1.0.0</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license>GPL</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>Track abandoned carts & show a funnel report to show statistics</summary>
|
10 |
+
<description>Track abandoned carts &amp; show a funnel report to show statistics</description>
|
11 |
+
<notes>First version - Track abandoned carts &amp; show a funnel report to show statistics</notes>
|
12 |
+
<authors><author><name>Anatta Design</name><user>anattadesign</user><email>nirav@anattadesign.com</email></author></authors>
|
13 |
+
<date>2012-12-05</date>
|
14 |
+
<time>15:37:55</time>
|
15 |
+
<contents><target name="magelocal"><dir name="AnattaDesign"><dir name="AbandonedCarts"><dir name="Block"><file name="Widget.php" hash="c6baf4dc8dc25ed7c62e3b89cc78c413"/></dir><dir name="Helper"><file name="Data.php" hash="093342a0866fc724b4b0e6c545ef177b"/></dir><dir name="Model"><dir name="Checkout"><dir name="Onepage"><file name="Observer.php" hash="e7fdbe25fa84f25b5d1d49913489c147"/></dir></dir><dir name="Mysql4"><dir name="Opstatistics"><file name="Collection.php" hash="66b29da439c196f325c9faec4b171f30"/></dir><file name="Opstatistics.php" hash="9ccaa740ddbfa607204bde0222fceafc"/><dir name="Statistics"><file name="Collection.php" hash="c92997470a39016001c196fa4bf17861"/></dir><file name="Statistics.php" hash="88d17295de3fc05637ac55e71c52a3a3"/></dir><file name="Observer.php" hash="ab8d6551678e9169e80dd947e53d4329"/><file name="Opstatistics.php" hash="f3bfe7b7e44ef33e15877332a767b25a"/><file name="Statistics.php" hash="50a8c9c194a30613cb98471f4a66e7a9"/></dir><dir name="controllers"><file name="WidgetController.php" hash="ca23d690e09435f5f4df54c539dea74b"/></dir><dir name="etc"><file name="config.xml" hash="558f158a47ded67e33e113df97a1ef8a"/></dir><dir name="sql"><dir name="anattadesign_abandonedcarts_setup"><file name="mysql4-install-1.0.php" hash="b235486189e6609d4f9abc82ffae76c7"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="anattadesign_abandonedcarts.xml" hash="c14481626a0c367d974a5660ed930482"/></dir><dir name="template"><dir name="anattadesign"><dir name="abandonedcarts"><file name="widget.phtml" hash="afb7f04d2c91e878b7afed12c5f96966"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="AnattaDesign_AbandonedCarts.xml" hash="fba54c1c1c7b2abd323a56bcdd56be20"/></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="anattadesign"><dir name="abandonedcarts"><dir name="css"><dir><dir name="images"><file name="ar-lt.jpg" hash="2e4a2506cc63419a0e0c9a41669a8bda"/><file name="ar-rt.jpg" hash="8ef650d1c452ea7a4cc846f0999fddc0"/><file name="logo.jpg" hash="df05a2e01e46a38652d3514046ae30cd"/><file name="notifications-btm.gif" hash="716074a30178e9aca3b1fe0698cfcfb7"/><file name="notifications.gif" hash="343c52ea083df65f1a02c6d7334281c2"/></dir></dir><file name="style.css" hash="9f88676ad1011b2569a1f1c3e08d9297"/></dir></dir></dir></dir></dir></dir></target></contents>
|
16 |
+
<compatible/>
|
17 |
+
<dependencies><required><php><min>5.2.0</min><max>5.4.6</max></php></required></dependencies>
|
18 |
+
</package>
|
skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/ar-lt.jpg
ADDED
Binary file
|
skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/ar-rt.jpg
ADDED
Binary file
|
skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/logo.jpg
ADDED
Binary file
|
skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/notifications-btm.gif
ADDED
Binary file
|
skin/adminhtml/default/default/anattadesign/abandonedcarts/css/images/notifications.gif
ADDED
Binary file
|
skin/adminhtml/default/default/anattadesign/abandonedcarts/css/style.css
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Abandoned Cart - styles */
|
2 |
+
#ac-wrapper{ width: 372px; overflow: hidden; position: relative; left: -7px; }
|
3 |
+
#ac-wrapper .left{ float: left;}
|
4 |
+
#ac-wrapper .right{ float: right;}
|
5 |
+
|
6 |
+
/* header */
|
7 |
+
#ac-header{ height: 35px; border-bottom: 1px solid #cccccc; overflow:hidden; font-family:"Exo"; font-size: 16px; font-weight: 600; padding: 10px 0 0 0; }
|
8 |
+
|
9 |
+
#cal-widget { width: auto;}
|
10 |
+
#cal-widget a{ width: 11px; height: 17px; display: block; margin: 2px 2px 0px 2px; }
|
11 |
+
#cal-widget span{ display: block; float: left;}
|
12 |
+
#cal-widget a.previous-month {background: url(images/ar-lt.jpg) no-repeat; float: left}
|
13 |
+
#cal-widget a.next-month{ background: url(images/ar-rt.jpg) no-repeat; float: right;}
|
14 |
+
|
15 |
+
/* content */
|
16 |
+
#ac-content { overflow:hidden;}
|
17 |
+
#ac-content ul{ list-style: none; padding: 5px 0 0 0; margin: 0; font-family: "Ubuntu"; width: 217px; float: left;}
|
18 |
+
#ac-content ul li{ clear: both; padding-top: 5px;}
|
19 |
+
|
20 |
+
.ac-steps{ font-size: 12px; color: #4d4d4d; width:70px; line-height: 16px; padding-top: 10px;}
|
21 |
+
.ac-steps span{ color: #b3b3b3;}
|
22 |
+
|
23 |
+
.ac-graph{ width: 100px; font-size: 10px;}
|
24 |
+
.ac-graph .ac-usr-in{ margin: 0px auto; background: #ff9200; height: 48px; border-radius: 3px;-moz-border-radius: 3px;-webkit-border-radius: 3px; position: relative;}
|
25 |
+
.ac-graph .ac-usr-out {background: #000; position: absolute; bottom: 0px; width: 100%; }
|
26 |
+
.ac-stats { color: #0071bc; font-size: 12px; padding: 45px 0 0 0; position: relative; }
|
27 |
+
.ac-stats div { position: absolute; left: 0; bottom: 0; }
|
28 |
+
|
29 |
+
/* sidebar */
|
30 |
+
#ac-sidebar { float: right; width: 154px; }
|
31 |
+
#ac-sidebar .ac-notif { background: url(images/notifications.gif) no-repeat; margin :15px 0 20px 0;}
|
32 |
+
#ac-sidebar .ac-notif div{background: url(images/notifications-btm.gif) left bottom no-repeat; width: 118px; min-height: 110px; padding: 21px 25px 20px 15px; }
|
33 |
+
|
34 |
+
.ac-side-content,.ac-notif { font-family: "Ubuntu"; color: #000000; font-size: 10px;}
|
35 |
+
.ac-notif a{ color: #0071bc;}
|
36 |
+
.ac-notif a:hover{ text-decoration: none;}
|
37 |
+
.ac-notif big{ text-transform: uppercase;}
|
38 |
+
.ac-notif p{ padding: 15px 0; margin: 0;}
|
39 |
+
|
40 |
+
.ac-side-content {}
|
41 |
+
.ac-side-content p{ padding: 3px 0 10px 0; margin: 0;}
|
42 |
+
.ac-side-content big{ font-size: 20px;font-family: "Ubuntu"; font-weight: 500;}
|
43 |
+
.ac-side-content p.btm{ font-size: 11px; text-transform: uppercase; padding: 37px 0 15px 0 ;}
|
44 |
+
.ac-side-content big.btm{ font-size: 36px;}
|
45 |
+
|
46 |
+
.out-of-data { width: 90%; }
|