Commit dd593502a79a30e138acf0a7ad62161bf3637510

Authored by Aurélio A. Heckert
1 parent 415d3eec

Fixing the ShoppingCartPlugin

This commit, with the participation of Lucas Melo, does:

* Make action list from ShoppingCartPluginController Fault-tolerant.
* Create a basic logging system and replace debug alerts.
* Limit the cart size to protect against cookie limit.
* Add more loading style possibilities.
* Add a little fault-tolerance to the process of loading the carts items.
* Force colorbox to "Shopping checkout" button.
* Add L10n to Cart JS interaction.
plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb
@@ -57,14 +57,24 @@ class ShoppingCartPluginController < PublicController @@ -57,14 +57,24 @@ class ShoppingCartPluginController < PublicController
57 def list 57 def list
58 if validate_cart_presence 58 if validate_cart_presence
59 products = self.cart[:items].collect do |id, quantity| 59 products = self.cart[:items].collect do |id, quantity|
60 - product = Product.find(id)  
61 - { :id => product.id,  
62 - :name => product.name,  
63 - :price => get_price(product, product.enterprise.environment),  
64 - :description => product.description,  
65 - :picture => product.default_image(:minor),  
66 - :quantity => quantity  
67 - } 60 + product = Product.find_by_id(id)
  61 + if product
  62 + { :id => product.id,
  63 + :name => product.name,
  64 + :price => get_price(product, product.enterprise.environment),
  65 + :description => product.description,
  66 + :picture => product.default_image(:minor),
  67 + :quantity => quantity
  68 + }
  69 + else
  70 + { :id => id,
  71 + :name => _('Undefined product'),
  72 + :price => 0,
  73 + :description => _('Wrong product id'),
  74 + :picture => '',
  75 + :quantity => quantity
  76 + }
  77 + end
68 end 78 end
69 render :text => { 79 render :text => {
70 :ok => true, 80 :ok => true,
plugins/shopping_cart/public/cart.js
@@ -21,7 +21,7 @@ function Cart(config) { @@ -21,7 +21,7 @@ function Cart(config) {
21 }, 21 },
22 cache: false, 22 cache: false,
23 error: function(ajax, status, errorThrown) { 23 error: function(ajax, status, errorThrown) {
24 - alert('Visibility - HTTP '+status+': '+errorThrown); 24 + log.error('Visibility - HTTP '+status, errorThrown);
25 } 25 }
26 }); 26 });
27 $(".cart-buy", this.cartElem).colorbox({ href: '/plugin/shopping_cart/buy' }); 27 $(".cart-buy", this.cartElem).colorbox({ href: '/plugin/shopping_cart/buy' });
@@ -36,12 +36,12 @@ function Cart(config) { @@ -36,12 +36,12 @@ function Cart(config) {
36 url: '/plugin/shopping_cart/list', 36 url: '/plugin/shopping_cart/list',
37 dataType: 'json', 37 dataType: 'json',
38 success: function(data, ststus, ajax){ 38 success: function(data, ststus, ajax){
39 - if ( !data.ok ) alert(data.error.message); 39 + if ( !data.ok ) log.error(data.error);
40 else me.addToList(data, true); 40 else me.addToList(data, true);
41 }, 41 },
42 cache: false, 42 cache: false,
43 error: function(ajax, status, errorThrown) { 43 error: function(ajax, status, errorThrown) {
44 - alert('List cart items - HTTP '+status+': '+errorThrown); 44 + log.error('List cart items - HTTP '+status, errorThrown);
45 } 45 }
46 }); 46 });
47 } 47 }
@@ -49,6 +49,7 @@ function Cart(config) { @@ -49,6 +49,7 @@ function Cart(config) {
49 Cart.prototype.addToList = function(data, clear) { 49 Cart.prototype.addToList = function(data, clear) {
50 if( clear ) this.itemsBox.empty(); 50 if( clear ) this.itemsBox.empty();
51 var me = this; 51 var me = this;
  52 + this.productsLength = data.products.length;
52 for( var item,i=0; item=data.products[i]; i++ ) { 53 for( var item,i=0; item=data.products[i]; i++ ) {
53 this.items[item.id] = { price:item.price, quantity:item.quantity }; 54 this.items[item.id] = { price:item.price, quantity:item.quantity };
54 this.updateTotal(); 55 this.updateTotal();
@@ -75,9 +76,7 @@ function Cart(config) { @@ -75,9 +76,7 @@ function Cart(config) {
75 input.onchange = function() { 76 input.onchange = function() {
76 me.updateQuantity(this, this.productId, this.value); 77 me.updateQuantity(this, this.productId, this.value);
77 }; 78 };
78 -// document.location.href = "#"+liId;  
79 -// document.location.href = "#"+this.cartElem.id;  
80 -// history.go(-2); 79 + // TODO: Scroll to newest item
81 var liBg = li.css("background-color"); 80 var liBg = li.css("background-color");
82 li[0].style.backgroundColor = "#FF0"; 81 li[0].style.backgroundColor = "#FF0";
83 li.animate({ backgroundColor: liBg }, 1000); 82 li.animate({ backgroundColor: liBg }, 1000);
@@ -103,7 +102,7 @@ function Cart(config) { @@ -103,7 +102,7 @@ function Cart(config) {
103 dataType: 'json', 102 dataType: 'json',
104 success: function(data, status, ajax){ 103 success: function(data, status, ajax){
105 if ( !data.ok ) { 104 if ( !data.ok ) {
106 - alert(data.error.message); 105 + log.error(data.error);
107 input.value = input.lastValue; 106 input.value = input.lastValue;
108 } 107 }
109 else { 108 else {
@@ -114,7 +113,7 @@ function Cart(config) { @@ -114,7 +113,7 @@ function Cart(config) {
114 }, 113 },
115 cache: false, 114 cache: false,
116 error: function(ajax, status, errorThrown) { 115 error: function(ajax, status, errorThrown) {
117 - alert('Add item - HTTP '+status+': '+errorThrown); 116 + log.error('Add item - HTTP '+status, errorThrown);
118 input.value = input.lastValue; 117 input.value = input.lastValue;
119 }, 118 },
120 complete: function(){ 119 complete: function(){
@@ -132,7 +131,14 @@ function Cart(config) { @@ -132,7 +131,14 @@ function Cart(config) {
132 } 131 }
133 132
134 Cart.addItem = function(itemId, link) { 133 Cart.addItem = function(itemId, link) {
  134 + if ( this.productsLength > 100 ) {
  135 + // This limit protect the user from losing data on cookie limit.
  136 + // This is NOT limiting to 100 products, is limiting to 100 kinds of products.
  137 + alert(shoppingCartPluginL10n.maxNumberOfItens);
  138 + return false;
  139 + }
135 link.intervalId = setInterval(function() { 140 link.intervalId = setInterval(function() {
  141 + $(link).addClass('loading');
136 steps = ['w', 'n', 'e', 's']; 142 steps = ['w', 'n', 'e', 's'];
137 if( !link.step || link.step==3 ) link.step = 0; 143 if( !link.step || link.step==3 ) link.step = 0;
138 link.step++; 144 link.step++;
@@ -140,6 +146,7 @@ function Cart(config) { @@ -140,6 +146,7 @@ function Cart(config) {
140 }, 100); 146 }, 100);
141 var stopBtLoading = function() { 147 var stopBtLoading = function() {
142 clearInterval(link.intervalId); 148 clearInterval(link.intervalId);
  149 + $(link).removeClass('loading');
143 $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false }); 150 $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false });
144 }; 151 };
145 this.instance.addItem(itemId, stopBtLoading); 152 this.instance.addItem(itemId, stopBtLoading);
@@ -151,12 +158,12 @@ function Cart(config) { @@ -151,12 +158,12 @@ function Cart(config) {
151 url: '/plugin/shopping_cart/add/'+ itemId, 158 url: '/plugin/shopping_cart/add/'+ itemId,
152 dataType: 'json', 159 dataType: 'json',
153 success: function(data, status, ajax){ 160 success: function(data, status, ajax){
154 - if ( !data.ok ) alert(data.error.message); 161 + if ( !data.ok ) log.error('Shopping cart data failure', data.error);
155 else me.addToList(data); 162 else me.addToList(data);
156 }, 163 },
157 cache: false, 164 cache: false,
158 error: function(ajax, status, errorThrown) { 165 error: function(ajax, status, errorThrown) {
159 - alert('Add item - HTTP '+status+': '+errorThrown); 166 + log.error('Add item - HTTP '+status, errorThrown);
160 }, 167 },
161 complete: callback 168 complete: callback
162 }); 169 });
@@ -174,12 +181,12 @@ function Cart(config) { @@ -174,12 +181,12 @@ function Cart(config) {
174 url: '/plugin/shopping_cart/remove/'+ itemId, 181 url: '/plugin/shopping_cart/remove/'+ itemId,
175 dataType: 'json', 182 dataType: 'json',
176 success: function(data, status, ajax){ 183 success: function(data, status, ajax){
177 - if ( !data.ok ) alert(data.error.message); 184 + if ( !data.ok ) log.error(data.error);
178 else me.removeFromList(data.product_id); 185 else me.removeFromList(data.product_id);
179 }, 186 },
180 cache: false, 187 cache: false,
181 error: function(ajax, status, errorThrown) { 188 error: function(ajax, status, errorThrown) {
182 - alert('Remove item - HTTP '+status+': '+errorThrown); 189 + log.error('Remove item - HTTP '+status, errorThrown);
183 } 190 }
184 }); 191 });
185 } 192 }
@@ -197,7 +204,7 @@ function Cart(config) { @@ -197,7 +204,7 @@ function Cart(config) {
197 dataType: 'json', 204 dataType: 'json',
198 cache: false, 205 cache: false,
199 error: function(ajax, status, errorThrown) { 206 error: function(ajax, status, errorThrown) {
200 - alert('Show - HTTP '+status+': '+errorThrown); 207 + log.error('Show - HTTP '+status, errorThrown);
201 } 208 }
202 }); 209 });
203 this.visible = true; 210 this.visible = true;
@@ -212,7 +219,7 @@ function Cart(config) { @@ -212,7 +219,7 @@ function Cart(config) {
212 dataType: 'json', 219 dataType: 'json',
213 cache: false, 220 cache: false,
214 error: function(ajax, status, errorThrown) { 221 error: function(ajax, status, errorThrown) {
215 - alert('Hide - HTTP '+status+': '+errorThrown); 222 + log.error('Hide - HTTP '+status, errorThrown);
216 } 223 }
217 }); 224 });
218 this.visible = false; 225 this.visible = false;
@@ -248,7 +255,7 @@ function Cart(config) { @@ -248,7 +255,7 @@ function Cart(config) {
248 url: '/plugin/shopping_cart/clean', 255 url: '/plugin/shopping_cart/clean',
249 dataType: 'json', 256 dataType: 'json',
250 success: function(data, status, ajax){ 257 success: function(data, status, ajax){
251 - if ( !data.ok ) alert(data.error.message); 258 + if ( !data.ok ) log.error(data.error);
252 else{ 259 else{
253 me.items = {}; 260 me.items = {};
254 $(me.cartElem).slideUp(500, function() { 261 $(me.cartElem).slideUp(500, function() {
@@ -261,7 +268,7 @@ function Cart(config) { @@ -261,7 +268,7 @@ function Cart(config) {
261 }, 268 },
262 cache: false, 269 cache: false,
263 error: function(ajax, status, errorThrown) { 270 error: function(ajax, status, errorThrown) {
264 - alert('Remove item - HTTP '+status+': '+errorThrown); 271 + log.error('Remove item - HTTP '+status, errorThrown);
265 } 272 }
266 }); 273 });
267 } 274 }
@@ -288,7 +295,7 @@ function Cart(config) { @@ -288,7 +295,7 @@ function Cart(config) {
288 }, 295 },
289 cache: false, 296 cache: false,
290 error: function(ajax, status, errorThrown) { 297 error: function(ajax, status, errorThrown) {
291 - alert('Send request - HTTP '+status+': '+errorThrown); 298 + log.error('Send request - HTTP '+status, errorThrown);
292 }, 299 },
293 complete: function() { 300 complete: function() {
294 $.colorbox.close(); 301 $.colorbox.close();
@@ -311,7 +318,10 @@ function Cart(config) { @@ -311,7 +318,10 @@ function Cart(config) {
311 }, 318 },
312 cache: false, 319 cache: false,
313 error: function(ajax, status, errorThrown) { 320 error: function(ajax, status, errorThrown) {
314 - alert('Error getting shopping cart - HTTP '+status+': '+errorThrown); 321 + log.error('Error getting shopping cart - HTTP '+status, errorThrown);
  322 + if ( confirm(shoppingCartPluginL10n.getProblemConfirmReload) ) {
  323 + document.location.reload();
  324 + }
315 } 325 }
316 }); 326 });
317 }); 327 });
plugins/shopping_cart/views/cart.html.erb
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a> 7 <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a>
8 <ul class="cart-items"></ul> 8 <ul class="cart-items"></ul>
9 <div class="cart-total"><%=_('Total:')%> <b></b></div> 9 <div class="cart-total"><%=_('Total:')%> <b></b></div>
10 - <a href="/plugin/shopping_cart/buy" class="cart-buy"><%=_('Shopping checkout')%></a> 10 + <a href="/plugin/shopping_cart/buy" class="cart-buy colorbox"><%=_('Shopping checkout')%></a>
11 </div> 11 </div>
12 <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle"> 12 <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle">
13 <span class="str-show"><%=_('Show basket')%></span> 13 <span class="str-show"><%=_('Show basket')%></span>
@@ -15,3 +15,15 @@ @@ -15,3 +15,15 @@
15 </a> 15 </a>
16 </div> 16 </div>
17 </div> 17 </div>
  18 +<script>
  19 + shoppingCartPluginL10n = {
  20 + getProblemConfirmReload: <%= (
  21 + _('Ups... I had a problem to load the basket list.') +
  22 + "\n" +
  23 + _('Did you want to reload this page?')
  24 + ).to_json %>,
  25 + maxNumberOfItens: <%= (
  26 + _('Sorry, you can\'t have more then 100 kinds of items on this basket.')
  27 + ).to_json %>
  28 + }
  29 +</script>
public/javascripts/application.js
@@ -918,6 +918,41 @@ function facet_options_toggle(id, url) { @@ -918,6 +918,41 @@ function facet_options_toggle(id, url) {
918 }); 918 });
919 } 919 }
920 920
  921 +if ( !console ) console = {};
  922 +if ( !console.log ) console.log = function(){};
  923 +
  924 +// Two ways to call it:
  925 +// log(mixin1[, mixin2[, ...]]);
  926 +// log('<type>', mixin1[, mixin2[, ...]]);
  927 +// Where <type> may be: log, info warn, or error
  928 +window.log = function log() {
  929 + var type = arguments[0];
  930 + var argsClone = jQuery.merge([], arguments); // cloning the read-only arguments array.
  931 + if ( ['info', 'warn', 'error'].indexOf(type) == -1 ) {
  932 + type = 'log';
  933 + } else {
  934 + argsClone.shift();
  935 + }
  936 + var method = type;
  937 + if ( !console[method] ) method = 'log';
  938 + console[method].apply( console, jQuery.merge([(new Date).toISOString()], argsClone) );
  939 +}
  940 +
  941 +// Call log.info(mixin1[, mixin2[, ...]]);
  942 +log.info = function() {
  943 + window.log.apply(window, jQuery.merge(['info'], arguments));
  944 +}
  945 +
  946 +// Call log.warn(mixin1[, mixin2[, ...]]);
  947 +log.warn = function() {
  948 + window.log.apply(window, jQuery.merge(['warn'], arguments));
  949 +}
  950 +
  951 +// Call log.error(mixin1[, mixin2[, ...]]);
  952 +log.error = function() {
  953 + window.log.apply(window, jQuery.merge(['error'], arguments));
  954 +}
  955 +
921 jQuery(function($) { 956 jQuery(function($) {
922 $('.colorbox').live('click', function() { 957 $('.colorbox').live('click', function() {
923 $.fn.colorbox({ 958 $.fn.colorbox({