spree_core 1.2.2 → 1.2.3
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.
- data/app/assets/javascripts/admin/admin.js.erb +27 -2
- data/app/assets/javascripts/admin/taxon_autocomplete.js.erb +25 -23
- data/app/assets/stylesheets/store/screen.css.scss +13 -0
- data/app/controllers/spree/admin/images_controller.rb +0 -11
- data/app/controllers/spree/admin/option_types_controller.rb +3 -14
- data/app/controllers/spree/admin/option_values_controller.rb +11 -0
- data/app/controllers/spree/admin/product_properties_controller.rb +12 -0
- data/app/controllers/spree/admin/resource_controller.rb +10 -0
- data/app/controllers/spree/admin/taxons_controller.rb +1 -1
- data/app/controllers/spree/admin/variants_controller.rb +0 -11
- data/app/controllers/spree/products_controller.rb +9 -3
- data/app/helpers/spree/admin/base_helper.rb +2 -1
- data/app/helpers/spree/admin/products_helper.rb +1 -1
- data/app/helpers/spree/checkout_helper.rb +15 -0
- data/app/models/spree/calculator/per_item.rb +3 -1
- data/app/models/spree/image.rb +1 -1
- data/app/models/spree/line_item.rb +1 -1
- data/app/models/spree/order.rb +25 -4
- data/app/models/spree/order/checkout.rb +4 -1
- data/app/models/spree/payment.rb +1 -1
- data/app/models/spree/payment/processing.rb +1 -4
- data/app/models/spree/preferences/preferable_class_methods.rb +6 -9
- data/app/models/spree/preferences/store.rb +22 -11
- data/app/models/spree/product.rb +1 -1
- data/app/models/spree/shipment.rb +19 -15
- data/app/models/spree/taxonomy.rb +2 -0
- data/app/views/spree/admin/image_settings/edit.html.erb +7 -0
- data/app/views/spree/admin/orders/edit.html.erb +1 -1
- data/app/views/spree/admin/products/_form.html.erb +6 -1
- data/app/views/spree/admin/shared/_order_tabs.html.erb +1 -1
- data/app/views/spree/admin/shared/_translations.html.erb +1 -1
- data/app/views/spree/admin/shipping_methods/_form.html.erb +1 -1
- data/app/views/spree/admin/taxonomies/_list.html.erb +2 -1
- data/app/views/spree/checkout/_address.html.erb +47 -47
- data/app/views/spree/checkout/payment/_gateway.html.erb +2 -2
- data/app/views/spree/layouts/spree_application.html.erb +1 -1
- data/app/views/spree/orders/_line_item.html.erb +1 -1
- data/app/views/spree/products/_thumbnails.html.erb +17 -15
- data/app/views/spree/products/show.html.erb +1 -1
- data/app/views/spree/shared/_footer.html.erb +1 -1
- data/app/views/spree/shared/_google_analytics.html.erb +8 -8
- data/app/views/spree/shared/_header.html.erb +1 -1
- data/app/views/spree/shared/_main_nav_bar.html.erb +1 -1
- data/app/views/spree/shared/_nav_bar.html.erb +1 -1
- data/app/views/spree/shared/_order_details.html.erb +3 -1
- data/app/views/spree/shared/_sidebar.html.erb +1 -1
- data/app/views/spree/taxons/show.html.erb +1 -1
- data/config/locales/en.yml +6 -15
- data/config/routes.rb +7 -0
- data/db/migrate/20121124203911_add_position_to_taxonomies.rb +5 -0
- data/lib/spree/core/controller_helpers.rb +1 -0
- data/lib/spree/core/testing_support/factories/payment_factory.rb +0 -18
- data/lib/spree/core/validators/email.rb +1 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/tasks/taxon.rake +1 -1
- metadata +147 -54
@@ -29,6 +29,7 @@ module Spree
|
|
29
29
|
|
30
30
|
def capture!
|
31
31
|
return true if completed?
|
32
|
+
started_processing!
|
32
33
|
protect_from_connection_error do
|
33
34
|
check_environment
|
34
35
|
|
@@ -165,10 +166,6 @@ module Spree
|
|
165
166
|
end
|
166
167
|
end
|
167
168
|
|
168
|
-
def record_log(response)
|
169
|
-
log_entries.create({:details => response.to_yaml}, :without_protection => true)
|
170
|
-
end
|
171
|
-
|
172
169
|
def gateway_error(error)
|
173
170
|
if error.is_a? ActiveMerchant::Billing::Response
|
174
171
|
text = error.params['message'] || error.params['response_reason_text'] || error.message
|
@@ -10,16 +10,13 @@ module Spree::Preferences
|
|
10
10
|
# cache_key will be nil for new objects, then if we check if there
|
11
11
|
# is a pending preference before going to default
|
12
12
|
define_method preference_getter_method(name) do
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
# perference_cache_key will only be nil/false for new records
|
15
|
+
#
|
16
|
+
if preference_cache_key(name)
|
17
|
+
preference_store.get(preference_cache_key(name), default)
|
15
18
|
else
|
16
|
-
|
17
|
-
get_pending_preference(name)
|
18
|
-
elsif Spree::Preference.table_exists? && preference = Spree::Preference.find_by_name(name)
|
19
|
-
preference.value
|
20
|
-
else
|
21
|
-
send self.class.preference_default_getter_method(name)
|
22
|
-
end
|
19
|
+
get_pending_preference(name) || default
|
23
20
|
end
|
24
21
|
end
|
25
22
|
alias_method prefers_getter_method(name), preference_getter_method(name)
|
@@ -27,25 +27,36 @@ module Spree::Preferences
|
|
27
27
|
should_persist? && Spree::Preference.where(:key => key).exists?
|
28
28
|
end
|
29
29
|
|
30
|
-
def get(key)
|
30
|
+
def get(key,fallback=nil)
|
31
31
|
# return the retrieved value, if it's in the cache
|
32
|
-
|
32
|
+
# use unless nil? incase the value is actually boolean false
|
33
|
+
#
|
34
|
+
unless (val = @cache.read(key)).nil?
|
33
35
|
return val
|
34
36
|
end
|
35
37
|
|
36
|
-
|
38
|
+
if should_persist?
|
39
|
+
# If it's not in the cache, maybe it's in the database, but
|
40
|
+
# has been cleared from the cache
|
37
41
|
|
38
|
-
|
39
|
-
|
42
|
+
# does it exist in the database?
|
43
|
+
if Spree::Preference.table_exists? && preference = Spree::Preference.find_by_key(key)
|
44
|
+
# it does exist, so let's put it back into the cache
|
45
|
+
@cache.write(preference.key, preference.value)
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
47
|
+
# and return the value
|
48
|
+
return preference.value
|
49
|
+
end
|
50
|
+
end
|
45
51
|
|
46
|
-
|
47
|
-
|
52
|
+
unless fallback.nil?
|
53
|
+
# cache fallback so we won't hit the db above on
|
54
|
+
# subsequent queries for the same key
|
55
|
+
#
|
56
|
+
@cache.write(key, fallback)
|
48
57
|
end
|
58
|
+
|
59
|
+
return fallback
|
49
60
|
end
|
50
61
|
|
51
62
|
def delete(key)
|
data/app/models/spree/product.rb
CHANGED
@@ -37,7 +37,7 @@ module Spree
|
|
37
37
|
has_many :variants,
|
38
38
|
:class_name => 'Spree::Variant',
|
39
39
|
:conditions => { :is_master => false, :deleted_at => nil },
|
40
|
-
:order =>
|
40
|
+
:order => "#{::Spree::Variant.quoted_table_name}.position ASC"
|
41
41
|
|
42
42
|
has_many :variants_including_master,
|
43
43
|
:class_name => 'Spree::Variant',
|
@@ -28,7 +28,7 @@ module Spree
|
|
28
28
|
|
29
29
|
scope :with_state, lambda { |s| where(:state => s) }
|
30
30
|
scope :shipped, with_state('shipped')
|
31
|
-
scope :ready,
|
31
|
+
scope :ready, with_state('ready')
|
32
32
|
scope :pending, with_state('pending')
|
33
33
|
|
34
34
|
def to_param
|
@@ -47,12 +47,16 @@ module Spree
|
|
47
47
|
def cost
|
48
48
|
adjustment ? adjustment.amount : 0
|
49
49
|
end
|
50
|
+
|
50
51
|
alias_method :amount, :cost
|
51
52
|
|
52
53
|
# shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
|
53
54
|
state_machine :initial => 'pending', :use_transactions => false do
|
54
55
|
event :ready do
|
55
|
-
transition :from => 'pending', :to => 'ready'
|
56
|
+
transition :from => 'pending', :to => 'ready', :if => lambda { |shipment|
|
57
|
+
# Fix for #2040
|
58
|
+
shipment.determine_state(shipment.order) == 'ready'
|
59
|
+
}
|
56
60
|
end
|
57
61
|
event :pend do
|
58
62
|
transition :from => 'ready', :to => 'pending'
|
@@ -92,12 +96,24 @@ module Spree
|
|
92
96
|
after_ship if new_state == 'shipped' and old_state != 'shipped'
|
93
97
|
end
|
94
98
|
|
99
|
+
# Determines the appropriate +state+ according to the following logic:
|
100
|
+
#
|
101
|
+
# pending unless order is complete and +order.payment_state+ is +paid+
|
102
|
+
# shipped if already shipped (ie. does not change the state)
|
103
|
+
# ready all other cases
|
104
|
+
def determine_state(order)
|
105
|
+
return 'pending' unless order.can_ship?
|
106
|
+
return 'pending' if inventory_units.any? &:backordered?
|
107
|
+
return 'shipped' if state == 'shipped'
|
108
|
+
order.paid? ? 'ready' : 'pending'
|
109
|
+
end
|
110
|
+
|
95
111
|
private
|
96
112
|
def generate_shipment_number
|
97
113
|
return number unless number.blank?
|
98
114
|
record = true
|
99
115
|
while record
|
100
|
-
random = "H#{Array.new(11){rand(9)}.join}"
|
116
|
+
random = "H#{Array.new(11) { rand(9) }.join}"
|
101
117
|
record = self.class.where(:number => random).first
|
102
118
|
end
|
103
119
|
self.number = random
|
@@ -113,18 +129,6 @@ module Spree
|
|
113
129
|
end
|
114
130
|
end
|
115
131
|
|
116
|
-
# Determines the appropriate +state+ according to the following logic:
|
117
|
-
#
|
118
|
-
# pending unless order is complete and +order.payment_state+ is +paid+
|
119
|
-
# shipped if already shipped (ie. does not change the state)
|
120
|
-
# ready all other cases
|
121
|
-
def determine_state(order)
|
122
|
-
return 'pending' unless order.complete?
|
123
|
-
return 'pending' if inventory_units.any? &:backordered?
|
124
|
-
return 'shipped' if state == 'shipped'
|
125
|
-
order.paid? ? 'ready' : 'pending'
|
126
|
-
end
|
127
|
-
|
128
132
|
# Determines whether or not inventory units should be associated with the shipment. This is always +false+ when
|
129
133
|
# +Spree::Config[:track_inventory_levels]+ is set to +false.+ Otherwise its +true+ whenever the order is completed
|
130
134
|
# (and not canceled.)
|
@@ -13,6 +13,13 @@
|
|
13
13
|
</label>
|
14
14
|
</p>
|
15
15
|
|
16
|
+
<p data-hook="attachment_url">
|
17
|
+
<label>
|
18
|
+
<%= label_tag 'preferences[attachment_url]', t(:attachment_url) %>
|
19
|
+
<%= preference_field_tag 'preferences[attachment_url]', Spree::Config[:attachment_url], :type => :string, :size => 60 %>
|
20
|
+
</label>
|
21
|
+
</p>
|
22
|
+
|
16
23
|
<p data-hook="attachment_default_url">
|
17
24
|
<label>
|
18
25
|
<%= label_tag 'preferences[attachment_default_url]', t(:attachment_default_url) %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<%= event_links %>
|
5
5
|
</div>
|
6
6
|
<div class="toolbar">
|
7
|
-
<%= button_link_to t(:resend), resend_admin_order_url(@order), :method => :post, :icon => 'send-email'
|
7
|
+
<%= button_link_to t(:resend), resend_admin_order_url(@order), :method => :post, :icon => 'send-email' %>
|
8
8
|
</div>
|
9
9
|
</div>
|
10
10
|
|
@@ -35,7 +35,12 @@
|
|
35
35
|
<%= f.field_container :available_on do %>
|
36
36
|
<%= f.label :available_on, t(:available_on) %><br />
|
37
37
|
<%= f.error_message_on :available_on %>
|
38
|
-
|
38
|
+
<% if @product.available_on? %>
|
39
|
+
<% available_on = l(@product.available_on, :format => t('spree.date_picker.format')) %>
|
40
|
+
<% else %>
|
41
|
+
<% available_on = nil %>
|
42
|
+
<% end %>
|
43
|
+
<%= f.text_field :available_on, :value => available_on, :class => 'datepicker' %>
|
39
44
|
<% end %>
|
40
45
|
|
41
46
|
<% unless @product.has_variants? %>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<h5 id="order_status" data-hook><%= t(:status) %>: <%= t(@order.state, :scope => :order_state) %></h5>
|
7
7
|
<h5 id="order_total" data-hook><%= t(:total) %>: <%= money @order.total %></h5>
|
8
8
|
<% if @order.completed? %>
|
9
|
-
<h5 id="shipment_status"><%= t(:shipment) %>: <%= t(@order.shipment_state, :scope => :
|
9
|
+
<h5 id="shipment_status"><%= t(:shipment) %>: <%= t(@order.shipment_state, :scope => :shipment_states, :default => [:missing, "none"]) %></h5>
|
10
10
|
<h5 id="payment_status"><%= t(:payment) %>: <%= t(@order.payment_state, :scope => :payment_states, :default => [:missing, "none"]) %></h5>
|
11
11
|
<h5 id="date_completed" style="width:100%; float:none;" data-hook><%= t(:date_completed) %>: <%= I18n.l(@order.completed? ? @order.completed_at : @order.created_at) %></h5>
|
12
12
|
<% end %>
|
@@ -18,7 +18,7 @@
|
|
18
18
|
<% end %>
|
19
19
|
</div>
|
20
20
|
|
21
|
-
<div data-hook"admin_shipping_method_form_availability_fields">
|
21
|
+
<div data-hook="admin_shipping_method_form_availability_fields">
|
22
22
|
<fieldset class="categories">
|
23
23
|
<legend><%= t(:availability) %></legend>
|
24
24
|
<%= f.field_container :shipping_category do %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<table class="index" id='listing_taxonomies' data-hook>
|
1
|
+
<table class="index sortable" id='listing_taxonomies' data-hook data-sortable-link="<%= update_positions_admin_taxonomies_url %>">
|
2
2
|
<tr data-hook="taxonomies_header">
|
3
3
|
<th><%= t(:name) %></th>
|
4
4
|
<th></th>
|
@@ -6,6 +6,7 @@
|
|
6
6
|
<% @taxonomies.each do |taxonomy| %>
|
7
7
|
<tr id="<%= spree_dom_id taxonomy %>" data-hook="taxonomies_row">
|
8
8
|
<td>
|
9
|
+
<span class="handle"></span>
|
9
10
|
<%= taxonomy.name %>
|
10
11
|
</td>
|
11
12
|
<td class="actions">
|
@@ -38,29 +38,30 @@
|
|
38
38
|
</span>
|
39
39
|
</p>
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
41
|
+
<p class="field" id="bstate">
|
42
|
+
<% have_states = !@order.bill_address.country.states.empty? %>
|
43
|
+
|
44
|
+
<%= bill_form.label :state, t(:state) %><%= state_required_label %><br />
|
45
|
+
|
46
|
+
<% state_elements = [
|
47
|
+
bill_form.collection_select(:state_id, @order.bill_address.country.states,
|
48
|
+
:id, :name,
|
49
|
+
{:include_blank => true},
|
50
|
+
{:class => have_states ? state_required_class : 'hidden',
|
51
|
+
:disabled => !have_states}) +
|
52
|
+
bill_form.text_field(:state_name,
|
53
|
+
:class => !have_states ? state_required_class : 'hidden',
|
54
|
+
:disabled => have_states)
|
55
|
+
].join.gsub('"', "'").gsub("\n", "")
|
56
|
+
%>
|
57
|
+
<%= javascript_tag do -%>
|
58
|
+
document.write("<%== state_elements %>");
|
59
|
+
<% end -%>
|
60
|
+
</p>
|
61
|
+
|
62
|
+
<noscript>
|
63
|
+
<%= bill_form.text_field :state_name, :class => state_required_class %>
|
64
|
+
</noscript>
|
64
65
|
|
65
66
|
<p class="field" id="bzipcode">
|
66
67
|
<%= bill_form.label :zipcode, t(:zip) %><span class="required">*</span><br />
|
@@ -86,7 +87,7 @@
|
|
86
87
|
<%= form.fields_for :ship_address do |ship_form| %>
|
87
88
|
<legend><%= t(:shipping_address) %></legend>
|
88
89
|
<p class="field checkbox" data-hook="use_billing">
|
89
|
-
<%= check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address)) %>
|
90
|
+
<%= check_box_tag 'order[use_billing]', '1', (!(@order.bill_address.empty? && @order.ship_address.empty?) && @order.bill_address.same_as?(@order.ship_address)) %>
|
90
91
|
<%= label_tag :order_use_billing, t(:use_billing_address), :id => 'use_billing' %>
|
91
92
|
</p>
|
92
93
|
<div class="inner" data-hook="shipping_inner">
|
@@ -125,29 +126,28 @@
|
|
125
126
|
</span>
|
126
127
|
</p>
|
127
128
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
<% end %>
|
129
|
+
<p class="field" id="sstate">
|
130
|
+
<% have_states = !@order.ship_address.country.states.empty? %>
|
131
|
+
<%= ship_form.label :state, t(:state) %><%= state_required_label %><br />
|
132
|
+
<% state_elements = [
|
133
|
+
ship_form.collection_select(:state_id, @order.ship_address.country.states,
|
134
|
+
:id, :name,
|
135
|
+
{:include_blank => true},
|
136
|
+
{:class => have_states ? state_required_class : 'hidden',
|
137
|
+
:disabled => !have_states}) +
|
138
|
+
ship_form.text_field(:state_name,
|
139
|
+
:class => !have_states ? state_required_class : 'hidden',
|
140
|
+
:disabled => have_states)
|
141
|
+
].join.gsub('"', "'").gsub("\n", "")
|
142
|
+
%>
|
143
|
+
<%= javascript_tag do -%>
|
144
|
+
document.write("<%== state_elements %>");
|
145
|
+
<% end %>
|
146
|
+
</p>
|
147
|
+
|
148
|
+
<noscript>
|
149
|
+
<%= ship_form.text_field :state_name, :class => state_required_class %>
|
150
|
+
</noscript>
|
151
151
|
|
152
152
|
<p class="field" id="szipcode">
|
153
153
|
<%= ship_form.label :zipcode, t(:zip) %><span class="required">*</span><br />
|
@@ -15,8 +15,8 @@
|
|
15
15
|
</p>
|
16
16
|
<p class="field" data-hook="card_expiration">
|
17
17
|
<%= label_tag nil, t(:expiration) %><br />
|
18
|
-
<%= select_month(Date.today, :prefix => param_prefix, :field_name => 'month', :use_month_numbers => true, :class => 'required') %>
|
19
|
-
<%= select_year(Date.today, :prefix => param_prefix, :field_name => 'year', :start_year => Date.today.year, :end_year => Date.today.year + 15, :class => 'required') %>
|
18
|
+
<%= select_month(Date.today, { :prefix => param_prefix, :field_name => 'month', :use_month_numbers => true }, :class => 'required') %>
|
19
|
+
<%= select_year(Date.today, { :prefix => param_prefix, :field_name => 'year', :start_year => Date.today.year, :end_year => Date.today.year + 15 }, :class => 'required') %>
|
20
20
|
<span class="required">*</span>
|
21
21
|
</p>
|
22
22
|
<p class="field" data-hook="card_code">
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<div id="wrapper" class="row" data-hook>
|
14
14
|
<%= breadcrumbs(@taxon) %>
|
15
15
|
<%= render :partial => 'spree/shared/sidebar' if content_for? :sidebar %>
|
16
|
-
<div id="content" class="columns
|
16
|
+
<div id="content" class="columns <%= !content_for?(:sidebar) ? "sixteen" : "twelve" %>" data-hook>
|
17
17
|
<%= flash_messages %>
|
18
18
|
<%= yield %>
|
19
19
|
</div>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<%= variant.options_text %>
|
12
12
|
<% if @order.insufficient_stock_lines.include? line_item %>
|
13
13
|
<span class="out-of-stock">
|
14
|
-
<%= variant.in_stock? ? t(:insufficient_stock, :on_hand => variant.on_hand) : t(:out_of_stock)
|
14
|
+
<%= variant.in_stock? ? t(:insufficient_stock, :on_hand => variant.on_hand) : t(:out_of_stock) %> <br />
|
15
15
|
</span>
|
16
16
|
<% end %>
|
17
17
|
<%= line_item_description(variant) %>
|
@@ -1,19 +1,21 @@
|
|
1
|
-
|
2
|
-
<% if product.images
|
1
|
+
<%# no need for thumbnails unless there is more than one image %>
|
2
|
+
<% if (@product.images + @product.variant_images).uniq.size > 1 %>
|
3
3
|
<ul id="product-thumbnails" class="thumbnails inline" data-hook>
|
4
|
-
<% product.images.each do |i| %>
|
5
|
-
<li class=
|
4
|
+
<% @product.images.each do |i| %>
|
5
|
+
<li class='tmb-all' id='tmb-<%= i.id %>'>
|
6
|
+
<%= link_to(image_tag(i.attachment.url(:mini)), i.attachment.url(:product), :class => 'tmb-all', :id => "tmb-#{i.id}") %>
|
7
|
+
</li>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<% if @product.has_variants? %>
|
11
|
+
<% @variants.select(&:available?).each do |v| %>
|
12
|
+
<% v.images.each do |i| %>
|
13
|
+
<% next if @product.images.include?(i) %>
|
14
|
+
<li class='vtmb-<%= v.id %> vtmb' id='tmb-<%= i.id %>'>
|
15
|
+
<%= link_to(image_tag(i.attachment.url(:mini)), i.attachment.url(:product)) %>
|
16
|
+
</li>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
6
19
|
<% end %>
|
7
|
-
<% if @product.has_variants?
|
8
|
-
@variants.each do |v|
|
9
|
-
if v.available?
|
10
|
-
v.images.each do |i| %>
|
11
|
-
<li class="vtmb-<%= v.id.to_s %> vtmb" id="tmb-<%= i.id.to_s %>"><%= link_to image_tag(i.attachment.url(:mini)), i.attachment.url(:product) %></li>
|
12
|
-
<%
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
%>
|
18
20
|
</ul>
|
19
21
|
<% end %>
|