Commit 53d8ceb4d43536a7311c19bc15d4ed5911f5b88a

Authored by Rodrigo Souto
2 parents 208311e8 45b48472

Merge branch 'merge-requests/248' into redemoinho

Conflicts:
	lib/noosfero/plugin/settings.rb
	test/unit/plugin_settings_test.rb
Showing 37 changed files with 690 additions and 117 deletions   Show diff stats
app/controllers/public/catalog_controller.rb
1 class CatalogController < PublicController 1 class CatalogController < PublicController
2 needs_profile 2 needs_profile
  3 + no_design_blocks
3 4
4 before_filter :check_enterprise_and_environment 5 before_filter :check_enterprise_and_environment
5 6
6 def index 7 def index
7 - @products = @profile.products.paginate(:order => 'name asc', :per_page => 9, :page => params[:page]) 8 + @category = params[:level] ? ProductCategory.find(params[:level]) : nil
  9 + @products = @profile.products.from_category(@category).paginate(:order => 'available desc, highlighted desc, name asc', :per_page => 9, :page => params[:page])
  10 + @categories = ProductCategory.on_level(params[:level])
8 end 11 end
9 12
10 protected 13 protected
app/helpers/catalog_helper.rb
@@ -3,4 +3,28 @@ module CatalogHelper @@ -3,4 +3,28 @@ module CatalogHelper
3 include DisplayHelper 3 include DisplayHelper
4 include ManageProductsHelper 4 include ManageProductsHelper
5 5
  6 + def breadcrumb(category)
  7 + start = link_to(_('Start'), {:action => 'index'})
  8 + ancestors = category.ancestors.map { |c| link_to(c.name, {:action => 'index', :level => c.id}) }.reverse
  9 + current_level = content_tag('strong', category.name)
  10 + all_items = [start] + ancestors + [current_level]
  11 + content_tag('div', all_items.join(' &rarr; '), :id => 'breadcrumb')
  12 + end
  13 +
  14 + def category_link(category, sub = false)
  15 + count = profile.products.from_category(category).count
  16 + name = truncate(category.name, :length => 22 - count.to_s.size)
  17 + link_name = sub ? name : content_tag('strong', name)
  18 + link = link_to(link_name, {:action => 'index', :level => category.id}, :title => category.name)
  19 + content_tag('li', "#{link} (#{count})") if count > 0
  20 + end
  21 +
  22 + def category_sub_links(category)
  23 + sub_categories = []
  24 + category.children.each do |sub_category|
  25 + sub_categories << category_link(sub_category, true)
  26 + end
  27 + content_tag('ul', sub_categories) if sub_categories.size > 1
  28 + end
  29 +
6 end 30 end
app/helpers/display_helper.rb
@@ -8,6 +8,14 @@ module DisplayHelper @@ -8,6 +8,14 @@ module DisplayHelper
8 opts 8 opts
9 end 9 end
10 10
  11 + def themed_path(file)
  12 + if File.exists?(File.join(Rails.root, 'public', theme_path, file))
  13 + File.join(theme_path, file)
  14 + else
  15 + file
  16 + end
  17 + end
  18 +
11 def image_link_to_product(product, opts={}) 19 def image_link_to_product(product, opts={})
12 return _('No product') unless product 20 return _('No product') unless product
13 target = product_path(product) 21 target = product_path(product)
app/models/category.rb
@@ -13,6 +13,16 @@ class Category &lt; ActiveRecord::Base @@ -13,6 +13,16 @@ class Category &lt; ActiveRecord::Base
13 {:conditions => ['parent_id is null and environment_id = ?', environment.id ]} 13 {:conditions => ['parent_id is null and environment_id = ?', environment.id ]}
14 } 14 }
15 15
  16 + named_scope :on_level, lambda { |parent| {:conditions => {:parent_id => parent}} }
  17 +
  18 + named_scope :sub_categories, lambda { |category|
  19 + {:conditions => ['categories.path LIKE ? AND categories.id != ?', "%#{category.slug}%", category.id]}
  20 + }
  21 +
  22 + named_scope :sub_tree, lambda { |category|
  23 + {:conditions => ['categories.path LIKE ?', "%#{category.slug}%"]}
  24 + }
  25 +
16 acts_as_filesystem 26 acts_as_filesystem
17 27
18 has_many :article_categorizations, :dependent => :destroy 28 has_many :article_categorizations, :dependent => :destroy
app/models/organization.rb
@@ -78,6 +78,8 @@ class Organization &lt; Profile @@ -78,6 +78,8 @@ class Organization &lt; Profile
78 country 78 country
79 tag_list 79 tag_list
80 template_id 80 template_id
  81 + district
  82 + address_reference
81 ] 83 ]
82 84
83 def self.fields 85 def self.fields
@@ -96,8 +98,8 @@ class Organization &lt; Profile @@ -96,8 +98,8 @@ class Organization &lt; Profile
96 [] 98 []
97 end 99 end
98 100
99 - N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Tag list')  
100 - settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information 101 + N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Tag list'); N_('District'); N_('Address reference')
  102 + settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information, :district, :address_reference
101 103
102 validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT 104 validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT
103 validates_format_of :contact_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |org| !org.contact_email.blank? }) 105 validates_format_of :contact_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |org| !org.contact_email.blank? })
app/models/person.rb
@@ -147,6 +147,9 @@ class Person &lt; Profile @@ -147,6 +147,9 @@ class Person &lt; Profile
147 contact_phone 147 contact_phone
148 contact_information 148 contact_information
149 description 149 description
  150 + image
  151 + district
  152 + address_reference
150 ] 153 ]
151 154
152 validates_multiparameter_assignments 155 validates_multiparameter_assignments
@@ -201,8 +204,8 @@ class Person &lt; Profile @@ -201,8 +204,8 @@ class Person &lt; Profile
201 N_('Education'); N_('Custom education'); N_('Custom area of study'); 204 N_('Education'); N_('Custom education'); N_('Custom area of study');
202 settings_items :formation, :custom_formation, :custom_area_of_study 205 settings_items :formation, :custom_formation, :custom_area_of_study
203 206
204 - N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code')  
205 - settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code 207 + N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code'); N_('District'); N_('Address reference')
  208 + settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code, :district, :address_reference
206 209
207 extend SetProfileRegionFromCityState::ClassMethods 210 extend SetProfileRegionFromCityState::ClassMethods
208 set_profile_region_from_city_state 211 set_profile_region_from_city_state
app/models/product.rb
@@ -23,6 +23,10 @@ class Product &lt; ActiveRecord::Base @@ -23,6 +23,10 @@ class Product &lt; ActiveRecord::Base
23 23
24 named_scope :more_recent, :order => "created_at DESC" 24 named_scope :more_recent, :order => "created_at DESC"
25 25
  26 + named_scope :from_category, lambda { |category|
  27 + {:joins => :product_category, :conditions => ['categories.path LIKE ?', "%#{category.slug}%"]} if category
  28 + }
  29 +
26 after_update :save_image 30 after_update :save_image
27 31
28 def lat 32 def lat
app/models/profile.rb
@@ -141,6 +141,10 @@ class Profile &lt; ActiveRecord::Base @@ -141,6 +141,10 @@ class Profile &lt; ActiveRecord::Base
141 141
142 acts_as_having_settings :field => :data 142 acts_as_having_settings :field => :data
143 143
  144 + def settings
  145 + data
  146 + end
  147 +
144 settings_items :redirect_l10n, :type => :boolean, :default => false 148 settings_items :redirect_l10n, :type => :boolean, :default => false
145 settings_items :public_content, :type => :boolean, :default => true 149 settings_items :public_content, :type => :boolean, :default => true
146 settings_items :description 150 settings_items :description
@@ -229,7 +233,7 @@ class Profile &lt; ActiveRecord::Base @@ -229,7 +233,7 @@ class Profile &lt; ActiveRecord::Base
229 if myregion 233 if myregion
230 myregion.hierarchy.reverse.first(2).map(&:name).join(separator) 234 myregion.hierarchy.reverse.first(2).map(&:name).join(separator)
231 else 235 else
232 - %w[address city state country_name zip_code ].map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator) 236 + %w[address district city state country_name zip_code ].map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator)
233 end 237 end
234 end 238 end
235 239
@@ -694,7 +698,7 @@ private :generate_url, :url_options @@ -694,7 +698,7 @@ private :generate_url, :url_options
694 def custom_footer_expanded 698 def custom_footer_expanded
695 footer = custom_footer 699 footer = custom_footer
696 if footer 700 if footer
697 - %w[contact_person contact_email contact_phone location address economic_activity city state country zip_code].each do |att| 701 + %w[contact_person contact_email contact_phone location address district address_reference economic_activity city state country zip_code].each do |att|
698 if self.respond_to?(att) && footer.match(/\{[^{]*#{att}\}/) 702 if self.respond_to?(att) && footer.match(/\{[^{]*#{att}\}/)
699 if !self.send(att).nil? && !self.send(att).blank? 703 if !self.send(att).nil? && !self.send(att).blank?
700 footer = footer.gsub(/\{([^{]*)#{att}\}/, '\1' + self.send(att)) 704 footer = footer.gsub(/\{([^{]*)#{att}\}/, '\1' + self.send(att))
app/views/catalog/index.rhtml
1 <% extra_content = [] %> 1 <% extra_content = [] %>
2 <% extra_content_list = [] %> 2 <% extra_content_list = [] %>
3 3
4 -<ul id="product-list">  
5 - <li><h1><%= _('Products/Services') %></h1></li> 4 +<h1><%= _('Products/Services') %></h1>
6 5
  6 +<%= breadcrumb(@category) if params[:level] %>
  7 +
  8 +<div class='l-sidebar-left-bar'>
  9 + <ul>
  10 + <% if @categories.size > 0 %>
  11 + <% @categories.each do |category| %>
  12 + <%= category_link(category) %>
  13 + <%= category_sub_links(category) %>
  14 + <% end %>
  15 + <% else %>
  16 + <%= content_tag('li', _('There are no sub-categories for %s') % @category.name, :style => 'color: #555753; padding-bottom: 0.5em;') %>
  17 + <% end %>
  18 + </ul>
  19 +</div>
  20 +
  21 +<ul id="product-list" class="l-sidebar-left-content">
7 <% @products.each do |product| %> 22 <% @products.each do |product| %>
8 <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %> 23 <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %>
9 <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %> 24 <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %>
10 25
11 - <li class="product <%= "not-available" unless product.available %>"> 26 + <% status = [] %>
  27 + <% status << 'not-available' if !product.available %>
  28 + <% status << 'highlighted' if product.highlighted %>
  29 +
  30 + <li class="product <%= status.join(' ') %>">
12 <ul> 31 <ul>
13 <li class="product-image-link"> 32 <li class="product-image-link">
  33 + <% if product.highlighted? %>
  34 + <%= link_to image_tag(themed_path('/images/star.png'), :class => 'star', :alt => _('Highlighted product')), product_path(product) %>
  35 + <% end %>
14 <% if product.image %> 36 <% if product.image %>
15 <div class="zoomable-image"> 37 <div class="zoomable-image">
16 <%= link_to_product product, :class => 'product-big', :style => "background-image: url(#{product.default_image(:big)})" %> 38 <%= link_to_product product, :class => 'product-big', :style => "background-image: url(#{product.default_image(:big)})" %>
app/views/profile_editor/_person_form.rhtml
@@ -21,6 +21,8 @@ @@ -21,6 +21,8 @@
21 <%= optional_field(@person, 'city', f.text_field(:city, :rel => _('City'))) %> 21 <%= optional_field(@person, 'city', f.text_field(:city, :rel => _('City'))) %>
22 <%= optional_field(@person, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code, :rel => _('ZIP code')))) %> 22 <%= optional_field(@person, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code, :rel => _('ZIP code')))) %>
23 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %> 23 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %>
  24 +<%= optional_field(profile, 'address_reference', labelled_form_field(_('Address reference'), text_field(object_name, :address_reference, :rel => _('Address reference')))) %>
  25 +<%= optional_field(@person, 'district', labelled_form_field(_('District'), text_field(:profile_data, :district, :rel => _('District')))) %>
24 26
25 <% optional_field(@person, 'schooling') do %> 27 <% optional_field(@person, 'schooling') do %>
26 <div class="formfieldline"> 28 <div class="formfieldline">
app/views/search/_product.rhtml
1 <% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %> 1 <% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%> 2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%>
3 3
4 -<li class="search-product-item"> 4 +<li class="search-product-item <%= 'highlighted' if product.highlighted? %>">
5 5
6 <div class="search-product-item-first-column"> 6 <div class="search-product-item-first-column">
7 <%= render :partial => 'search/image', :object => product %> 7 <%= render :partial => 'search/image', :object => product %>
app/views/shared/_organization_custom_fields.rhtml
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 <%= optional_field(profile, 'economic_activity', f.text_field(:economic_activity)) %> 10 <%= optional_field(profile, 'economic_activity', f.text_field(:economic_activity)) %>
11 <%= optional_field(profile, 'management_information', f.text_area(:management_information, :rows => 5)) %> 11 <%= optional_field(profile, 'management_information', f.text_area(:management_information, :rows => 5)) %>
12 <%= optional_field(profile, 'address', labelled_form_field(_('Address (street and number)'), text_field(object_name, :address))) %> 12 <%= optional_field(profile, 'address', labelled_form_field(_('Address (street and number)'), text_field(object_name, :address))) %>
  13 +<%= optional_field(profile, 'address_reference', labelled_form_field(_('Address reference'), text_field(object_name, :address_reference))) %>
  14 +<%= optional_field(profile, 'district', labelled_form_field(_('District'), text_field(object_name, :district))) %>
13 <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(object_name, :zip_code))) %> 15 <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(object_name, :zip_code))) %>
14 <%= optional_field(profile, 'city', f.text_field(:city)) %> 16 <%= optional_field(profile, 'city', f.text_field(:city)) %>
15 <%= optional_field(profile, 'state', f.text_field(:state)) %> 17 <%= optional_field(profile, 'state', f.text_field(:state)) %>
lib/noosfero/plugin/settings.rb
1 class Noosfero::Plugin::Settings 1 class Noosfero::Plugin::Settings
2 2
3 - def initialize(environment, plugin, attributes = nil)  
4 - @environment = environment 3 + def initialize(base, plugin, attributes = nil)
  4 + @base = base
5 @plugin = plugin 5 @plugin = plugin
6 attributes ||= {} 6 attributes ||= {}
7 attributes.each do |k,v| 7 attributes.each do |k,v|
@@ -10,7 +10,7 @@ class Noosfero::Plugin::Settings @@ -10,7 +10,7 @@ class Noosfero::Plugin::Settings
10 end 10 end
11 11
12 def settings 12 def settings
13 - @environment.settings["#{@plugin.public_name}_plugin".to_sym] ||= {} 13 + @base.settings["#{@plugin.public_name}_plugin".to_sym] ||= {}
14 end 14 end
15 15
16 def method_missing(method, *args, &block) 16 def method_missing(method, *args, &block)
@@ -38,7 +38,11 @@ class Noosfero::Plugin::Settings @@ -38,7 +38,11 @@ class Noosfero::Plugin::Settings
38 end 38 end
39 39
40 def save! 40 def save!
  41 +<<<<<<< HEAD
41 @environment.save! 42 @environment.save!
  43 +=======
  44 + @base.save!
  45 +>>>>>>> merge-requests/248
42 end 46 end
43 47
44 end 48 end
plugins/shopping_cart/controllers/shopping_cart_plugin_myprofile_controller.rb
@@ -4,9 +4,12 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController @@ -4,9 +4,12 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController
4 append_view_path File.join(File.dirname(__FILE__) + '/../views') 4 append_view_path File.join(File.dirname(__FILE__) + '/../views')
5 5
6 def edit 6 def edit
  7 + params[:settings] = treat_cart_options(params[:settings])
  8 +
  9 + @settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin, params[:settings])
7 if request.post? 10 if request.post?
8 begin 11 begin
9 - profile.update_attributes!(params[:profile_attr]) 12 + @settings.save!
10 session[:notice] = _('Option updated successfully.') 13 session[:notice] = _('Option updated successfully.')
11 rescue Exception => exception 14 rescue Exception => exception
12 session[:notice] = _('Option wasn\'t updated successfully.') 15 session[:notice] = _('Option wasn\'t updated successfully.')
@@ -46,4 +49,25 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController @@ -46,4 +49,25 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController
46 order.save! 49 order.save!
47 redirect_to :action => 'reports', :from => params[:context_from], :to => params[:context_to], :filter_status => params[:context_status] 50 redirect_to :action => 'reports', :from => params[:context_from], :to => params[:context_to], :filter_status => params[:context_status]
48 end 51 end
  52 +
  53 + private
  54 +
  55 + def treat_cart_options(settings)
  56 + return if settings.blank?
  57 + settings[:enabled] = settings[:enabled] == '1'
  58 + settings[:delivery] = settings[:delivery] == '1'
  59 + settings[:free_delivery_price] = settings[:free_delivery_price].blank? ? nil : settings[:free_delivery_price].to_d
  60 + settings[:delivery_options] = treat_delivery_options(settings[:delivery_options])
  61 + settings
  62 + end
  63 +
  64 + def treat_delivery_options(params)
  65 + result = {}
  66 + params[:options].size.times do |counter|
  67 + if params[:options][counter].present? && params[:prices][counter].present?
  68 + result[params[:options][counter]] = params[:prices][counter]
  69 + end
  70 + end
  71 + result
  72 + end
49 end 73 end
plugins/shopping_cart/controllers/shopping_cart_plugin_profile_controller.rb
@@ -85,14 +85,15 @@ class ShoppingCartPluginProfileController &lt; ProfileController @@ -85,14 +85,15 @@ class ShoppingCartPluginProfileController &lt; ProfileController
85 85
86 def buy 86 def buy
87 @environment = profile.environment 87 @environment = profile.environment
  88 + @settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin)
88 render :layout => false 89 render :layout => false
89 end 90 end
90 91
91 def send_request 92 def send_request
92 register_order(params[:customer], session[:cart][:items]) 93 register_order(params[:customer], session[:cart][:items])
93 begin 94 begin
94 - ShoppingCartPlugin::Mailer.deliver_customer_notification(params[:customer], profile, session[:cart][:items])  
95 - ShoppingCartPlugin::Mailer.deliver_supplier_notification(params[:customer], profile, session[:cart][:items]) 95 + ShoppingCartPlugin::Mailer.deliver_customer_notification(params[:customer], profile, session[:cart][:items], params[:delivery_option])
  96 + ShoppingCartPlugin::Mailer.deliver_supplier_notification(params[:customer], profile, session[:cart][:items], params[:delivery_option])
96 render :text => { 97 render :text => {
97 :ok => true, 98 :ok => true,
98 :message => _('Request sent successfully. Check your email.'), 99 :message => _('Request sent successfully. Check your email.'),
@@ -151,6 +152,24 @@ class ShoppingCartPluginProfileController &lt; ProfileController @@ -151,6 +152,24 @@ class ShoppingCartPluginProfileController &lt; ProfileController
151 end 152 end
152 end 153 end
153 154
  155 + def update_delivery_option
  156 + settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin)
  157 + delivery_price = settings.delivery_options[params[:delivery_option]]
  158 + delivery = Product.new(:name => params[:delivery_option], :price => delivery_price)
  159 + delivery.save(false)
  160 + items = session[:cart][:items].clone
  161 + items[delivery.id] = 1
  162 + total_price = get_total_on_currency(items, environment)
  163 + delivery.destroy
  164 + render :text => {
  165 + :ok => true,
  166 + :delivery_price => float_to_currency_cart(delivery_price, environment),
  167 + :total_price => total_price,
  168 + :message => _('Delivery option updated.'),
  169 + :error => {:code => 0}
  170 + }.to_json
  171 + end
  172 +
154 private 173 private
155 174
156 def validate_same_enterprise 175 def validate_same_enterprise
plugins/shopping_cart/db/migrate/20121022190819_move_fields_included_on_profiles_table_to_settings.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +class MoveFieldsIncludedOnProfilesTableToSettings < ActiveRecord::Migration
  2 + def self.up
  3 + Profile.find_each do |profile|
  4 + settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin)
  5 + settings.enabled = profile.shopping_cart
  6 + settings.delivery = profile.shopping_cart_delivery
  7 + settings.delivery_price = profile.shopping_cart_delivery_price
  8 + settings.save!
  9 + end
  10 +
  11 + remove_column :profiles, :shopping_cart
  12 + remove_column :profiles, :shopping_cart_delivery
  13 + remove_column :profiles, :shopping_cart_delivery_price
  14 + end
  15 +
  16 + def self.down
  17 + say "This migration can not be reverted!"
  18 + end
  19 +end
plugins/shopping_cart/lib/shopping_cart_plugin.rb
@@ -3,16 +3,35 @@ require_dependency &#39;shopping_cart_plugin/ext/person&#39; @@ -3,16 +3,35 @@ require_dependency &#39;shopping_cart_plugin/ext/person&#39;
3 3
4 class ShoppingCartPlugin < Noosfero::Plugin 4 class ShoppingCartPlugin < Noosfero::Plugin
5 5
6 - def self.plugin_name 6 + class << self
  7 + def plugin_name
7 "Shopping Basket" 8 "Shopping Basket"
8 - end 9 + end
  10 +
  11 + def plugin_description
  12 + _("A shopping basket feature for enterprises")
  13 + end
  14 +
  15 + def enabled_default_setting
  16 + true
  17 + end
9 18
10 - def self.plugin_description  
11 - _("A shopping basket feature for enterprises") 19 + def delivery_default_setting
  20 + false
  21 + end
  22 +
  23 + def delivery_price_default_setting
  24 + 0
  25 + end
  26 +
  27 + def delivery_options_default_setting
  28 + {}
  29 + end
12 end 30 end
13 31
14 def add_to_cart_button(item, enterprise = context.profile) 32 def add_to_cart_button(item, enterprise = context.profile)
15 - if enterprise.shopping_cart && item.available 33 + settings = Noosfero::Plugin::Settings.new(enterprise, ShoppingCartPlugin)
  34 + if settings.enabled && item.available
16 lambda { 35 lambda {
17 link_to(_('Add to basket'), "add:#{item.name}", 36 link_to(_('Add to basket'), "add:#{item.name}",
18 :class => 'cart-add-item', 37 :class => 'cart-add-item',
@@ -39,11 +58,12 @@ class ShoppingCartPlugin &lt; Noosfero::Plugin @@ -39,11 +58,12 @@ class ShoppingCartPlugin &lt; Noosfero::Plugin
39 end 58 end
40 59
41 def control_panel_buttons 60 def control_panel_buttons
  61 + settings = Noosfero::Plugin::Settings.new(context.profile, ShoppingCartPlugin)
42 buttons = [] 62 buttons = []
43 if context.profile.enterprise? 63 if context.profile.enterprise?
44 buttons << { :title => _('Shopping basket'), :icon => 'shopping-cart-icon', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'edit'} } 64 buttons << { :title => _('Shopping basket'), :icon => 'shopping-cart-icon', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'edit'} }
45 end 65 end
46 - if context.profile.enterprise? && context.profile.shopping_cart 66 + if context.profile.enterprise? && settings.enabled
47 buttons << { :title => _('Purchase reports'), :icon => 'shopping-cart-purchase-report', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'reports'} } 67 buttons << { :title => _('Purchase reports'), :icon => 'shopping-cart-purchase-report', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'reports'} }
48 end 68 end
49 69
plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb
@@ -7,46 +7,69 @@ module ShoppingCartPlugin::CartHelper @@ -7,46 +7,69 @@ module ShoppingCartPlugin::CartHelper
7 product.discount ? product.price_with_discount : product.price 7 product.discount ? product.price_with_discount : product.price
8 end 8 end
9 9
10 - def get_price(product, environment)  
11 - float_to_currency_cart(sell_price(product), environment) 10 + def get_price(product, environment, quantity=1)
  11 + float_to_currency_cart(price_with_quantity(product,quantity), environment)
12 end 12 end
13 13
14 - def get_total(items, environment)  
15 - float_to_currency_cart(items.map { |id, quantity| sell_price(Product.find(id)) * quantity}.sum, environment) 14 + def price_with_quantity(product, quantity=1)
  15 + quantity = 1 if !quantity.kind_of?(Numeric)
  16 + sell_price(product)*quantity
16 end 17 end
17 18
18 - def items_table(items, profile, by_mail = false) 19 + def get_total(items)
  20 + items.map { |id, quantity| price_with_quantity(Product.find(id),quantity)}.sum
  21 + end
  22 +
  23 + def get_total_on_currency(items, environment)
  24 + float_to_currency_cart(get_total(items), environment)
  25 + end
  26 +
  27 + def items_table(items, profile, delivery_option = nil, by_mail = false)
19 environment = profile.environment 28 environment = profile.environment
  29 + settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin)
20 items = items.to_a 30 items = items.to_a
21 - if profile.shopping_cart_delivery  
22 - delivery = Product.create!(:name => _('Delivery'), :price => profile.shopping_cart_delivery_price, :product_category => ProductCategory.last)  
23 - items << [delivery.id, 1]  
24 - end  
25 31
26 quantity_opts = { :class => 'cart-table-quantity' } 32 quantity_opts = { :class => 'cart-table-quantity' }
27 quantity_opts.merge!({:align => 'center'}) if by_mail 33 quantity_opts.merge!({:align => 'center'}) if by_mail
28 price_opts = {:class => 'cart-table-price'} 34 price_opts = {:class => 'cart-table-price'}
29 price_opts.merge!({:align => 'right'}) if by_mail 35 price_opts.merge!({:align => 'right'}) if by_mail
  36 + items.sort! {|a, b| Product.find(a.first).name <=> Product.find(b.first).name}
  37 +
  38 + if settings.delivery
  39 + if settings.free_delivery_price && get_total(items) >= settings.free_delivery_price
  40 + delivery = Product.new(:name => _('Free delivery'), :price => 0)
  41 + else
  42 + delivery = Product.new(:name => delivery_option || _('Delivery'), :price => settings.delivery_options[delivery_option])
  43 + end
  44 + delivery.save(false)
  45 + items << [delivery.id, '']
  46 + end
30 47
31 table = '<table id="cart-items-table" cellpadding="2" cellspacing="0" 48 table = '<table id="cart-items-table" cellpadding="2" cellspacing="0"
32 border="'+(by_mail ? '1' : '0')+'" 49 border="'+(by_mail ? '1' : '0')+'"
33 style="'+(by_mail ? 'border-collapse:collapse' : '')+'">' + 50 style="'+(by_mail ? 'border-collapse:collapse' : '')+'">' +
34 content_tag('tr', 51 content_tag('tr',
35 - content_tag('th', _('Item name')) +  
36 - content_tag('th', by_mail ? '&nbsp;#&nbsp;' : '#') +  
37 - content_tag('th', _('Price')) 52 + content_tag('th', _('Item name')) +
  53 + content_tag('th', by_mail ? '&nbsp;#&nbsp;' : '#') +
  54 + content_tag('th', _('Price'))
38 ) + 55 ) +
39 items.map do |id, quantity| 56 items.map do |id, quantity|
40 product = Product.find(id) 57 product = Product.find(id)
  58 + name_opts = {}
  59 + is_delivery = quantity.kind_of?(String)
  60 + if is_delivery
  61 + price_opts.merge!({:id => 'delivery-price'})
  62 + name_opts.merge!({:id => 'delivery-name'})
  63 + end
41 content_tag('tr', 64 content_tag('tr',
42 - content_tag('td', product.name) +  
43 - content_tag('td', quantity, quantity_opts ) +  
44 - content_tag('td', get_price(product, environment), price_opts )  
45 - ) 65 + content_tag('td', product.name, name_opts) +
  66 + content_tag('td', quantity, quantity_opts ) +
  67 + content_tag('td', get_price(product, environment, quantity), price_opts)
  68 + )
46 end.join("\n") 69 end.join("\n")
47 70
48 - total = get_total(items, environment)  
49 - delivery.destroy if profile.shopping_cart_delivery 71 + total = get_total_on_currency(items, environment)
  72 + delivery.destroy if settings.delivery
50 73
51 table + 74 table +
52 content_tag('th', _('Total:'), :colspan => 2, :class => 'cart-table-total-label') + 75 content_tag('th', _('Total:'), :colspan => 2, :class => 'cart-table-total-label') +
@@ -55,6 +78,14 @@ module ShoppingCartPlugin::CartHelper @@ -55,6 +78,14 @@ module ShoppingCartPlugin::CartHelper
55 end 78 end
56 79
57 def float_to_currency_cart(value, environment) 80 def float_to_currency_cart(value, environment)
58 - number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :precision => 2, :format => "%u %n") 81 + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :precision => 2, :format => "%u%n")
  82 + end
  83 +
  84 + def select_delivery_options(options, environment)
  85 + result = options.map do |option, price|
  86 + ["#{option} (#{float_to_currency_cart(price, environment)})", option]
  87 + end
  88 + result << ["#{_('Delivery')} (#{float_to_currency_cart(0, environment)})", 'delivery'] if result.empty?
  89 + result
59 end 90 end
60 end 91 end
plugins/shopping_cart/lib/shopping_cart_plugin/mailer.rb
1 class ShoppingCartPlugin::Mailer < Noosfero::Plugin::MailerBase 1 class ShoppingCartPlugin::Mailer < Noosfero::Plugin::MailerBase
2 2
3 - def customer_notification(customer, supplier, items) 3 + def customer_notification(customer, supplier, items, delivery_option)
4 domain = supplier.hostname || supplier.environment.default_hostname 4 domain = supplier.hostname || supplier.environment.default_hostname
5 recipients customer[:email] 5 recipients customer[:email]
6 from 'no-reply@' + domain 6 from 'no-reply@' + domain
@@ -10,10 +10,11 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase @@ -10,10 +10,11 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
10 body :customer => customer, 10 body :customer => customer,
11 :supplier => supplier, 11 :supplier => supplier,
12 :items => items, 12 :items => items,
13 - :environment => supplier.environment 13 + :environment => supplier.environment,
  14 + :delivery_option => delivery_option
14 end 15 end
15 16
16 - def supplier_notification(customer, supplier, items) 17 + def supplier_notification(customer, supplier, items, delivery_option)
17 domain = supplier.environment.default_hostname 18 domain = supplier.environment.default_hostname
18 recipients supplier.contact_email 19 recipients supplier.contact_email
19 from 'no-reply@' + domain 20 from 'no-reply@' + domain
@@ -23,6 +24,7 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase @@ -23,6 +24,7 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
23 body :customer => customer, 24 body :customer => customer,
24 :supplier => supplier, 25 :supplier => supplier,
25 :items => items, 26 :items => items,
26 - :environment => supplier.environment 27 + :environment => supplier.environment,
  28 + :delivery_option => delivery_option
27 end 29 end
28 end 30 end
plugins/shopping_cart/public/buy.js 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +jQuery(document).ready(function(){
  2 + jQuery("#cart-request-form").validate({
  3 + submitHandler: function(form) {
  4 + jQuery(form).find('input.submit').attr('disabled', true);
  5 + jQuery('#cboxLoadingOverlay').show().addClass('loading');
  6 + jQuery('#cboxLoadingGraphic').show().addClass('loading');
  7 + }
  8 + });
  9 +});
  10 +
  11 +jQuery('#delivery_option').change(function(){
  12 + jQuery('#cboxLoadingGraphic').show();
  13 + me = this;
  14 + enterprise = jQuery(me).attr('data-profile-identifier');
  15 + option = jQuery(me).val();
  16 + jQuery.ajax({
  17 + url: '/profile/'+ enterprise +'/plugin/shopping_cart/update_delivery_option',
  18 + dataType: "json",
  19 + data: 'delivery_option='+option,
  20 + success: function(data, st, ajax) {
  21 + jQuery('#delivery-price').text(data.delivery_price);
  22 + jQuery('.cart-table-total-value').text(data.total_price);
  23 + jQuery('#delivery-name').text(option);
  24 + jQuery('#cboxLoadingGraphic').hide();
  25 + },
  26 + error: function(ajax, st, errorThrown) {
  27 + alert('Update delivery option - HTTP '+st+': '+errorThrown);
  28 + },
  29 + });
  30 +});
  31 +
  32 +jQuery('#customer_payment').change(function(){
  33 + jQuery(this).closest('.formfieldline').next().slideToggle('fast');
  34 +});
plugins/shopping_cart/public/cart.js
@@ -14,7 +14,7 @@ function Cart(config) { @@ -14,7 +14,7 @@ function Cart(config) {
14 this.enterprise = config.enterprise; 14 this.enterprise = config.enterprise;
15 me = this; 15 me = this;
16 $.ajax({ 16 $.ajax({
17 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/visibility', 17 + url: '/profile/'+ this.enterprise +'/plugin/shopping_cart/visibility',
18 dataType: 'json', 18 dataType: 'json',
19 success: function(data, status, ajax){ 19 success: function(data, status, ajax){
20 me.visible = /^true$/i.test(data); 20 me.visible = /^true$/i.test(data);
@@ -25,7 +25,7 @@ function Cart(config) { @@ -25,7 +25,7 @@ function Cart(config) {
25 alert('Visibility - HTTP '+status+': '+errorThrown); 25 alert('Visibility - HTTP '+status+': '+errorThrown);
26 } 26 }
27 }); 27 });
28 - $(".cart-buy", this.cartElem).colorbox({href: '/profile/' + this.enterprise + '/plugins/shopping_cart/buy'}); 28 + $(".cart-buy", this.cartElem).colorbox({href: '/profile/' + this.enterprise + '/plugin/shopping_cart/buy'});
29 } 29 }
30 } 30 }
31 31
@@ -34,7 +34,7 @@ function Cart(config) { @@ -34,7 +34,7 @@ function Cart(config) {
34 Cart.prototype.listProducts = function() { 34 Cart.prototype.listProducts = function() {
35 var me = this; 35 var me = this;
36 $.ajax({ 36 $.ajax({
37 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/list', 37 + url: '/profile/'+ this.enterprise +'/plugin/shopping_cart/list',
38 dataType: 'json', 38 dataType: 'json',
39 success: function(data, ststus, ajax){ 39 success: function(data, ststus, ajax){
40 if ( !data.ok ) alert(data.error.message); 40 if ( !data.ok ) alert(data.error.message);
@@ -100,7 +100,7 @@ function Cart(config) { @@ -100,7 +100,7 @@ function Cart(config) {
100 var me = this; 100 var me = this;
101 if( quantity == NaN ) return input.value = input.lastValue; 101 if( quantity == NaN ) return input.value = input.lastValue;
102 $.ajax({ 102 $.ajax({
103 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity, 103 + url: '/profile/'+ this.enterprise +'/plugin/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity,
104 dataType: 'json', 104 dataType: 'json',
105 success: function(data, status, ajax){ 105 success: function(data, status, ajax){
106 if ( !data.ok ) { 106 if ( !data.ok ) {
@@ -150,12 +150,12 @@ function Cart(config) { @@ -150,12 +150,12 @@ function Cart(config) {
150 Cart.prototype.addItem = function(enterprise, itemId, callback) { 150 Cart.prototype.addItem = function(enterprise, itemId, callback) {
151 if(!this.enterprise) { 151 if(!this.enterprise) {
152 this.enterprise = enterprise; 152 this.enterprise = enterprise;
153 - $(".cart-buy", this.cartElem).colorbox({href: '/profile/' + this.enterprise + '/plugins/shopping_cart/buy'}); 153 + $(".cart-buy", this.cartElem).colorbox({href: '/profile/' + this.enterprise + '/plugin/shopping_cart/buy'});
154 // $(this.cartElem).show(); 154 // $(this.cartElem).show();
155 } 155 }
156 var me = this; 156 var me = this;
157 $.ajax({ 157 $.ajax({
158 - url: '/profile/'+ enterprise +'/plugins/shopping_cart/add/'+ itemId, 158 + url: '/profile/'+ enterprise +'/plugin/shopping_cart/add/'+ itemId,
159 dataType: 'json', 159 dataType: 'json',
160 success: function(data, status, ajax){ 160 success: function(data, status, ajax){
161 if ( !data.ok ) alert(data.error.message); 161 if ( !data.ok ) alert(data.error.message);
@@ -178,7 +178,7 @@ function Cart(config) { @@ -178,7 +178,7 @@ function Cart(config) {
178 if ($("li", this.itemsBox).size() < 2) return this.clean(); 178 if ($("li", this.itemsBox).size() < 2) return this.clean();
179 var me = this; 179 var me = this;
180 $.ajax({ 180 $.ajax({
181 - url: '/profile/'+ enterprise +'/plugins/shopping_cart/remove/'+ itemId, 181 + url: '/profile/'+ enterprise +'/plugin/shopping_cart/remove/'+ itemId,
182 dataType: 'json', 182 dataType: 'json',
183 success: function(data, status, ajax){ 183 success: function(data, status, ajax){
184 if ( !data.ok ) alert(data.error.message); 184 if ( !data.ok ) alert(data.error.message);
@@ -200,7 +200,7 @@ function Cart(config) { @@ -200,7 +200,7 @@ function Cart(config) {
200 200
201 Cart.prototype.show = function() { 201 Cart.prototype.show = function() {
202 $.ajax({ 202 $.ajax({
203 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/show', 203 + url: '/profile/'+ this.enterprise +'/plugin/shopping_cart/show',
204 dataType: 'json', 204 dataType: 'json',
205 cache: false, 205 cache: false,
206 error: function(ajax, status, errorThrown) { 206 error: function(ajax, status, errorThrown) {
@@ -215,7 +215,7 @@ function Cart(config) { @@ -215,7 +215,7 @@ function Cart(config) {
215 } 215 }
216 Cart.prototype.hide = function() { 216 Cart.prototype.hide = function() {
217 $.ajax({ 217 $.ajax({
218 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/hide', 218 + url: '/profile/'+ this.enterprise +'/plugin/shopping_cart/hide',
219 dataType: 'json', 219 dataType: 'json',
220 cache: false, 220 cache: false,
221 error: function(ajax, status, errorThrown) { 221 error: function(ajax, status, errorThrown) {
@@ -252,7 +252,7 @@ function Cart(config) { @@ -252,7 +252,7 @@ function Cart(config) {
252 Cart.prototype.clean = function() { 252 Cart.prototype.clean = function() {
253 var me = this; 253 var me = this;
254 $.ajax({ 254 $.ajax({
255 - url: '/profile/'+ me.enterprise +'/plugins/shopping_cart/clean', 255 + url: '/profile/'+ me.enterprise +'/plugin/shopping_cart/clean',
256 dataType: 'json', 256 dataType: 'json',
257 success: function(data, status, ajax){ 257 success: function(data, status, ajax){
258 if ( !data.ok ) alert(data.error.message); 258 if ( !data.ok ) alert(data.error.message);
@@ -284,7 +284,7 @@ function Cart(config) { @@ -284,7 +284,7 @@ function Cart(config) {
284 var me = this; 284 var me = this;
285 $.ajax({ 285 $.ajax({
286 type: 'POST', 286 type: 'POST',
287 - url: '/profile/'+ me.enterprise +'/plugins/shopping_cart/send_request', 287 + url: '/profile/'+ me.enterprise +'/plugin/shopping_cart/send_request',
288 data: params, 288 data: params,
289 dataType: 'json', 289 dataType: 'json',
290 success: function(data, status, ajax){ 290 success: function(data, status, ajax){
plugins/shopping_cart/public/edit.js 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +jQuery('#settings_delivery').click(function(){
  2 + jQuery('#delivery_settings').toggle('fast');
  3 +});
  4 +
  5 +jQuery('#add-new-option').click(function(){
  6 + new_option = jQuery('#empty-option').clone();
  7 + new_option.removeAttr('id');
  8 + jQuery('#add-new-option-row').before(new_option);
  9 + new_option.show();
  10 + return false;
  11 +});
  12 +
  13 +jQuery('.remove-option').live('click', function(){
  14 + jQuery(this).closest('tr').remove();
  15 + return false;
  16 +});
plugins/shopping_cart/test/functional/shopping_cart_plugin_myprofile_controller_test.rb
@@ -13,46 +13,42 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -13,46 +13,42 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase
13 attr_reader :enterprise 13 attr_reader :enterprise
14 14
15 should 'be able to enable shopping cart' do 15 should 'be able to enable shopping cart' do
16 - enterprise.shopping_cart = false  
17 - enterprise.save  
18 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart => '1'}  
19 - enterprise.reload 16 + settings.enabled = false
  17 + settings.save!
  18 + post :edit, :profile => enterprise.identifier, :settings => {:enabled => '1'}
20 19
21 - assert enterprise.shopping_cart 20 + assert settings.enabled
22 end 21 end
23 22
24 should 'be able to disable shopping cart' do 23 should 'be able to disable shopping cart' do
25 - enterprise.shopping_cart = true  
26 - enterprise.save  
27 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart => '0'}  
28 - enterprise.reload 24 + settings.enabled = true
  25 + settings.save!
  26 + post :edit, :profile => enterprise.identifier, :settings => {:enabled => '0'}
29 27
30 - assert !enterprise.shopping_cart 28 + assert !settings.enabled
31 end 29 end
32 30
33 should 'be able to enable shopping cart delivery' do 31 should 'be able to enable shopping cart delivery' do
34 - enterprise.shopping_cart_delivery = false  
35 - enterprise.save  
36 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart_delivery => '1'}  
37 - enterprise.reload 32 + settings.delivery = false
  33 + settings.save!
  34 + post :edit, :profile => enterprise.identifier, :settings => {:delivery => '1'}
38 35
39 - assert enterprise.shopping_cart_delivery 36 + assert settings.delivery
40 end 37 end
41 38
42 should 'be able to disable shopping cart delivery' do 39 should 'be able to disable shopping cart delivery' do
43 - enterprise.shopping_cart_delivery = true  
44 - enterprise.save  
45 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart_delivery => '0'}  
46 - enterprise.reload 40 + settings.delivery = true
  41 + settings.save!
  42 + post :edit, :profile => enterprise.identifier, :settings => {:delivery => '0'}
47 43
48 - assert !enterprise.shopping_cart_delivery 44 + assert !settings.delivery
49 end 45 end
50 46
51 should 'be able to choose the delivery price' do 47 should 'be able to choose the delivery price' do
52 price = 4.35 48 price = 4.35
53 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart_delivery_price => price}  
54 - enterprise.reload  
55 - assert enterprise.shopping_cart_delivery_price == price 49 + post :edit, :profile => enterprise.identifier, :settings => {:delivery_price => price}
  50 +
  51 + assert settings.delivery_price == price
56 end 52 end
57 53
58 should 'filter the reports correctly' do 54 should 'filter the reports correctly' do
@@ -112,4 +108,11 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -112,4 +108,11 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase
112 po.reload 108 po.reload
113 assert_equal ShoppingCartPlugin::PurchaseOrder::Status::CONFIRMED, po.status 109 assert_equal ShoppingCartPlugin::PurchaseOrder::Status::CONFIRMED, po.status
114 end 110 end
  111 +
  112 + private
  113 +
  114 + def settings
  115 + @enterprise.reload
  116 + Noosfero::Plugin::Settings.new(@enterprise, ShoppingCartPlugin)
  117 + end
115 end 118 end
plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb
@@ -17,25 +17,35 @@ @@ -17,25 +17,35 @@
17 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li> 17 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li>
18 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li> 18 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li>
19 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li> 19 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li>
20 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 20 + <li><b><%= _('Payment') %>: </b><%= @customer[:payment] == 'money' ? _('Money') : _('Check') %></li>
  21 + <% if @customer[:payment] == 'money' %>
  22 + <li><b><%= _('Change') %>: </b><%= @customer[:change] %></li>
  23 + <% end %>
  24 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
21 <li><b><%= _('Address') %>:</b> 25 <li><b><%= _('Address') %>:</b>
22 <% end %> 26 <% end %>
23 <% if !@customer[:address].blank? %> 27 <% if !@customer[:address].blank? %>
24 <%= @customer[:address] %><br \> 28 <%= @customer[:address] %><br \>
25 <% end %> 29 <% end %>
  30 + <% if !@customer[:district].blank? %>
  31 + <%= @customer[:district] %><br \>
  32 + <% end %>
26 <% if !@customer[:city].blank? %> 33 <% if !@customer[:city].blank? %>
27 <%= @customer[:city] %><br \> 34 <%= @customer[:city] %><br \>
28 <% end %> 35 <% end %>
29 <% if !@customer[:zip_code].blank? %> 36 <% if !@customer[:zip_code].blank? %>
30 <%= @customer[:zip_code] %> 37 <%= @customer[:zip_code] %>
31 <% end %> 38 <% end %>
32 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 39 + <% if !@customer[:address_reference].blank? %>
  40 + <%= @customer[:address_reference] %><br \>
  41 + <% end %>
  42 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
33 </li> 43 </li>
34 <% end %> 44 <% end %>
35 </ul> 45 </ul>
36 46
37 <p><%=_('Here are the products you bought:')%></p> 47 <p><%=_('Here are the products you bought:')%></p>
38 - <%= items_table(@items, @supplier, true) %> 48 + <%= items_table(@items, @supplier, @delivery_option, true) %>
39 49
40 <p> 50 <p>
41 --<br/> 51 --<br/>
plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb
@@ -15,25 +15,35 @@ @@ -15,25 +15,35 @@
15 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li> 15 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li>
16 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li> 16 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li>
17 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li> 17 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li>
18 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 18 + <li><b><%= _('Payment') %>: </b><%= @customer[:payment] == 'money' ? _('Money') : _('Check') %></li>
  19 + <% if @customer[:payment] == 'money' %>
  20 + <li><b><%= _('Change') %>: </b><%= @customer[:change] %></li>
  21 + <% end %>
  22 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
19 <li><b><%= _('Address') %>:</b> 23 <li><b><%= _('Address') %>:</b>
20 <% end %> 24 <% end %>
21 <% if !@customer[:address].blank? %> 25 <% if !@customer[:address].blank? %>
22 <%= @customer[:address] %><br \> 26 <%= @customer[:address] %><br \>
23 <% end %> 27 <% end %>
  28 + <% if !@customer[:district].blank? %>
  29 + <%= @customer[:district] %><br \>
  30 + <% end %>
24 <% if !@customer[:city].blank? %> 31 <% if !@customer[:city].blank? %>
25 <%= @customer[:city] %><br \> 32 <%= @customer[:city] %><br \>
26 <% end %> 33 <% end %>
27 <% if !@customer[:zip_code].blank? %> 34 <% if !@customer[:zip_code].blank? %>
28 <%= @customer[:zip_code] %> 35 <%= @customer[:zip_code] %>
29 <% end %> 36 <% end %>
30 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 37 + <% if !@customer[:address_reference].blank? %>
  38 + <%= @customer[:address_reference] %><br \>
  39 + <% end %>
  40 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
31 </li> 41 </li>
32 <% end %> 42 <% end %>
33 </ul> 43 </ul>
34 44
35 <p><%=_('And here are the items bought by this customer:')%></p> 45 <p><%=_('And here are the items bought by this customer:')%></p>
36 - <%= items_table(@items, @supplier, true) %> 46 + <%= items_table(@items, @supplier, @delivery_option, true) %>
37 47
38 <p> 48 <p>
39 --<br/> 49 --<br/>
plugins/shopping_cart/views/shopping_cart_plugin_myprofile/edit.html.erb
1 <h1> <%= _('Basket options') %> </h1> 1 <h1> <%= _('Basket options') %> </h1>
2 2
3 -<% form_for(:profile_attr, profile, :url => {:action => 'edit'}, :html => {:method => 'post'}) do |f| %>  
4 - <%= labelled_form_field(_('Enabled?'), f.check_box(:shopping_cart)) %>  
5 - <%= labelled_form_field(_('Delivery?'), f.check_box(:shopping_cart_delivery)) %>  
6 - <%= labelled_form_field(_('Delivery price:'), f.text_field(:shopping_cart_delivery_price)) %> 3 +<% form_for(:settings, @settings, :url => {:action => 'edit'}, :html => {:method => 'post'}) do |f| %>
  4 + <%= labelled_form_field(_('Enabled?'), f.check_box(:enabled)) %>
  5 + <%= labelled_form_field(_('Delivery?'), f.check_box(:delivery)) %>
  6 + <% display_delivery_settings = @settings.delivery ? 'auto' : 'none' %>
  7 + <fieldset id='delivery_settings' style="display: <%= display_delivery_settings %>"><legend><%=_('Delivery')%></legend>
  8 + <table>
  9 + <tr>
  10 + <th><%= _('Option') %></th>
  11 + <th><%= _('Price') %></th>
  12 + <th>&nbsp;</th>
  13 + </tr>
  14 + <% @settings.delivery_options.each do |option, price| %>
  15 + <tr>
  16 + <td><%= text_field_tag('settings[delivery_options][options][]', option, :style => 'width: 100%') %></td>
  17 + <td><%= text_field_tag('settings[delivery_options][prices][]', price, :style => 'width: 100%') %></td>
  18 + <td><%= button_without_text(:close, _('Remove option'), '', :class => 'remove-option') %></td>
  19 + </tr>
  20 + <% end %>
  21 + <tr>
  22 + <td><%= text_field_tag('settings[delivery_options][options][]', nil, :style => 'width: 100%') %></td>
  23 + <td><%= text_field_tag('settings[delivery_options][prices][]', nil, :style => 'width: 100%') %></td>
  24 + <td><%= button_without_text(:close, _('Remove option'), '', :class => 'remove-option') %></td>
  25 + </tr>
  26 + <tr id='add-new-option-row'>
  27 + <td colspan='3' style='background-color: #EEE; text-align: center'><%= link_to(_('ADD NEW OPTION'), '', :id => 'add-new-option') %></td>
  28 + </tr>
  29 + <tr id="empty-option" style='display: none'>
  30 + <td><%= text_field_tag('settings[delivery_options][options][]', nil, :style => 'width: 100%') %></td>
  31 + <td><%= text_field_tag('settings[delivery_options][prices][]', nil, :style => 'width: 100%') %></td>
  32 + <td><%= button_without_text(:close, _('Remove option'), '', :class => 'remove-option') %></td>
  33 + </tr>
  34 + </table>
  35 +
  36 + <%= labelled_form_field(_('Free delivery price:'), f.text_field(:free_delivery_price)) %>
  37 + <%= content_tag('small', _('Empty stands for no free delivery price.')) %>
  38 + </fieldset>
7 <br style='clear: both'/> 39 <br style='clear: both'/>
8 <br style='clear: both'/> 40 <br style='clear: both'/>
9 <div> 41 <div>
@@ -11,3 +43,5 @@ @@ -11,3 +43,5 @@
11 <%= button :back, _('Back to control panel'), :controller => 'profile_editor' %> 43 <%= button :back, _('Back to control panel'), :controller => 'profile_editor' %>
12 </div> 44 </div>
13 <% end%> 45 <% end%>
  46 +
  47 +<%= javascript_include_tag '../plugins/shopping_cart/edit' %>
plugins/shopping_cart/views/shopping_cart_plugin_profile/buy.html.erb
@@ -6,30 +6,26 @@ @@ -6,30 +6,26 @@
6 <%= labelled_form_field('* ' + _("Name"), f.text_field(:name, :class => 'required') ) %> 6 <%= labelled_form_field('* ' + _("Name"), f.text_field(:name, :class => 'required') ) %>
7 <%= labelled_form_field('* ' + _("Email"), f.text_field(:email, :class => 'required email') ) %> 7 <%= labelled_form_field('* ' + _("Email"), f.text_field(:email, :class => 'required email') ) %>
8 <%= labelled_form_field('* ' + _("Contact phone"), f.text_field(:contact_phone, :class => 'required') ) %> 8 <%= labelled_form_field('* ' + _("Contact phone"), f.text_field(:contact_phone, :class => 'required') ) %>
  9 + <%= labelled_form_field(_('Delivery option'), select_tag(:delivery_option, options_for_select(select_delivery_options(@settings.delivery_options, environment)), 'data-profile-identifier' => profile.identifier)) unless !@settings.delivery || (@settings.free_delivery_price && get_total(session[:cart][:items]) >= @settings.free_delivery_price) %>
  10 + <%= labelled_form_field(_('Payment'), select_tag('customer[payment]', options_for_select([[_("Money"), :money],[_('Check'), :check]]))) %>
  11 + <%= labelled_form_field(_('Change'), text_field_tag('customer[change]')) %>
9 </div> 12 </div>
10 - <fieldset><legend><%=_('Delivery Address')%></legend>  
11 - <%= labelled_form_field(_('Address (street and number)'), f.text_field(:address)) %>  
12 - <%= labelled_form_field( _("City"), f.text_field(:city)) %>  
13 - <%= labelled_form_field(_('ZIP code'), f.text_field(:zip_code)) %>  
14 - </fieldset> 13 + <% if @settings.delivery %>
  14 + <fieldset><legend><%=_('Delivery Address')%></legend>
  15 + <%= labelled_form_field(_('Address (street and number)'), f.text_field(:address)) %>
  16 + <%= labelled_form_field(_('Address reference'), f.text_field(:address_reference)) %>
  17 + <%= labelled_form_field(_('District'), f.text_field(:district)) %>
  18 + <%= labelled_form_field( _("City"), f.text_field(:city)) %>
  19 + <%= labelled_form_field(_('ZIP code'), f.text_field(:zip_code)) %>
  20 + </fieldset>
  21 + <% end %>
15 <div id="cart-form-actions"> 22 <div id="cart-form-actions">
16 <%= submit_button(:send, _('Send buy request')) %> 23 <%= submit_button(:send, _('Send buy request')) %>
17 </div> 24 </div>
18 <% end %> 25 <% end %>
19 - <%= items_table(session[:cart][:items], profile) %> 26 + <% delivery_option = @settings.delivery_options.first && @settings.delivery_options.first.first %>
  27 + <%= items_table(session[:cart][:items], profile, delivery_option) %>
20 <%= link_to '', '#', :onclick => "Cart.colorbox_close(this);", :class => 'cart-box-close icon-cancel' %> 28 <%= link_to '', '#', :onclick => "Cart.colorbox_close(this);", :class => 'cart-box-close icon-cancel' %>
21 </div> 29 </div>
22 30
23 -<script type="text/javascript">  
24 -//<![CDATA[  
25 - jQuery(document).ready(function(){  
26 - jQuery("#cart-request-form").validate({  
27 - submitHandler: function(form) {  
28 - jQuery(form).find('input.submit').attr('disabled', true);  
29 - jQuery('#cboxLoadingOverlay').show().addClass('loading');  
30 - jQuery('#cboxLoadingGraphic').show().addClass('loading');  
31 - }  
32 - });  
33 - });  
34 -//]]>  
35 -</script> 31 +<%= javascript_include_tag '../plugins/shopping_cart/buy' %>
public/designs/themes/noosfero/images/rails.png 0 → 100644

1.75 KB

public/images/star.png 0 → 100644

3.58 KB

public/stylesheets/application.css
@@ -2677,6 +2677,40 @@ div#activation_enterprise label, div#activation_enterprise input, div#activation @@ -2677,6 +2677,40 @@ div#activation_enterprise label, div#activation_enterprise input, div#activation
2677 .msie #product-list h3 { 2677 .msie #product-list h3 {
2678 margin-top: -15px; 2678 margin-top: -15px;
2679 } 2679 }
  2680 +
  2681 +.l-sidebar-left-bar ul,
  2682 +.l-sidebar-right-bar ul {
  2683 + list-style-type: none;
  2684 + margin-left: 0;
  2685 + padding: 1em 1em 0.5em 1em;
  2686 +}
  2687 +
  2688 +.l-sidebar-left-bar ul ul,
  2689 +.l-sidebar-right-bar ul ul {
  2690 + padding-top: 0;
  2691 +}
  2692 +
  2693 +.l-sidebar-left-bar ul{
  2694 + background-color: #eeeeec;
  2695 + border-radius: 5px;
  2696 +}
  2697 +
  2698 +.l-sidebar-left-bar a,
  2699 +.l-sidebar-right-bar a {
  2700 + text-decoration: none;
  2701 +}
  2702 +
  2703 +.l-sidebar-left-bar a:hover,
  2704 +.l-sidebar-right-bar a:hover {
  2705 + text-decoration: underline;
  2706 +}
  2707 +
  2708 +.l-sidebar-left-bar li,
  2709 +.l-sidebar-right-bar li {
  2710 + margin: 0;
  2711 + padding: 0;
  2712 +}
  2713 +
2680 /* * * Show Product * * * * * * * * * * * * */ 2714 /* * * Show Product * * * * * * * * * * * * */
2681 2715
2682 .controller-catalog #show_product .product-pic { 2716 .controller-catalog #show_product .product-pic {
@@ -6066,6 +6100,35 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img { @@ -6066,6 +6100,35 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img {
6066 line-height: 1.5; 6100 line-height: 1.5;
6067 } 6101 }
6068 6102
  6103 +/* Sidebar Layout */
  6104 +
  6105 +.l-sidebar-left-bar {
  6106 + float: left;
  6107 + width: 20%;
  6108 +}
  6109 +
  6110 +.l-sidebar-left-content {
  6111 + float: right;
  6112 + width: 78%;
  6113 +}
  6114 +
  6115 +.l-sidebar-right-bar {
  6116 + float: right;
  6117 + width: 20%;
  6118 +}
  6119 +
  6120 +.l-sidebar-right-content {
  6121 + float: left;
  6122 + width: 78%;
  6123 +}
  6124 +
  6125 +/* Breadcrumb */
  6126 +
  6127 +#breadcrumb {
  6128 + font-size: 16px;
  6129 + margin: 15px 0;
  6130 +}
  6131 +
6069 .controller-profile_editor #profile-data { 6132 .controller-profile_editor #profile-data {
6070 display: table; 6133 display: table;
6071 width: auto; 6134 width: auto;
@@ -6114,3 +6177,10 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img { @@ -6114,3 +6177,10 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img {
6114 width: 100px; 6177 width: 100px;
6115 text-align: center; 6178 text-align: center;
6116 } 6179 }
  6180 +
  6181 +#product-list .highlighted img.star {
  6182 + position: absolute;
  6183 + top: 2px;
  6184 + right: 2px;
  6185 + z-index: 10;
  6186 +}
test/functional/catalog_controller_test.rb
@@ -109,4 +109,71 @@ class CatalogControllerTest &lt; ActionController::TestCase @@ -109,4 +109,71 @@ class CatalogControllerTest &lt; ActionController::TestCase
109 assert_tag :tag => 'span', :content => 'This is Plugin2 speaking!', :attributes => {:id => 'plugin2'} 109 assert_tag :tag => 'span', :content => 'This is Plugin2 speaking!', :attributes => {:id => 'plugin2'}
110 end 110 end
111 111
  112 + should 'get categories of the right level' do
  113 + pc1 = ProductCategory.create!(:name => "PC1", :environment => @enterprise.environment)
  114 + pc2 = ProductCategory.create!(:name => "PC2", :environment => @enterprise.environment, :parent_id => pc1.id)
  115 + pc3 = ProductCategory.create!(:name => "PC3", :environment => @enterprise.environment, :parent_id => pc1.id)
  116 + pc4 = ProductCategory.create!(:name => "PC4", :environment => @enterprise.environment, :parent_id => pc2.id)
  117 + p1 = fast_create(Product, :product_category_id => pc1.id, :enterprise_id => @enterprise.id)
  118 + p2 = fast_create(Product, :product_category_id => pc2.id, :enterprise_id => @enterprise.id)
  119 + p3 = fast_create(Product, :product_category_id => pc3.id, :enterprise_id => @enterprise.id)
  120 + p4 = fast_create(Product, :product_category_id => pc4.id, :enterprise_id => @enterprise.id)
  121 +
  122 + get :index, :profile => @enterprise.identifier, :level => pc1.id
  123 +
  124 + assert_not_includes assigns(:categories), pc1
  125 + assert_includes assigns(:categories), pc2
  126 + assert_includes assigns(:categories), pc3
  127 + assert_not_includes assigns(:categories), pc4
  128 + end
  129 +
  130 + should 'filter products based on level selected' do
  131 + pc1 = ProductCategory.create!(:name => "PC1", :environment => @enterprise.environment)
  132 + pc2 = ProductCategory.create!(:name => "PC2", :environment => @enterprise.environment, :parent_id => pc1.id)
  133 + pc3 = ProductCategory.create!(:name => "PC3", :environment => @enterprise.environment, :parent_id => pc1.id)
  134 + pc4 = ProductCategory.create!(:name => "PC4", :environment => @enterprise.environment, :parent_id => pc2.id)
  135 + p1 = fast_create(Product, :product_category_id => pc1.id, :enterprise_id => @enterprise.id)
  136 + p2 = fast_create(Product, :product_category_id => pc2.id, :enterprise_id => @enterprise.id)
  137 + p3 = fast_create(Product, :product_category_id => pc3.id, :enterprise_id => @enterprise.id)
  138 + p4 = fast_create(Product, :product_category_id => pc4.id, :enterprise_id => @enterprise.id)
  139 +
  140 + get :index, :profile => @enterprise.identifier, :level => pc2.id
  141 +
  142 + assert_not_includes assigns(:products), p1
  143 + assert_includes assigns(:products), p2
  144 + assert_not_includes assigns(:products), p3
  145 + assert_includes assigns(:products), p4
  146 + end
  147 +
  148 + should 'get products ordered by availability, highlighted and then name' do
  149 + p1 = fast_create(Product, :enterprise_id => @enterprise.id, :name => 'Zebra', :available => true, :highlighted => true)
  150 + p2 = fast_create(Product, :enterprise_id => @enterprise.id, :name => 'Car', :available => true)
  151 + p3 = fast_create(Product, :enterprise_id => @enterprise.id, :name => 'Panda', :available => true)
  152 + p4 = fast_create(Product, :enterprise_id => @enterprise.id, :name => 'Pen', :available => false, :highlighted => true)
  153 + p5 = fast_create(Product, :enterprise_id => @enterprise.id, :name => 'Ball', :available => false)
  154 + p6 = fast_create(Product, :enterprise_id => @enterprise.id, :name => 'Medal', :available => false)
  155 +
  156 + get :index, :profile => @enterprise.identifier
  157 +
  158 + assert_equal [p1,p2,p3,p4,p5,p6], assigns(:products)
  159 + end
  160 +
  161 + should 'add highlighted CSS class around a highlighted product' do
  162 + prod = @enterprise.products.create!(:name => 'Highlighted Product', :product_category => @product_category, :highlighted => true)
  163 + get :index, :profile => @enterprise.identifier
  164 + assert_tag :tag => 'li', :attributes => { :class => 'product highlighted' }, :content => /Highlighted Product/
  165 + end
  166 +
  167 + should 'do not add highlighted CSS class around an ordinary product' do
  168 + prod = @enterprise.products.create!(:name => 'Ordinary Product', :product_category => @product_category, :highlighted => false)
  169 + get :index, :profile => @enterprise.identifier
  170 + assert_no_tag :tag => 'li', :attributes => { :class => 'product highlighted' }, :content => /Ordinary Product/
  171 + end
  172 +
  173 + should 'display star image in highlighted product' do
  174 + prod = @enterprise.products.create!(:name => 'The Eyes Are The Light', :product_category => @product_category, :highlighted => true)
  175 + get :index, :profile => @enterprise.identifier
  176 + assert_tag :tag => 'img', :attributes => { :class => 'star', :src => /star.png/ }
  177 + end
  178 +
112 end 179 end
test/functional/search_controller_test.rb
@@ -926,6 +926,20 @@ class SearchControllerTest &lt; ActionController::TestCase @@ -926,6 +926,20 @@ class SearchControllerTest &lt; ActionController::TestCase
926 end 926 end
927 end 927 end
928 928
  929 + should 'add highlighted CSS class around a highlighted product' do
  930 + enterprise = fast_create(Enterprise)
  931 + product = Product.create!(:name => 'Enter Sandman', :enterprise_id => enterprise.id, :product_category_id => @product_category.id, :highlighted => true)
  932 + get :products
  933 + assert_tag :tag => 'li', :attributes => { :class => 'search-product-item highlighted' }, :content => /Enter Sandman/
  934 + end
  935 +
  936 + should 'do not add highlighted CSS class around an ordinary product' do
  937 + enterprise = fast_create(Enterprise)
  938 + product = Product.create!(:name => 'Holier Than Thou', :enterprise_id => enterprise.id, :product_category_id => @product_category.id, :highlighted => false)
  939 + get :products
  940 + assert_no_tag :tag => 'li', :attributes => { :class => 'search-product-item highlighted' }, :content => /Holier Than Thou/
  941 + end
  942 +
929 ################################################################## 943 ##################################################################
930 ################################################################## 944 ##################################################################
931 945
test/unit/category_test.rb
@@ -552,4 +552,62 @@ class CategoryTest &lt; ActiveSupport::TestCase @@ -552,4 +552,62 @@ class CategoryTest &lt; ActiveSupport::TestCase
552 cat.save! 552 cat.save!
553 end 553 end
554 554
  555 + should 'return categories of a level' do
  556 + c1 = fast_create(Category)
  557 + c2 = fast_create(Category)
  558 + c3 = fast_create(Category, :parent_id => c1)
  559 + c4 = fast_create(Category, :parent_id => c1)
  560 + c5 = fast_create(Category, :parent_id => c2)
  561 + c6 = fast_create(Category, :parent_id => c3)
  562 +
  563 + assert_includes Category.on_level(nil), c1
  564 + assert_includes Category.on_level(nil), c2
  565 + assert_includes Category.on_level(c1), c3
  566 + assert_includes Category.on_level(c1), c4
  567 + assert_includes Category.on_level(c2), c5
  568 + assert_includes Category.on_level(c3), c6
  569 + end
  570 +
  571 + should 'on level named_scope must be able to receive parent or parent_id' do
  572 + parent = fast_create(Category)
  573 + category = fast_create(Category, :parent_id => parent)
  574 +
  575 + assert_includes Category.on_level(parent), category
  576 + assert_includes Category.on_level(parent.id), category
  577 + end
  578 +
  579 + should 'list category sub-categories' do
  580 + c1 = Category.create!(:name => 'Category 1', :environment => Environment.default)
  581 + c2 = Category.create!(:name => 'Category 2', :environment => Environment.default)
  582 + c3 = Category.create!(:name => 'Category 3', :environment => Environment.default, :parent_id => c1)
  583 + c4 = Category.create!(:name => 'Category 4', :environment => Environment.default, :parent_id => c1)
  584 + c5 = Category.create!(:name => 'Category 5', :environment => Environment.default, :parent_id => c3)
  585 +
  586 + sub_categories = Category.sub_categories(c1)
  587 +
  588 + assert ActiveRecord::NamedScope::Scope, sub_categories.class
  589 + assert_not_includes sub_categories, c1
  590 + assert_not_includes sub_categories, c2
  591 + assert_includes sub_categories, c3
  592 + assert_includes sub_categories, c4
  593 + assert_includes sub_categories, c5
  594 + end
  595 +
  596 + should 'list category sub-tree' do
  597 + c1 = Category.create!(:name => 'Category 1', :environment => Environment.default)
  598 + c2 = Category.create!(:name => 'Category 2', :environment => Environment.default)
  599 + c3 = Category.create!(:name => 'Category 3', :environment => Environment.default, :parent_id => c1)
  600 + c4 = Category.create!(:name => 'Category 4', :environment => Environment.default, :parent_id => c1)
  601 + c5 = Category.create!(:name => 'Category 5', :environment => Environment.default, :parent_id => c3)
  602 +
  603 + sub_tree = Category.sub_tree(c1)
  604 +
  605 + assert ActiveRecord::NamedScope::Scope, sub_tree.class
  606 + assert_includes sub_tree, c1
  607 + assert_not_includes sub_tree, c2
  608 + assert_includes sub_tree, c3
  609 + assert_includes sub_tree, c4
  610 + assert_includes sub_tree, c5
  611 + end
  612 +
555 end 613 end
test/unit/display_helper_test.rb
@@ -44,4 +44,14 @@ class DisplayHelperTest &lt; ActiveSupport::TestCase @@ -44,4 +44,14 @@ class DisplayHelperTest &lt; ActiveSupport::TestCase
44 assert_equal 'go to <a href="http://www.noosfero.org" onclick="return confirm(\'Are you sure you want to visit this web site?\')" rel="nofolow" target="_blank">www.&#x200B;noos&#x200B;fero&#x200B;.org</a> yeah!', html 44 assert_equal 'go to <a href="http://www.noosfero.org" onclick="return confirm(\'Are you sure you want to visit this web site?\')" rel="nofolow" target="_blank">www.&#x200B;noos&#x200B;fero&#x200B;.org</a> yeah!', html
45 end 45 end
46 46
  47 + should 'return path to file under theme dir if theme has that file' do
  48 + stubs(:theme_path).returns('/designs/themes/noosfero')
  49 + assert_equal '/designs/themes/noosfero/images/rails.png', themed_path('/images/rails.png')
  50 + end
  51 +
  52 + should 'return path to file under public dir if theme hasnt that file' do
  53 + stubs(:theme_path).returns('/designs/themes/noosfero')
  54 + assert_equal '/images/invalid-file.png', themed_path('/images/invalid-file.png')
  55 + end
  56 +
47 end 57 end
test/unit/person_test.rb
@@ -64,7 +64,7 @@ class PersonTest &lt; ActiveSupport::TestCase @@ -64,7 +64,7 @@ class PersonTest &lt; ActiveSupport::TestCase
64 64
65 should "have person info fields" do 65 should "have person info fields" do
66 p = Person.new(:environment => Environment.default) 66 p = Person.new(:environment => Environment.default)
67 - [ :name, :photo, :contact_information, :birth_date, :sex, :address, :city, :state, :country, :zip_code, :image ].each do |i| 67 + [ :name, :photo, :contact_information, :birth_date, :sex, :address, :city, :state, :country, :zip_code, :image, :district, :address_reference ].each do |i|
68 assert_respond_to p, i 68 assert_respond_to p, i
69 end 69 end
70 end 70 end
test/unit/plugin_settings_test.rb
@@ -10,29 +10,41 @@ class PluginSettingsTest &lt; ActiveSupport::TestCase @@ -10,29 +10,41 @@ class PluginSettingsTest &lt; ActiveSupport::TestCase
10 10
11 def setup 11 def setup
12 @environment = Environment.new 12 @environment = Environment.new
  13 + @profile = Profile.new
13 @plugin = SolarSystemPlugin 14 @plugin = SolarSystemPlugin
14 - @settings = Noosfero::Plugin::Settings.new(@environment, @plugin)  
15 end 15 end
16 16
17 - attr_accessor :environment, :plugin, :settings 17 + attr_accessor :environment, :profile, :plugin, :settings
18 18
19 - should 'store setttings in environment' do 19 + should 'store setttings on any model that offers settings' do
  20 + base = environment
  21 + settings = Noosfero::Plugin::Settings.new(base, plugin)
20 settings.star = 'sun' 22 settings.star = 'sun'
21 settings.planets = 8 23 settings.planets = 8
22 - assert_equal 'sun', environment.settings[:solar_system_plugin][:star]  
23 - assert_equal 8, environment.settings[:solar_system_plugin][:planets] 24 + assert_equal 'sun', base.settings[:solar_system_plugin][:star]
  25 + assert_equal 8, base.settings[:solar_system_plugin][:planets]
  26 + assert_equal 'sun', settings.star
  27 + assert_equal 8, settings.planets
  28 +
  29 + base = profile
  30 + settings = Noosfero::Plugin::Settings.new(base, plugin)
  31 + settings.star = 'sun'
  32 + settings.planets = 8
  33 + assert_equal 'sun', base.settings[:solar_system_plugin][:star]
  34 + assert_equal 8, base.settings[:solar_system_plugin][:planets]
24 assert_equal 'sun', settings.star 35 assert_equal 'sun', settings.star
25 assert_equal 8, settings.planets 36 assert_equal 8, settings.planets
26 end 37 end
27 38
28 - should 'save environment on save' do 39 + should 'save base on save' do
29 environment.expects(:save!) 40 environment.expects(:save!)
  41 + settings = Noosfero::Plugin::Settings.new(environment, plugin)
30 settings.save! 42 settings.save!
31 end 43 end
32 44
33 should 'use default value defined on the plugin class' do 45 should 'use default value defined on the plugin class' do
  46 + settings = Noosfero::Plugin::Settings.new(profile, plugin)
34 assert_equal 42, settings.secret 47 assert_equal 42, settings.secret
35 end 48 end
36 49
37 end 50 end
38 -  
test/unit/product_test.rb
@@ -772,4 +772,40 @@ class ProductTest &lt; ActiveSupport::TestCase @@ -772,4 +772,40 @@ class ProductTest &lt; ActiveSupport::TestCase
772 assert_equal [prod3, prod2, prod1], Product.more_recent 772 assert_equal [prod3, prod2, prod1], Product.more_recent
773 end 773 end
774 774
  775 + should 'return products from a category' do
  776 + pc1 = ProductCategory.create!(:name => 'PC1', :environment => Environment.default)
  777 + pc2 = ProductCategory.create!(:name => 'PC2', :environment => Environment.default)
  778 + pc3 = ProductCategory.create!(:name => 'PC3', :environment => Environment.default, :parent => pc1)
  779 + p1 = fast_create(Product, :product_category_id => pc1)
  780 + p2 = fast_create(Product, :product_category_id => pc1)
  781 + p3 = fast_create(Product, :product_category_id => pc2)
  782 + p4 = fast_create(Product, :product_category_id => pc3)
  783 +
  784 + products = Product.from_category(pc1)
  785 +
  786 + assert_includes products, p1
  787 + assert_includes products, p2
  788 + assert_not_includes products, p3
  789 + assert_includes products, p4
  790 + end
  791 +
  792 + should 'not crash if nil is passed to from_category' do
  793 + assert_nothing_raised do
  794 + Product.from_category(nil)
  795 + end
  796 + end
  797 +
  798 + should 'return from_category scope untouched if passed nil' do
  799 + enterprise = fast_create(Enterprise)
  800 + p1 = fast_create(Product, :enterprise_id => enterprise.id)
  801 + p2 = fast_create(Product, :enterprise_id => enterprise.id)
  802 + p3 = fast_create(Product, :enterprise_id => enterprise.id)
  803 +
  804 + products = enterprise.products.from_category(nil)
  805 +
  806 + assert_includes products, p1
  807 + assert_includes products, p2
  808 + assert_includes products, p3
  809 + end
  810 +
775 end 811 end