caboose-cms 0.3.20 → 0.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODg5MTMwYTQ4MmM5MDgwYzE4YTNhOTVkOTJmNjU1YjU4MmU0YmFkNg==
4
+ MzhlOGU4ZDRlZmYxZGIzODk0Zjk0NDhhM2IwYjM2ZTViMjlmZTA2ZA==
5
5
  data.tar.gz: !binary |-
6
- ZmQ3YzA1ZjU2ZGEwOTYyNWRiMjkxZWFhYzU5MDlkYjZhNmIxNGQzMw==
6
+ OWUwZmM5MjdkNmEwNWJkMWIzYjlkN2Q4NTBiMjliZmNkNDM5MzZiNQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NTIxZjMzNTBmMmJmOTE0NzdiNmQ4ZmIyZGVlMGRlZDQyNGQxOWEzNzAxZDA3
10
- ZTE1YTI4OGEzMmYwNGJjMzNkMGJkMjgyMzQwZDU3Njg2MTliNTdmOTZlMWFj
11
- OTU5MWYwY2M1MTY4ZDQ1NTgzMjU3MGZjNzc4OGViM2ZhNzdjNzI=
9
+ YjNhYjE1MzU1ZmU4NTQzOWFjMDY5ZmVjM2UwY2NhMzFmNDViMjE0ZTBhY2Ux
10
+ NDA1ODFiMGQyZGMzNWQ1MTcxZjMxMGI1MjAzZDliYjNlYzdlNWE4YTY4Yzc0
11
+ OTRmMzkwMmFkODcxYmVkM2I5MzAzMmI1YWU2NDI3NDQwZjJmYWU=
12
12
  data.tar.gz: !binary |-
13
- MmRmZWE4OGM0MTMzZmIyYzQwZmViMDVjYjJjMTExMWNiNWRlZGExZTI4YTgx
14
- NjNjYzgyODllMmYwZDMxY2ZhMTE4ZWY2NmU5YTcxNjMyNmI2NzQyNmQ2Nzc1
15
- ODIzN2VjYjQ0MThlYzdiNTg5M2ZkZjJhYmVjOTE4N2NjNTM5NGE=
13
+ ZmQ2YTQ4N2Q4M2U3MGJkYWYwNmNhYjczZDU4YTEzZmYwODM2NDBhN2RlMDRm
14
+ NDBkMGU4ZGJiZDAxMjI2NWRkMGQzMjc2ZTVkOTQ5YTNiYWFkNTMwNjMyNmE5
15
+ MTg5N2M5Yjc4ZjY3Yjg3Y2FhOTkwMTY5M2Q3NGIxM2UyM2Q1N2Y=
@@ -0,0 +1,57 @@
1
+ module Caboose
2
+ class AbOptionsController < ApplicationController
3
+ layout 'caboose/admin'
4
+
5
+ # GET /admin/ab_variants/:variant_id/options
6
+ def admin_index
7
+ return unless user_is_allowed_to 'view', 'ab_variants'
8
+ v = AbVariant.find(params[:variant_id])
9
+ render :json => v.ab_options
10
+ end
11
+
12
+ # POST admin/ab-variants/:variant_id/options'
13
+ def admin_create
14
+ return unless user_is_allowed_to 'edit','ab_variants'
15
+
16
+ resp = StdClass.new({
17
+ 'error' => nil,
18
+ 'redirect' => nil
19
+ })
20
+
21
+ opt = AbOption.create(
22
+ :ab_variant_id => params[:variant_id],
23
+ :text => params[:text]
24
+ )
25
+ resp.redirect = "/admin/ab-variants/#{params[:variant_id]}"
26
+ render :json => resp
27
+ end
28
+
29
+ # PUT /admin/ab_options/:id
30
+ def admin_update
31
+ return unless user_is_allowed_to 'edit', 'ab_variants'
32
+
33
+ resp = StdClass.new
34
+ opt = AbOption.find(params[:id])
35
+
36
+ save = true
37
+ params.each do |k,v|
38
+ case k
39
+ when 'text'
40
+ opt.text = v
41
+ break
42
+ end
43
+ end
44
+
45
+ resp.success = save && opt.save
46
+ render :json => resp
47
+ end
48
+
49
+ # DELETE /admin/ab_options/:id
50
+ def admin_delete
51
+ return unless user_is_allowed_to 'delete', 'ab_variants'
52
+ AbOption.find(params[:id]).destroy
53
+ render :json => true
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,88 @@
1
+ module Caboose
2
+ class AbVariantsController < ApplicationController
3
+ layout 'caboose/admin'
4
+
5
+ def before_action
6
+ @page = Page.page_with_uri('/admin')
7
+ end
8
+
9
+ # GET /admin/ab_variants
10
+ def admin_index
11
+ return unless user_is_allowed_to 'view', 'ab_variants'
12
+
13
+ @gen = PageBarGenerator.new(params, {'name' => '', 'analytics_name' => ''}, {
14
+ 'model' => 'Caboose::AbVariant',
15
+ 'sort' => 'name',
16
+ 'desc' => 'false',
17
+ 'base_url' => '/admin/ab-variants'
18
+ })
19
+ @variants = @gen.items
20
+ end
21
+
22
+ # GET /admin/ab_variants/new
23
+ def admin_new
24
+ return unless user_is_allowed_to 'add', 'ab_variants'
25
+ @variant = AbVariant.new
26
+ end
27
+
28
+ # GET /admin/ab_variants/:id
29
+ def admin_edit
30
+ return unless user_is_allowed_to 'edit', 'ab_variants'
31
+ @variant = AbVariant.find(params[:id])
32
+ end
33
+
34
+ # POST /admin/ab-variants
35
+ def admin_create
36
+ return unless user_is_allowed_to 'edit', 'ab_variants'
37
+
38
+ resp = StdClass.new({
39
+ 'error' => nil,
40
+ 'redirect' => nil
41
+ })
42
+
43
+ variant = AbVariant.new
44
+ variant.name = params[:name]
45
+ variant.analytics_name = params[:name].gsub(' ', '_').downcase
46
+
47
+ if (variant.name.length == 0)
48
+ resp.error = "A name is required."
49
+ elsif variant.save
50
+ resp.redirect = "/admin/ab-variants/#{variant.id}"
51
+ end
52
+
53
+ render json: resp
54
+ end
55
+
56
+ # PUT /admin/ab_variants/:id
57
+ def admin_update
58
+ return unless user_is_allowed_to 'edit', 'ab_variants'
59
+
60
+ resp = StdClass.new
61
+ variant = AbVariant.find(params[:id])
62
+
63
+ save = true
64
+ params.each do |k,v|
65
+ case k
66
+ when 'name'
67
+ variant.name = v
68
+ break
69
+ when 'analytics_name'
70
+ variant.analytics_name = v
71
+ break
72
+ end
73
+ end
74
+
75
+ resp.success = save && variant.save
76
+ render :json => resp
77
+ end
78
+
79
+ # DELETE /admin/ab_variants/:id
80
+ def admin_destroy
81
+ return unless user_is_allowed_to 'delete', 'ab_variants'
82
+ AbVariants.find(params[:id]).destroy
83
+ resp = StdClass.new('redirect' => '/admin/ab-variants')
84
+ render :json => resp
85
+ end
86
+
87
+ end
88
+ end
@@ -13,6 +13,7 @@ module Caboose
13
13
  @page = Page.page_with_uri(request.fullpath)
14
14
 
15
15
  session['use_redirect_urls'] = true if session['use_redirect_urls'].nil?
16
+ assign_ab_variants
16
17
 
17
18
  @crumb_trail = Caboose::Page.crumb_trail(@page)
18
19
  @subnav = {}
@@ -23,10 +24,10 @@ module Caboose
23
24
 
24
25
  # Sets an instance variable of the logged in user
25
26
  @logged_in_user = logged_in_user
26
-
27
+
27
28
  before_action
28
29
  end
29
-
30
+
30
31
  # Parses any parameters in the URL and adds them to the params
31
32
  def parse_url_params
32
33
  return if !Caboose.use_url_params
@@ -96,6 +97,8 @@ module Caboose
96
97
  return session["app_user"]
97
98
  end
98
99
 
100
+ # DEPRECATED: Use user_is_allowed_to(action, resource)
101
+ #
99
102
  # Checks to see if a user has permission to perform the given action
100
103
  # on the given resource.
101
104
  # Redirects to login if not logged in.
@@ -115,7 +118,31 @@ module Caboose
115
118
 
116
119
  return true
117
120
  end
118
-
121
+
122
+ # Checks to see if a user has permission
123
+ # to perform action on resource
124
+ #
125
+ # Redirects to login if not logged in
126
+ # Redirects to error page with message if not allowed
127
+ #
128
+ # useful for creating super-readable code, for example:
129
+ # > return unless user_is_allowed_to 'edit', 'pages'
130
+ # Even your mom could read that code.
131
+ def user_is_allowed_to(action, resource)
132
+ unless logged_in?
133
+ redirect_to "/login?return_url=" + URI.encode(request.fullpath)
134
+ return false
135
+ end
136
+
137
+ @user = logged_in_user
138
+ unless @user.is_allowed(resource, action)
139
+ @error = "You don't have permission to #{action} #{resource}"
140
+ render :template => "caboose/extras/error"
141
+ return false
142
+ end
143
+ return true
144
+ end
145
+
119
146
  # Redirects to login if not logged in.
120
147
  def verify_logged_in
121
148
  if (!logged_in?)
@@ -147,5 +174,44 @@ module Caboose
147
174
  return "" if v.nil?
148
175
  return v.val
149
176
  end
177
+
178
+ #===========================================================================
179
+ # AB Testing
180
+ #===========================================================================
181
+
182
+ # Sets the ab_variants for the user's session
183
+ def assign_ab_variants
184
+ return if session['ab_variants']
185
+
186
+ h = {}
187
+ arr = []
188
+ AbVariant.find_each do |var|
189
+ next if var.ab_options.nil? || var.ab_options.count == 0
190
+ i = rand(var.ab_options.count)
191
+ h[var.analytics_name] = var.ab_options[i].text
192
+ arr << "#{var.analytics_name}=#{i+1}"
193
+ end
194
+ session['ab_variants'] = h
195
+ session['ab_variants_analytics_string'] = "|#{arr.join('|')}|"
196
+ end
197
+
198
+ # Get the variant option text for the given variant name.
199
+ def ab_option_for(analytics_name)
200
+ assign_ab_variants if session['ab_variants'].nil?
201
+ return session['ab_variants'][analytics_name] if !session['ab_variants'][analytics_name].nil?
202
+
203
+ # Otherwise, add the new variant to the session
204
+ var = AbVariant.find(:analytics_name => analytics_name).first
205
+ i = rand(var.ab_options.count)
206
+ session['ab_variants'][var.analytics_name] = var.ab_options[i].text
207
+ session['ab_variants_analytics_string'] << "#{var.analytics_name}=#{i+1}|"
208
+ end
209
+
210
+ # Gets the string to be sent to google analytics
211
+ def analytics_string
212
+ assign_ab_variants if session['ab_variants_analytics_string'].nil?
213
+ return "#{session['ab_variants_analytics_string']}"
214
+ end
215
+
150
216
  end
151
217
  end
@@ -20,5 +20,10 @@ module Caboose
20
20
  return str
21
21
  end
22
22
 
23
+ def ab_variants_analytics_code
24
+ str = session['ab_variants_analytics_string']
25
+ return "var _gaq = _gaq || [];\n_gaq.push(['_setCustomVar', 1, 'caboose_ab_variants', #{Caboose.json(str)}]);"
26
+ end
27
+
23
28
  end
24
29
  end
@@ -0,0 +1,7 @@
1
+ class Caboose::AbOption < ActiveRecord::Base
2
+
3
+ self.table_name = "ab_options"
4
+ belongs_to :ab_variant
5
+ attr_accessible :text, :ab_variant_id
6
+
7
+ end
@@ -0,0 +1,22 @@
1
+ # Class for A-B testing variants. A variant is a set of changes
2
+ # to the same element on a page. For example, a variant can be used
3
+ # to change the button text, or images displayed, or even more
4
+ # complicated behavior
5
+ class Caboose::AbVariant < ActiveRecord::Base
6
+ self.table_name = "ab_variants"
7
+
8
+ has_many :ab_options, :order => 'id', :dependent => :destroy
9
+ attr_accessible :name, :analytics_name
10
+
11
+ def get_session_option
12
+ return "" unless self.ab_options
13
+ opt = self.ab_options.sample
14
+ return {text: opt.text, id: opt.id}
15
+ end
16
+
17
+ #def random_option
18
+ # return nil self.ab_options.nil? || self.ab_options.count == 0
19
+ # return self.ab_options.sample
20
+ #end
21
+
22
+ end
@@ -20,12 +20,13 @@ class Caboose::CorePlugin < Caboose::CaboosePlugin
20
20
  'text' => 'Settings',
21
21
  'children' => []
22
22
  }
23
-
23
+
24
24
  item['children'] << { 'id' => 'users' , 'text' => 'Users' , 'href' => '/admin/users' , 'modal' => false } if user.is_allowed('users' , 'view')
25
25
  item['children'] << { 'id' => 'roles' , 'text' => 'Roles' , 'href' => '/admin/roles' , 'modal' => false } if user.is_allowed('roles' , 'view')
26
26
  item['children'] << { 'id' => 'permissions' , 'text' => 'Permissions' , 'href' => '/admin/permissions' , 'modal' => false } if user.is_allowed('permissions' , 'view')
27
+ item['children'] << { 'id' => 'blocktypes' , 'text' => 'AB Test Variants' , 'href' => '/admin/ab-variants' , 'modal' => false } if user.is_allowed('abvariants' , 'view')
27
28
  item['children'] << { 'id' => 'variables' , 'text' => 'Variables' , 'href' => '/admin/settings' , 'modal' => false } if user.is_allowed('settings' , 'view')
28
- item['children'] << { 'id' => 'blocktypes' , 'text' => 'Page Block Types' , 'href' => '/admin/page-block-types' , 'modal' => false } if user.is_allowed('pageblocktypes' , 'view')
29
+ item['children'] << { 'id' => 'blocktypes' , 'text' => 'Page Block Types' , 'href' => '/admin/page-block-types' , 'modal' => false } if user.is_allowed('pageblocktypes' , 'view')
29
30
 
30
31
  nav << item if item['children'].count > 0
31
32
 
@@ -39,6 +40,9 @@ class Caboose::CorePlugin < Caboose::CaboosePlugin
39
40
  item['children'] << { 'id' => 'posts' , 'text' => 'Posts' , 'href' => '/admin/posts' , 'modal' => false } if user.is_allowed('posts' , 'view')
40
41
 
41
42
  nav << item if item['children'].count > 0
43
+ if user.is_allowed('ab-variants','view')
44
+ nav << { 'id' => 'ab_variants', 'text' => 'AB Testing', 'href' => '/admin/ab-variants', 'modal' => false }
45
+ end
42
46
  return nav
43
47
  end
44
48
 
@@ -143,6 +143,14 @@ class Caboose::Schema < Caboose::Utilities::Schema
143
143
  [ :page_id , :integer ],
144
144
  [ :action , :string ]
145
145
  ],
146
+ Caboose::AbVariant => [
147
+ [ :name , :string ],
148
+ [ :analytics_name , :string ],
149
+ ],
150
+ Caboose::AbOption => [
151
+ [ :text , :string ],
152
+ [ :ab_variant_id , :integer ]
153
+ ],
146
154
  Caboose::DatabaseSession => [
147
155
  [ :session_id , :string , :null => false ],
148
156
  [ :data , :text ],
@@ -45,11 +45,17 @@ class Caboose::Utilities::Schema
45
45
  c = ActiveRecord::Base.connection
46
46
  self.schema.each do |model, columns|
47
47
  tbl = model.table_name
48
- #puts "Creating table #{tbl}..."
48
+ puts "Creating table #{tbl}..."
49
49
  c.create_table tbl if !c.table_exists?(tbl)
50
50
  columns.each do |col|
51
- #puts "Creating column #{tbl}.#{col[0]}..."
51
+ puts "Creating column #{tbl}.#{col[0]}..."
52
52
 
53
+ # Special case for attachments
54
+ if col[1] == :attachment
55
+ c.add_attachment tbl, col[1] if !c.column_exists?(tbl, "#{col[1]}_file_size")
56
+ next
57
+ end
58
+
53
59
  # Skip if the column exists with the proper data type
54
60
  next if c.column_exists?(tbl, col[0], col[1])
55
61
 
@@ -0,0 +1,99 @@
1
+ <h1>Editing AB Test Variant</h1>
2
+
3
+ <div id="abvariant_<%= @variant.id %>_name"></div>
4
+ <div id="abvariant_<%= @variant.id %>_analytics_name"></div>
5
+
6
+ <p>Google analytics search term: <code>caboose_ab_variants="*|<%= @variant.analytics_name %>=1|*"</code></p>
7
+ <h2>Options</h2>
8
+ <% if @variant.ab_options && @variant.ab_options.count > 0 %>
9
+ <table>
10
+ <% @variant.ab_options.each do |opt| %>
11
+ <tr>
12
+ <td><div id="aboption_<%= opt.id %>_text"></div></td>
13
+ <td><input type='button' value='Delete' onclick="delete_option(<%= opt.id %>);" /></td>
14
+ </tr>
15
+ <% end %>
16
+ </table>
17
+ <% else %>
18
+ <p>This variant doesn't have any options.</p>
19
+ <% end %>
20
+
21
+ <div id="message"></div>
22
+ <p>
23
+ <input type='button' value='Back' onclick="window.location='/admin/ab-variants';" >
24
+ <input type='button' value='Add New Option' onclick="add_option();" >
25
+ </p>
26
+
27
+ <% content_for :caboose_js do %>
28
+ <%= javascript_include_tag "caboose/model/all" %>
29
+ <script type="text/javascript">
30
+
31
+ function delete_option(option_id, confirm)
32
+ {
33
+ if (!confirm)
34
+ {
35
+ var p = $('<p/>').addClass('note warning')
36
+ .append("Are you sure you want to delete the option? ")
37
+ .append($('<input/>').attr('type','button').val('Yes').click(function() { delete_option(option_id, true); })).append(' ')
38
+ .append($('<input/>').attr('type','button').val('No').click(function() { $('#message').empty(); }));
39
+ $('#message').empty().append(p);
40
+ return;
41
+ }
42
+ $('#message').empty().html($('<p/>').addClass('loading').html("Deleting option..."));
43
+ $.ajax({
44
+ url: '/admin/ab-options/' + option_id,
45
+ type: 'delete',
46
+ success: function(resp) {
47
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
48
+ else window.location.reload(true);
49
+ }
50
+ });
51
+ }
52
+
53
+ function add_option() {
54
+ $('#message').html("<p class='loading'>Adding new A/B Option...</p>");
55
+ $.ajax({
56
+ url: '/admin/ab-variants/<%= @variant.id %>/options',
57
+ type: 'post',
58
+ data: {
59
+ text: ""
60
+ },
61
+ success: function(resp) {
62
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
63
+ if (resp.redirect) window.location.reload(true);
64
+ }
65
+ });
66
+ }
67
+
68
+ $(document).ready(function() {
69
+
70
+ m = new ModelBinder({
71
+ name: 'abvariant',
72
+ id: <%= @variant.id %>,
73
+ update_url: '/admin/ab-variants/<%= @variant.id %>',
74
+ authenticity_token: '<%= form_authenticity_token %>',
75
+ attributes: [
76
+ { name: 'name' , nice_name: 'Variant Name' , type: 'text', value: <%= raw Caboose.json(@variant.name) %> , width: 800 },
77
+ { name: 'analytics_name' , nice_name: 'Google Analytics Name' , type: 'text', value: <%= raw Caboose.json(@variant.analytics_name) %> , width: 800 }
78
+ ]
79
+ });
80
+
81
+ <% if @variant.ab_options && @variant.ab_options.count > 0 %>
82
+ <% i = 1 %>
83
+ <% @variant.ab_options.each do |opt| %>
84
+ m = new ModelBinder({
85
+ name: 'aboption',
86
+ id: <%= opt.id %>,
87
+ update_url: '/admin/ab-options/<%= opt.id %>',
88
+ authenticity_token: '<%= form_authenticity_token %>',
89
+ attributes: [
90
+ { name: 'text', nice_name: 'Option <%= i %>', type: 'text', value: <%= raw Caboose.json(opt.text) %> , width: 800 }
91
+ ]
92
+ });
93
+ <% i = i + 1 %>
94
+ <% end %>
95
+ <% end %>
96
+ });
97
+
98
+ </script>
99
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <h1>A/B Test Variants</h1>
2
+
3
+ <p><a href='/admin/ab-variants/new'>New Variant</a></p>
4
+ <% if (@variants.count > 0) %>
5
+ <table class='data'>
6
+ <tr><%= raw @gen.sortable_table_headings({'name' => 'Variant Name', 'analytics_name' => 'Analytics Name'}) %></tr>
7
+ <% @variants.each do |v| %>
8
+ <tr onclick="window.location='/admin/ab-variants/<%= v.id %>';">
9
+ <td><%= v.name %></td>
10
+ <td><%= v.analytics_name %></td>
11
+ </tr>
12
+ <% end %>
13
+ </table>
14
+ <p><%= raw @gen.generate %></p>
15
+ <% else %>
16
+ <p>No A/B variants found.</p>
17
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <h1>New AB Test</h1>
2
+
3
+ <form action='/admin/ab-variants' method='post' id='ab_variant_form'>
4
+ <input type='hidden' name='authenticity_token' value='<%=form_authenticity_token %>'>
5
+ <p><input type='text' name='name' id='name' placeholder='AB Variant Name' style='width: 500px;'></p>
6
+ <div id='message'></div>
7
+ <p>
8
+ <input type='button' value='< Back' onclick="window.location='/admin/ab-variants';">
9
+ <input type='submit' value='Add New A/B Variant' onclick="add_ab_variant(); return false;">
10
+ </p>
11
+ </form>
12
+
13
+ <% content_for :caboose_js do %>
14
+ <script type='text/javascript'>
15
+
16
+ function add_ab_variant()
17
+ {
18
+ $('#message').html("<p class='loading'>Adding A/B Variant...</p>");
19
+ $.ajax({
20
+ url: '/admin/ab-variants',
21
+ type: 'post',
22
+ data: $('#ab_variant_form').serialize(),
23
+ success: function(resp) {
24
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
25
+ if (resp.redirect) window.location = resp.redirect;
26
+ }
27
+ });
28
+ }
29
+
30
+ var modal = false;
31
+ $(window).load(function() {
32
+ modal = new CabooseModal(800);
33
+ });
34
+
35
+ </script>
36
+ <% end %>
@@ -115,6 +115,18 @@ Caboose::Engine.routes.draw do
115
115
  get "admin/posts" => "posts#admin_index"
116
116
  post "admin/posts" => "posts#admin_add"
117
117
  delete "admin/posts/:id" => "posts#admin_delete"
118
+
119
+ get "admin/ab-variants" => "ab_variants#admin_index"
120
+ get "admin/ab-variants/new" => "ab_variants#admin_new"
121
+ get "admin/ab-variants/:id" => "ab_variants#admin_edit"
122
+ put "admin/ab-variants/:id" => "ab_variants#admin_update"
123
+ post "admin/ab-variants" => "ab_variants#admin_create"
124
+ delete "admin/ab-variants/:id" => "ab_variants#admin_delete"
125
+
126
+ get "admin/ab-variants/:variant_id/options" => "ab_options#admin_index"
127
+ put "admin/ab-options/:id" => "ab_options#admin_update"
128
+ post "admin/ab-variants/:variant_id/options" => "ab_options#admin_create"
129
+ delete "admin/ab-options/:id" => "ab_options#admin_delete"
118
130
 
119
131
  match '*path' => 'pages#show'
120
132
  root :to => 'pages#show'
@@ -1,3 +1,3 @@
1
1
  module Caboose
2
- VERSION = '0.3.20'
2
+ VERSION = '0.3.22'
3
3
  end
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.3.20
4
+ version: 0.3.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Barry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-17 00:00:00.000000000 Z
11
+ date: 2014-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -166,6 +166,8 @@ files:
166
166
  - app/assets/stylesheets/caboose/station_modal.css
167
167
  - app/assets/stylesheets/caboose/station_sidebar.css
168
168
  - app/assets/stylesheets/caboose/tinymce.css
169
+ - app/controllers/caboose/ab_options_controller.rb
170
+ - app/controllers/caboose/ab_variants_controller.rb
169
171
  - app/controllers/caboose/admin_controller.rb
170
172
  - app/controllers/caboose/application_controller.rb
171
173
  - app/controllers/caboose/login_controller.rb
@@ -186,6 +188,8 @@ files:
186
188
  - app/helpers/caboose/pages_helper.rb
187
189
  - app/helpers/caboose/permissions_helper.rb
188
190
  - app/helpers/caboose/products_helper.rb
191
+ - app/models/caboose/ab_option.rb
192
+ - app/models/caboose/ab_variant.rb
189
193
  - app/models/caboose/approval_request.rb
190
194
  - app/models/caboose/asset.rb
191
195
  - app/models/caboose/authenticator.rb
@@ -221,6 +225,9 @@ files:
221
225
  - app/models/caboose/std_class.rb
222
226
  - app/models/caboose/user.rb
223
227
  - app/models/caboose/utilities/schema.rb
228
+ - app/views/caboose/ab_variants/admin_edit.html.erb
229
+ - app/views/caboose/ab_variants/admin_index.html.erb
230
+ - app/views/caboose/ab_variants/admin_new.html.erb
224
231
  - app/views/caboose/admin/index.html.erb
225
232
  - app/views/caboose/application/show.html.erb
226
233
  - app/views/caboose/extras/error.html.erb