Commit b1e9dfaf901089394edf9f2af0a6bdcf74238d1d

Authored by Rodrigo Souto
2 parents cb82652b c270452d

Merge branch 'merge-requests/290' into stable

plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb
... ... @@ -13,9 +13,15 @@ class ShoppingCartPluginController < PublicController
13 13 def get
14 14 config =
15 15 if cart.nil?
16   - { 'enterprise_id' => nil, 'hasProducts' => false }
  16 + { :enterprise_id => nil,
  17 + :has_products => false,
  18 + :visible => false,
  19 + :products => []}
17 20 else
18   - { 'enterprise_id' => cart[:enterprise_id], 'hasProducts' => (cart[:items].keys.size > 0) }
  21 + { :enterprise_id => cart[:enterprise_id],
  22 + :has_products => (cart[:items].keys.size > 0),
  23 + :visible => visible?,
  24 + :products => products}
19 25 end
20 26 render :text => config.to_json
21 27 end
... ... @@ -56,16 +62,6 @@ class ShoppingCartPluginController < PublicController
56 62  
57 63 def list
58 64 if validate_cart_presence
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   - }
68   - end
69 65 render :text => {
70 66 :ok => true,
71 67 :error => {:code => 0},
... ... @@ -128,7 +124,7 @@ class ShoppingCartPluginController < PublicController
128 124 end
129 125  
130 126 def visibility
131   - render :text => self.cart.has_key?(:visibility) ? self.cart[:visibility].to_json : true.to_json
  127 + render :text => visible?.to_json
132 128 end
133 129  
134 130 def show
... ... @@ -317,4 +313,31 @@ class ShoppingCartPluginController < PublicController
317 313 :_noosfero_plugin_shopping_cart
318 314 end
319 315  
  316 + def visible?
  317 + !self.cart.has_key?(:visibility) || self.cart[:visibility]
  318 + end
  319 +
  320 + def products
  321 + self.cart[:items].collect do |id, quantity|
  322 + product = Product.find_by_id(id)
  323 + if product
  324 + { :id => product.id,
  325 + :name => product.name,
  326 + :price => get_price(product, product.enterprise.environment),
  327 + :description => product.description,
  328 + :picture => product.default_image(:minor),
  329 + :quantity => quantity
  330 + }
  331 + else
  332 + { :id => id,
  333 + :name => _('Undefined product'),
  334 + :price => 0,
  335 + :description => _('Wrong product id'),
  336 + :picture => '',
  337 + :quantity => quantity
  338 + }
  339 + end
  340 + end
  341 + end
  342 +
320 343 end
... ...
plugins/shopping_cart/public/cart.js
... ... @@ -6,50 +6,36 @@ function Cart(config) {
6 6 this.contentBox = $("#cart1 .cart-content");
7 7 this.itemsBox = $("#cart1 .cart-items");
8 8 this.items = {};
9   - this.empty = !config.hasProducts;
  9 + this.empty = !config.has_products;
10 10 this.visible = false;
11 11 $(".cart-buy", this.cartElem).button({ icons: { primary: 'ui-icon-cart'} });
12 12 if (!this.empty) {
13 13 $(this.cartElem).show();
14   - me = this;
15   - $.ajax({
16   - url: '/plugin/shopping_cart/visibility',
17   - dataType: 'json',
18   - success: function(data, status, ajax){
19   - me.visible = /^true$/i.test(data);
20   - me.listProducts();
21   - },
22   - cache: false,
23   - error: function(ajax, status, errorThrown) {
24   - alert('Visibility - HTTP '+status+': '+errorThrown);
25   - }
26   - });
27   - $(".cart-buy", this.cartElem).colorbox({ href: '/plugin/shopping_cart/buy' });
  14 + this.visible = config.visible;
  15 + this.addToList(config.products, true)
28 16 }
29 17 }
30 18  
31 19 (function($){
32 20  
33   - Cart.prototype.listProducts = function() {
  21 + // Forbidding the user to request more the one action on the cart
  22 + // simultaneously because the cart in the cookie doesn't supports it.
  23 + Cart.prototype.ajax = function(config){
34 24 var me = this;
35   - $.ajax({
36   - url: '/plugin/shopping_cart/list',
37   - dataType: 'json',
38   - success: function(data, ststus, ajax){
39   - if ( !data.ok ) alert(data.error.message);
40   - else me.addToList(data, true);
41   - },
42   - cache: false,
43   - error: function(ajax, status, errorThrown) {
44   - alert('List cart items - HTTP '+status+': '+errorThrown);
45   - }
46   - });
  25 + this.disabled = true;
  26 + var completeCallback = config.complete;
  27 + config.complete = function(){
  28 + me.disabled = false;
  29 + if (completeCallback) completeCallback();
  30 + };
  31 + $.ajax(config);
47 32 }
48 33  
49   - Cart.prototype.addToList = function(data, clear) {
  34 + Cart.prototype.addToList = function(products, clear) {
50 35 if( clear ) this.itemsBox.empty();
51 36 var me = this;
52   - for( var item,i=0; item=data.products[i]; i++ ) {
  37 + this.productsLength = products.length;
  38 + for( var item,i=0; item=products[i]; i++ ) {
53 39 this.items[item.id] = { price:item.price, quantity:item.quantity };
54 40 this.updateTotal();
55 41 var liId = "cart-item-"+item.id;
... ... @@ -75,9 +61,7 @@ function Cart(config) {
75 61 input.onchange = function() {
76 62 me.updateQuantity(this, this.productId, this.value);
77 63 };
78   -// document.location.href = "#"+liId;
79   -// document.location.href = "#"+this.cartElem.id;
80   -// history.go(-2);
  64 + // TODO: Scroll to newest item
81 65 var liBg = li.css("background-color");
82 66 li[0].style.backgroundColor = "#FF0";
83 67 li.animate({ backgroundColor: liBg }, 1000);
... ... @@ -86,24 +70,25 @@ function Cart(config) {
86 70 if (!clear && this.empty) $(this.cartElem).show();
87 71 if((!clear && this.empty) || (this.visible && clear)) {
88 72 this.contentBox.hide();
89   - this.show();
  73 + this.show(!clear);
90 74 }
91 75 this.empty = false;
92 76 }
93 77  
94 78 Cart.prototype.updateQuantity = function(input, itemId, quantity) {
  79 + if(this.disabled) return alert(shoppingCartPluginL10n.waitLastRequest);
95 80 quantity = parseInt(quantity);
96 81 input.disabled = true;
97 82 var originalBg = input.style.backgroundImage;
98 83 input.style.backgroundImage = "url(/images/loading-small.gif)";
99 84 var me = this;
100 85 if( quantity == NaN ) return input.value = input.lastValue;
101   - $.ajax({
  86 + this.ajax({
102 87 url: '/plugin/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity,
103 88 dataType: 'json',
104 89 success: function(data, status, ajax){
105 90 if ( !data.ok ) {
106   - alert(data.error.message);
  91 + log.error(data.error);
107 92 input.value = input.lastValue;
108 93 }
109 94 else {
... ... @@ -114,7 +99,7 @@ function Cart(config) {
114 99 },
115 100 cache: false,
116 101 error: function(ajax, status, errorThrown) {
117   - alert('Add item - HTTP '+status+': '+errorThrown);
  102 + log.error('Add item - HTTP '+status, errorThrown);
118 103 input.value = input.lastValue;
119 104 },
120 105 complete: function(){
... ... @@ -132,89 +117,103 @@ function Cart(config) {
132 117 }
133 118  
134 119 Cart.addItem = function(itemId, link) {
  120 + if(this.instance.disabled) return alert(shoppingCartPluginL10n.waitLastRequest);
  121 + if ( this.productsLength > 100 ) {
  122 + // This limit protect the user from losing data on cookie limit.
  123 + // This is NOT limiting to 100 products, is limiting to 100 kinds of products.
  124 + alert(shoppingCartPluginL10n.maxNumberOfItens);
  125 + return false;
  126 + }
135 127 link.intervalId = setInterval(function() {
  128 + $(link).addClass('loading');
136 129 steps = ['w', 'n', 'e', 's'];
137 130 if( !link.step || link.step==3 ) link.step = 0;
138 131 link.step++;
139   - $(link).button({ icons: { primary: 'ui-icon-arrowrefresh-1-'+steps[link.step]}, disable: true })
  132 + $(link).button({ icons: { primary: 'ui-icon-arrowrefresh-1-'+steps[link.step]}})
140 133 }, 100);
141 134 var stopBtLoading = function() {
142 135 clearInterval(link.intervalId);
143   - $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false });
  136 + $(link).removeClass('loading');
  137 + $(link).button({ icons: { primary: 'ui-icon-cart'}});
144 138 };
145 139 this.instance.addItem(itemId, stopBtLoading);
146 140 }
147 141  
148 142 Cart.prototype.addItem = function(itemId, callback) {
149 143 var me = this;
150   - $.ajax({
  144 + this.ajax({
151 145 url: '/plugin/shopping_cart/add/'+ itemId,
152 146 dataType: 'json',
153 147 success: function(data, status, ajax){
154   - if ( !data.ok ) alert(data.error.message);
155   - else me.addToList(data);
  148 + if ( !data.ok ) log.error('Shopping cart data failure', data.error);
  149 + else me.addToList(data.products);
156 150 },
157 151 cache: false,
158 152 error: function(ajax, status, errorThrown) {
159   - alert('Add item - HTTP '+status+': '+errorThrown);
  153 + log.error('Add item - HTTP '+status, errorThrown);
160 154 },
161 155 complete: callback
162 156 });
163 157 }
164 158  
165 159 Cart.removeItem = function(itemId) {
166   - var message = this.instance.cartElem.getAttribute('data-l10nRemoveItem');
167   - if( confirm(message) ) this.instance.removeItem(itemId);
  160 + if(this.instance.disabled) return alert(shoppingCartPluginL10n.waitLastRequest);
  161 + if( confirm(shoppingCartPluginL10n.removeItem) ) this.instance.removeItem(itemId);
168 162 }
169 163  
170 164 Cart.prototype.removeItem = function(itemId) {
171 165 if ($("li", this.itemsBox).size() < 2) return this.clean();
172 166 var me = this;
173   - $.ajax({
  167 + this.ajax({
174 168 url: '/plugin/shopping_cart/remove/'+ itemId,
175 169 dataType: 'json',
176 170 success: function(data, status, ajax){
177   - if ( !data.ok ) alert(data.error.message);
  171 + if ( !data.ok ) log.error(data.error);
178 172 else me.removeFromList(data.product_id);
179 173 },
180 174 cache: false,
181 175 error: function(ajax, status, errorThrown) {
182   - alert('Remove item - HTTP '+status+': '+errorThrown);
  176 + log.error('Remove item - HTTP '+status, errorThrown);
183 177 }
184 178 });
185 179 }
186 180  
187 181 Cart.toggle = function(link) {
  182 + if(this.instance.disabled) return alert(shoppingCartPluginL10n.waitLastRequest);
188 183 link.parentNode.parentNode.cartObj.toggle();
189 184 }
190 185 Cart.prototype.toggle = function() {
191   - this.visible ? this.hide() : this.show();
  186 + this.visible ? this.hide(true) : this.show(true);
192 187 }
193 188  
194   - Cart.prototype.show = function() {
195   - $.ajax({
196   - url: '/plugin/shopping_cart/show',
197   - dataType: 'json',
198   - cache: false,
199   - error: function(ajax, status, errorThrown) {
200   - alert('Show - HTTP '+status+': '+errorThrown);
201   - }
202   - });
  189 + Cart.prototype.show = function(register) {
  190 + if(register) {
  191 + this.ajax({
  192 + url: '/plugin/shopping_cart/show',
  193 + dataType: 'json',
  194 + cache: false,
  195 + error: function(ajax, status, errorThrown) {
  196 + log.error('Show - HTTP '+status, errorThrown);
  197 + }
  198 + });
  199 + }
203 200 this.visible = true;
204 201 this.contentBox.slideDown(500);
205 202 $(".cart-toggle .str-show", this.cartElem).hide();
206 203 $(".cart-toggle .str-hide", this.cartElem).show();
207 204  
208 205 }
209   - Cart.prototype.hide = function() {
210   - $.ajax({
211   - url: '/plugin/shopping_cart/hide',
212   - dataType: 'json',
213   - cache: false,
214   - error: function(ajax, status, errorThrown) {
215   - alert('Hide - HTTP '+status+': '+errorThrown);
216   - }
217   - });
  206 + Cart.prototype.hide = function(register) {
  207 + if(register) {
  208 + this.ajax({
  209 + url: '/plugin/shopping_cart/hide',
  210 + dataType: 'json',
  211 + cache: false,
  212 + error: function(ajax, status, errorThrown) {
  213 + log.error('Hide - HTTP '+status, errorThrown);
  214 + }
  215 + });
  216 + }
218 217 this.visible = false;
219 218 this.contentBox.slideUp(500);
220 219 $(".cart-toggle .str-show", this.cartElem).show();
... ... @@ -238,17 +237,17 @@ function Cart(config) {
238 237 }
239 238  
240 239 Cart.clean = function(link) {
241   - var message = this.instance.cartElem.getAttribute('data-l10nCleanCart');
242   - if( confirm(message) ) link.parentNode.parentNode.parentNode.cartObj.clean();
  240 + if(this.instance.disabled) return alert(shoppingCartPluginL10n.waitLastRequest);
  241 + if( confirm(shoppingCartPluginL10n.cleanCart) ) link.parentNode.parentNode.parentNode.cartObj.clean();
243 242 }
244 243  
245 244 Cart.prototype.clean = function() {
246 245 var me = this;
247   - $.ajax({
  246 + this.ajax({
248 247 url: '/plugin/shopping_cart/clean',
249 248 dataType: 'json',
250 249 success: function(data, status, ajax){
251   - if ( !data.ok ) alert(data.error.message);
  250 + if ( !data.ok ) log.error(data.error);
252 251 else{
253 252 me.items = {};
254 253 $(me.cartElem).slideUp(500, function() {
... ... @@ -261,7 +260,7 @@ function Cart(config) {
261 260 },
262 261 cache: false,
263 262 error: function(ajax, status, errorThrown) {
264   - alert('Remove item - HTTP '+status+': '+errorThrown);
  263 + log.error('Remove item - HTTP '+status, errorThrown);
265 264 }
266 265 });
267 266 }
... ... @@ -274,7 +273,7 @@ function Cart(config) {
274 273  
275 274 Cart.prototype.send_request = function(params) {
276 275 var me = this;
277   - $.ajax({
  276 + this.ajax({
278 277 type: 'POST',
279 278 url: '/plugin/shopping_cart/send_request',
280 279 data: params,
... ... @@ -288,7 +287,7 @@ function Cart(config) {
288 287 },
289 288 cache: false,
290 289 error: function(ajax, status, errorThrown) {
291   - alert('Send request - HTTP '+status+': '+errorThrown);
  290 + log.error('Send request - HTTP '+status, errorThrown);
292 291 },
293 292 complete: function() {
294 293 $.colorbox.close();
... ... @@ -300,6 +299,11 @@ function Cart(config) {
300 299 $.colorbox.close();
301 300 }
302 301  
  302 + $(window).bind('beforeunload', function(){
  303 + log('Page unload.');
  304 + Cart.unloadingPage = true;
  305 + });
  306 +
303 307 $(function(){
304 308  
305 309 $.ajax({
... ... @@ -311,7 +315,18 @@ function Cart(config) {
311 315 },
312 316 cache: false,
313 317 error: function(ajax, status, errorThrown) {
314   - alert('Error getting shopping cart - HTTP '+status+': '+errorThrown);
  318 + // Give some time to register page unload.
  319 + setTimeout(function() {
  320 + // page unload is not our problem.
  321 + if (Cart.unloadingPage) {
  322 + log('Page unload before cart load.');
  323 + } else {
  324 + log.error('Error getting shopping cart - HTTP '+status, errorThrown);
  325 + if ( confirm(shoppingCartPluginL10n.getProblemConfirmReload) ) {
  326 + document.location.reload();
  327 + }
  328 + }
  329 + }, 100);
315 330 }
316 331 });
317 332 });
... ...
plugins/shopping_cart/views/cart.html.erb
1   -<div id="cart1" class="cart" style="display:none"
2   - data-l10nRemoveItem="<%=_('Are you sure you want to remove this item?')%>"
3   - data-l10nCleanCart="<%=_('Are you sure you want to clean your basket?')%>">
  1 +<div id="cart1" class="cart" style="display:none">
4 2 <div class="cart-inner">
5 3 <div class="cart-content">
6 4 <h3><%= _("Basket") %></h3>
7 5 <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a>
8 6 <ul class="cart-items"></ul>
9 7 <div class="cart-total"><%=_('Total:')%> <b></b></div>
10   - <a href="/plugin/shopping_cart/buy" class="cart-buy"><%=_('Shopping checkout')%></a>
  8 + <a href="/plugin/shopping_cart/buy" class="cart-buy colorbox"><%=_('Shopping checkout')%></a>
11 9 </div>
12 10 <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle">
13 11 <span class="str-show"><%=_('Show basket')%></span>
... ... @@ -15,3 +13,18 @@
15 13 </a>
16 14 </div>
17 15 </div>
  16 +<script>
  17 + var shoppingCartPluginL10n = {
  18 + getProblemConfirmReload: <%= (
  19 + _('Ups... I had a problem to load the basket list.') +
  20 + "\n" +
  21 + _('Did you want to reload this page?')
  22 + ).to_json %>,
  23 + maxNumberOfItens: <%= (
  24 + _('Sorry, you can\'t have more then 100 kinds of items on this basket.')
  25 + ).to_json %>,
  26 + waitLastRequest: <%= _('Oops, you must wait your last request to finish first!').to_json %>,
  27 + removeItem: <%= _('Are you sure you want to remove this item?').to_json %>,
  28 + cleanCart: <%= _('Are you sure you want to clean your basket?').to_json %>
  29 + }
  30 +</script>
... ...
public/javascripts/application.js
... ... @@ -918,13 +918,55 @@ 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 956 jQuery(function($) {
922 957 $('.colorbox').live('click', function() {
923   - $.fn.colorbox({
924   - href:$(this).attr('href'),
925   - maxWidth: '600',
926   - maxHeight: '550',
927   - open:true
  958 + $.colorbox({
  959 + href: $(this).attr('href'),
  960 + maxWidth: $(window).width()-50,
  961 + height: $(window).height()-50,
  962 + open: true,
  963 + fixed: true,
  964 + close: 'Cancel',
  965 + onComplete: function(bt) {
  966 + var opt = {}, maxH = $(window).height()-50;
  967 + if ($('#cboxLoadedContent *:first').height() > maxH) opt.height = maxH;
  968 + $.colorbox.resize(opt);
  969 + }
928 970 });
929 971 return false;
930 972 });
... ...