caboose-cms 0.5.189 → 0.5.190
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/app/assets/javascripts/caboose/product.js +1 -1
- data/app/assets/javascripts/caboose/product_new.js +305 -0
- data/app/assets/javascripts/caboose/product_old.js +324 -0
- data/app/controllers/caboose/checkout_controller.rb +1 -1
- data/app/controllers/caboose/pages_controller.rb +2 -1
- data/app/controllers/caboose/retargeting_controller.rb +41 -0
- data/app/controllers/caboose/sites_controller.rb +1 -0
- data/app/helpers/caboose/application_helper.rb +15 -0
- data/app/models/caboose/page.rb +1 -1
- data/app/models/caboose/retargeting_config.rb +40 -0
- data/app/models/caboose/schema.rb +11 -4
- data/app/models/caboose/site.rb +10 -0
- data/app/views/caboose/retargeting/admin_edit.html.erb +42 -0
- data/app/views/caboose/sites/_admin_header.html.erb +3 -2
- data/config/routes.rb +10 -3
- data/lib/caboose/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
Zjk2ZjJkNGZlOWM0YmViYmY0ZTlmYmQzYmRjZmRiZjY4ZjU2MTI4NA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZTVjOWQxNGY4NDlmNmE4ZjY1ZDk3ZDk3ZTUyMWM0ZjkzYjkzY2M3OQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZmU3YjRhNDc4NzNhMWYyYTU5M2FmYWE3ZDVmMjg3NGM0ZDhlMTY4NWY2NTcz
|
10
|
+
N2ExNmY1NTk1YWEwMzZhZWQ0OWM5MGI4MTYwYjlmNTQwYzU1NzNiY2E3ZWE2
|
11
|
+
NjcwM2YxN2JkZmFjZDAzZjdiMTdmZDZjYzdkZjQzMjVjMDllYmI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NDU2ZGYxZDcyZGZjNGQ4ZDdkYTkxNGI1YmRlNTI5YmJkODMwOWE0MDc3NGFm
|
14
|
+
OTgwMzdmZjVlNTVjOTFjZTNhNzczZjEwYTE2OTRhMDQ4ZjFmOTIyOGMwNzg5
|
15
|
+
Mjk5NWExYTViZDI3YmFkMzczYTBiZDg2MzFhZTAyOGExNmE5YjA=
|
@@ -266,7 +266,7 @@ Caboose.Store.Modules.Product = (function() {
|
|
266
266
|
Caboose.Store.Modules.Cart.set_variant(variant);
|
267
267
|
if (variant) self.set_image_from_variant(variant);
|
268
268
|
if (variant && self.$price.length) self.$price.empty().text('$' + parseFloat((variant.price * 100) / 100).toFixed(2));
|
269
|
-
if (self.variant_on_sale(variant)) {
|
269
|
+
if (variant && self.variant_on_sale(variant)) {
|
270
270
|
self.$price.addClass("on-sale");
|
271
271
|
var percent = 100 - ((variant.sale_price / variant.price) * 100).toFixed(0);
|
272
272
|
$("#percent-off").text("SALE! Save " + percent + "%");
|
@@ -0,0 +1,305 @@
|
|
1
|
+
|
2
|
+
var ProductController = function(params) { this.init(params); };
|
3
|
+
|
4
|
+
ProductController.prototype = {
|
5
|
+
|
6
|
+
product: false,
|
7
|
+
option_values: [[], [], []],
|
8
|
+
|
9
|
+
init: function() {
|
10
|
+
self.$price = self.$product.find('#product-price');
|
11
|
+
$("<span id='percent-off'></span").insertAfter(self.$price);
|
12
|
+
$("<span id='sale-price'></span").insertBefore(self.$price);
|
13
|
+
if (!self.$product.length) return false;
|
14
|
+
},
|
15
|
+
|
16
|
+
refresh: function(after)
|
17
|
+
{
|
18
|
+
var that = this;
|
19
|
+
$.ajax({
|
20
|
+
url: '/products/' + that.product.id + '/info',
|
21
|
+
type: 'get',
|
22
|
+
success: function(resp) {
|
23
|
+
that.product = resp.product;
|
24
|
+
that.option_values = [that.option1_values, that.option2_values, that.option3_values];
|
25
|
+
that.render();
|
26
|
+
that.set_variant(self.get_initial_variant());
|
27
|
+
that.set_options_from_variant(self.variant);
|
28
|
+
}
|
29
|
+
);
|
30
|
+
},
|
31
|
+
|
32
|
+
render: function()
|
33
|
+
{
|
34
|
+
var that = this;
|
35
|
+
that.render_images();
|
36
|
+
that.render_options();
|
37
|
+
$('#message').empty();
|
38
|
+
},
|
39
|
+
|
40
|
+
initalize_zoom: function(image_url)
|
41
|
+
{
|
42
|
+
var big_image = $("#product-images").children("figure").first();
|
43
|
+
big_image.data("zoom-image",image_url);
|
44
|
+
big_image.elevateZoom();
|
45
|
+
},
|
46
|
+
|
47
|
+
render_images: function()
|
48
|
+
{
|
49
|
+
var that = this;
|
50
|
+
var div = $('<div/>').append($('<figure/>').attr('id', 'main_image').click(function(e) {
|
51
|
+
window.location = $(e.target).css('background-image').match(/^url\("(.*)"\)$/)[1];
|
52
|
+
}));
|
53
|
+
|
54
|
+
if (that.product.images.length > 0)
|
55
|
+
{
|
56
|
+
var ul = $('<ul/>');
|
57
|
+
$.each(product.images, function(i, image) {
|
58
|
+
ul.append(
|
59
|
+
$('<li/>').data('id', image.id).append(
|
60
|
+
$('<figure/>')
|
61
|
+
.data('url-large', image.urls.large)
|
62
|
+
.data('url-huge' , image.urls.huge)
|
63
|
+
.css('background-image', "url(" + image.urls.thumb + ")")
|
64
|
+
.click(function(e) {
|
65
|
+
$('#main_image').css('background-image', 'url(' + $(this).data('url-large') + ')');
|
66
|
+
that.initalize_zoom($(this).data('url-huge'));
|
67
|
+
})
|
68
|
+
)
|
69
|
+
);
|
70
|
+
});
|
71
|
+
div.append(ul);
|
72
|
+
}
|
73
|
+
else
|
74
|
+
{
|
75
|
+
div.append($('<p/>').html("This product doesn't have any images yet."));
|
76
|
+
}
|
77
|
+
$('#product-images').empty().append(div);
|
78
|
+
},
|
79
|
+
|
80
|
+
render_options: function()
|
81
|
+
{
|
82
|
+
var that = this;
|
83
|
+
var div = $('<div/>');
|
84
|
+
|
85
|
+
if (that.product.options.length > 0)
|
86
|
+
{
|
87
|
+
$.each(that.product.options, function(i, option) {
|
88
|
+
div.append($('<h3>').html(option));
|
89
|
+
var ul = $('<ul/>').attr('id', 'option' + (i + 1)).data('name', option);
|
90
|
+
$.each(that.option_values[i], function(j, option_value) {
|
91
|
+
ul.append($('<li/>')
|
92
|
+
.attr('id', 'option_' + i + '_' + j)
|
93
|
+
.data('i', i).data('j', j).data('value', option_value)
|
94
|
+
.html(option_value)
|
95
|
+
.click(function(e) {
|
96
|
+
that.select_option($(this).data('i'), $(this).data('value'));
|
97
|
+
})
|
98
|
+
);
|
99
|
+
});
|
100
|
+
div.append(ul);
|
101
|
+
});
|
102
|
+
}
|
103
|
+
else
|
104
|
+
{
|
105
|
+
div.append($('<p/>').html("This product doesn't have any options."));
|
106
|
+
}
|
107
|
+
$('#product-options').empty().append(div);
|
108
|
+
},
|
109
|
+
|
110
|
+
selected_options: false,
|
111
|
+
|
112
|
+
select_option: function(option_index, value)
|
113
|
+
{
|
114
|
+
var that = this;
|
115
|
+
that.selected_options[option_index] = (that.selected_options[option_index] == value ? false : value);
|
116
|
+
$('#product-options li').removeClass('available').removeClass('unavailable').removeClass('selected');
|
117
|
+
|
118
|
+
if (that.product.options.length > 0)
|
119
|
+
{
|
120
|
+
$.each(that.product.options, function(i, option) {
|
121
|
+
$.each(that.option_values[i], function(j, option_value) {
|
122
|
+
var el = $('#option_' + i + '_' + j);
|
123
|
+
if (that.variant_is_available(i, option_value))
|
124
|
+
{
|
125
|
+
el.addClass('available');
|
126
|
+
if (that.selected_options[i] == option_value)
|
127
|
+
el.addClass('selected');
|
128
|
+
}
|
129
|
+
else
|
130
|
+
{
|
131
|
+
el.addClass('unavailable');
|
132
|
+
}
|
133
|
+
});
|
134
|
+
});
|
135
|
+
}
|
136
|
+
//self.set_variant(self.get_variant_from_options(self.get_current_options()));
|
137
|
+
},
|
138
|
+
|
139
|
+
variant_is_available: function(option_index, option_value)
|
140
|
+
{
|
141
|
+
var that = this;
|
142
|
+
var exists = true;
|
143
|
+
|
144
|
+
var options_array = [];
|
145
|
+
$.each(that.product.options, function(i, option) {
|
146
|
+
if (that.selected_option[i] == false) // Any option will work
|
147
|
+
{
|
148
|
+
options_array[i] = that.option_values[i];
|
149
|
+
}
|
150
|
+
else if (i == option_index) // Only the given option_value will work
|
151
|
+
{
|
152
|
+
options_array[i] = [option_value];
|
153
|
+
}
|
154
|
+
else // Only the previously selected option will work
|
155
|
+
{
|
156
|
+
options_array[i] = [that.selected_option[i]];
|
157
|
+
}
|
158
|
+
});
|
159
|
+
|
160
|
+
var found_it = false;
|
161
|
+
$.each(that.product.variants, function(i, v) {
|
162
|
+
var matches = true;
|
163
|
+
$.each(options_array, function(j, option_values) {
|
164
|
+
if (option_values.index_of(v['option'+j]) != -1)
|
165
|
+
{
|
166
|
+
matches = false;
|
167
|
+
return false;
|
168
|
+
}
|
169
|
+
});
|
170
|
+
if (matches)
|
171
|
+
{
|
172
|
+
found_it = true;
|
173
|
+
return false;
|
174
|
+
}
|
175
|
+
});
|
176
|
+
return found_it;
|
177
|
+
},
|
178
|
+
|
179
|
+
variant_for_options: function()
|
180
|
+
{
|
181
|
+
var that = this;
|
182
|
+
var exists = true;
|
183
|
+
|
184
|
+
var options_array = [];
|
185
|
+
$.each(that.product.options, function(i, option) {
|
186
|
+
options_array[i] = that.selected_option[i];
|
187
|
+
});
|
188
|
+
|
189
|
+
var matching_variant = false;
|
190
|
+
$.each(that.product.variants, function(i, v) {
|
191
|
+
var matches = true;
|
192
|
+
$.each(options_array, function(j, option_value) {
|
193
|
+
if (option_value != v['option'+j]) { matches = false; return false; }
|
194
|
+
});
|
195
|
+
if (matches) { matching_variant = v; return false; }
|
196
|
+
});
|
197
|
+
return matching_variant;
|
198
|
+
},
|
199
|
+
|
200
|
+
//
|
201
|
+
// Variant Methods
|
202
|
+
//
|
203
|
+
|
204
|
+
initial_variant: function ()
|
205
|
+
{
|
206
|
+
var that = this;
|
207
|
+
var available_variants = $.grep(that.product.variants, function(v) { return v.quantity_in_stock > 0; });
|
208
|
+
|
209
|
+
if (!available_variants)
|
210
|
+
$('#add-to-cart').after($('<p/>').addClass('message error').text('Out of Stock')).remove();
|
211
|
+
|
212
|
+
variant = available_variants ? available_variants[0] : that.product.variants[0];
|
213
|
+
return variant;
|
214
|
+
},
|
215
|
+
|
216
|
+
self.set_variant = function(variant) {
|
217
|
+
self.variant = variant;
|
218
|
+
Caboose.Store.Modules.Cart.set_variant(variant);
|
219
|
+
if (variant) self.set_image_from_variant(variant);
|
220
|
+
if (variant && self.$price.length) self.$price.empty().text('$' + parseFloat((variant.price * 100) / 100).toFixed(2));
|
221
|
+
if (variant && self.variant_on_sale(variant)) {
|
222
|
+
self.$price.addClass("on-sale");
|
223
|
+
var percent = 100 - ((variant.sale_price / variant.price) * 100).toFixed(0);
|
224
|
+
$("#percent-off").text("SALE! Save " + percent + "%");
|
225
|
+
$("#sale-price").text('$' + parseFloat((variant.sale_price * 100) / 100).toFixed(2));
|
226
|
+
}
|
227
|
+
else {
|
228
|
+
self.$price.removeClass("on-sale");
|
229
|
+
$("#percent-off").text('');
|
230
|
+
$("#sale-price").text('');
|
231
|
+
}
|
232
|
+
},
|
233
|
+
|
234
|
+
variant_on_sale: function(variant)
|
235
|
+
{
|
236
|
+
if (variant.sale_price != "" && variant.sale_price != 0)
|
237
|
+
{
|
238
|
+
var d = new Date();
|
239
|
+
if (variant.date_sale_starts && d < variant.date_sale_starts) return false;
|
240
|
+
if (variant.date_sale_ends && d > variant.date_sale_ends) return false;
|
241
|
+
return true;
|
242
|
+
}
|
243
|
+
return false;
|
244
|
+
},
|
245
|
+
|
246
|
+
get_variant: function(variant_id)
|
247
|
+
{
|
248
|
+
for (var v in this.product.variants)
|
249
|
+
if (v.id == variant_id)
|
250
|
+
return v;
|
251
|
+
return false;
|
252
|
+
},
|
253
|
+
|
254
|
+
//
|
255
|
+
// Option Methods
|
256
|
+
//
|
257
|
+
|
258
|
+
self.get_option_attribute = function(option)
|
259
|
+
{
|
260
|
+
optionName = _.isObject(option) ? option.name : option;
|
261
|
+
if (self.product.option1 == optionName) { return 'option1'; }
|
262
|
+
else if (self.product.option2 == optionName) { return 'option2'; }
|
263
|
+
else if (self.product.option3 == optionName) { return 'option3'; }
|
264
|
+
},
|
265
|
+
|
266
|
+
self.get_current_options = function()
|
267
|
+
{
|
268
|
+
var options = [];
|
269
|
+
|
270
|
+
self.$options.children('ul').each(function(index, element) {
|
271
|
+
var $option = $(element);
|
272
|
+
|
273
|
+
options.push({
|
274
|
+
name: $option.data('name'),
|
275
|
+
value: $option.children('.selected').first().data('value')
|
276
|
+
});
|
277
|
+
});
|
278
|
+
|
279
|
+
return options;
|
280
|
+
},
|
281
|
+
|
282
|
+
//
|
283
|
+
// Image Methods
|
284
|
+
//
|
285
|
+
|
286
|
+
set_image_from_variant: function(variant)
|
287
|
+
{
|
288
|
+
if (!variant || !variant.images || variant.images.length == 0 || !variant.images[0]) return;
|
289
|
+
|
290
|
+
$('#main_image').css('background-image', 'url(' + $(this).data('url-large') + ')');
|
291
|
+
that.initalize_zoom($(this).data('url-huge'));
|
292
|
+
|
293
|
+
var $figure = self.$images.children('figure');
|
294
|
+
if (variant.images && variant.images.length > 0 && variant.images[0]) {
|
295
|
+
$figure.css('background-image', 'url(' + variant.images[0].urls.large + ')');
|
296
|
+
self.initalize_zoom(variant.images[0].urls.huge);
|
297
|
+
} else if ($figure.css('background-image').toLowerCase() == 'none') {
|
298
|
+
$figure.css('background-image', 'url(' + _.first(self.product.images).urls.large + ')');
|
299
|
+
self.initalize_zoom(_.first(self.product.images).urls.huge);
|
300
|
+
}
|
301
|
+
|
302
|
+
};
|
303
|
+
|
304
|
+
return self;
|
305
|
+
}).call(Caboose.Store);
|
@@ -0,0 +1,324 @@
|
|
1
|
+
//
|
2
|
+
// Product
|
3
|
+
//
|
4
|
+
|
5
|
+
Caboose.Store.Modules.Product = (function() {
|
6
|
+
var self = {
|
7
|
+
templates: {
|
8
|
+
images: JST['caboose/product/images'],
|
9
|
+
options: JST['caboose/product/options']
|
10
|
+
}
|
11
|
+
};
|
12
|
+
|
13
|
+
//
|
14
|
+
// Initialize
|
15
|
+
//
|
16
|
+
|
17
|
+
self.initialize = function() {
|
18
|
+
self.$product = $('#product');
|
19
|
+
self.$price = self.$product.find('#product-price');
|
20
|
+
$("<span id='percent-off'></span").insertAfter(self.$price);
|
21
|
+
$("<span id='sale-price'></span").insertBefore(self.$price);
|
22
|
+
if (!self.$product.length) return false;
|
23
|
+
|
24
|
+
$.get('/products/' + self.$product.data('id') + '/info', function(response) {
|
25
|
+
self.product = response.product;
|
26
|
+
self.option1_values = response.option1_values;
|
27
|
+
self.option2_values = response.option2_values;
|
28
|
+
self.option3_values = response.option3_values;
|
29
|
+
self.render();
|
30
|
+
self.bind_events();
|
31
|
+
self.set_variant(self.get_initial_variant());
|
32
|
+
self.set_options_from_variant(self.variant);
|
33
|
+
});
|
34
|
+
|
35
|
+
};
|
36
|
+
|
37
|
+
//
|
38
|
+
// Render
|
39
|
+
//
|
40
|
+
self.render = function() {
|
41
|
+
var render_functions = [];
|
42
|
+
render_functions.push(self.render_images);
|
43
|
+
render_functions.push(self.render_options);
|
44
|
+
|
45
|
+
_.each(render_functions, function(render_function, index) {
|
46
|
+
var finished = index == (render_functions.length - 1);
|
47
|
+
|
48
|
+
render_function(function() {
|
49
|
+
if (finished) self.$product.removeClass('loading');
|
50
|
+
});
|
51
|
+
});
|
52
|
+
};
|
53
|
+
|
54
|
+
self.initalize_zoom = function(image_url) {
|
55
|
+
var big_image = $("#product-images").children("figure").first();
|
56
|
+
big_image.data("zoom-image",image_url);
|
57
|
+
big_image.elevateZoom();
|
58
|
+
}
|
59
|
+
|
60
|
+
self.render_images = function(callback) {
|
61
|
+
self.$images = $('#product-images', self.$product);
|
62
|
+
if (!self.$images.length) return false;
|
63
|
+
self.$images.empty().html(self.templates.images({ images: self.product.images }));
|
64
|
+
if (callback) callback();
|
65
|
+
};
|
66
|
+
|
67
|
+
self.render_options = function(callback) {
|
68
|
+
self.$options = $('#product-options', self.$options);
|
69
|
+
if (!self.$options.length) return false;
|
70
|
+
self.$options.empty().html(self.templates.options({ options: self.get_options_with_all_values() }));
|
71
|
+
if (callback) callback();
|
72
|
+
};
|
73
|
+
|
74
|
+
//
|
75
|
+
// Out of Stock
|
76
|
+
//
|
77
|
+
|
78
|
+
self.out_of_stock = function() {
|
79
|
+
self.$product.find('#add-to-cart').after($('<p/>').addClass('message error').text('Out of Stock')).remove();
|
80
|
+
};
|
81
|
+
|
82
|
+
//
|
83
|
+
// Events
|
84
|
+
//
|
85
|
+
|
86
|
+
self.bind_events = function() {
|
87
|
+
self.$images.find('ul > li > figure').on('click', self.thumb_click_handler);
|
88
|
+
self.$images.children('figure').on('click', self.image_click_handler);
|
89
|
+
self.$options.find('ul').on('click', 'li', self.option_click_handler);
|
90
|
+
};
|
91
|
+
|
92
|
+
self.thumb_click_handler = function(event) {
|
93
|
+
self.$images.children('figure').css('background-image', 'url(' + $(event.target).data('url-large') + ')');
|
94
|
+
self.initalize_zoom($(event.target).data('url-large').replace('large','huge'));
|
95
|
+
};
|
96
|
+
|
97
|
+
self.image_click_handler = function(event) {
|
98
|
+
window.location = $(event.target).css('background-image').match(/^url\("(.*)"\)$/)[1];
|
99
|
+
};
|
100
|
+
|
101
|
+
self.option_click_handler = function(event) {
|
102
|
+
var $target_option = $(event.delegateTarget)
|
103
|
+
var $target_value = $(event.target);
|
104
|
+
|
105
|
+
if ($target_value.hasClass('selected')) {
|
106
|
+
$target_value.removeClass('selected');
|
107
|
+
$target_value = $();
|
108
|
+
} else {
|
109
|
+
$target_value.addClass('selected').siblings().removeClass('selected');
|
110
|
+
|
111
|
+
self.$options.find('ul').not($target_option).each(function(index, element) {
|
112
|
+
var $currentOption = $(element)
|
113
|
+
, $currentValue = $currentOption.children('.selected')
|
114
|
+
, $otherOption = self.$options.find('ul').not($target_option.add($currentOption))
|
115
|
+
, $otherValue = $otherOption.children('.selected')
|
116
|
+
, options = [];
|
117
|
+
|
118
|
+
if (!$currentValue.length) return true;
|
119
|
+
|
120
|
+
options.push({ name: $currentOption.data('name'), value: $currentValue.data('value') });
|
121
|
+
options.push({ name: $target_option.data('name'), value: $target_value.data('value') });
|
122
|
+
|
123
|
+
if (!!!self.get_variant_from_options(options)) {
|
124
|
+
$currentValue.removeClass('selected');
|
125
|
+
} else if ($otherOption.length && $otherValue.length) {
|
126
|
+
options.push({ name: $otherOption.data('name'), value: $otherValue.data('value') });
|
127
|
+
if (!!!self.get_variant_from_options(options)) $otherValue.removeClass('selected');
|
128
|
+
}
|
129
|
+
});
|
130
|
+
|
131
|
+
$target_option.children().each(function(index, element) {
|
132
|
+
var $currentOption = $target_option
|
133
|
+
, $currentValue = $(element)
|
134
|
+
, $otherOption = self.$options.find('ul').not($target_option).first()
|
135
|
+
, $otherValue = $otherOption.children('.selected')
|
136
|
+
, $otherOtherOption = self.$options.find('ul').not($target_option.add($otherOption))
|
137
|
+
, $otherOtherValue = $otherOtherOption.children('.selected')
|
138
|
+
, options = [];
|
139
|
+
|
140
|
+
options.push({ name: $currentOption.data('name'), value: $currentValue.data('value') });
|
141
|
+
if ($otherOption.length && $otherValue.length) options.push({ name: $otherOption.data('name'), value: $otherValue.data('value') });
|
142
|
+
if ($otherOtherOption.length && $otherOtherValue.length) options.push({ name: $otherOtherOption.data('name'), value: $otherOtherValue.data('value') });
|
143
|
+
self.toggle_option_value($currentValue, !!self.get_variant_from_options(options));
|
144
|
+
});
|
145
|
+
}
|
146
|
+
|
147
|
+
self.$options.find('ul').not($target_option).each(function(index, element) {
|
148
|
+
var $currentOption = $(element);
|
149
|
+
|
150
|
+
$currentOption.children().each(function(index, element) {
|
151
|
+
var $currentValue = $(element)
|
152
|
+
, $otherOption = self.$options.find('ul').not($target_option.add($currentOption))
|
153
|
+
, $otherValue = $otherOption.children('.selected')
|
154
|
+
, options = [];
|
155
|
+
|
156
|
+
options.push({ name: $currentOption.data('name'), value: $currentValue.data('value') });
|
157
|
+
if ($target_option.length && $target_value.length) options.push({ name: $target_option.data('name'), value: $target_value.data('value') });
|
158
|
+
if ($otherOption.length && $otherValue.length) options.push({ name: $otherOption.data('name'), value: $otherValue.data('value') });
|
159
|
+
self.toggle_option_value($currentValue, !!self.get_variant_from_options(options));
|
160
|
+
});
|
161
|
+
});
|
162
|
+
|
163
|
+
self.set_variant(self.get_variant_from_options(self.get_current_options()));
|
164
|
+
};
|
165
|
+
|
166
|
+
//
|
167
|
+
// Option Methods
|
168
|
+
//
|
169
|
+
|
170
|
+
self.get_options_from_product = function() {
|
171
|
+
return _.compact([
|
172
|
+
self.product.option1 ? self.product.option1 : undefined,
|
173
|
+
self.product.option2 ? self.product.option2 : undefined,
|
174
|
+
self.product.option3 ? self.product.option3 : undefined
|
175
|
+
]);
|
176
|
+
};
|
177
|
+
|
178
|
+
|
179
|
+
self.get_options_from_variant = function(variant) {
|
180
|
+
return _.compact([
|
181
|
+
self.product.option1 ? { name: self.product.option1, value: variant.option1 } : undefined,
|
182
|
+
self.product.option2 ? { name: self.product.option2, value: variant.option2 } : undefined,
|
183
|
+
self.product.option3 ? { name: self.product.option3, value: variant.option3 } : undefined
|
184
|
+
]);
|
185
|
+
};
|
186
|
+
|
187
|
+
self.get_options_with_all_values = function() {
|
188
|
+
var options = [];
|
189
|
+
if (self.product.option1) options.push({ name: self.product.option1, values: self.option1_values });
|
190
|
+
if (self.product.option2) options.push({ name: self.product.option2, values: self.option2_values });
|
191
|
+
if (self.product.option3) options.push({ name: self.product.option3, values: self.option3_values });
|
192
|
+
return options;
|
193
|
+
};
|
194
|
+
|
195
|
+
self.get_option_attribute = function(option) {
|
196
|
+
optionName = _.isObject(option) ? option.name : option;
|
197
|
+
|
198
|
+
if (self.product.option1 == optionName) {
|
199
|
+
return 'option1';
|
200
|
+
} else if (self.product.option2 == optionName) {
|
201
|
+
return 'option2';
|
202
|
+
} else if (self.product.option3 == optionName) {
|
203
|
+
return 'option3';
|
204
|
+
}
|
205
|
+
};
|
206
|
+
|
207
|
+
self.get_current_options = function() {
|
208
|
+
var options = [];
|
209
|
+
|
210
|
+
self.$options.children('ul').each(function(index, element) {
|
211
|
+
var $option = $(element);
|
212
|
+
|
213
|
+
options.push({
|
214
|
+
name: $option.data('name'),
|
215
|
+
value: $option.children('.selected').first().data('value')
|
216
|
+
});
|
217
|
+
});
|
218
|
+
|
219
|
+
return options;
|
220
|
+
};
|
221
|
+
|
222
|
+
self.toggle_option_value = function($value, on) {
|
223
|
+
if (on) {
|
224
|
+
$value.addClass('available').removeClass('unavailable');
|
225
|
+
} else {
|
226
|
+
$value.addClass('unavailable').removeClass('available selected');
|
227
|
+
}
|
228
|
+
};
|
229
|
+
|
230
|
+
//
|
231
|
+
// Variant Methods
|
232
|
+
//
|
233
|
+
|
234
|
+
self.get_initial_variant = function () {
|
235
|
+
var variant = _.find(self.product.variants, function(variant) {
|
236
|
+
return variant.quantity_in_stock > 0;
|
237
|
+
});
|
238
|
+
|
239
|
+
if (!variant) {
|
240
|
+
variant = _.first(self.product.variants);
|
241
|
+
self.out_of_stock();
|
242
|
+
}
|
243
|
+
|
244
|
+
return variant;
|
245
|
+
};
|
246
|
+
|
247
|
+
self.get_variant_from_options = function(options) {
|
248
|
+
if (_.find(options, function(option) { return option.value == undefined })) return false;
|
249
|
+
|
250
|
+
var attributes = _.object(_.map(options, function(option) {
|
251
|
+
return [self.get_option_attribute(option.name), option.value.toString()]
|
252
|
+
}));
|
253
|
+
|
254
|
+
var variants = _.sortBy(_.where(self.product.variants, attributes), function(variant) { return variant.price });
|
255
|
+
return _.find(variants, function(variant) { return variant.quantity_in_stock > 0 });
|
256
|
+
};
|
257
|
+
|
258
|
+
self.set_options_from_variant = function(variant) {
|
259
|
+
if (variant.option1) $('#option1 li[data-value="' + variant.option1 + '"]', self.$options).click();
|
260
|
+
if (variant.option1) $('#option2 li[data-value="' + variant.option2 + '"]', self.$options).click();
|
261
|
+
if (variant.option1) $('#option3 li[data-value="' + variant.option3 + '"]', self.$options).click();
|
262
|
+
};
|
263
|
+
|
264
|
+
self.set_variant = function(variant) {
|
265
|
+
self.variant = variant;
|
266
|
+
Caboose.Store.Modules.Cart.set_variant(variant);
|
267
|
+
if (variant) self.set_image_from_variant(variant);
|
268
|
+
if (variant && self.$price.length) self.$price.empty().text('$' + parseFloat((variant.price * 100) / 100).toFixed(2));
|
269
|
+
if (variant && self.variant_on_sale(variant)) {
|
270
|
+
self.$price.addClass("on-sale");
|
271
|
+
var percent = 100 - ((variant.sale_price / variant.price) * 100).toFixed(0);
|
272
|
+
$("#percent-off").text("SALE! Save " + percent + "%");
|
273
|
+
$("#sale-price").text('$' + parseFloat((variant.sale_price * 100) / 100).toFixed(2));
|
274
|
+
}
|
275
|
+
else {
|
276
|
+
self.$price.removeClass("on-sale");
|
277
|
+
$("#percent-off").text('');
|
278
|
+
$("#sale-price").text('');
|
279
|
+
}
|
280
|
+
};
|
281
|
+
|
282
|
+
self.variant_on_sale = function(variant) {
|
283
|
+
var is_on_sale = false;
|
284
|
+
if (variant.sale_price != "" && variant.sale_price != 0) {
|
285
|
+
var d = new Date();
|
286
|
+
if (variant.date_sale_starts && d < variant.date_sale_starts) {
|
287
|
+
is_on_sale = false;
|
288
|
+
}
|
289
|
+
else if (variant.date_sale_ends && d > variant.date_sale_ends) {
|
290
|
+
is_on_sale = false;
|
291
|
+
}
|
292
|
+
else {
|
293
|
+
is_on_sale = true;
|
294
|
+
}
|
295
|
+
}
|
296
|
+
return is_on_sale;
|
297
|
+
}
|
298
|
+
|
299
|
+
self.get_variant = function(id) {
|
300
|
+
return _.find(self.product.variants, function(variant) { return variant.id == (id || self.variant.id) });
|
301
|
+
};
|
302
|
+
|
303
|
+
//
|
304
|
+
// Image Methods
|
305
|
+
//
|
306
|
+
|
307
|
+
self.set_image_from_variant = function(variant) {
|
308
|
+
if (!variant || !variant.images || variant.images.length == 0 || !variant.images[0]) return;
|
309
|
+
self.$product.trigger('variant:updated');
|
310
|
+
|
311
|
+
var $figure = self.$images.children('figure');
|
312
|
+
if (variant.images && variant.images.length > 0 && variant.images[0]) {
|
313
|
+
$figure.css('background-image', 'url(' + variant.images[0].urls.large + ')');
|
314
|
+
self.initalize_zoom(variant.images[0].urls.huge);
|
315
|
+
} else if ($figure.css('background-image').toLowerCase() == 'none') {
|
316
|
+
$figure.css('background-image', 'url(' + _.first(self.product.images).urls.large + ')');
|
317
|
+
self.initalize_zoom(_.first(self.product.images).urls.huge);
|
318
|
+
}
|
319
|
+
|
320
|
+
};
|
321
|
+
|
322
|
+
return self;
|
323
|
+
}).call(Caboose.Store);
|
324
|
+
|
@@ -167,7 +167,7 @@ module Caboose
|
|
167
167
|
|
168
168
|
# Find the last order for the user
|
169
169
|
@last_order = Order.where(:customer_id => @logged_in_user.id).order("id desc").limit(1).first
|
170
|
-
|
170
|
+
|
171
171
|
end
|
172
172
|
|
173
173
|
#===========================================================================
|
@@ -444,8 +444,9 @@ module Caboose
|
|
444
444
|
current_tags.each{ |t| PageTag.where(:page_id => page.id, :tag => t).destroy_all if !new_tags.include?(t) }
|
445
445
|
|
446
446
|
# Add any new tags not in current_tags
|
447
|
-
new_tags.each{ |t| PageTag.create(:page_id => page.id, :tag => t) if !current_tags.include?(t) }
|
447
|
+
new_tags.each{ |t| PageTag.create(:page_id => page.id, :tag => t) if !current_tags.include?(t) }
|
448
448
|
end
|
449
|
+
|
449
450
|
end
|
450
451
|
|
451
452
|
resp.success = save && page.save
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module Caboose
|
3
|
+
class RetargetingController < ApplicationController
|
4
|
+
layout 'caboose/admin'
|
5
|
+
|
6
|
+
def before_action
|
7
|
+
@page = Page.page_with_uri(request.host_with_port, '/admin')
|
8
|
+
end
|
9
|
+
|
10
|
+
# GET /admin/sites/:site_id/retargeting
|
11
|
+
def admin_edit
|
12
|
+
return if !user_is_allowed('sites', 'edit')
|
13
|
+
if !@site.is_master
|
14
|
+
@error = "You are not allowed to manage sites."
|
15
|
+
render :file => 'caboose/extras/error' and return
|
16
|
+
end
|
17
|
+
@site = Site.find(params[:site_id])
|
18
|
+
end
|
19
|
+
|
20
|
+
# PUT /admin/sites/:id/retargeting
|
21
|
+
def admin_update
|
22
|
+
render :json => { :error => "You are not allowed to manage sites." } and return if !user_is_allowed('sites', 'edit') || !@site.is_master
|
23
|
+
|
24
|
+
resp = StdClass.new
|
25
|
+
site = Site.find(params[:site_id])
|
26
|
+
rc = site.retargeting_config
|
27
|
+
|
28
|
+
params.each do |name,value|
|
29
|
+
case name
|
30
|
+
when 'conversion_id' then rc.conversion_id = value
|
31
|
+
when 'labels_function' then rc.labels_function = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
resp.success = rc.save
|
36
|
+
render :json => resp
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -126,6 +126,7 @@ module Caboose
|
|
126
126
|
when 'description' then site.description = value
|
127
127
|
when 'under_construction_html' then site.under_construction_html = value
|
128
128
|
when 'use_store' then site.use_store = value
|
129
|
+
when 'use_retargeting' then site.use_retargeting = value
|
129
130
|
end
|
130
131
|
end
|
131
132
|
|
@@ -26,5 +26,20 @@ tinyMCE.init({
|
|
26
26
|
def parent_categories
|
27
27
|
Caboose::Category.find(1).children.where(:status => 'Active')
|
28
28
|
end
|
29
|
+
|
30
|
+
def analytics_js
|
31
|
+
return "" if @site.analytics_id.nil? || @site.analytics_id.strip.length == 0
|
32
|
+
str = ''
|
33
|
+
str << "<script>\n"
|
34
|
+
str << " (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n"
|
35
|
+
str << " (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n"
|
36
|
+
str << " m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n"
|
37
|
+
str << " })(window,document,'script','//www.google-analytics.com/analytics.js','ga');\n"
|
38
|
+
str << " ga('create', '#{@site.analytics_id}', 'auto');\n"
|
39
|
+
str << " ga('send', 'pageview');\n"
|
40
|
+
str << "</script>\n"
|
41
|
+
return str
|
42
|
+
end
|
43
|
+
|
29
44
|
end
|
30
45
|
end
|
data/app/models/caboose/page.rb
CHANGED
@@ -7,7 +7,7 @@ class Caboose::Page < ActiveRecord::Base
|
|
7
7
|
has_many :children, :class_name => 'Caboose::Page', :foreign_key => 'parent_id', :order => 'sort_order, title'
|
8
8
|
has_many :page_permissions
|
9
9
|
has_many :blocks, :order => 'sort_order'
|
10
|
-
has_many :page_tags, :class_name => 'Caboose::PageTag', :dependent => :delete_all, :order => 'tag'
|
10
|
+
has_many :page_tags, :class_name => 'Caboose::PageTag', :dependent => :delete_all, :order => 'tag'
|
11
11
|
has_one :page_cache
|
12
12
|
attr_accessible :id ,
|
13
13
|
:site_id ,
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Caboose
|
2
|
+
class RetargetingConfig < ActiveRecord::Base
|
3
|
+
self.table_name = "retargeting_configs"
|
4
|
+
|
5
|
+
belongs_to :site
|
6
|
+
attr_accessible :id,
|
7
|
+
:site_id,
|
8
|
+
:conversion_id,
|
9
|
+
:labels_function
|
10
|
+
|
11
|
+
def labels(page)
|
12
|
+
return eval(self.custom_labels_function)
|
13
|
+
end
|
14
|
+
|
15
|
+
def js_code(request, page)
|
16
|
+
return "" if !self.site.use_retargeting
|
17
|
+
|
18
|
+
str = ""
|
19
|
+
labels = self.labels(request, page)
|
20
|
+
labels.each do |label|
|
21
|
+
str << "<script type='text/javascript'>\n"
|
22
|
+
str << "/* <![CDATA[ */\n"
|
23
|
+
str << "var google_conversion_id = #{self.conversion_id};\n"
|
24
|
+
str << "var google_conversion_label = '#{label}';\n"
|
25
|
+
str << "var google_custom_params = window.google_tag_params;\n"
|
26
|
+
str << "var google_remarketing_only = true;\n"
|
27
|
+
str << "/* ]]> */\n"
|
28
|
+
str << "</script>\n"
|
29
|
+
str << "<script type='text/javascript' src='//www.googleadservices.com/pagead/conversion.js'></script>\n"
|
30
|
+
str << "<noscript>\n"
|
31
|
+
str << "<div style='display:inline;'>\n"
|
32
|
+
str << "<img height='1' width='1' style='border-style:none;' alt='' src='//googleads.g.doubleclick.net/pagead/viewthroughconversion/#{self.conversion_id}/?value=1.00&currency_code=USD&label=#{label}&guid=ON&script=0'/>\n"
|
33
|
+
str << "</div>\n"
|
34
|
+
str << "</noscript>\n"
|
35
|
+
end
|
36
|
+
return str
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -130,7 +130,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
130
130
|
[ :country , :string ],
|
131
131
|
[ :country_code , :string ],
|
132
132
|
[ :phone , :string ]
|
133
|
-
],
|
133
|
+
],
|
134
134
|
Caboose::Asset => [
|
135
135
|
[ :page_id , :integer ],
|
136
136
|
[ :user_id , :integer ],
|
@@ -502,6 +502,11 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
502
502
|
[ :product_image_id , :integer ],
|
503
503
|
[ :variant_id , :integer ]
|
504
504
|
],
|
505
|
+
Caboose::RetargetingConfig => [
|
506
|
+
[ :site_id , :integer ],
|
507
|
+
[ :conversion_id , :string ],
|
508
|
+
[ :labels_function , :text ]
|
509
|
+
],
|
505
510
|
Caboose::Review => [
|
506
511
|
[ :product_id , :integer ],
|
507
512
|
[ :content , :string ],
|
@@ -568,13 +573,15 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
568
573
|
[ :under_construction_html , :text ],
|
569
574
|
[ :use_store , :boolean , { :default => false }],
|
570
575
|
[ :logo , :attachment ],
|
571
|
-
[ :is_master , :boolean , { :default => false }]
|
576
|
+
[ :is_master , :boolean , { :default => false }],
|
577
|
+
[ :analytics_id , :string ],
|
578
|
+
[ :use_retargeting , :boolean , { :default => false }]
|
572
579
|
],
|
573
580
|
Caboose::SiteMembership => [
|
574
581
|
[ :site_id , :integer ],
|
575
582
|
[ :user_id , :integer ],
|
576
583
|
[ :role , :string ]
|
577
|
-
],
|
584
|
+
],
|
578
585
|
Caboose::SmtpConfig => [
|
579
586
|
[ :site_id , :integer ],
|
580
587
|
[ :address , :string , { :default => 'localhost' }],
|
@@ -646,7 +653,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
646
653
|
[ :weight_unit , :string , { :default => 'oz' }],
|
647
654
|
[ :download_url_expires_in , :string , { :default => 5 }],
|
648
655
|
[ :starting_order_number , :integer , { :default => 1000 }]
|
649
|
-
],
|
656
|
+
],
|
650
657
|
Caboose::User => [
|
651
658
|
[ :site_id , :integer ],
|
652
659
|
[ :first_name , :string ],
|
data/app/models/caboose/site.rb
CHANGED
@@ -21,10 +21,20 @@ class Caboose::Site < ActiveRecord::Base
|
|
21
21
|
|
22
22
|
def smtp_config
|
23
23
|
c = Caboose::SmtpConfig.where(:site_id => self.id).first
|
24
|
+
c = Caboose::SmtpConfig.create(:site_id => self.id) if c.nil?
|
25
|
+
return c
|
24
26
|
end
|
25
27
|
|
26
28
|
def social_config
|
27
29
|
s = Caboose::SocialConfig.where(:site_id => self.id).first
|
30
|
+
s = Caboose::SocialConfig.create(:site_id => self.id) if s.nil?
|
31
|
+
return s
|
32
|
+
end
|
33
|
+
|
34
|
+
def retargeting_config
|
35
|
+
c = Caboose::RetargetingConfig.where(:site_id => self.id).first
|
36
|
+
c = Caboose::RetargetingConfig.create(:site_id => self.id) if c.nil?
|
37
|
+
return c
|
28
38
|
end
|
29
39
|
|
30
40
|
def self.id_for_domain(domain)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<%
|
2
|
+
s = @site
|
3
|
+
rc = s.retargeting_config
|
4
|
+
%>
|
5
|
+
<%= render :partial => 'caboose/sites/admin_header' %>
|
6
|
+
|
7
|
+
<p><div id='site_<%= s.id %>_use_retargeting' ></div></p>
|
8
|
+
<p><div id='retargetingconfig_<%= rc.id %>_conversion_id' ></div></p>
|
9
|
+
<h2>Conversion Labels</h2>
|
10
|
+
<p><code>def conversion_labels_function(request, page) {</code></p>
|
11
|
+
<p><div id='retargetingconfig_<%= rc.id %>_labels_function' ></div></p>
|
12
|
+
<p><code># This function returns an array of conversion labels<br />}</code></p>
|
13
|
+
|
14
|
+
<%= render :partial => 'caboose/sites/admin_footer' %>
|
15
|
+
|
16
|
+
<% content_for :caboose_js do %>
|
17
|
+
<script type="text/javascript">
|
18
|
+
|
19
|
+
$(document).ready(function() {
|
20
|
+
new ModelBinder({
|
21
|
+
name: 'Site',
|
22
|
+
id: <%= @site.id %>,
|
23
|
+
update_url: "/admin/sites/<%= @site.id %>",
|
24
|
+
authenticity_token: '<%= form_authenticity_token %>',
|
25
|
+
attributes: [
|
26
|
+
{ name: 'use_retargeting' , nice_name: 'Use Retargeting' , type: 'checkbox' , value: <%= raw Caboose.json(@site.use_retargeting ? true : false) %>, width: 400 }
|
27
|
+
]
|
28
|
+
});
|
29
|
+
new ModelBinder({
|
30
|
+
name: 'RetargetingConfig',
|
31
|
+
id: <%= rc.id %>,
|
32
|
+
update_url: "/admin/sites/<%= @site.id %>/retargeting",
|
33
|
+
authenticity_token: '<%= form_authenticity_token %>',
|
34
|
+
attributes: [
|
35
|
+
{ name: 'conversion_id' , nice_name: 'Conversion ID' , type: 'text' , value: <%= raw Caboose.json(rc.conversion_id ) %>, width: 600 },
|
36
|
+
{ name: 'labels_function' , nice_name: 'Conversion Labels Function' , type: 'textarea' , value: <%= raw Caboose.json(rc.labels_function) %>, width: 600, height: 75, fixed_placeholder: false }
|
37
|
+
]
|
38
|
+
});
|
39
|
+
});
|
40
|
+
|
41
|
+
</script>
|
42
|
+
<% end %>
|
@@ -24,8 +24,9 @@
|
|
24
24
|
<ul id='tabs'>
|
25
25
|
<%
|
26
26
|
tabs = {
|
27
|
-
'General' => "/admin/sites/#{@site.id}",
|
28
|
-
'Block Types' => "/admin/sites/#{@site.id}/block-types",
|
27
|
+
'General' => "/admin/sites/#{@site.id}",
|
28
|
+
'Block Types' => "/admin/sites/#{@site.id}/block-types",
|
29
|
+
'Retargeting' => "/admin/sites/#{@site.id}/retargeting",
|
29
30
|
'Delete Site' => "/admin/sites/#{@site.id}/delete"
|
30
31
|
}
|
31
32
|
|
data/config/routes.rb
CHANGED
@@ -48,13 +48,20 @@ Caboose::Engine.routes.draw do
|
|
48
48
|
|
49
49
|
get "my-account" => "my_account#index"
|
50
50
|
put "my-account" => "my_account#update"
|
51
|
-
|
51
|
+
|
52
|
+
#=============================================================================
|
53
|
+
# Retargeting
|
54
|
+
#=============================================================================
|
55
|
+
|
56
|
+
get "admin/sites/:site_id/retargeting" => "retargeting#admin_edit"
|
57
|
+
put "admin/sites/:site_id/retargeting" => "retargeting#admin_update"
|
58
|
+
|
52
59
|
#=============================================================================
|
53
60
|
# Sites
|
54
61
|
#=============================================================================
|
55
62
|
|
56
63
|
get "admin/sites/options" => "sites#options"
|
57
|
-
get "admin/sites/new" => "sites#admin_new"
|
64
|
+
get "admin/sites/new" => "sites#admin_new"
|
58
65
|
get "admin/sites/:id/block-types" => "sites#admin_edit_block_types"
|
59
66
|
get "admin/sites/:id/delete" => "sites#admin_delete_form"
|
60
67
|
get "admin/sites/:id" => "sites#admin_edit"
|
@@ -71,7 +78,7 @@ Caboose::Engine.routes.draw do
|
|
71
78
|
put "admin/sites/:site_id/domains/:id" => "domains#admin_update"
|
72
79
|
delete "admin/sites/:site_id/domains/:id" => "domains#admin_delete"
|
73
80
|
put "admin/sites/:site_id/domains/:id/set-primary" => "domains#admin_set_primary"
|
74
|
-
|
81
|
+
|
75
82
|
#=============================================================================
|
76
83
|
# Store
|
77
84
|
#=============================================================================
|
data/lib/caboose/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caboose-cms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.190
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Barry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -454,6 +454,8 @@ files:
|
|
454
454
|
- app/assets/javascripts/caboose/model/s3.js
|
455
455
|
- app/assets/javascripts/caboose/my_account_edit_order.js
|
456
456
|
- app/assets/javascripts/caboose/product.js
|
457
|
+
- app/assets/javascripts/caboose/product_new.js
|
458
|
+
- app/assets/javascripts/caboose/product_old.js
|
457
459
|
- app/assets/javascripts/caboose/s3upload.js
|
458
460
|
- app/assets/javascripts/caboose/shortcut.js
|
459
461
|
- app/assets/javascripts/caboose/spectrum.js
|
@@ -547,6 +549,7 @@ files:
|
|
547
549
|
- app/controllers/caboose/products_controller.rb
|
548
550
|
- app/controllers/caboose/redirects_controller.rb
|
549
551
|
- app/controllers/caboose/register_controller.rb
|
552
|
+
- app/controllers/caboose/retargeting_controller.rb
|
550
553
|
- app/controllers/caboose/reviews_controller.rb
|
551
554
|
- app/controllers/caboose/roles_controller.rb
|
552
555
|
- app/controllers/caboose/settings_controller.rb
|
@@ -634,6 +637,7 @@ files:
|
|
634
637
|
- app/models/caboose/product_category_sort.rb
|
635
638
|
- app/models/caboose/product_image.rb
|
636
639
|
- app/models/caboose/product_image_variant.rb
|
640
|
+
- app/models/caboose/retargeting_config.rb
|
637
641
|
- app/models/caboose/review.rb
|
638
642
|
- app/models/caboose/rich_text_block_parser.rb
|
639
643
|
- app/models/caboose/role.rb
|
@@ -854,6 +858,7 @@ files:
|
|
854
858
|
- app/views/caboose/redirects/admin_index.html.erb
|
855
859
|
- app/views/caboose/redirects/admin_new.html.erb
|
856
860
|
- app/views/caboose/register/index.html.erb
|
861
|
+
- app/views/caboose/retargeting/admin_edit.html.erb
|
857
862
|
- app/views/caboose/roles/edit.html.erb
|
858
863
|
- app/views/caboose/roles/index.html.erb
|
859
864
|
- app/views/caboose/roles/new.html.erb
|