Commit 31868a1af19ba6c59d9c2fe104c7328a20772ab0

Authored by Braulio Bhavamitra
1 parent bb150fb8

Move products into a plugin

This will many further improvements to the products, such as:
- Interaction with other plugins such as suppliers, stock and
currencies. The inteface can change in such a way that if done
usings hotspots would hurt usability and coherence.
- More autonomy to develop and integrate other features
(in the next months we will integrate batch edition and stock,
currencies might come next)
- More declouping from core and other components
Showing 299 changed files with 8263 additions and 7886 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 299 files displayed.

app/controllers/admin/categories_controller.rb
... ... @@ -7,7 +7,6 @@ class CategoriesController < AdminController
7 7 def index
8 8 @categories = environment.categories.where("parent_id is null AND type is null")
9 9 @regions = environment.regions.where(:parent_id => nil)
10   - @product_categories = environment.product_categories.where(:parent_id => nil)
11 10 end
12 11  
13 12 def get_children
... ...
app/controllers/admin/edit_template_controller.rb
1 1 class EditTemplateController < AdminController
2   -
  2 +
3 3 protect 'edit_environment_design', :environment
4   -
  4 +
5 5 #FIXME
6 6 #design_editor :holder => 'environment', :autosave => true, :block_types => :block_types
7 7  
... ... @@ -9,7 +9,6 @@ class EditTemplateController &lt; AdminController
9 9 %w[
10 10 FavoriteLinks
11 11 ListBlock
12   - SellersSearchBlock
13 12 ]
14 13 end
15 14  
... ...
app/controllers/admin/environment_design_controller.rb
1 1 class EnvironmentDesignController < BoxOrganizerController
2   -
  2 +
3 3 protect 'edit_environment_design', :environment
4 4  
5 5 def available_blocks
6   - @available_blocks ||= [ ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ]
  6 + @available_blocks ||= [ ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ]
7 7 @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment)
8 8 end
9 9  
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -409,9 +409,6 @@ class CmsController &lt; MyProfileController
409 409 ]
410 410 articles += special_article_types if params && params[:cms]
411 411 parent_id = params ? params[:parent_id] : nil
412   - if profile.enterprise?
413   - articles << EnterpriseHomepage
414   - end
415 412 if @parent && @parent.blog?
416 413 articles -= Article.folder_types.map(&:constantize)
417 414 end
... ...
app/controllers/my_profile/manage_products_controller.rb
... ... @@ -1,229 +0,0 @@
1   -class ManageProductsController < ApplicationController
2   - needs_profile
3   -
4   - protect 'manage_products', :profile, :except => [:show]
5   - before_filter :check_environment_feature
6   - before_filter :login_required, :except => [:show]
7   - before_filter :create_product?, :only => [:new]
8   -
9   - protected
10   -
11   - def check_environment_feature
12   - unless profile.environment.enabled?('products_for_enterprises')
13   - render_not_found
14   - return
15   - end
16   - end
17   -
18   - def create_product?
19   - if !profile.create_product?
20   - render_access_denied
21   - return
22   - end
23   - end
24   -
25   - public
26   -
27   - def index
28   - @products = @profile.products.paginate(:per_page => 10, :page => params[:page])
29   - end
30   -
31   - def show
32   - @product = @profile.products.find(params[:id])
33   - @inputs = @product.inputs
34   - @allowed_user = user && user.has_permission?('manage_products', profile)
35   - end
36   -
37   - def categories_for_selection
38   - @category = environment.categories.find_by id: params[:category_id]
39   - @object_name = params[:object_name]
40   - if @category
41   - @categories = @category.children
42   - @level = @category.leaf? ? @category.level : @categories.first.level
43   - else
44   - @categories = ProductCategory.top_level_for(environment)
45   - @level = 0
46   - end
47   - render :partial => 'categories_for_selection', :locals => { :categories => @categories, :level => @level }
48   - end
49   -
50   - def new
51   - @no_design_blocks = true
52   - @category = params[:selected_category_id] ? Category.find(params[:selected_category_id]) : nil
53   - @product = @profile.products.build(:product_category => @category)
54   - @categories = ProductCategory.top_level_for(environment)
55   - @level = 0
56   - if request.post?
57   - if @product.save
58   - session[:notice] = _('Product succesfully created')
59   - render :partial => 'shared/redirect_via_javascript',
60   - :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
61   - else
62   - render_dialog_error_messages 'product'
63   - end
64   - end
65   - end
66   -
67   - def edit
68   - @product = @profile.products.find(params[:id])
69   - field = params[:field]
70   - if request.post?
71   - begin
72   - @product.update!(params[:product])
73   - render :partial => "display_#{field}", :locals => {:product => @product}
74   - rescue Exception => e
75   - render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true}
76   - end
77   - else
78   - render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false}
79   - end
80   - end
81   -
82   - def edit_category
83   - @product = @profile.products.find(params[:id])
84   - @category = @product.product_category || ProductCategory.first
85   - @categories = ProductCategory.top_level_for(environment)
86   - @edit = true
87   - @level = @category.level
88   - if request.post?
89   - if @product.update({:product_category_id => params[:selected_category_id]}, :without_protection => true)
90   - render :partial => 'shared/redirect_via_javascript',
91   - :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
92   - else
93   - render_dialog_error_messages 'product'
94   - end
95   - end
96   - end
97   -
98   - def show_category_tree
99   - @category = environment.categories.find params[:category_id]
100   - render :partial => 'selected_category_tree'
101   - end
102   -
103   - def search_categories
104   - @term = params[:term].downcase
105   - conditions = ['LOWER(name) LIKE ? OR LOWER(name) LIKE ?', "#{@term}%", "% #{@term}%"]
106   - @categories = ProductCategory.where(conditions).limit(10)
107   - render :json => (@categories.map do |category|
108   - {:label => category.name, :value => category.id}
109   - end)
110   - end
111   -
112   - def add_input
113   - @product = @profile.products.find(params[:id])
114   - @input = @product.inputs.build
115   - @categories = ProductCategory.top_level_for(environment)
116   - @level = 0
117   - if request.post?
118   - if @input.update(:product_category_id => params[:selected_category_id])
119   - @inputs = @product.inputs
120   - render :partial => 'display_inputs'
121   - else
122   - render_dialog_error_messages 'product'
123   - end
124   - else
125   - render :partial => 'add_input'
126   - end
127   - end
128   -
129   - def manage_product_details
130   - @product = @profile.products.find(params[:id])
131   - if request.post?
132   - @product.update_price_details(params[:price_details]) if params[:price_details]
133   - render :partial => 'display_price_details'
134   - else
135   - render :partial => 'manage_product_details'
136   - end
137   - end
138   -
139   - def remove_price_detail
140   - @product = @profile.products.find(params[:product])
141   - @price_detail = @product.price_details.find(params[:id])
142   - @product = @price_detail.product
143   - if request.post?
144   - @price_detail.destroy
145   - render :nothing => true
146   - end
147   - end
148   -
149   - def display_price_composition_bar
150   - @product = @profile.products.find(params[:id])
151   - render :partial => 'price_composition_bar'
152   - end
153   -
154   - def display_inputs_cost
155   - @product = @profile.products.find(params[:id])
156   - render :inline => "<%= float_to_currency(@product.inputs_cost) %>"
157   - end
158   -
159   - def destroy
160   - @product = @profile.products.find(params[:id])
161   - if @product.destroy
162   - session[:notice] = _('Product succesfully removed')
163   - redirect_back_or_default :action => 'index'
164   - else
165   - session[:notice] = _('Could not remove the product')
166   - redirect_back_or_default :action => 'show', :id => @product
167   - end
168   - end
169   -
170   - def edit_input
171   - if request.xhr?
172   - @input = @profile.inputs.find_by id: params[:id]
173   - if @input
174   - if request.post?
175   - if @input.update(params[:input])
176   - render :partial => 'display_input', :locals => {:input => @input}
177   - else
178   - render :partial => 'edit_input'
179   - end
180   - else
181   - render :partial => 'edit_input'
182   - end
183   - else
184   - render :text => _('The input was not found')
185   - end
186   - end
187   - end
188   -
189   - def order_inputs
190   - @product = @profile.products.find(params[:id])
191   - @product.order_inputs!(params[:input]) if params[:input]
192   - render :nothing => true
193   - end
194   -
195   - def remove_input
196   - @input = @profile.inputs.find(params[:id])
197   - @product = @input.product
198   - if request.post?
199   - if @input.destroy
200   - @inputs = @product.inputs
201   - render :partial => 'display_inputs'
202   - else
203   - render_dialog_error_messages 'input'
204   - end
205   - end
206   - end
207   -
208   - def certifiers_for_selection
209   - @qualifier = Qualifier.exists?(:id => params[:id]) ? Qualifier.find(params[:id]) : nil
210   - render :update do |page|
211   - page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection'
212   - end
213   - end
214   -
215   - def create_production_cost
216   - cost = @profile.production_costs.create(:name => params[:id])
217   - if cost.valid?
218   - cost.save
219   - render :text => {:name => cost.name,
220   - :id => cost.id,
221   - :ok => true
222   - }.to_json
223   - else
224   - render :text => {:ok => false,
225   - :error_msg => _(cost.errors['name'].join('\n')) % {:fn => _('Name')}
226   - }.to_json
227   - end
228   - end
229   -end
app/controllers/my_profile/profile_design_controller.rb
... ... @@ -45,17 +45,10 @@ class ProfileDesignController &lt; BoxOrganizerController
45 45 if profile.enterprise?
46 46 blocks << DisabledEnterpriseMessageBlock
47 47 blocks << HighlightsBlock
48   - blocks << ProductCategoriesBlock
49   - blocks << FeaturedProductsBlock
50 48 blocks << FansBlock
51 49 blocks += plugins.dispatch(:extra_blocks, :type => Enterprise)
52 50 end
53 51  
54   - # product block exclusive for enterprises in environments that permits it
55   - if profile.enterprise? && profile.environment.enabled?('products_for_enterprises')
56   - blocks << ProductsBlock
57   - end
58   -
59 52 # block exclusive to profiles that have blog
60 53 if profile.has_blog?
61 54 blocks << BlogArchivesBlock
... ...
app/controllers/public/catalog_controller.rb
... ... @@ -1,19 +0,0 @@
1   -class CatalogController < PublicController
2   - needs_profile
3   -
4   - before_filter :check_enterprise_and_environment
5   -
6   - def index
7   - extend CatalogHelper
8   - catalog_load_index
9   - end
10   -
11   - protected
12   -
13   - def check_enterprise_and_environment
14   - unless profile.enterprise? && @profile.environment.enabled?('products_for_enterprises')
15   - redirect_to :controller => 'profile', :profile => profile.identifier, :action => 'index'
16   - end
17   - end
18   -
19   -end
app/controllers/public/search_controller.rb
... ... @@ -52,7 +52,6 @@ class SearchController &lt; PublicController
52 52 [
53 53 [ :people, _('People'), :recent_people ],
54 54 [ :enterprises, _('Enterprises'), :recent_enterprises ],
55   - [ :products, _('Products'), :recent_products ],
56 55 [ :events, _('Upcoming events'), :upcoming_events ],
57 56 [ :communities, _('Communities'), :recent_communities ],
58 57 [ :articles, _('Contents'), :recent_articles ]
... ... @@ -78,16 +77,17 @@ class SearchController &lt; PublicController
78 77 full_text_search
79 78 end
80 79  
81   - def products
82   - @scope = @environment.products
83   - full_text_search
84   - end
85   -
86 80 def enterprises
87 81 @scope = visible_profiles(Enterprise)
88 82 full_text_search
89 83 end
90 84  
  85 + # keep URL compatibility
  86 + def products
  87 + return render_not_found unless defined? ProductsPlugin
  88 + redirect_to url_for(params.merge controller: 'products_plugin/search', action: :products)
  89 + end
  90 +
91 91 def communities
92 92 @scope = visible_profiles(Community)
93 93 full_text_search
... ... @@ -186,7 +186,6 @@ class SearchController &lt; PublicController
186 186 people: _('People'),
187 187 communities: _('Communities'),
188 188 enterprises: _('Enterprises'),
189   - products: _('Products and Services'),
190 189 events: _('Events'),
191 190 }
192 191 end
... ... @@ -260,12 +259,11 @@ class SearchController &lt; PublicController
260 259 end
261 260  
262 261 def available_assets
263   - assets = {
  262 + {
264 263 articles: _('Contents'),
265 264 enterprises: _('Enterprises'),
266 265 people: _('People'),
267 266 communities: _('Communities'),
268   - products: _('Products and Services'),
269 267 }
270 268 end
271 269  
... ...
app/helpers/action_tracker_helper.rb
... ... @@ -67,24 +67,6 @@ module ActionTrackerHelper
67 67 }
68 68 end
69 69  
70   - def create_product_description ta
71   - _('created the product %{title}') % {
72   - title: link_to(truncate(ta.get_name), ta.get_url),
73   - }
74   - end
75   -
76   - def update_product_description ta
77   - _('updated the product %{title}') % {
78   - title: link_to(truncate(ta.get_name), ta.get_url),
79   - }
80   - end
81   -
82   - def remove_product_description ta
83   - _('removed the product %{title}') % {
84   - title: truncate(ta.get_name),
85   - }
86   - end
87   -
88 70 def favorite_enterprise_description ta
89 71 _('favorited enterprise %{title}') % {
90 72 title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url),
... ...
app/helpers/application_helper.rb
... ... @@ -44,8 +44,6 @@ module ApplicationHelper
44 44  
45 45 include TokenHelper
46 46  
47   - include CatalogHelper
48   -
49 47 include PluginsHelper
50 48  
51 49 include ButtonsHelper
... ... @@ -787,7 +785,7 @@ module ApplicationHelper
787 785 return "" if categories.blank?
788 786 content_tag(:ul) do
789 787 categories.map do |category|
790   - category_path = category.kind_of?(ProductCategory) ? {:controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id} : { :controller => 'search', :action => 'category_index', :category_path => category.explode_path }
  788 + category_path = { :controller => 'search', :action => 'category_index', :category_path => category.explode_path }
791 789 if category.display_in_menu?
792 790 content_tag(:li) do
793 791 if !category.is_leaf_displayable_in_menu?
... ...
app/helpers/catalog_helper.rb
... ... @@ -1,45 +0,0 @@
1   -module CatalogHelper
2   -
3   - include DisplayHelper
4   - include ManageProductsHelper
5   -
6   - def catalog_load_index options = {:page => params[:page], :show_categories => true}
7   - if options[:show_categories]
8   - @category = params[:level] ? ProductCategory.find(params[:level]) : nil
9   - @categories = ProductCategory.on_level(params[:level]).order(:name)
10   - end
11   -
12   - @products = profile.products.from_category(@category).
13   - reorder('available desc, highlighted desc, name asc').
14   - paginate(:per_page => @profile.products_per_catalog_page, :page => options[:page])
15   - end
16   -
17   - def breadcrumb(category)
18   - start = link_to(_('Start'), {:controller => :catalog, :action => 'index'})
19   - ancestors = category.ancestors.map { |c| link_to(c.name, {:controller => :catalog, :action => 'index', :level => c.id}) }.reverse
20   - current_level = content_tag('strong', category.name)
21   - all_items = [start] + ancestors + [current_level]
22   - content_tag('div', safe_join(all_items, ' &rarr; '), :id => 'breadcrumb')
23   - end
24   -
25   - def category_link(category)
26   - count = profile.products.from_category(category).count
27   - name = truncate(category.name, :length => 22 - count.to_s.size)
28   - link = link_to(name, {:controller => 'catalog', :action => 'index', :level => category.id}, :title => category.name)
29   - content_tag('div', "#{link} <span class=\"count\">#{count}</span>".html_safe) if count > 0
30   - end
31   -
32   - def category_with_sub_list(category)
33   - content_tag 'li', "#{category_link(category)}\n#{sub_category_list(category)}".html_safe
34   - end
35   -
36   - def sub_category_list(category)
37   - sub_categories = []
38   - category.children.order(:name).each do |sub_category|
39   - cat_link = category_link sub_category
40   - sub_categories << content_tag('li', cat_link) unless cat_link.nil?
41   - end
42   - content_tag('ul', sub_categories.join.html_safe) if sub_categories.size > 0
43   - end
44   -
45   -end
app/helpers/categories_helper.rb
... ... @@ -2,7 +2,6 @@ module CategoriesHelper
2 2  
3 3 TYPES = [
4 4 [ _('General Category'), Category.to_s ],
5   - [ _('Product Category'), ProductCategory.to_s ],
6 5 [ _('Region'), Region.to_s ],
7 6 ]
8 7  
... ...
app/helpers/display_helper.rb
1 1 module DisplayHelper
2 2  
3   - def link_to_product(product, opts={})
4   - return _('No product') unless product
5   - target = product_path(product)
6   - link_to content_tag( 'span', product.name ),
7   - target,
8   - opts
9   - end
10   -
11 3 def themed_path(file)
12 4 if File.exists?(File.join(Rails.root, 'public', theme_path, file))
13 5 File.join(theme_path, file)
... ... @@ -16,42 +8,21 @@ module DisplayHelper
16 8 end
17 9 end
18 10  
19   - def image_link_to_product(product, opts={})
20   - return _('No product') unless product
21   - target = product_path(product)
22   - link_to image_tag(product.default_image(:big), :alt => product.name),
23   - target,
24   - opts
25   - end
26   -
27 11 def price_span(price, options = {})
28 12 content_tag 'span',
29 13 number_to_currency(price, :unit => environment.currency_unit, :delimiter => environment.currency_delimiter, :separator => environment.currency_separator),
30 14 options
31 15 end
32 16  
33   - def product_path(product)
34   - product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url
35   - end
36   -
37 17 def link_to_tag(tag, html_options = {})
38 18 link_to tag.name, {:controller => 'search', :action => 'tag', :tag => tag.name}, html_options
39 19 end
40 20  
41 21 def link_to_category(category, full = true, html_options = {})
42   - return _('Uncategorized product') unless category
43 22 name = full ? category.full_name(' &rarr; ') : category.name
44 23 link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }), html_options
45 24 end
46 25  
47   - def link_to_product_category(category)
48   - if category
49   - link_to(category.name, :controller => 'search', :action => 'products', :category_path => category.explode_path)
50   - else
51   - _('Uncategorized product')
52   - end
53   - end
54   -
55 26 def txt2html(txt)
56 27 ret = txt.strip.
57 28 gsub( /\s*\n\s*\n\s*/, "\r<p/>\r" ).
... ...
app/helpers/enterprise_homepage_helper.rb
... ... @@ -1,25 +0,0 @@
1   -module EnterpriseHomepageHelper
2   -
3   - def display_profile_info(profile)
4   - data = ''
5   - [
6   - [ _('Contact person:'), :contact_person ],
7   - [ _('e-Mail:'), :contact_email ],
8   - [ _('Phone(s):'), :contact_phone ],
9   - [ _('Location:'), :location ],
10   - [ _('Address:'), :address ],
11   - [ _('Economic activity:'), :economic_activity ]
12   - ].each { | name, att |
13   - if profile.send( att ) and not profile.send( att ).blank?
14   - data << content_tag( 'li', content_tag('strong', name) +' '+ profile.send( att ).to_s ) +"\n"
15   - end
16   - }
17   - if profile.respond_to?(:distance) and !profile.distance.nil?
18   - data << content_tag( 'li',
19   - content_tag('strong',_('Distance:')) +' '+
20   - "%.2f%" % profile.distance
21   - ) + "\n"
22   - end
23   - content_tag('div', content_tag('ul', data), :class => 'enterprise-info')
24   - end
25   -end
app/helpers/manage_products_helper.rb
... ... @@ -1,289 +0,0 @@
1   -# encoding: UTF-8
2   -
3   -module ManageProductsHelper
4   -
5   - def remote_function_to_update_categories_selection(container_id, options = {})
6   - remote_function({
7   - :update => container_id,
8   - :url => { :action => "categories_for_selection" },
9   - :loading => "loading('hierarchy_navigation', '#{ _('loading…') }'); loading('#{container_id}', '&nbsp;')",
10   - :complete => "loading_done('hierarchy_navigation'); loading_done('#{container_id}')"
11   - }.merge(options))
12   - end
13   -
14   - def hierarchy_category_item(category, make_links, title = nil)
15   - title ||= category.name
16   - if make_links
17   - link_to(title, '#',
18   - :title => title,
19   - :onclick => remote_function_to_update_categories_selection("categories_container_level#{ category.level + 1 }",
20   - :with => "'category_id=#{ category.id }'"
21   - )
22   - )
23   - else
24   - title
25   - end
26   - end
27   -
28   - def hierarchy_category_navigation(current_category, options = {})
29   - hierarchy = []
30   - if current_category
31   - hierarchy << current_category.name unless options[:hide_current_category]
32   - ancestors = current_category.ancestors
33   - ancestors.each do |category|
34   - hierarchy << hierarchy_category_item(category, options[:make_links])
35   - end
36   - end
37   - hierarchy.reverse.join(options[:separator] || ' &rarr; ')
38   - end
39   -
40   - def options_for_select_categories(categories, selected = nil)
41   - safe_join(categories.sort_by{ |cat|
42   - cat.name.transliterate}.map do |category|
43   - selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '')
44   - "<option value='#{category.id}' title='#{category.name}' #{selected_attribute}>#{category.name + (category.leaf? ? '': ' &raquo;')}</option>".html_safe
45   - end, "\n")
46   - end
47   -
48   - def build_selects_for_ancestors(ancestors, current_category)
49   - current_ancestor = ancestors.shift
50   - if current_ancestor.nil?
51   - select_for_new_category(current_category.children, current_category.level + 1)
52   - else
53   - content_tag('div',
54   - select_tag('category_id',
55   - options_for_select_categories(current_ancestor.siblings + [current_ancestor], current_ancestor),
56   - :size => 10,
57   - :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_ancestor.level + 1 }", :with => "'category_id=' + this.value")
58   - ) +
59   - build_selects_for_ancestors(ancestors, current_category),
60   - :class => 'categories_container',
61   - :id => "categories_container_level#{ current_ancestor.level }"
62   - )
63   - end
64   - end
65   -
66   - def selects_for_all_ancestors(current_category)
67   - build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category)
68   - end
69   -
70   - def select_for_new_category(categories, level)
71   - content_tag('div',
72   - render(:partial => 'categories_for_selection', :locals => { :categories => categories, :level => level }),
73   - :class => 'categories_container',
74   - :id => "categories_container_level#{ level }"
75   - )
76   - end
77   -
78   - def categories_container(categories_selection_html, hierarchy_html = '')
79   - content_tag 'div',
80   - safe_join(
81   - [
82   - render('categories_autocomplete'),
83   - hidden_field_tag('selected_category_id'),
84   - content_tag('div', hierarchy_html, :id => 'hierarchy_navigation'),
85   - content_tag('div', categories_selection_html, :id => 'categories_container_wrapper')
86   - ], ''),
87   - :id => 'categories-container'
88   - end
89   -
90   - def select_for_categories(categories, level = 0)
91   - if categories.empty?
92   - content_tag('div', '', :id => 'no_subcategories')
93   - else
94   - select_tag('category_id',
95   - options_for_select_categories(categories),
96   - :size => 10,
97   - :onchange => remote_function_to_update_categories_selection("categories_container_level#{ level + 1 }", :with => "'category_id=' + this.value")
98   - ) +
99   - content_tag('div', '', :class => 'categories_container', :id => "categories_container_level#{ level + 1 }")
100   - end
101   - end
102   -
103   - def edit_link(label, url, html_options = {})
104   - return '' unless (user && user.has_permission?('manage_products', profile))
105   - link_to(label, url, html_options)
106   - end
107   -
108   - def edit_product_link_to_remote(product, field, label, html_options = {})
109   - return '' unless (user && user.has_permission?('manage_products', profile))
110   - options = html_options.merge(:id => 'link-edit-product-' + field)
111   - options[:class] = options[:class] ? options[:class] + ' link-to-remote' : 'link-to-remote'
112   -
113   - link_to_remote(label,
114   - {:update => "product-#{field}",
115   - :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },
116   - :method => :get,
117   - :loading => "loading_for_button('#link-edit-product-#{field}')"},
118   - options)
119   - end
120   -
121   - def edit_button(type, label, url, html_options = {})
122   - return '' unless (user && user.has_permission?('manage_products', profile))
123   - button(type, label, url, html_options)
124   - end
125   -
126   - def edit_product_button_to_remote(product, field, label, html_options = {})
127   - the_class = 'button with-text icon-edit'
128   - if html_options.has_key?(:class)
129   - the_class << ' ' << html_options[:class]
130   - end
131   - edit_product_link_to_remote(product, field, label, html_options.merge(:class => the_class))
132   - end
133   -
134   - def edit_ui_button(label, url, html_options = {})
135   - return '' unless (user && user.has_permission?('manage_products', profile))
136   - ui_button(label, url, html_options)
137   - end
138   -
139   - def edit_product_ui_button_to_remote(product, field, label, html_options = {})
140   - return '' unless (user && user.has_permission?('manage_products', profile))
141   - id = 'edit-product-remote-button-ui-' + field
142   - options = html_options.merge(:id => id)
143   -
144   - ui_button_to_remote(label,
145   - {:update => "product-#{field}",
146   - :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },
147   - :complete => "jQuery('#edit-product-button-ui-#{field}').hide()",
148   - :method => :get,
149   - :loading => "loading_for_button('##{id}')"},
150   - options)
151   - end
152   -
153   - def cancel_edit_product_link(product, field, html_options = {})
154   - return '' unless (user && user.has_permission?('manage_products', profile))
155   - button_to_function(:cancel, _('Cancel'), nil, html_options) do |page|
156   - page.replace_html "product-#{field}", CGI::escapeHTML(render :partial => "display_#{field}", :locals => {:product => product})
157   - end
158   - end
159   -
160   - def edit_product_category_link(product, html_options = {})
161   - return '' unless (user && user.has_permission?('manage_products', profile))
162   - options = html_options.merge(:id => 'link-edit-product-category')
163   - link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options)
164   - end
165   -
166   - def display_value(product)
167   - price = product.price
168   - return '' if price.blank? || price.zero?
169   - discount = product.discount
170   - if discount.blank? || discount.zero?
171   - result = display_price(_('Price: '), price)
172   - else
173   - result = display_price_with_discount(price, product.price_with_discount)
174   - end
175   - content_tag('span', content_tag('span', result, :class => 'product-price'), :class => "#{product.available? ? '' : 'un'}available-product")
176   - end
177   -
178   - def display_availability(product)
179   - if !product.available?
180   - ui_highlight(_('Product not available!'))
181   - end
182   - end
183   -
184   - def display_price(label, price)
185   - content_tag('span', label, :class => 'field-name') +
186   - content_tag('span', float_to_currency(price), :class => 'field-value')
187   - end
188   -
189   - def display_price_with_discount(price, price_with_discount)
190   - original_value = content_tag('span', display_price(_('List price: '), price), :class => 'list-price')
191   - discount_value = content_tag('span', display_price(_('On sale: '), price_with_discount), :class => 'on-sale-price')
192   - original_value + tag('br') + discount_value
193   - end
194   -
195   - def display_qualifiers(product)
196   - data = ''
197   - product.product_qualifiers.each do |pq|
198   - certified_by = ''
199   - certifier = pq.certifier
200   - if certifier
201   - certifier_name = certifier.link.blank? ? certifier.name : link_to(certifier.name, certifier.link)
202   - certified_by = _('certified by %s') % certifier_name
203   - else
204   - certified_by = _('(Self declared)')
205   - end
206   - data << content_tag('li', "✔ #{pq.qualifier.name} #{certified_by}", :class => 'product-qualifiers-item')
207   - end
208   - content_tag('ul', data, :id => 'product-qualifiers')
209   - end
210   -
211   - def qualifiers_for_select
212   - [[_('Select...'), nil]] + environment.qualifiers.sort.map{ |c| [c.name, c.id] }
213   - end
214   - def certifiers_for_select(qualifier)
215   - [[_('Self declared'), nil]] + qualifier.certifiers.sort.map{ |c| [c.name, c.id] }
216   - end
217   - def select_qualifiers(product, selected = nil)
218   - select_tag('selected_qualifier', options_for_select(qualifiers_for_select, selected),
219   - :onchange => remote_function(
220   - :url => {:action => 'certifiers_for_selection'},
221   - :with => "'id=' + value + '&certifier_area=' + jQuery(this).parent().next().attr('id')",
222   - :before => "small_loading(jQuery(this).parent().next().attr('id'), '&nbsp;')"
223   - ),
224   - :id => nil
225   - )
226   - end
227   - def select_certifiers(qualifier, product = nil)
228   - if qualifier
229   - selected = product ? product.product_qualifiers.find_by(qualifier_id: qualifier.id).certifier_id : nil
230   - select_tag("product[qualifiers_list][#{qualifier.id}]", options_for_select(certifiers_for_select(qualifier), selected))
231   - else
232   - select_tag("product[qualifiers_list][nil]")
233   - end
234   - end
235   -
236   - def remove_qualifier_button
237   - button_to_function(:delete, content_tag('span', _('Delete qualifier')), "jQuery(this).parents('tr').remove()")
238   - end
239   -
240   - def select_unit(object)
241   - collection_select(object.class.name.downcase, :unit_id, environment.units, :id, :singular, {:include_blank => _('Select the unit')})
242   - end
243   -
244   - def input_icon(input)
245   - if input.is_from_solidarity_economy?
246   - hint = _('Product from solidarity economy')
247   - image_tag("/images/solidarity-economy.png", :class => 'solidatiry-economy-icon', :alt => hint, :title => hint)
248   - end
249   - end
250   -
251   - def display_price_by(unit)
252   - selected_unit = content_tag('span', unit, :class => 'selected-unit')
253   - content_tag('span', _('by') + ' ' + selected_unit, :class => 'price-by-unit')
254   - end
255   -
256   - def label_amount_used(input)
257   - product_unit = input.product.unit
258   - if product_unit.blank?
259   - _('Amount used in this product or service')
260   - else
261   - _('Amount used by %s of this product or service') % product_unit.singular.downcase
262   - end
263   - end
264   -
265   - def display_unit(input)
266   - input_amount_used = content_tag('span', input.formatted_amount, :class => 'input-amount-used')
267   - return input_amount_used if input.unit.blank?
268   - n_('1 %{singular_unit}', '%{num} %{plural_unit}', input.amount_used.to_f) % { :num => input_amount_used, :singular_unit => content_tag('span', input.unit.singular, :class => 'input-unit'), :plural_unit => content_tag('span', input.unit.plural, :class => 'input-unit') }
269   - end
270   -
271   - def select_production_cost(product,selected=nil)
272   - url = url_for( :controller => 'manage_products', :action => 'create_production_cost' )
273   - prompt_msg = _('Insert the name of the new cost:')
274   - error_msg = _('Something went wrong. Please, try again')
275   - select_tag('price_details[][production_cost_id]',
276   - '<option value="" disabled="disabled">' + _('Select...') + '</option>' +
277   - options_for_select(product.available_production_costs.map {|item| [truncate(item.name, {:length => 10, :omission => '...'}), item.id]} + [[_('Other cost'), '']], selected),
278   - {:class => 'production-cost-selection',
279   - :onchange => "productionCostTypeChange(this, '#{url}', '#{prompt_msg}', '#{error_msg}')"})
280   - end
281   -
282   - def price_composition_progressbar_text(product, args = {})
283   - currency = environment.currency_unit
284   - production_cost = args[:production_cost_value] || product.formatted_value(:total_production_cost)
285   - product_price = args[:product_price] || product.formatted_value(:price)
286   -
287   - _("%{currency} %{production_cost} of %{currency} %{product_price}") % {:currency => currency, :production_cost => content_tag('span', production_cost, :class => 'production_cost'), :product_price => content_tag('span', product_price, :class => 'product_price')}
288   - end
289   -end
app/helpers/profile_image_helper.rb
... ... @@ -82,7 +82,6 @@ module ProfileImageHelper
82 82 ]
83 83 elsif profile.kind_of?(Enterprise)
84 84 [
85   - {_('Products') => {:href => catalog_path(profile.identifier)}},
86 85 {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}},
87 86 {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}},
88 87 {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}},
... ...
app/models/article.rb
... ... @@ -604,7 +604,7 @@ class Article &lt; ApplicationRecord
604 604 end
605 605  
606 606 def accept_category?(cat)
607   - !cat.is_a?(ProductCategory)
  607 + true
608 608 end
609 609  
610 610 def public?
... ...
app/models/box.rb
... ... @@ -41,7 +41,6 @@ class Box &lt; ApplicationRecord
41 41 ProfileImageBlock,
42 42 RawHTMLBlock,
43 43 RecentDocumentsBlock,
44   - SellersSearchBlock,
45 44 TagsBlock ]
46 45 end
47 46  
... ... @@ -54,21 +53,17 @@ class Box &lt; ApplicationRecord
54 53 EnterprisesBlock,
55 54 FansBlock,
56 55 FavoriteEnterprisesBlock,
57   - FeaturedProductsBlock,
58 56 FeedReaderBlock,
59 57 HighlightsBlock,
60 58 LinkListBlock,
61 59 LocationBlock,
62 60 LoginBlock,
63 61 MyNetworkBlock,
64   - ProductsBlock,
65   - ProductCategoriesBlock,
66 62 ProfileImageBlock,
67 63 ProfileInfoBlock,
68 64 ProfileSearchBlock,
69 65 RawHTMLBlock,
70 66 RecentDocumentsBlock,
71   - SellersSearchBlock,
72 67 SlideshowBlock,
73 68 TagsBlock
74 69 ]
... ...
app/models/category.rb
... ... @@ -35,8 +35,6 @@ class Category &lt; ApplicationRecord
35 35 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
36 36 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community'
37 37  
38   - has_many :products, :through => :enterprises
39   -
40 38 acts_as_having_image
41 39  
42 40 before_save :normalize_display_color
... ... @@ -64,10 +62,6 @@ class Category &lt; ApplicationRecord
64 62 self.communities.reorder('created_at DESC, id DESC').paginate(page: 1, per_page: limit)
65 63 end
66 64  
67   - def recent_products(limit = 10)
68   - self.products.reorder('created_at DESC, id DESC').paginate(page: 1, per_page: limit)
69   - end
70   -
71 65 def recent_articles(limit = 10)
72 66 self.articles.recent(limit)
73 67 end
... ...
app/models/certifier.rb
... ... @@ -1,35 +0,0 @@
1   -class Certifier < ApplicationRecord
2   -
3   - attr_accessible :name, :environment
4   -
5   - SEARCHABLE_FIELDS = {
6   - :name => {:label => _('Name'), :weight => 10},
7   - :description => {:label => _('Description'), :weight => 3},
8   - :link => {:label => _('Link'), :weight => 1},
9   - }
10   -
11   - belongs_to :environment
12   -
13   - has_many :qualifier_certifiers, :dependent => :destroy
14   - has_many :qualifiers, :through => :qualifier_certifiers
15   -
16   - has_many :product_qualifiers
17   - has_many :products, :through => :product_qualifiers, :source => :product
18   -
19   - validates_presence_of :environment_id
20   - validates_presence_of :name
21   -
22   - def destroy
23   - product_qualifiers.each { |pq| pq.update! :certifier => nil }
24   - super
25   - end
26   -
27   - def link
28   - self[:link] || ''
29   - end
30   -
31   - def <=>(b)
32   - self.name.downcase.transliterate <=> b.name.downcase.transliterate
33   - end
34   -
35   -end
app/models/enterprise.rb
1   -# An enterprise is a kind of organization. According to the system concept,
2   -# only enterprises can offer products and services.
3 1 class Enterprise < Organization
4 2  
5   - attr_accessible :business_name, :address_reference, :district, :tag_list, :organization_website, :historic_and_current_context, :activities_short_description, :products_per_catalog_page
  3 + attr_accessible :business_name, :address_reference, :district, :tag_list,
  4 + :organization_website, :historic_and_current_context, :activities_short_description
6 5  
7 6 SEARCH_FILTERS = {
8 7 :order => %w[more_recent more_popular more_active],
... ... @@ -17,11 +16,6 @@ class Enterprise &lt; Organization
17 16  
18 17 acts_as_trackable after_add: proc{ |p, t| notify_activity t }
19 18  
20   - has_many :products, :foreign_key => :profile_id, :dependent => :destroy
21   - has_many :product_categories, :through => :products
22   - has_many :inputs, :through => :products
23   - has_many :production_costs, :as => :owner
24   -
25 19 has_many :favorite_enterprise_people
26 20 has_many :fans, source: :person, through: :favorite_enterprise_people
27 21  
... ... @@ -29,10 +23,6 @@ class Enterprise &lt; Organization
29 23  
30 24 settings_items :organization_website, :historic_and_current_context, :activities_short_description
31 25  
32   - settings_items :products_per_catalog_page, :type => :integer, :default => 6
33   - alias_method :products_per_catalog_page_before_type_cast, :products_per_catalog_page
34   - validates_numericality_of :products_per_catalog_page, :allow_nil => true, :greater_than => 0
35   -
36 26 extend SetProfileRegionFromCityState::ClassMethods
37 27 set_profile_region_from_city_state
38 28  
... ... @@ -66,10 +56,6 @@ class Enterprise &lt; Organization