diff --git a/app/controllers/my_profile/manage_products_controller.rb b/app/controllers/my_profile/manage_products_controller.rb index d442d60..0fc0e83 100644 --- a/app/controllers/my_profile/manage_products_controller.rb +++ b/app/controllers/my_profile/manage_products_controller.rb @@ -1,10 +1,12 @@ class ManageProductsController < ApplicationController needs_profile - protect 'manage_products', :profile + protect 'manage_products', :profile, :except => [:show] before_filter :check_environment_feature + before_filter :login_required, :except => [:show] protected + def check_environment_feature if profile.environment.enabled?('disable_products_for_enterprises') render_not_found @@ -13,6 +15,7 @@ class ManageProductsController < ApplicationController end public + def index @products = @profile.products @consumptions = @profile.consumptions @@ -51,15 +54,34 @@ class ManageProductsController < ApplicationController end def edit - @object = @product = @profile.products.find(params[:id]) - @current_category = @product.product_category - @categories = @current_category.nil? ? [] : @current_category.children + @product = @profile.products.find(params[:id]) + field = params[:field] if request.post? - if @product.update_attributes(params[:product]) - flash[:notice] = _('Product succesfully updated') - redirect_back_or_default :action => 'show', :id => @product - else + begin + @product.update_attributes!(params[:product]) + render :partial => "display_#{field}", :locals => {:product => @product} + rescue Exception => e + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true} + end + else + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false} + end + end + + def edit_category + @product = @profile.products.find(params[:id]) + @category = @product.product_category + @categories = ProductCategory.top_level_for(environment) + @edit = true + @level = @category.level + if request.post? + begin + @product.update_attributes!(params[:product]) + render :partial => 'shared/redirect_via_javascript', + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } + rescue Exception => e flash[:notice] = _('Could not update the product') + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } end end end diff --git a/app/controllers/public/catalog_controller.rb b/app/controllers/public/catalog_controller.rb index 6f59dd0..1620001 100644 --- a/app/controllers/public/catalog_controller.rb +++ b/app/controllers/public/catalog_controller.rb @@ -1,15 +1,12 @@ class CatalogController < PublicController needs_profile + before_filter :check_enterprise_and_environment def index @products = @profile.products end - def show - @product = @profile.products.find(params[:id]) - end - protected def check_enterprise_and_environment unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2de8e2a..2577a97 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -907,7 +907,7 @@ module ApplicationHelper (@section ? @section.title + ' - ' : '') + (@toc ? _('Online Manual') + ' - ' : '') + environment.name + - (@category ? "→ #{@category.full_name}" : '') + (@category ? " - #{@category.full_name}" : '') end def noosfero_javascript @@ -921,6 +921,7 @@ module ApplicationHelper 'lightbox', 'colorpicker', pngfix_stylesheet_path, + 'jquery.ui/sunny/jquery-ui-1.8.2.custom', ] end @@ -960,13 +961,16 @@ module ApplicationHelper text_field_tag(name, value, options.merge(:class => 'colorpicker_field')) end - # for now force currency to Brazillian format, like: "12.345,20" - def float_to_currency(price) - number_to_currency(price, :unit => 'R$', :separator => ',', :delimiter => '.') - end - def ui_icon(icon_type) "" end + def ui_button(label, url, html_options = {}) + link_to(label, url, html_options.merge(:class => 'ui_button fg-button')) + end + + def ui_button_to_remote(label, options, html_options = {}) + link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button')) + end + end diff --git a/app/helpers/catalog_helper.rb b/app/helpers/catalog_helper.rb index c1195e7..2bfe021 100644 --- a/app/helpers/catalog_helper.rb +++ b/app/helpers/catalog_helper.rb @@ -1,6 +1,7 @@ module CatalogHelper include DisplayHelper +include ManageProductsHelper def display_products_list(profile, products) data = '' @@ -19,7 +20,7 @@ include DisplayHelper content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list') end -private + private def product_category_name(profile, product_category) if profile.enabled? diff --git a/app/helpers/display_helper.rb b/app/helpers/display_helper.rb index 8409c34..989121c 100644 --- a/app/helpers/display_helper.rb +++ b/app/helpers/display_helper.rb @@ -2,7 +2,7 @@ module DisplayHelper def link_to_product(product, opts={}) return _('No product') unless product - target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => product) : product.enterprise.url + target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url link_to content_tag( 'span', product.name ), target, opts diff --git a/app/helpers/manage_products_helper.rb b/app/helpers/manage_products_helper.rb index 3638ffb..6ff212e 100644 --- a/app/helpers/manage_products_helper.rb +++ b/app/helpers/manage_products_helper.rb @@ -49,12 +49,52 @@ module ManageProductsHelper hierarchy.reverse.join(options[:separator] || ' → ') end - def options_for_select_categories(categories) + def options_for_select_categories(categories, selected = nil) categories.sort_by{|cat| cat.name.transliterate}.map do |category| - "" + selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '') + "" end.join("\n") end + def build_selects_for_ancestors(ancestors, last_level) + current_category = ancestors.shift + if current_category.nil? + content_tag('div', '', + :class => 'categories_container', + :id => "categories_container_level#{ last_level }" + ) + else + content_tag('div', + select_tag('category_id', + options_for_select_categories(current_category.siblings + [current_category], current_category), + :size => 10, + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_category.level + 1 }", :with => "'category_id=' + this.value") + ) + + build_selects_for_ancestors(ancestors, last_level), + :class => 'categories_container', + :id => "categories_container_level#{ current_category.level }" + ) + end + end + + def selects_for_all_ancestors(current_category) + build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category.level + 1) + end + + def select_for_new_category + content_tag('div', + render(:partial => 'categories_for_selection'), + :class => 'categories_container', + :id => 'categories_container_level0' + ) + end + + def categories_container(field_id_html, categories_selection_html, hierarchy_html = '') + field_id_html + + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') + + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper') + end + def select_for_categories(categories, level = 0) if categories.empty? content_tag('div', '', :id => 'no_subcategories') @@ -68,4 +108,98 @@ module ManageProductsHelper end end + def edit_product_link(product, field, label, html_options = {}) + return '' unless (user && user.has_permission?('manage_products', profile)) + options = html_options.merge(:id => 'link-edit-product-' + field) + + link_to_remote(label, + {:update => "product-#{field}", + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, + :method => :get}, + options) + end + + def edit_product_button(product, field, label, html_options = {}) + the_class = 'button with-text icon-edit' + if html_options.has_key?(:class) + the_class << ' ' << html_options[:class] + end + edit_product_link(product, field, label, html_options.merge(:class => the_class)) + end + + def edit_product_ui_button(product, field, label, html_options = {}) + return '' unless (user && user.has_permission?('manage_products', profile)) + options = html_options.merge(:id => 'edit-product-button-ui-' + field) + + ui_button_to_remote(label, + {:update => "product-#{field}", + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, + :complete => "$('edit-product-button-ui-#{field}').hide()", + :method => :get}, + options) + end + + def cancel_edit_product_link(product, field, html_options = {}) + return '' unless (user && user.has_permission?('manage_products', profile)) + button_to_function(:cancel, _('Cancel'), nil, html_options) do |page| + page.replace_html "product-#{field}", :partial => "display_#{field}", :locals => {:product => product} + end + end + + def edit_product_category_link(product, html_options = {}) + return '' unless (user && user.has_permission?('manage_products', profile)) + options = html_options.merge(:id => 'link-edit-product-category') + link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options) + end + + def float_to_currency(value) + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :format => "%u %n") + end + + def display_value(product) + price = product.price + unless price.blank? || price.zero? + unit = product.unit + return '' if unit.blank? + discount = product.discount + if discount.blank? || discount.zero? + display_price(_('Price: '), price, unit) + else + display_price_with_discount(price, unit, product.price_with_discount) + end + else + '' + end + end + + def display_price(label, price, unit) + content_tag('span', label, :class => 'field-name') + + content_tag('span', float_to_currency(price), :class => 'field-value') + + ' (%s)' % unit + end + + def display_price_with_discount(price, unit, price_with_discount) + original_value = content_tag('span', float_to_currency(price), :class => 'field-value') + discount_value = display_price(_('On sale: '), price_with_discount, unit) + content_tag('span', _('List price: '), :class => 'field-name') + original_value + "
" + discount_value + end + + def display_qualifiers(product) + data = '' + product.product_qualifiers.each do |pq| + certified_by = pq.certifier ? _(' certified by %s') % link_to(pq.certifier.name, pq.certifier.link) : '' + data << content_tag('li', '✔ ' + pq.qualifier.name + certified_by, :class => 'product-qualifiers-item') + end + content_tag('ul', data, :id => 'product-qualifiers') + end + + def checkboxes_qualifiers(product, qualifier) + check_box_tag("product[qualifiers_list][#{qualifier.id}][qualifier_id]", qualifier.id, product.qualifiers.include?(qualifier)) + qualifier.name + end + + def select_certifiers(product, qualifier, certifiers) + relation = product.product_qualifiers.find(:first, :conditions => {:qualifier_id => qualifier.id}) + selected = relation.nil? ? 0 : relation.certifier_id + select_tag("product[qualifiers_list][#{qualifier.id}][certifier_id]", options_for_select([[_('Select...') , 0 ]] + certifiers.map {|c|[c.name, c.id]}, selected)) + end end diff --git a/app/models/certifier.rb b/app/models/certifier.rb new file mode 100644 index 0000000..cd68824 --- /dev/null +++ b/app/models/certifier.rb @@ -0,0 +1,8 @@ +class Certifier < ActiveRecord::Base + + belongs_to :environment + + def link + self[:link] || '' + end +end diff --git a/app/models/environment.rb b/app/models/environment.rb index 2d6b4d1..c70a01a 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -152,6 +152,9 @@ class Environment < ActiveRecord::Base has_many :roles + has_many :qualifiers + has_many :certifiers + acts_as_accessible def superior_intances @@ -209,6 +212,10 @@ class Environment < ActiveRecord::Base settings_items :help_message_to_add_enterprise, :type => String, :default => '' settings_items :tip_message_enterprise_activation_question, :type => String, :default => '' + settings_items :currency_unit, :type => String, :default => '$' + settings_items :currency_separator, :type => String, :default => '.' + settings_items :currency_delimiter, :type => String, :default => ',' + def news_amount_by_folder=(amount) settings[:news_amount_by_folder] = amount.to_i end diff --git a/app/models/product.rb b/app/models/product.rb index 923ca66..ffcc59c 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -2,10 +2,14 @@ class Product < ActiveRecord::Base belongs_to :enterprise belongs_to :product_category has_many :product_categorizations + has_many :product_qualifiers + has_many :qualifiers, :through => :product_qualifiers validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true validates_presence_of :product_category + validates_numericality_of :price, :allow_nil => true + validates_numericality_of :discount, :allow_nil => true after_update :save_image @@ -31,10 +35,36 @@ class Product < ActiveRecord::Base acts_as_searchable :fields => [ :name, :description, :category_full_name ] - xss_terminate :only => [ :name, :description ], :on => 'validation' + xss_terminate :only => [ :name ], :on => 'validation' + xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation' acts_as_mappable - + + def self.units + { + _('Litre') => 'litre', + _('Kilo') => 'kilo', + _('Meter') => 'meter', + _('Unit') => 'unit', + } + end + + def name + self[:name].blank? ? category_name : self[:name] + end + + def name=(value) + if (value == category_name) + self[:name] = nil + else + self[:name] = value + end + end + + def default_image(size='thumb') + '/images/icons-app/product-default-pic-%s.png' % size + end + def category_full_name product_category ? product_category.full_name.split('/') : nil end @@ -60,13 +90,21 @@ class Product < ActiveRecord::Base end def url - enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => id) + enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => id) end def public? enterprise.public_profile end + def formatted_value(value) + ("%.2f" % self[value]).to_s.gsub('.', enterprise.environment.currency_separator) if self[value] + end + + def price_with_discount + price - discount if discount + end + def price=(value) if value.is_a?(String) super(currency_to_float(value)) @@ -75,6 +113,14 @@ class Product < ActiveRecord::Base end end + def discount=(value) + if value.is_a?(String) + super(currency_to_float(value)) + else + super(value) + end + end + def currency_to_float( num ) if num.count('.') == 1 && num.count(',') == 0 # number like "12.34" @@ -99,4 +145,17 @@ class Product < ActiveRecord::Base return num.tr(',.','').to_f end + def has_basic_info? + %w[description price].each do |field| + return true if !self.send(field).blank? + end + false + end + + def qualifiers_list=(qualifiers) + self.product_qualifiers.delete_all + qualifiers.each do |item| + self.product_qualifiers.create(item[1]) if item[1].has_key?('qualifier_id') + end + end end diff --git a/app/models/product_qualifier.rb b/app/models/product_qualifier.rb new file mode 100644 index 0000000..f60773b --- /dev/null +++ b/app/models/product_qualifier.rb @@ -0,0 +1,5 @@ +class ProductQualifier < ActiveRecord::Base + belongs_to :qualifier + belongs_to :product + belongs_to :certifier +end diff --git a/app/models/products_block.rb b/app/models/products_block.rb index eb724ae..2b64092 100644 --- a/app/models/products_block.rb +++ b/app/models/products_block.rb @@ -20,7 +20,7 @@ class ProductsBlock < Block block_title(title) + content_tag( 'ul', - products.map {|product| content_tag('li', link_to(product.name, product.url, :style => 'background-image:url(%s)' % ( product.image ? product.image.public_filename(:minor) : '/images/icons-app/product-default-pic-minor.png' )), :class => 'product' )} + products.map {|product| content_tag('li', link_to(product.name, product.url, :style => 'background-image:url(%s)' % ( product.image ? product.image.public_filename(:minor) : product.default_image('minor'))), :class => 'product' )} ) end diff --git a/app/models/profile.rb b/app/models/profile.rb index 042a8a9..d9871d0 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -386,7 +386,7 @@ class Profile < ActiveRecord::Base def url_options options = { :host => default_hostname, :profile => (own_hostname ? nil : self.identifier) } - Noosfero.url_options.merge(options) + options.merge(Noosfero.url_options.merge(options)) end private :generate_url, :url_options diff --git a/app/models/qualifier.rb b/app/models/qualifier.rb new file mode 100644 index 0000000..d738a97 --- /dev/null +++ b/app/models/qualifier.rb @@ -0,0 +1,5 @@ +class Qualifier < ActiveRecord::Base + + belongs_to :environment + +end diff --git a/app/views/catalog/show.rhtml b/app/views/catalog/show.rhtml deleted file mode 100644 index 22330c5..0000000 --- a/app/views/catalog/show.rhtml +++ /dev/null @@ -1,25 +0,0 @@ -- <%= _('Price: %s') % ( "%.2f" % @product.price) %> -
-<% end %> - --<%= _('Category: %s ') % link_to_product_category(@product.product_category) %> -
- -<% button_bar do %> - <%= button :back, _("%s's products/services listing") % profile.name, :action => 'index', :id => nil %> -<% end %> - -