Commit 50def8451caa76a3e69203359c069c08c6585038

Authored by Rafael Martins
2 parents 3ebe0593 74a91f4b

Merge remote branch 'noosfero-bsc/new-search-merge'

Conflicts:
	app/controllers/application.rb
	app/controllers/public/browse_controller.rb
	app/helpers/application_helper.rb
	app/helpers/display_helper.rb
	app/models/environment_finder.rb
	app/models/product.rb
	app/views/layouts/_javascript.rhtml
	app/views/layouts/application-ng.rhtml
	public/javascripts/application.js
	public/stylesheets/application.css
	test/functional/browse_controller_test.rb
	test/functional/search_controller_test.rb
	vendor/plugins/acts_as_solr_reloaded/lib/tasks/solr.rake
Showing 129 changed files with 7962 additions and 5141 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 129 files displayed.

app/controllers/application.rb
1 1 require 'application_controller'
  2 +
  3 +# his is the application's main controller. Features defined here are
  4 +# available in all controllers.
  5 +class ApplicationController < ActionController::Base
  6 +
  7 + before_filter :change_pg_schema
  8 +
  9 + include ApplicationHelper
  10 + layout :get_layout
  11 + def get_layout
  12 + theme_option(:layout) || 'application'
  13 + end
  14 +
  15 + filter_parameter_logging :password
  16 +
  17 + def log_processing
  18 + super
  19 + return unless ENV['RAILS_ENV'] == 'production'
  20 + if logger && logger.info?
  21 + logger.info(" HTTP Referer: #{request.referer}")
  22 + logger.info(" User Agent: #{request.user_agent}")
  23 + logger.info(" Accept-Language: #{request.headers['HTTP_ACCEPT_LANGUAGE']}")
  24 + end
  25 + end
  26 +
  27 + helper :document
  28 + helper :language
  29 +
  30 + def self.no_design_blocks
  31 + @no_design_blocks = true
  32 + end
  33 + def self.uses_design_blocks?
  34 + !@no_design_blocks
  35 + end
  36 + def uses_design_blocks?
  37 + !@no_design_blocks && self.class.uses_design_blocks?
  38 + end
  39 +
  40 + # Be sure to include AuthenticationSystem in Application Controller instead
  41 + include AuthenticatedSystem
  42 + include PermissionCheck
  43 +
  44 + def self.require_ssl(*options)
  45 + before_filter :check_ssl, *options
  46 + end
  47 + def check_ssl
  48 + return true if (request.ssl? || ENV['RAILS_ENV'] == 'development')
  49 + redirect_to_ssl
  50 + end
  51 + def redirect_to_ssl
  52 + if environment.enable_ssl
  53 + redirect_to(params.merge(:protocol => 'https://', :host => ssl_hostname))
  54 + true
  55 + else
  56 + false
  57 + end
  58 + end
  59 +
  60 + def self.refuse_ssl(*options)
  61 + before_filter :avoid_ssl, *options
  62 + end
  63 + def avoid_ssl
  64 + if (!request.ssl? || ENV['RAILS_ENV'] == 'development')
  65 + true
  66 + else
  67 + redirect_to(params.merge(:protocol => 'http://'))
  68 + false
  69 + end
  70 + end
  71 +
  72 + before_filter :set_locale
  73 + def set_locale
  74 + FastGettext.available_locales = Noosfero.available_locales
  75 + FastGettext.default_locale = Noosfero.default_locale
  76 + FastGettext.set_locale(params[:lang] || session[:lang] || Noosfero.default_locale || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
  77 + if params[:lang]
  78 + session[:lang] = params[:lang]
  79 + end
  80 + end
  81 +
  82 + include NeedsProfile
  83 +
  84 + before_filter :detect_stuff_by_domain
  85 + before_filter :init_noosfero_plugins
  86 + attr_reader :environment
  87 +
  88 + before_filter :load_terminology
  89 +
  90 + # declares that the given <tt>actions</tt> cannot be accessed by other HTTP
  91 + # method besides POST.
  92 + def self.post_only(actions, redirect = { :action => 'index'})
  93 + verify :method => :post, :only => actions, :redirect_to => redirect
  94 + end
  95 +
  96 + helper_method :current_person, :current_person
  97 +
  98 + def change_pg_schema
  99 + if Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?
  100 + Noosfero::MultiTenancy.db_by_host = request.host
  101 + end
  102 + end
  103 +
  104 + protected
  105 +
  106 + def boxes_editor?
  107 + false
  108 + end
  109 +
  110 + def content_editor?
  111 + false
  112 + end
  113 +
  114 + def user
  115 + current_user.person if logged_in?
  116 + end
  117 +
  118 + alias :current_person :user
  119 +
  120 + # TODO: move this logic somewhere else (Domain class?)
  121 + def detect_stuff_by_domain
  122 + @domain = Domain.find_by_name(request.host)
  123 + if @domain.nil?
  124 + @environment = Environment.default
  125 + else
  126 + @environment = @domain.environment
  127 + @profile = @domain.profile
  128 + end
  129 + end
  130 +
  131 + def init_noosfero_plugins
  132 + @plugins = Noosfero::Plugin::Manager.new(self)
  133 + @plugins.enabled_plugins.map(&:class).each do |plugin|
  134 + prepend_view_path(plugin.view_path)
  135 + end
  136 + init_noosfero_plugins_controller_filters
  137 + end
  138 +
  139 + # This is a generic method that initialize any possible filter defined by a
  140 + # plugin to the current controller being initialized.
  141 + def init_noosfero_plugins_controller_filters
  142 + @plugins.enabled_plugins.each do |plugin|
  143 + plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter|
  144 + self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {}))
  145 + self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block])
  146 + end
  147 + end
  148 + end
  149 +
  150 + def load_terminology
  151 + # cache terminology for performance
  152 + @@terminology_cache ||= {}
  153 + @@terminology_cache[environment.id] ||= environment.terminology
  154 + Noosfero.terminology = @@terminology_cache[environment.id]
  155 + end
  156 +
  157 + def render_not_found(path = nil)
  158 + @no_design_blocks = true
  159 + @path ||= request.path
  160 + render :template => 'shared/not_found.rhtml', :status => 404, :layout => get_layout
  161 + end
  162 + alias :render_404 :render_not_found
  163 +
  164 + def render_access_denied(message = nil, title = nil)
  165 + @no_design_blocks = true
  166 + @message = message
  167 + @title = title
  168 + render :template => 'shared/access_denied.rhtml', :status => 403
  169 + end
  170 +
  171 +end
... ...
app/controllers/public/browse_controller.rb
... ... @@ -1,79 +0,0 @@
1   -class BrowseController < PublicController
2   -
3   - no_design_blocks
4   -
5   - FILTERS = %w(
6   - more_recent
7   - more_active
8   - more_popular
9   - more_comments
10   - more_views
11   - )
12   -
13   - def per_page
14   - 27
15   - end
16   -
17   - def people
18   - @filter = filter
19   - @title = self.filter_description(params[:action] + '_' + @filter )
20   -
21   - @results = @environment.people.visible.send(@filter)
22   -
23   - if !params[:query].blank?
24   - @results = @results.find_by_contents(params[:query], {:per_page => per_page, :page => params[:page]})[:results]
25   - else
26   - @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
27   - end
28   - end
29   -
30   - def communities
31   - @filter = filter
32   - @title = self.filter_description(params[:action] + '_' + @filter )
33   -
34   - @results = @environment.communities.visible.send(@filter)
35   -
36   - if !params[:query].blank?
37   - @results = @results.find_by_contents(params[:query], {:per_page => per_page, :page => params[:page]})[:results]
38   - else
39   - @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
40   - end
41   - end
42   -
43   - def contents
44   - @filter = filter
45   - @title = self.filter_description(params[:action] + '_' + @filter )
46   -
47   - @results = @environment.articles.published.text_articles.send(@filter)
48   -
49   - if !params[:query].blank?
50   - @results = @results.find_by_contents(params[:query])[:results]
51   - end
52   - @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
53   - end
54   -
55   - protected
56   -
57   - def filter
58   - if FILTERS.include?(params[:filter])
59   - params[:filter]
60   - else
61   - 'more_recent'
62   - end
63   - end
64   -
65   - def filter_description(str)
66   - {
67   - 'people_more_recent' => _('More recent people'),
68   - 'people_more_active' => _('More active people'),
69   - 'people_more_popular' => _('More popular people'),
70   - 'communities_more_recent' => _('More recent communities'),
71   - 'communities_more_active' => _('More active communities'),
72   - 'communities_more_popular' => _('More popular communities'),
73   - 'contents_more_recent' => _('More recent contents'),
74   - 'contents_more_views' => _('Most viewed contents'),
75   - 'contents_more_comments' => _('Most commented contents'),
76   - }[str] || str
77   - end
78   -
79   -end
app/controllers/public/map_balloon_controller.rb 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +class MapBalloonController < PublicController
  2 +
  3 + before_filter :profile, :only => [:person, :enterprise, :community]
  4 +
  5 + def product
  6 + @product = Product.find(params[:id])
  7 + render :action => 'product', :layout => false
  8 + end
  9 +
  10 + def person
  11 + render :action => 'profile', :layout => false
  12 + end
  13 +
  14 + def enterprise
  15 + render :action => 'profile', :layout => false
  16 + end
  17 +
  18 + def community
  19 + render :action => 'profile', :layout => false
  20 + end
  21 +
  22 + protected
  23 +
  24 + def profile
  25 + @profile = Profile.find(params[:id])
  26 + end
  27 +
  28 +end
... ...
app/controllers/public/search_controller.rb
1 1 class SearchController < PublicController
2 2  
  3 + MAP_SEARCH_LIMIT = 2000
  4 + LIST_SEARCH_LIMIT = 20
  5 + BLOCKS_SEARCH_LIMIT = 18
  6 + MULTIPLE_SEARCH_LIMIT = 8
  7 +
3 8 helper TagsHelper
  9 + include SearchHelper
  10 + include ActionView::Helpers::NumberHelper
4 11  
5 12 before_filter :load_category
6   - before_filter :prepare_filter
7   - before_filter :check_search_whole_site
8 13 before_filter :load_search_assets
9   - before_filter :check_valid_assets, :only => [ :assets ]
  14 + before_filter :load_query
10 15  
11 16 no_design_blocks
12 17  
13   - protected
  18 + def facets_browse
  19 + @asset = params[:asset]
  20 + @asset_class = asset_class(@asset)
14 21  
15   - def load_search_assets
16   - @search_in = where_to_search
17   - @searching = {}
18   - @search_in.each do |key, name|
19   - @searching[key] = (params[:asset].blank? && (params[:find_in].nil? || params[:find_in].empty? || params[:find_in].include?(key.to_s))) || (params[:asset] == key.to_s)
  22 + @facets_only = true
  23 + send(@asset)
  24 +
  25 + @facet = @asset_class.map_facets_for(environment).find { |facet| facet[:id] == params[:facet_id] }
  26 + raise 'Facet not found' if @facet.nil?
  27 +
  28 + render :layout => false
  29 + end
  30 +
  31 + def articles
  32 + @filter = params[:filter] ? filter : nil
  33 + @filter_title = params[:filter] ? filter_description(@asset, @filter) : nil
  34 + if !@empty_query
  35 + full_text_search ['public:true']
  36 + elsif params[:filter]
  37 + @results[@asset] = @environment.articles.public.send(@filter).paginate(paginate_options)
20 38 end
21 39 end
22 40  
23   - def prepare_filter
24   - if @category
25   - @noosfero_finder = CategoryFinder.new(@category)
  41 + def contents
  42 + redirect_to params.merge(:action => :articles)
  43 + end
  44 +
  45 + def people
  46 + if !@empty_query
  47 + full_text_search ['public:true']
26 48 else
27   - @noosfero_finder = EnvironmentFinder.new(@environment)
  49 + @results[@asset] = @environment.people.visible.send(@filter).paginate(paginate_options)
  50 + @facets = {}
  51 + end
  52 + end
  53 +
  54 + def products
  55 + if !@empty_query
  56 + full_text_search ['public:true']
28 57 end
29 58 end
30 59  
31   - def check_search_whole_site
32   - if params[:search_whole_site_yes] or params[:search_whole_site] == 'yes'
33   - redirect_to params.merge(:category_path => [], :search_whole_site => nil, :search_whole_site_yes => nil)
  60 + def enterprises
  61 + if !@empty_query
  62 + full_text_search ['public:true']
  63 + else
  64 + @filter_title = _('Enterprises from network')
  65 + @results[@asset] = @environment.enterprises.visible.paginate(paginate_options)
34 66 end
35 67 end
36 68  
37   - def check_valid_assets
38   - @asset = params[:asset].to_sym
39   - if !where_to_search.map(&:first).include?(@asset)
40   - render :text => 'go away', :status => 403
41   - return
  69 + def communities
  70 + if !@empty_query
  71 + full_text_search ['public:true']
  72 + else
  73 + @results[@asset] = @environment.communities.visible.send(@filter).paginate(paginate_options)
42 74 end
43 75 end
44 76  
45 77 def events
46 78 @category_id = @category ? @category.id : nil
47 79  
  80 + if params[:year] || params[:month]
  81 + date = Date.new(year.to_i, month.to_i, 1)
  82 + date_range = (date - 1.month)..(date + 1.month).at_end_of_month
  83 + end
  84 +
  85 + if @query.blank?
  86 + # Ignore pagination for asset events
  87 + if date_range
  88 + @results[@asset] = Event.send('find', :all,
  89 + :conditions => [
  90 + 'start_date BETWEEN :start_day AND :end_day OR end_date BETWEEN :start_day AND :end_day',
  91 + {:start_day => date_range.first, :end_day => date_range.last}
  92 + ])
  93 + else
  94 + @results[@asset] = Event.send('find', :all)
  95 + end
  96 + else
  97 + full_text_search
  98 + end
  99 +
48 100 @selected_day = nil
49 101 @events_of_the_day = []
50 102 date = build_date(params[:year], params[:month], params[:day])
... ... @@ -58,118 +110,27 @@ class SearchController &lt; PublicController
58 110 end
59 111 end
60 112  
61   - events = @results[:events]
62   -
  113 + events = @results[@asset]
63 114 @calendar = populate_calendar(date, events)
64 115 @previous_calendar = populate_calendar(date - 1.month, events)
65 116 @next_calendar = populate_calendar(date + 1.month, events)
66 117 end
67 118  
68   - def people
69   - #nothing, just to enable
70   - end
71   - def enterprises
72   - load_product_categories_menu(:enterprises)
73   - @categories_menu = true
74   - end
75   - def communities
76   - #nothing, just to enable
77   - end
78   - def articles
79   - #nothins, just to enable
80   - end
81   -
82   - def products
83   - load_product_categories_menu(:products)
84   - @categories_menu = true
85   - end
86   -
87   - def load_product_categories_menu(asset)
88   - @results[asset].uniq!
89   - # REFACTOR DUPLICATED CODE inner loop doing the same thing that outter loop
90   -
91   - if !@query.blank? || @region && !params[:radius].blank?
92   - ret = @noosfero_finder.find(asset, @query, calculate_find_options(asset, nil, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]).merge({:limit => :all}))
93   - @result_ids = ret.is_a?(Hash) ? ret[:results] : ret
94   - end
95   -
96   - end
97   -
98   - def calculate_find_options(asset, limit, page, product_category, region, radius, year, month)
99   - result = { :product_category => product_category, :per_page => limit, :page => page }
100   - if [:enterprises, :people, :products].include?(asset) && region
101   - result.merge!(:within => radius, :region => region.id)
102   - end
103   -
104   - if month || year
105   - date = Date.new(year.to_i, month.to_i, 1)
106   - result[:date_range] = (date - 1.month)..(date + 1.month).at_end_of_month
107   - end
108   -
109   - result
110   - end
111   -
112   - # limit the number of results per page
113   - # TODO: dont hardcore like this
114   - def limit
115   - searching = @searching.values.select{|v|v}
116   - if params[:display] == 'map'
117   - 2000
118   - else
119   - (searching.size == 1) ? 20 : 6
120   - end
121   - end
122   -
123   - public
124   -
125   - include SearchHelper
126   -
127   - ######################################################
128   -
129   - def where_to_search
130   - [
131   - [ :articles, N_('Articles') ],
132   - [ :enterprises, N_('Enterprises') ],
133   - [ :people, N_('People') ],
134   - [ :communities, N_('Communities') ],
135   - [ :products, N_('Products') ],
136   - [ :events, N_('Events') ]
137   - ].select {|key, name| !environment.enabled?('disable_asset_' + key.to_s) }
138   - end
139   -
140   - def cities
141   - @cities = City.find(:all, :order => 'name', :conditions => ['parent_id = ? and lat is not null and lng is not null', params[:state_id]])
142   - render :action => 'cities', :layout => false
143   - end
144   -
145   - def complete_region
146   - # FIXME this logic should be in the model
147   - @regions = Region.find(:all, :conditions => [ '(name like ? or name like ?) and lat is not null and lng is not null', '%' + params[:region][:name] + '%', '%' + params[:region][:name].capitalize + '%' ])
148   - render :action => 'complete_region', :layout => false
149   - end
150   -
151 119 def index
152   - @query = params[:query] || ''
153   - @product_category = ProductCategory.find(params[:product_category]) if params[:product_category]
154   -
155   - @region = City.find_by_id(params[:city]) if !params[:city].blank? && params[:city] =~ /^\d+$/
156   -
157   - # how many assets we are searching for?
158   - number_of_result_assets = @searching.values.select{|v| v}.size
159   -
160 120 @results = {}
161   - @facets = {}
162 121 @order = []
163 122 @names = {}
  123 + @results_only = true
164 124  
165   - where_to_search.select { |key,description| @searching[key] }.each do |key, description|
  125 + @enabled_searchs.select { |key,description| @searching[key] }.each do |key, description|
  126 + load_query
  127 + @asset = key
  128 + send(key)
166 129 @order << key
167   - find_options = calculate_find_options(key, limit, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]);
168   - ret = @noosfero_finder.find(key, @query, find_options)
169   - @results[key] = ret.is_a?(Hash) ? ret[:results] : ret
170   - @facets[key] = ret.is_a?(Hash) ? ret[:facets] : {}
171 130 @names[key] = getterm(description)
172 131 end
  132 + @asset = nil
  133 + @facets = {}
173 134  
174 135 if @results.keys.size == 1
175 136 specific_action = @results.keys.first
... ... @@ -180,34 +141,32 @@ class SearchController &lt; PublicController
180 141 return
181 142 end
182 143 end
183   -
184   - render :action => 'index'
185 144 end
186 145  
187   - alias :assets :index
188   -
189   - #######################################################
  146 + def assets
  147 + params[:action] = params[:asset].is_a?(Array) ? :index : params.delete(:asset)
  148 + redirect_to params
  149 + end
190 150  
191 151 # view the summary of one category
192 152 def category_index
193 153 @results = {}
194 154 @order = []
195 155 @names = {}
  156 + limit = MULTIPLE_SEARCH_LIMIT
196 157 [
197   - [ :people, _('People'), @noosfero_finder.recent('people', limit) ],
198   - [ :enterprises, __('Enterprises'), @noosfero_finder.recent('enterprises', limit) ],
199   - [ :products, _('Products'), @noosfero_finder.recent('products', limit) ],
200   - [ :events, _('Upcoming events'), @noosfero_finder.upcoming_events({:per_page => limit}) ],
201   - [ :communities, __('Communities'), @noosfero_finder.recent('communities', limit) ],
202   - [ :most_commented_articles, _('Most commented articles'), @noosfero_finder.most_commented_articles(limit) ],
203   - [ :articles, _('Articles'), @noosfero_finder.recent('text_articles', limit) ]
204   - ].each do |key, name, list|
205   - @order << key
206   - @results[key] = list
207   - @names[key] = name
  158 + [ :people, _('People'), :recent_people ],
  159 + [ :enterprises, _('Enterprises'), :recent_enterprises ],
  160 + [ :products, _('Products'), :recent_products ],
  161 + [ :events, _('Upcoming events'), :upcoming_events ],
  162 + [ :communities, _('Communities'), :recent_communities ],
  163 + [ :articles, _('Contents'), :recent_articles ]
  164 + ].each do |asset, name, filter|
  165 + @order << asset
  166 + @results[asset] = @category.send(filter, limit)
  167 + @names[asset] = name
208 168 end
209 169 end
210   - attr_reader :category
211 170  
212 171 def tags
213 172 @tags_cache_key = "tags_env_#{environment.id.to_s}"
... ... @@ -224,21 +183,123 @@ class SearchController &lt; PublicController
224 183 end
225 184 end
226 185  
  186 + def events_by_day
  187 + @selected_day = build_date(params[:year], params[:month], params[:day])
  188 + @events_of_the_day = environment.events.by_day(@selected_day)
  189 + render :partial => 'events/events_by_day'
  190 + end
  191 +
227 192 #######################################################
  193 + protected
  194 +
  195 + def load_query
  196 + @asset = params[:action].to_sym
  197 + @order ||= [@asset]
  198 + @results ||= {}
  199 + @filter = filter
  200 + @filter_title = filter_description(@asset, @filter)
  201 +
  202 + @query = params[:query] || ''
  203 + @empty_query = @category.nil? && @query.blank?
  204 + end
228 205  
229   - def popup
230   - @regions = Region.find(:all).select{|r|r.lat && r.lng}
231   - render :action => 'popup', :layout => false
  206 + def load_category
  207 + unless params[:category_path].blank?
  208 + path = params[:category_path].join('/')
  209 + @category = environment.categories.find_by_path(path)
  210 + if @category.nil?
  211 + render_not_found(path)
  212 + else
  213 + @category_id = @category.id
  214 + end
  215 + end
232 216 end
233 217  
234   - def events_by_day
235   - @selected_day = build_date(params[:year], params[:month], params[:day])
236   - if params[:category_id] and Category.exists?(params[:category_id])
237   - @events_of_the_day = environment.events.by_day(@selected_day).in_category(Category.find(params[:category_id]))
  218 + FILTERS = %w(
  219 + more_recent
  220 + more_active
  221 + more_popular
  222 + )
  223 + def filter
  224 + if FILTERS.include?(params[:filter])
  225 + params[:filter]
238 226 else
239   - @events_of_the_day = environment.events.by_day(@selected_day)
  227 + 'more_recent'
240 228 end
241   - render :partial => 'events/events_by_day'
  229 + end
  230 +
  231 + def filter_description(asset, filter)
  232 + {
  233 + 'articles_more_recent' => _('More recent contents from network'),
  234 + 'articles_more_popular' => _('More read contents from network'),
  235 + 'people_more_recent' => _('More recent people from network'),
  236 + 'people_more_active' => _('More active people from network'),
  237 + 'people_more_popular' => _('More popular people from network'),
  238 + 'communities_more_recent' => _('More recent communities from network'),
  239 + 'communities_more_active' => _('More active communities from network'),
  240 + 'communities_more_popular' => _('More popular communities from network'),
  241 + }[asset.to_s + '_' + filter]
  242 + end
  243 +
  244 + def load_search_assets
  245 + @enabled_searchs = [
  246 + [ :articles, _('Contents') ],
  247 + [ :enterprises, _('Enterprises') ],
  248 + [ :people, _('People') ],
  249 + [ :communities, _('Communities') ],
  250 + [ :products, _('Products and Services') ],
  251 + [ :events, _('Events') ]
  252 + ].select {|key, name| !environment.enabled?('disable_asset_' + key.to_s) }
  253 +
  254 + @searching = {}
  255 + @titles = {}
  256 + @enabled_searchs.each do |key, name|
  257 + @titles[key] = name
  258 + @searching[key] = params[:action] == 'index' || params[:action] == key.to_s
  259 + end
  260 + end
  261 +
  262 + def limit
  263 + searching = @searching.values.select{ |v| v }
  264 + if params[:display] == 'map'
  265 + MAP_SEARCH_LIMIT
  266 + elsif searching.size <= 1
  267 + if [:people, :communities].include? @asset
  268 + BLOCKS_SEARCH_LIMIT
  269 + elsif @asset == :enterprises and @empty_query
  270 + BLOCKS_SEARCH_LIMIT
  271 + else
  272 + LIST_SEARCH_LIMIT
  273 + end
  274 + else
  275 + MULTIPLE_SEARCH_LIMIT
  276 + end
  277 + end
  278 +
  279 + def paginate_options(page = params[:page])
  280 + { :per_page => limit, :page => page }
  281 + end
  282 +
  283 + def full_text_search(filters = [])
  284 + paginate_options = paginate_options(params[:page])
  285 + asset_class = asset_class(@asset)
  286 +
  287 + solr_options = {}
  288 + if !@results_only and asset_class.methods.include?('facets')
  289 + solr_options.merge! asset_class.facets_find_options(params[:facet])
  290 + solr_options[:all_facets] = true
  291 + solr_options[:limit] = 0 if @facets_only
  292 + solr_options[:facets][:browse] << asset_class.facet_category_query.call(@category) if @category and asset_class.facet_category_query
  293 + end
  294 + solr_options[:order] = params[:order_by] if params[:order_by]
  295 + solr_options[:filter_queries] ||= []
  296 + solr_options[:filter_queries] += filters
  297 + solr_options[:filter_queries] << "environment_id:#{environment.id}"
  298 +
  299 + ret = asset_class.find_by_contents(@query, paginate_options, solr_options)
  300 + @results[@asset] = ret[:results]
  301 + @facets = ret[:facets]
  302 + @all_facets = ret[:all_facets]
242 303 end
243 304  
244 305 end
... ...
app/helpers/application_helper.rb
... ... @@ -574,7 +574,7 @@ module ApplicationHelper
574 574 end
575 575 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
576 576 links = links_for_balloon(profile)
577   - content_tag tag,
  577 + content_tag('div', content_tag(tag,
578 578 (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
579 579 link_to(
580 580 content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
... ... @@ -584,7 +584,7 @@ module ApplicationHelper
584 584 :class => 'profile_link url',
585 585 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
586 586 :title => profile.name ),
587   - :class => 'vcard'
  587 + :class => 'vcard'), :class => 'common-profile-list-block')
588 588 end
589 589  
590 590 def gravatar_url_for(email, options = {})
... ... @@ -890,22 +890,6 @@ module ApplicationHelper
890 890 result
891 891 end
892 892  
893   - def search_page_title(title, options={})
894   - title = "<h1>" + title + "</h1>"
895   - title += "<h2 class='query'>" + _("Searched for '%s'") % options[:query] + "</h2>" if !options[:query].blank?
896   - title += "<h2 class='query'>" + _("In category %s") % options[:category] + "</h2>" if !options[:category].blank?
897   - title += "<h2 class='query'>" + _("within %d km from %s") % [options[:distance], options[:region]] + "</h2>" if !options[:distance].blank? && !options[:region].blank?
898   - title += "<h2 class='query'>" + _("%d results found") % options[:total_results] + "</h2>" if !options[:total_results].blank?
899   - title
900   - end
901   -
902   - def search_page_link_to_all(options={})
903   - if options[:category]
904   - title = "<div align='center'>" + _('In all categories') + "</div>"
905   - link_to title, :action => 'assets', :asset => options[:asset], :category_path => []
906   - end
907   - end
908   -
909 893 def template_stylesheet_path
910 894 if profile.nil?
911 895 "/designs/templates/#{environment.layout_template}/stylesheets/style.css"
... ... @@ -982,6 +966,7 @@ module ApplicationHelper
982 966 def noosfero_stylesheets
983 967 [
984 968 'application',
  969 + 'search',
985 970 'thickbox',
986 971 'lightbox',
987 972 'colorpicker',
... ... @@ -1100,33 +1085,46 @@ module ApplicationHelper
1100 1085 ") : '')
1101 1086 end
1102 1087  
1103   - def browse_people_menu
  1088 + def search_contents_menu
  1089 + links = [
  1090 + {s_('contents|More Recent') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_recent'})}},
  1091 + {s_('contents|More Read') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_popular'})}}
  1092 + ]
  1093 + if logged_in?
  1094 + links.push(_('New Content') => lightbox_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_user.login, :cms => true})}))
  1095 + end
  1096 +
  1097 + link_to(content_tag(:span, _('Contents'), :class => 'icon-menu-contents'), {:controller => "search", :action => 'contents', :category_path => ''}, :id => 'submenu-contents') +
  1098 + link_to(content_tag(:span, _('Contents Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-contents-trigger')
  1099 + end
  1100 +
  1101 + def search_people_menu
1104 1102 links = [
1105   - {s_('people|More Recent') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_recent'})}},
1106   - {s_('people|More Active') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_active'})}},
1107   - {s_('people|More Popular') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_popular'})}}
  1103 + {s_('people|More Recent') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_recent'})}},
  1104 + {s_('people|More Active') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_active'})}},
  1105 + {s_('people|More Popular') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_popular'})}}
1108 1106 ]
1109 1107 if logged_in?
1110 1108 links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})})
1111 1109 links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})})
1112 1110 end
1113 1111  
1114   - link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') +
  1112 + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "search", :action => 'people', :category_path => ''}, :id => 'submenu-people') +
1115 1113 link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger')
1116 1114 end
1117 1115  
1118   - def browse_communities_menu
  1116 + def search_communities_menu
1119 1117 links = [
1120   - {s_('communities|More Recent') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_recent'})}},
1121   - {s_('communities|More Active') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_active'})}},
1122   - {s_('communities|More Popular') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_popular'})}}
  1118 + {s_('communities|More Recent') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_recent'})}},
  1119 + {s_('communities|More Active') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_active'})}},
  1120 + {s_('communities|More Popular') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_popular'})}}
1123 1121 ]
1124 1122 if logged_in?
1125 1123 links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})})
1126 1124 links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})})
1127 1125 end
1128 1126  
1129   - link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') +
  1127 + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "search", :action => 'communities'}, :id => 'submenu-communities') +
1130 1128 link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger')
1131 1129 end
1132 1130  
... ... @@ -1330,4 +1328,8 @@ module ApplicationHelper
1330 1328 end
1331 1329 end
1332 1330  
  1331 + def jquery_token_input_messages_json(hintText = _('Type in an keyword'), noResultsText = _('No results'), searchingText = _('Searching...'))
  1332 + "hintText: '#{hintText}', noResultsText: '#{noResultsText}', searchingText: '#{searchingText}'"
  1333 + end
  1334 +
1333 1335 end
... ...
app/helpers/display_helper.rb
... ... @@ -26,15 +26,19 @@ module DisplayHelper
26 26 product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url
27 27 end
28 28  
29   - def link_to_category(category, full = true)
  29 + def link_to_tag(tag, html_options = {})
  30 + link_to tag.name, {:controller => 'search', :action => 'tag', :tag => tag.name}, html_options
  31 + end
  32 +
  33 + def link_to_category(category, full = true, html_options = {})
30 34 return _('Uncategorized product') unless category
31 35 name = full ? category.full_name(' &rarr; ') : category.name
32   - link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname })
  36 + link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }), html_options
33 37 end
34 38  
35 39 def link_to_product_category(category)
36 40 if category
37   - link_to(category.name, :controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id, :host => category.environment.default_hostname)
  41 + link_to(category.name, :controller => 'search', :action => 'products', :category_path => category.explode_path)
38 42 else
39 43 _('Uncategorized product')
40 44 end
... ...
app/helpers/lightbox_helper.rb
... ... @@ -22,10 +22,7 @@ module LightboxHelper
22 22 def lightbox_options(options, lightbox_type = 'lbOn')
23 23 the_class = lightbox_type
24 24 the_class << " #{options[:class]}" if options.has_key?(:class)
25   - options.merge(
26   - :class => the_class,
27   - :onclick => 'alert("%s"); return false' % _('Please, try again when the page loading completes.')
28   - )
  25 + options.merge(:class => the_class)
29 26 end
30 27  
31 28 def lightbox?
... ...
app/helpers/product_category_viewer_helper.rb
... ... @@ -1,2 +0,0 @@
1   -module ProductCategoryViewerHelper
2   -end
app/helpers/search_helper.rb
... ... @@ -3,116 +3,160 @@ module SearchHelper
3 3 # FIXME remove it after search_controler refactored
4 4 include EventsHelper
5 5  
6   - def relevance_for(hit)
7   - n = (hit.ferret_score if hit.respond_to?(:ferret_score))
8   - n ||= 1.0
9   - (n * 100.0).round
  6 + def search_page_title(title, category = nil)
  7 + title = "<h1>" + title
  8 + title += '<small>' + category.name + '</small>' if category
  9 + title + "</h1>"
10 10 end
11 11  
12   - def display_results(use_map = true)
  12 + def category_context(category, url)
  13 + content_tag('div', category.full_name + _(', ') +
  14 + link_to(_('search in all categories'),
  15 + url.merge(:category_path => [], :action => (params[:action] == 'category_index' ? 'index' : params[:action]) )),
  16 + :align => 'center', :class => 'search-category-context') if category
  17 + end
13 18  
14   - unless use_map && GoogleMaps.enabled?(environment.default_hostname)
15   - return render(:partial => 'display_results')
  19 + def display_results(use_map = false)
  20 + if params[:display] == 'map' && use_map && GoogleMaps.enabled?(environment.default_hostname)
  21 + partial = 'google_maps'
  22 + klass = 'map'
  23 + else
  24 + partial = 'display_results'
  25 + klass = 'list'
16 26 end
17 27  
18   - data =
19   - if params[:display] == 'map'
20   - {
21   - :partial => 'google_maps',
22   - :toggle => button(:search, _('Display in list'), params.merge(:display => 'list'), :class => "map-toggle-button" ),
23   - :class => 'map' ,
24   - }
25   - else
26   - {
27   - :partial => 'display_results',
28   - :toggle => button(:search, _('Display in map'), params.merge(:display => 'map'), :class => "map-toggle-button" ),
29   - :class => 'list' ,
30   - }
31   - end
  28 + content_tag('div', render(:partial => partial), :class => "map-or-list-search-results #{klass}")
  29 + end
32 30  
33   - content_tag('div', data[:toggle] + (render :partial => data[:partial]), :class => "map-or-list-search-results #{data[:class]}")
  31 + def display_map_list_button
  32 + button(:search, params[:display] == 'map' ? _('Display in list') : _('Display in map'),
  33 + params.merge(:display => (params[:display] == 'map' ? 'list' : 'map')),
  34 + :class => "map-toggle-button" ) if GoogleMaps.enabled?(environment.default_hostname)
34 35 end
35 36  
36   - def display_item_map_info(item)
37   - if item.kind_of?(Profile)
38   - display_profile_info(item)
39   - elsif item.kind_of?(Product)
40   - display_product_info(item)
  37 + def city_with_state(city)
  38 + s = city.parent
  39 + if city and city.kind_of?(City) and s and s.kind_of?(State) and s.acronym
  40 + city.name + ', ' + s.acronym
  41 + else
  42 + city.name
41 43 end
42 44 end
  45 +
  46 + def facets_menu(asset, _facets)
  47 + @asset_class = asset_class(asset)
  48 + @facets = _facets
  49 + render(:partial => 'facets_menu')
  50 + end
  51 +
  52 + def facets_unselect_menu(asset)
  53 + @asset_class = asset_class(asset)
  54 + render(:partial => 'facets_unselect_menu')
  55 + end
43 56  
44   - def display_profile_info(profile)
45   - data = ''
46   - unless profile.contact_email.nil?
47   - data << content_tag('strong', _('E-Mail: ')) + profile.contact_email + '<br/>'
48   - end
49   - unless profile.contact_phone.nil?
50   - data << content_tag('strong', _('Phone(s): ')) + profile.contact_phone + '<br/>'
51   - end
52   - unless profile.region.nil?
53   - data << content_tag('strong', _('Location: ')) + profile.region.name + '<br/>'
54   - end
55   - unless profile.address.nil?
56   - data << content_tag('strong', _('Address: ')) + profile.address + '<br/>'
57   - end
58   - unless profile.products.empty?
59   - data << content_tag('strong', _('Products/Services: ')) + profile.products.map{|i| link_to(i.name, :controller => 'manage_products', :profile => profile.identifier, :action => 'show', :id => i.id)}.join(', ') + '<br/>'
60   - end
61   - if profile.respond_to?(:distance) and !profile.distance.nil?
62   - data << content_tag('strong', _('Distance: ')) + "%.2f%" % profile.distance + '<br/>'
63   - end
64   - content_tag('table',
65   - content_tag('tr',
66   - content_tag('td', content_tag('div', profile_image(profile, :thumb), :class => 'profile-info-picture')) +
67   - content_tag('td', content_tag('strong', link_to(profile.name, url_for(profile.url))) + '<br/>' + data
68   - )
69   - ),
70   - :class => 'profile-info'
71   - )
  57 + def facet_javascript(input_id, facet, array)
  58 + hintText = _('Type in an option')
  59 + text_field_tag('facet['+input_id+']', '', :id => input_id) +
  60 + javascript_tag("jQuery.TokenList(jQuery('##{input_id}'), #{array.to_json},
  61 + {searchDelay: 0, permanentDropdown: true, theme: 'facet', dontAdd: true, preventDuplicates: true,
  62 + #{jquery_token_input_messages_json(hintText)}});")
72 63 end
73 64  
74   - def display_product_info(product)
75   - data = ''
76   - unless product.price.nil?
77   - data << content_tag('strong', _('Price: ')) + product.price + '<br/>'
78   - end
79   - unless product.enterprise.nil?
80   - data << content_tag('strong', _('Provider: ')) + link_to_profile(product.enterprise.name, product.enterprise.identifier)
81   - end
82   - unless product.product_category.nil?
83   - data << content_tag('strong', _('Category: ')) + link_to(product.product_category.name, :controller => 'search', :action => 'assets', :asset => 'products', :product_category => product.product_category.id)
  65 + def facet_link_html(facet, params, value, label, count)
  66 + params = params.dup
  67 + has_extra = label.kind_of?(Array)
  68 + link_label = has_extra ? label[0] : label
  69 + id = facet[:solr_field].to_s
  70 + params[:facet] ||= {}
  71 + params[:facet][id] ||= {}
  72 +
  73 + selected = facet[:label_id].nil? ? params[:facet][id] == value : params[:facet][id][facet[:label_id]].to_a.include?(value)
  74 +
  75 + if count > 0
  76 + url = params.merge(:facet => params[:facet].merge(
  77 + id => facet[:label_id].nil? ? value : params[:facet][id].merge( facet[:label_id] => params[:facet][id][facet[:label_id]].to_a.push(value) )
  78 + ))
  79 + else
  80 + url = params.merge(:facet => {
  81 + id => facet[:label_id].nil? ? value : { facet[:label_id] => value }
  82 + })
84 83 end
85   - content_tag('table',
86   - content_tag('tr',
87   - content_tag('td', content_tag('div', image_tag(product.image ? product.image.public_filename(:thumb) : '/images/icons-app/product-default-pic-portrait.png'), :class => 'profile-info-picture')) +
88   - content_tag('td', content_tag('strong', link_to(product.name, :controller => 'catalog', :profile => product.enterprise.identifier, :action => 'show', :id => product)) + '<br/>' + data)
89   - ), :class => 'profile-info')
  84 +
  85 + content_tag 'div', link_to(link_label, url, :class => 'facet-result-link-label') +
  86 + content_tag('span', (has_extra ? label[1] : ''), :class => 'facet-result-extra-label') +
  87 + (count > 0 ? content_tag('span', " (#{count})", :class => 'facet-result-count') : ''),
  88 + :class => 'facet-menu-item' + (selected ? ' facet-result-link-selected' : '')
90 89 end
91 90  
92   - def product_categories_menu(asset, product_category, object_ids = nil)
93   - cats = ProductCategory.menu_categories(@product_category, environment)
94   - cats += cats.select { |c| c.children_count > 0 }.map(&:children).flatten
95   - product_categories_ids = cats.map(&:id)
96   -
97   - counts = @noosfero_finder.product_categories_count(asset, product_categories_ids, object_ids)
98   -
99   - product_categories_menu = ProductCategory.menu_categories(product_category, environment).map do |cat|
100   - hits = counts[cat.id]
101   - childs = []
102   - if hits
103   - if cat.children_count > 0
104   - childs = cat.children.map do |child|
105   - child_hits = counts[child.id]
106   - [child, child_hits]
107   - end.select{|child, child_hits| child_hits }
108   - else
109   - childs = []
  91 + def facet_selecteds_html_for(environment, klass, params)
  92 + def name_with_extra(klass, facet, value)
  93 + name = klass.facet_result_name(facet, value)
  94 + name = name[0] + name[1] if name.kind_of?(Array)
  95 + name
  96 + end
  97 +
  98 + ret = []
  99 + params = params.dup
  100 + params[:facet].each do |id, value|
  101 + facet = klass.facet_by_id(id.to_sym)
  102 + if value.kind_of?(Hash)
  103 + label_hash = facet[:label].call(environment)
  104 + value.each do |label_id, value|
  105 + facet[:label_id] = label_id
  106 + facet[:label] = label_hash[label_id]
  107 + value.to_a.each do |value|
  108 + ret << [facet[:label], name_with_extra(klass, facet, value),
  109 + params.merge(:facet => params[:facet].merge(id => params[:facet][id].merge(label_id => params[:facet][id][label_id].to_a.reject{ |v| v == value })))]
  110 + end
110 111 end
  112 + else
  113 + ret << [klass.facet_label(facet), name_with_extra(klass, facet, value),
  114 + params.merge(:facet => params[:facet].reject{ |k,v| k == id })]
111 115 end
112   - [cat, hits, childs]
113   - end.select{|cat, hits| hits }
  116 + end
  117 +
  118 + ret.map do |label, name, url|
  119 + content_tag('div', content_tag('span', label, :class => 'facet-selected-label') +
  120 + content_tag('span', name, :class => 'facet-selected-name') +
  121 + link_to('', url, :class => 'facet-selected-remove'), :class => 'facet-selected')
  122 + end.join
  123 + end
  124 +
  125 + def order_by(asset)
  126 + options = {
  127 + :products => [[_('Relevance'), ''], [_('Name'), 'name_or_category_sort asc'], [_('Lower price'), 'price_sort asc'], [_('Higher price'), 'price_sort desc']],
  128 + :events => [[_('Relevance'), ''], [_('Name'), 'name_sort asc']],
  129 + :articles => [[_('Relevance'), ''], [_('Name'), 'name_sort asc'], [_('Most recent'), 'updated_at desc']],
  130 + :enterprises => [[_('Relevance'), ''], [_('Name'), 'name_sort asc']],
  131 + :people => [[_('Relevance'), ''], [_('Name'), 'name_sort asc']],
  132 + :communities => [[_('Relevance'), ''], [_('Name'), 'name_sort asc']],
  133 + }
  134 +
  135 + content_tag('div', _('Sort results by ') +
  136 + select_tag(asset.to_s + '[order]', options_for_select(options[asset], params[:order_by]),
  137 + {:onchange => "window.location=jQuery.param.querystring(window.location.href, { 'order_by' : this.options[this.selectedIndex].value})"}),
  138 + :class => "search-ordering")
  139 + end
114 140  
115   - render(:partial => 'product_categories_menu', :object => product_categories_menu)
  141 + def label_total_found(asset, total_found)
  142 + labels = {
  143 + :products => _("%s products offers found"),
  144 + :articles => _("%s articles found"),
  145 + :events => _("%s events found"),
  146 + :people => _("%s people found"),
  147 + :enterprises => _("%s enterprises found"),
  148 + :communities => _("%s communities found"),
  149 + }
  150 + content_tag('span', labels[asset] % total_found,
  151 + :class => "total-pages-found") if labels[asset]
  152 + end
  153 +
  154 + def asset_class(asset)
  155 + asset.to_s.singularize.camelize.constantize
  156 + end
  157 +
  158 + def asset_table(asset)
  159 + asset_class(asset).table_name
116 160 end
117 161  
118 162 end
... ...
app/models/article.rb
... ... @@ -2,6 +2,12 @@ require &#39;hpricot&#39;
2 2  
3 3 class Article < ActiveRecord::Base
4 4  
  5 + # use for internationalizable human type names in search facets
  6 + # reimplement on subclasses
  7 + def self.type_name
  8 + _('Content')
  9 + end
  10 +
5 11 track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && !a.image? }, :custom_target => :action_tracker_target
6 12 track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && (a.body_changed? || a.name_changed?) }, :custom_target => :action_tracker_target
7 13 track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target
... ... @@ -125,8 +131,6 @@ class Article &lt; ActiveRecord::Base
125 131  
126 132 acts_as_versioned
127 133  
128   - acts_as_searchable :additional_fields => [ :comment_data ]
129   -
130 134 def comment_data
131 135 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
132 136 end
... ... @@ -141,13 +145,31 @@ class Article &lt; ActiveRecord::Base
141 145 {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]}
142 146 }
143 147  
  148 + named_scope :public,
  149 + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ?", true, true, true, true ]
  150 +
  151 + named_scope :more_recent,
  152 + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ? AND
  153 + ((articles.type != ?) OR articles.type is NULL)",
  154 + true, true, true, true, 'RssFeed'
  155 + ],
  156 + :order => 'articles.published_at desc, articles.id desc'
  157 +
  158 + # retrives the most commented articles, sorted by the comment count (largest
  159 + # first)
  160 + def self.most_commented(limit)
  161 + paginate(:order => 'comments_count DESC', :page => 1, :per_page => limit)
  162 + end
  163 +
  164 + named_scope :more_popular, :order => 'hits DESC'
  165 +
144 166 # retrieves the latest +limit+ articles, sorted from the most recent to the
145 167 # oldest.
146 168 #
147 169 # Only includes articles where advertise == true
148   - def self.recent(limit, extra_conditions = {})
  170 + def self.recent(limit = nil, extra_conditions = {})
149 171 # FIXME this method is a horrible hack
150   - options = { :limit => limit,
  172 + options = { :page => 1, :per_page => limit,
151 173 :conditions => [
152 174 "advertise = ? AND
153 175 published = ? AND
... ... @@ -165,20 +187,14 @@ class Article &lt; ActiveRecord::Base
165 187 options.delete(:include)
166 188 end
167 189 if extra_conditions == {}
168   - self.find(:all, options)
  190 + self.paginate(options)
169 191 else
170 192 with_scope :find => {:conditions => extra_conditions} do
171   - self.find(:all, options)
  193 + self.paginate(options)
172 194 end
173 195 end
174 196 end
175 197  
176   - # retrives the most commented articles, sorted by the comment count (largest
177   - # first)
178   - def self.most_commented(limit)
179   - find(:all, :order => 'comments_count DESC', :limit => limit)
180   - end
181   -
182 198 # produces the HTML code that is to be displayed as this article's contents.
183 199 #
184 200 # The implementation in this class just provides the +body+ attribute as the
... ... @@ -561,6 +577,81 @@ class Article &lt; ActiveRecord::Base
561 577  
562 578 private
563 579  
  580 + # FIXME: workaround for development env.
  581 + # Subclasses aren't (re)loaded, and acts_as_solr
  582 + # depends on subclasses method to search
  583 + # see http://stackoverflow.com/questions/4138957/activerecordsubclassnotfound-error-when-using-sti-in-rails/4139245
  584 + UploadedFile
  585 + TextArticle
  586 + TinyMceArticle
  587 + TextileArticle
  588 + Folder
  589 + EnterpriseHomepage
  590 + Gallery
  591 + Blog
  592 + Forum
  593 + Event
  594 +
  595 + def self.f_type_proc(klass)
  596 + klass.constantize.type_name
  597 + end
  598 + def self.f_profile_type_proc(klass)
  599 + klass.constantize.type_name
  600 + end
  601 +
  602 + def f_type
  603 + #join common types
  604 + case self.class.name
  605 + when 'TinyMceArticle', 'TextileArticle'
  606 + TextArticle.name
  607 + else
  608 + self.class.name
  609 + end
  610 + end
  611 + def f_profile_type
  612 + self.profile.class.name
  613 + end
  614 + def f_published_at
  615 + self.published_at
  616 + end
  617 + def f_category
  618 + self.categories.collect(&:name)
  619 + end
  620 + def name_sort
  621 + name
  622 + end
  623 + def public
  624 + self.public?
  625 + end
  626 + def environment_id
  627 + profile.environment_id
  628 + end
  629 + public
  630 +
  631 + acts_as_faceted :fields => {
  632 + :f_type => {:label => _('Type'), :proc => proc{|klass| f_type_proc(klass)}},
  633 + :f_published_at => {:type => :date, :label => _('Published date'), :queries => {'[* TO NOW-1YEARS/DAY]' => _("Older than one year"),
  634 + '[NOW-1YEARS TO NOW/DAY]' => _("Last year"), '[NOW-1MONTHS TO NOW/DAY]' => _("Last month"), '[NOW-7DAYS TO NOW/DAY]' => _("Last week"), '[NOW-1DAYS TO NOW/DAY]' => _("Last day")},
  635 + :queries_order => ['[NOW-1DAYS TO NOW/DAY]', '[NOW-7DAYS TO NOW/DAY]', '[NOW-1MONTHS TO NOW/DAY]', '[NOW-1YEARS TO NOW/DAY]', '[* TO NOW-1YEARS/DAY]']},
  636 + :f_profile_type => {:label => _('Profile'), :proc => proc{|klass| f_profile_type_proc(klass)}},
  637 + :f_category => {:label => _('Categories')}},
  638 + :category_query => proc { |c| "f_category:\"#{c.name}\"" },
  639 + :order => [:f_type, :f_published_at, :f_profile_type, :f_category]
  640 +
  641 + acts_as_searchable :additional_fields => [
  642 + {:name => {:type => :string}},
  643 + {:public => {:type => :boolean}},
  644 + {:environment_id => {:type => :integer}},
  645 + ] + facets_fields_for_solr,
  646 + :exclude_fields => [:setting],
  647 + :include => [:profile],
  648 + :facets => facets_option_for_solr,
  649 + :boost => proc {|a| 10 if a.profile.enabled},
  650 + :if => proc{|a| ! ['RssFeed'].include?(a.class.name)}
  651 + handle_asynchronously :solr_save
  652 +
  653 + private
  654 +
564 655 def sanitize_tag_list
565 656 sanitizer = HTML::FullSanitizer.new
566 657 self.tag_list.names.map!{|i| strip_tag_name sanitizer.sanitize(i) }
... ...
app/models/blog.rb
... ... @@ -9,6 +9,10 @@ class Blog &lt; Folder
9 9 end
10 10 alias_method_chain :posts, :no_folders
11 11  
  12 + def self.type_name
  13 + _('Blog')
  14 + end
  15 +
12 16 def self.short_description
13 17 _('Blog')
14 18 end
... ...
app/models/category.rb
... ... @@ -36,18 +36,38 @@ class Category &lt; ActiveRecord::Base
36 36 { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] }
37 37 }
38 38  
  39 + def recent_people(limit = 10)
  40 + self.people.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  41 + end
  42 +
  43 + def recent_enterprises(limit = 10)
  44 + self.enterprises.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  45 + end
  46 +
  47 + def recent_communities(limit = 10)
  48 + self.communities.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  49 + end
  50 +
  51 + def recent_products(limit = 10)
  52 + self.products.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  53 + end
  54 +
39 55 def recent_articles(limit = 10)
40 56 self.articles.recent(limit)
41 57 end
42 58  
43 59 def recent_comments(limit = 10)
44   - comments.find(:all, :order => 'created_at DESC, comments.id DESC', :limit => limit)
  60 + comments.paginate(:all, :order => 'created_at DESC, comments.id DESC', :page => 1, :per_page => limit)
45 61 end
46 62  
47 63 def most_commented_articles(limit = 10)
48 64 self.articles.most_commented(limit)
49 65 end
50 66  
  67 + def upcoming_events(limit = 10)
  68 + self.events.find(:all, :conditions => [ 'start_date >= ?', Date.today ], :order => 'start_date')
  69 + end
  70 +
51 71 def display_in_menu?
52 72 display_in_menu
53 73 end
... ...
app/models/category_finder.rb
... ... @@ -1,146 +0,0 @@
1   -class CategoryFinder
2   -
3   - def initialize(cat)
4   - @category = cat
5   - @category_id = @category.id
6   - end
7   -
8   - attr_reader :category_id
9   -
10   - def find(asset, query='', options={})
11   - @region = Region.find_by_id(options.delete(:region)) if options.has_key?(:region)
12   - if @region && options[:within]
13   - options[:origin] = [@region.lat, @region.lng]
14   - else
15   - options.delete(:within)
16   - end
17   -
18   - date_range = options.delete(:date_range)
19   -
20   - options = {:page => 1, :per_page => options.delete(:limit)}.merge(options)
21   -
22   - if asset == :events
23   - finder_method = 'find'
24   - options.delete(:page)
25   - options.delete(:per_page)
26   - else
27   - finder_method = 'paginate'
28   - end
29   -
30   - if query.blank?
31   - options.delete(:facets)
32   - asset_class(asset).send(finder_method, :all, options_for_find(asset_class(asset), {:order => "#{asset_table(asset)}.name"}.merge(options), date_range))
33   - else
34   - pg_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}
35   - solr_options = {:facets => options.delete(:facets)}
36   - asset_class(asset).find_by_contents(query, pg_options, solr_options, options_for_find(asset_class(asset), options, date_range))[:results]
37   - end
38   - end
39   -
40   - def recent(asset, limit = nil)
41   - find(asset, nil, :limit => limit, :order => 'created_at DESC, id DESC')
42   - end
43   -
44   - def most_commented_articles(limit=10, options={})
45   - options = {:page => 1, :per_page => limit, :order => 'comments_count DESC'}.merge(options)
46   - Article.paginate(:all, options_for_find(Article, options))
47   - end
48   -
49   - def current_events(year, month, options={})
50   - options.delete(:page)
51   - options.delete(:per_page)
52   -
53   - range = Event.date_range(year, month)
54   -
55   - Event.find(:all, {:include => :categories, :conditions => { 'categories.id' => category_id, :start_date => range }}.merge(options))
56   - end
57   -
58   - def upcoming_events(options = {})
59   - options.delete(:page)
60   - options.delete(:per_page)
61   -
62   - Event.find(:all, {:include => :categories, :conditions => [ 'categories.id = ? and start_date >= ?', category_id, Date.today ], :order => 'start_date' }.merge(options))
63   - end
64   -
65   - def product_categories_count(asset, product_categories_ids, objects_ids=nil)
66   - conditions = [ "product_categorizations.category_id in (?) and #{ProfileCategorization.table_name}.category_id = ?", product_categories_ids, category_id]
67   -
68   - if asset == :products
69   - if objects_ids
70   - conditions[0] += ' and product_categorizations.product_id in (?)'
71   - conditions << objects_ids
72   - end
73   - ProductCategory.find(
74   - :all,
75   - :select => 'categories.id, count(*) as total',
76   - :joins => "inner join product_categorizations on (product_categorizations.category_id = categories.id) inner join products on (products.id = product_categorizations.product_id) inner join #{ProfileCategorization.table_name} on (#{ProfileCategorization.table_name}.profile_id = products.enterprise_id)",
77   - :group => 'categories.id',
78   - :conditions => conditions
79   - )
80   - elsif asset == :enterprises
81   - if objects_ids
82   - conditions[0] += ' and products.enterprise_id in (?)'
83   - conditions << objects_ids
84   - end
85   - ProductCategory.find(
86   - :all,
87   - :select => 'categories.id, count(distinct products.enterprise_id) as total',
88   - :joins => "inner join product_categorizations on (product_categorizations.category_id = categories.id) inner join products on (products.id = product_categorizations.product_id) inner join #{ProfileCategorization.table_name} on (#{ProfileCategorization.table_name}.profile_id = products.enterprise_id)",
89   - :group => 'categories.id',
90   - :conditions => conditions
91   - )
92   - else
93   - raise ArgumentError, 'only products and enterprises supported'
94   - end.inject({}) do |results,pc|
95   - results[pc.id]= pc.total.to_i
96   - results
97   - end
98   - end
99   -
100   - protected
101   -
102   - def options_for_find(klass, options={}, date_range = nil)
103   - if defined? options[:product_category]
104   - prod_cat = options.delete(:product_category)
105   - end
106   -
107   - case klass.name
108   - when 'Comment'
109   - {:joins => 'inner join articles_categories on articles_categories.article_id = comments.article_id', :conditions => ['articles_categories.category_id = (?)', category_id]}.merge!(options)
110   - when 'Product'
111   - if prod_cat
112   - {:joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id inner join product_categorizations on (product_categorizations.product_id = products.id)', :conditions => ['categories_profiles.category_id = (?) and product_categorizations.category_id = (?)', category_id, prod_cat.id]}.merge!(options)
113   - else
114   - {:joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id = (?)', category_id]}.merge!(options)
115   - end
116   - when 'Article', 'TextArticle'
117   - {:joins => 'inner join articles_categories on (articles_categories.article_id = articles.id)', :conditions => ['articles_categories.category_id = (?)', category_id]}.merge!(options)
118   - when 'Event'
119   - conditions =
120   - if date_range
121   - ['articles_categories.category_id = (:category_id) and (start_date BETWEEN :start_day AND :end_day OR end_date BETWEEN :start_day AND :end_day)', {:category_id => category_id, :start_day => date_range.first, :end_day => date_range.last} ]
122   - else
123   - ['articles_categories.category_id = (?) ', category_id ]
124   - end
125   - {:joins => 'inner join articles_categories on (articles_categories.article_id = articles.id)', :conditions => conditions}.merge!(options)
126   - when 'Enterprise'
127   - if prod_cat
128   - {:joins => 'inner join categories_profiles on (categories_profiles.profile_id = profiles.id) inner join products on (products.enterprise_id = profiles.id) inner join product_categorizations on (product_categorizations.product_id = products.id)', :conditions => ['categories_profiles.category_id = (?) and product_categorizations.category_id = (?)', category_id, prod_cat.id]}.merge!(options)
129   - else
130   - {:joins => 'inner join categories_profiles on (categories_profiles.profile_id = profiles.id)', :conditions => ['categories_profiles.category_id = (?)', category_id]}.merge!(options)
131   - end
132   - when 'Person', 'Community'
133   - {:joins => 'inner join categories_profiles on (categories_profiles.profile_id = profiles.id)', :conditions => ['categories_profiles.category_id = (?)', category_id]}.merge!(options)
134   - else
135   - raise "unreconized class #{klass.name}"
136   - end
137   - end
138   -
139   - def asset_class(asset)
140   - asset.to_s.singularize.camelize.constantize
141   - end
142   -
143   - def asset_table(asset)
144   - asset_class(asset).table_name
145   - end
146   -end
app/models/certifier.rb
... ... @@ -2,9 +2,12 @@ class Certifier &lt; ActiveRecord::Base
2 2  
3 3 belongs_to :environment
4 4  
5   - has_many :qualifier_certifiers
  5 + has_many :qualifier_certifiers, :dependent => :destroy
6 6 has_many :qualifiers, :through => :qualifier_certifiers
7 7  
  8 + has_many :product_qualifiers, :dependent => :destroy
  9 + has_many :products, :through => :product_qualifiers, :source => :product
  10 +
8 11 validates_presence_of :environment_id
9 12 validates_presence_of :name
10 13  
... ...
app/models/communities_block.rb
... ... @@ -21,7 +21,7 @@ class CommunitiesBlock &lt; ProfileListBlock
21 21 end
22 22 when Environment
23 23 lambda do
24   - link_to s_('communities|View all'), :controller => 'browse', :action => 'communities'
  24 + link_to s_('communities|View all'), :controller => 'search', :action => 'communities'
25 25 end
26 26 else
27 27 ''
... ...
app/models/community.rb
1 1 class Community < Organization
  2 +
  3 + def self.type_name
  4 + _('Community')
  5 + end
  6 +
2 7 N_('Community')
3 8 N_('Language')
4 9  
... ...
app/models/enterprise.rb
... ... @@ -2,6 +2,10 @@
2 2 # only enterprises can offer products and services.
3 3 class Enterprise < Organization
4 4  
  5 + def self.type_name
  6 + _('Enterprise')
  7 + end
  8 +
5 9 N_('Enterprise')
6 10  
7 11 has_many :products, :dependent => :destroy, :order => 'name ASC'
... ... @@ -163,6 +167,10 @@ class Enterprise &lt; Organization
163 167 end
164 168 end
165 169  
  170 + def control_panel_settings_button
  171 + {:title => __('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'}
  172 + end
  173 +
166 174 settings_items :enable_contact_us, :type => :boolean, :default => true
167 175  
168 176 def enable_contact?
... ...
app/models/enterprise_homepage.rb
1 1 class EnterpriseHomepage < Article
2 2  
  3 + def self.type_name
  4 + _('Homepage')
  5 + end
  6 +
3 7 def self.short_description
4 8 __('Enterprise homepage.')
5 9 end
... ...
app/models/environment.rb
... ... @@ -247,6 +247,10 @@ class Environment &lt; ActiveRecord::Base
247 247  
248 248 settings_items :enabled_plugins, :type => Array, :default => []
249 249  
  250 + settings_items :search_hints, :type => Hash, :default => {}
  251 +
  252 + settings_items :top_level_category_as_facet_ids, :type => Array, :default => []
  253 +
250 254 def news_amount_by_folder=(amount)
251 255 settings[:news_amount_by_folder] = amount.to_i
252 256 end
... ... @@ -353,11 +357,11 @@ class Environment &lt; ActiveRecord::Base
353 357 end
354 358  
355 359 def terminology
356   - if self.settings[:terminology]
357   - self.settings[:terminology].constantize.instance
358   - else
  360 + #if self.settings[:terminology]
  361 + #self.settings[:terminology].constantize.instance
  362 + #else
359 363 Noosfero.terminology
360   - end
  364 + #end
361 365 end
362 366  
363 367 def terminology=(value)
... ...
app/models/environment_finder.rb
... ... @@ -1,115 +0,0 @@
1   -class EnvironmentFinder
2   -
3   - def initialize env
4   - @environment = env
5   - end
6   -
7   - def find(asset, query = nil, options={}, finder_method = 'paginate')
8   - @region = Region.find_by_id(options.delete(:region)) if options.has_key?(:region)
9   - if @region && options[:within]
10   - options[:origin] = [@region.lat, @region.lng]
11   - else
12   - options.delete(:within)
13   - end
14   -
15   - product_category = options.delete(:product_category)
16   -
17   - date_range = options.delete(:date_range)
18   -
19   - # FIXME this test is in more than one place
20   - if finder_method == 'paginate'
21   - options = {:page => 1, :per_page => options.delete(:limit)}.merge(options)
22   - end
23   -
24   - if query.blank?
25   - options.delete(:facets)
26   -
27   - # FIXME this test is in more than one place
28   - if finder_method == 'paginate'
29   - options = {:order => "#{asset_table(asset)}.name"}.merge(options)
30   - end
31   - if product_category && asset == :products
32   - @environment.send(asset).send(finder_method, :all, options.merge(:include => 'product_categorizations', :conditions => ['product_categorizations.category_id = (?)', product_category.id]))
33   - elsif product_category && asset == :enterprises
34   - @environment.send(asset).send(finder_method, :all, options.merge( :order => 'profiles.name', :joins => 'inner join products on (products.enterprise_id = profiles.id) inner join product_categorizations on (product_categorizations.product_id = products.id)', :conditions => ['product_categorizations.category_id = (?)', product_category.id]))
35   - else
36   - if asset == :events
37   - # Ignore pagination for asset events
38   - options.delete(:per_page)
39   - options.delete(:page)
40   - if date_range
41   - @environment.send(asset).send('find', :all, options.merge(:conditions => [
42   - 'start_date BETWEEN :start_day AND :end_day OR end_date BETWEEN :start_day AND :end_day',
43   - {:start_day => date_range.first, :end_day => date_range.last}
44   - ]))
45   - else
46   - @environment.send(asset).send('find', :all, options)
47   - end
48   - else
49   - @environment.send(asset).send(finder_method, :all, options)
50   - end
51   - end
52   - else
53   - pg_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}
54   - solr_options = {:facets => options.delete(:facets)}
55   - if product_category && asset == :products
56   - # SECURITY no risk of SQL injection, since product_category_ids comes from trusted source
57   - ret = @environment.send(asset).find_by_contents(query, pg_options, solr_options, options.merge({:include => 'product_categorizations', :conditions => 'product_categorizations.category_id = (%s)' % product_category.id }))
58   - elsif product_category && asset == :enterprises
59   - ret = @environment.send(asset).find_by_contents(query, pg_options, solr_options, options.merge(:joins => 'inner join products on products.enterprise_id = profiles.id inner join product_categorizations on (product_categorizations.product_id = products.id)', :include => 'products', :conditions => "product_categorizations.category_id = (#{product_category.id})"))
60   - else
61   - ret = @environment.send(asset).find_by_contents(query, pg_options, solr_options, options)
62   - end
63   - if solr_options[:facets].nil?
64   - ret[:results]
65   - else
66   - ret
67   - end
68   - end
69   - end
70   -
71   - def recent(asset, limit = nil)
72   - find(asset, nil, :limit => limit)
73   - end
74   -
75   - def product_categories_count(asset, product_categories_ids, objects_ids=nil)
76   - conditions = ['product_categorizations.category_id in (?)', product_categories_ids]
77   -
78   - if asset == :products
79   - if objects_ids
80   - conditions[0] += ' and product_categorizations.product_id in (?)'
81   - conditions << objects_ids
82   - end
83   - ProductCategory.find(:all, :select => 'categories.id, count(*) as total', :joins => 'inner join product_categorizations on (product_categorizations.category_id = categories.id)', :group => 'categories.id', :conditions => conditions )
84   - elsif asset == :enterprises
85   - if objects_ids
86   - conditions[0] += ' and products.enterprise_id in (?)'
87   - conditions << objects_ids
88   - end
89   - ProductCategory.find(
90   - :all,
91   - :select => 'categories.id, count(distinct products.enterprise_id) as total',
92   - :joins => 'inner join product_categorizations on (product_categorizations.category_id = categories.id) inner join products on (products.id = product_categorizations.product_id)',
93   - :group => 'categories.id',
94   - :conditions => conditions
95   - )
96   - else
97   - raise ArgumentError, 'only products and enterprises supported'
98   - end.inject({}) do |results,pc|
99   - results[pc.id]= pc.total.to_i
100   - results
101   - end
102   -
103   - end
104   -
105   - protected
106   -
107   - def asset_class(asset)
108   - asset.to_s.singularize.camelize.constantize
109   - end
110   -
111   - def asset_table(asset)
112   - asset_class(asset).table_name
113   - end
114   -
115   -end
app/models/event.rb
1 1 class Event < Article
2 2  
  3 + def self.type_name
  4 + _('Event')
  5 + end
  6 +
3 7 settings_items :address, :type => :string
4 8  
5 9 def link=(value)
... ...
app/models/folder.rb
1 1 class Folder < Article
2 2  
  3 + def self.type_name
  4 + _('Folder')
  5 + end
  6 +
3 7 validate :not_belong_to_blog
4 8  
5 9 def not_belong_to_blog
... ...
app/models/forum.rb
... ... @@ -2,6 +2,10 @@ class Forum &lt; Folder
2 2  
3 3 acts_as_having_posts :order => 'updated_at DESC'
4 4  
  5 + def self.type_name
  6 + _('Forum')
  7 + end
  8 +
5 9 def self.short_description
6 10 _('Forum')
7 11 end
... ...
app/models/gallery.rb
1 1 class Gallery < Folder
2 2  
  3 + def self.type_name
  4 + _('Gallery')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Gallery')
5 9 end
... ...
app/models/people_block.rb
... ... @@ -18,7 +18,7 @@ class PeopleBlock &lt; ProfileListBlock
18 18  
19 19 def footer
20 20 lambda do
21   - link_to _('View all'), :controller => 'browse', :action => 'people'
  21 + link_to _('View all'), :controller => 'search', :action => 'people'
22 22 end
23 23 end
24 24  
... ...
app/models/person.rb
1 1 # A person is the profile of an user holding all relationships with the rest of the system
2 2 class Person < Profile
3 3  
  4 + def self.type_name
  5 + _('Person')
  6 + end
  7 +
4 8 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
5 9 acts_as_accessor
6 10  
... ...
app/models/product.rb
1 1 class Product < ActiveRecord::Base
2 2 belongs_to :enterprise
  3 + has_one :region, :through => :enterprise
  4 +
3 5 belongs_to :product_category
4   - has_many :product_categorizations
5   - has_many :product_qualifiers
6   - has_many :qualifiers, :through => :product_qualifiers
  6 +
7 7 has_many :inputs, :dependent => :destroy, :order => 'position'
8 8 has_many :price_details, :dependent => :destroy
9 9 has_many :production_costs, :through => :price_details
10 10  
  11 + has_many :product_qualifiers, :dependent => :destroy
  12 + has_many :qualifiers, :through => :product_qualifiers
  13 + has_many :certifiers, :through => :product_qualifiers
  14 +
11 15 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true
12 16 validates_presence_of :product_category_id
13 17 validates_associated :product_category
... ... @@ -28,17 +32,6 @@ class Product &lt; ActiveRecord::Base
28 32 p.enterprise.product_updated if p.enterprise
29 33 end
30 34  
31   - after_save do |p|
32   - if (p.product_category && !ProductCategorization.find(:first, :conditions => {:category_id => p.product_category.id, :product_id => p.id})) || (!p.product_category)
33   - ProductCategorization.remove_all_for(p)
34   - if p.product_category
35   - ProductCategorization.add_category_to_product(p.product_category, p)
36   - end
37   - end
38   - end
39   -
40   - acts_as_searchable :fields => [ :name, :description, :category_full_name ]
41   -
42 35 xss_terminate :only => [ :name ], :on => 'validation'
43 36 xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation'
44 37  
... ... @@ -90,9 +83,11 @@ class Product &lt; ActiveRecord::Base
90 83 end
91 84  
92 85 def enterprise_updated(e)
93   - self.lat = e.lat
94   - self.lng = e.lng
95   - save!
  86 + if self.lat != e.lat or self.lng != e.lng
  87 + self.lat = e.lat
  88 + self.lng = e.lng
  89 + save!
  90 + end
96 91 end
97 92  
98 93 def url
... ... @@ -100,7 +95,7 @@ class Product &lt; ActiveRecord::Base
100 95 end
101 96  
102 97 def public?
103   - enterprise.public_profile
  98 + enterprise.public?
104 99 end
105 100  
106 101 def formatted_value(method)
... ... @@ -213,4 +208,64 @@ class Product &lt; ActiveRecord::Base
213 208 url_for({:host => enterprise.default_hostname, :controller => 'manage_products', :action => 'display_inputs_cost', :profile => enterprise.identifier, :id => self.id }.merge(Noosfero.url_options))
214 209 end
215 210  
  211 + private
  212 + def name_or_category
  213 + name ? name : product_category.name
  214 + end
  215 + def price_sort
  216 + (price.nil? or price.zero?) ? 999999999.9 : price
  217 + end
  218 + def f_category
  219 + self.product_category.name
  220 + end
  221 + def f_region
  222 + self.enterprise.region.id if self.enterprise.region
  223 + end
  224 + def self.f_region_proc(id)
  225 + c = Region.find(id)
  226 + s = c.parent
  227 + if c and c.kind_of?(City) and s and s.kind_of?(State) and s.acronym
  228 + [c.name, ', ' + s.acronym]
  229 + else
  230 + c.name
  231 + end
  232 + end
  233 + def self.f_qualifier_proc(id)
  234 + pq = ProductQualifier.find(id)
  235 + if pq.certifier
  236 + [pq.qualifier.name, _(' cert. ') + pq.certifier.name]
  237 + else
  238 + pq.qualifier.name
  239 + end
  240 + end
  241 + def f_qualifier
  242 + product_qualifier_ids
  243 + end
  244 + def public
  245 + self.public?
  246 + end
  247 + def environment_id
  248 + enterprise.environment_id
  249 + end
  250 + public
  251 +
  252 + acts_as_faceted :fields => {
  253 + :f_category => {:label => _('Related products')},
  254 + :f_region => {:label => _('City'), :proc => proc { |id| f_region_proc(id) }},
  255 + :f_qualifier => {:label => _('Qualifiers'), :proc => proc { |id| f_qualifier_proc(id) }}},
  256 + :category_query => proc { |c| "f_category:\"#{c.name}\"" },
  257 + :order => [:f_category, :f_region, :f_qualifier]
  258 +
  259 + acts_as_searchable :additional_fields => [
  260 + {:name => {:type => :text, :boost => 5.0}},
  261 + {:price_sort => {:type => :decimal}},
  262 + {:public => {:type => :boolean}},
  263 + {:environment_id => {:type => :integer}},
  264 + {:name_or_category => {:type => :string, :as => :name_or_category_sort, :boost => 2.0}},
  265 + :category_full_name ] + facets.keys.map{|i| {i => :facet}},
  266 + :include => [:enterprise, :qualifiers, :certifiers, :product_category],
  267 + :boost => proc {|p| 10 if p.enterprise.enabled},
  268 + :facets => facets.keys
  269 + handle_asynchronously :solr_save
  270 +
216 271 end
... ...
app/models/product_categorization.rb
... ... @@ -1,13 +0,0 @@
1   -class ProductCategorization < ActiveRecord::Base
2   - belongs_to :product_category, :foreign_key => 'category_id'
3   - belongs_to :product
4   -
5   - extend Categorization
6   -
7   - class << self
8   - alias :add_category_to_product :add_category_to_object
9   - def object_id_column
10   - :product_id
11   - end
12   - end
13   -end
app/models/profile.rb
... ... @@ -3,6 +3,12 @@
3 3 # which by default is the one returned by Environment:default.
4 4 class Profile < ActiveRecord::Base
5 5  
  6 + # use for internationalizable human type names in search facets
  7 + # reimplement on subclasses
  8 + def self.type_name
  9 + _('Profile')
  10 + end
  11 +
6 12 module Roles
7 13 def self.admin(env_id)
8 14 find_role('admin', env_id)
... ... @@ -70,8 +76,6 @@ class Profile &lt; ActiveRecord::Base
70 76  
71 77 acts_as_having_boxes
72 78  
73   - acts_as_searchable :additional_fields => [ :extra_data_for_index ]
74   -
75 79 acts_as_taggable
76 80  
77 81 def self.qualified_column_names
... ... @@ -179,6 +183,15 @@ class Profile &lt; ActiveRecord::Base
179 183  
180 184 has_many :abuse_complaints, :foreign_key => 'requestor_id'
181 185  
  186 + def top_level_categorization
  187 + ret = {}
  188 + self.profile_categorizations.each do |c|
  189 + p = c.category.top_ancestor
  190 + ret[p] = (ret[p] || []) + [c.category]
  191 + end
  192 + ret
  193 + end
  194 +
182 195 def interests
183 196 categories.select {|item| !item.is_a?(Region)}
184 197 end
... ... @@ -818,18 +831,66 @@ private :generate_url, :url_options
818 831 name
819 832 end
820 833  
821   - protected
  834 + private
  835 + def self.f_categories_label_proc(environment)
  836 + ids = environment.top_level_category_as_facet_ids
  837 + r = Category.find(ids)
  838 + map = {}
  839 + ids.map{ |id| map[id.to_s] = r.detect{|c| c.id == id}.name }
  840 + map
  841 + end
  842 + def self.f_categories_proc(facet, id)
  843 + id = id.to_i
  844 + c = Category.find(id)
  845 + c.name if c.top_ancestor.id == facet[:label_id].to_i or facet[:label_id] == 0
  846 + end
  847 + def f_categories
  848 + category_ids
  849 + end
822 850  
823   - def followed_by?(person)
824   - person.is_member_of?(self)
825   - end
  851 + def f_type
  852 + self.class.name
  853 + end
  854 + def self.f_type_proc(klass)
  855 + klass.constantize.type_name
  856 + end
  857 + def name_sort
  858 + name
  859 + end
  860 + def public
  861 + self.public?
  862 + end
  863 + public
826 864  
827   - def display_private_info_to?(user)
828   - if user.nil?
829   - false
830   - else
831   - (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)
832   - end
  865 + acts_as_faceted :fields => {
  866 + :f_type => {:label => _('Type'), :type_if => proc { |klass| klass.kind_of?(Enterprise) }, :proc => proc { |id| f_type_proc(id) }},
  867 + :f_categories => {:multi => true, :proc => proc {|facet, id| f_categories_proc(facet, id)},
  868 + :label => proc { |env| f_categories_label_proc(env) }, :label_abbrev => proc { |env| f_categories_label_abbrev_proc(env) }}},
  869 + :category_query => proc { |c| "f_categories:#{c.id}" },
  870 + :order => [:f_type, :f_categories]
  871 +
  872 + acts_as_searchable :additional_fields => [
  873 + {:name_sort => {:type => :string}},
  874 + {:public => {:type => :boolean}},
  875 + :extra_data_for_index ] + facets.keys.map{|i| {i => :facet}},
  876 + :boost => proc {|p| 10 if p.enabled},
  877 + :facets => facets.keys
  878 + handle_asynchronously :solr_save
  879 +
  880 + def control_panel_settings_button
  881 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  882 + end
  883 +
  884 + def followed_by?(person)
  885 + person.is_member_of?(self)
  886 + end
  887 +
  888 + def display_private_info_to?(user)
  889 + if user.nil?
  890 + false
  891 + else
  892 + (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)
833 893 end
  894 + end
834 895  
835 896 end
... ...
app/models/qualifier.rb
... ... @@ -2,14 +2,15 @@ class Qualifier &lt; ActiveRecord::Base
2 2  
3 3 belongs_to :environment
4 4  
5   - has_many :qualifier_certifiers
  5 + has_many :qualifier_certifiers, :dependent => :destroy
6 6 has_many :certifiers, :through => :qualifier_certifiers
7 7  
  8 + has_many :product_qualifiers, :dependent => :destroy
  9 + has_many :products, :through => :product_qualifiers, :source => :product
  10 +
8 11 validates_presence_of :environment_id
9 12 validates_presence_of :name
10 13  
11   - has_many :product_qualifiers, :dependent => :destroy
12   -
13 14 def <=>(b)
14 15 self.name.downcase.transliterate <=> b.name.downcase.transliterate
15 16 end
... ...
app/models/qualifier_certifier.rb
1 1 class QualifierCertifier < ActiveRecord::Base
2 2 belongs_to :qualifier
3 3 belongs_to :certifier
  4 +
  5 + validates_presence_of :qualifier
4 6 end
... ...
app/models/raw_html_article.rb
1 1 class RawHTMLArticle < TextArticle
2 2  
  3 + def self.type_name
  4 + _('HTML')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Raw HTML text article.')
5 9 end
... ...
app/models/rss_feed.rb
1 1 class RssFeed < Article
2 2  
  3 + def self.type_name
  4 + _('RssFeed')
  5 + end
  6 +
3 7 # i dont know why before filter dont work here
4 8 def initialize(*args)
5 9 super(*args)
... ...
app/models/text_article.rb
... ... @@ -3,6 +3,10 @@ class TextArticle &lt; Article
3 3  
4 4 xss_terminate :only => [ :name ], :on => 'validation'
5 5  
  6 + def self.type_name
  7 + _('Article')
  8 + end
  9 +
6 10 include Noosfero::TranslatableContent
7 11  
8 12 def self.icon_name(article = nil)
... ...
app/models/textile_article.rb
1 1 class TextileArticle < TextArticle
2 2  
  3 + def self.type_name
  4 + _('Article')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Text article with Textile markup language')
5 9 end
... ...
app/models/tiny_mce_article.rb
1 1 class TinyMceArticle < TextArticle
2 2  
  3 + def self.type_name
  4 + _('Article')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Text article with visual editor.')
5 9 end
... ...
app/models/uploaded_file.rb
... ... @@ -4,6 +4,10 @@
4 4 # of the file itself is kept. (FIXME?)
5 5 class UploadedFile < Article
6 6  
  7 + def self.type_name
  8 + _('File')
  9 + end
  10 +
7 11 track_actions :upload_image, :after_create, :keep_params => ["view_url", "thumbnail_path", "parent.url", "parent.name"], :if => Proc.new { |a| a.published? && a.image? && !a.parent.nil? && a.parent.gallery? }
8 12  
9 13 include ShortFilename
... ...
app/views/browse/_article.rhtml
... ... @@ -1,11 +0,0 @@
1   -<li class="<%= 'browse-results-type-content ' + icon_for_article(result) %>">
2   - <strong><%= link_to(result.title, result.view_url) %></strong>
3   - <div class="item_meta">
4   - <span class="item_by">
5   - <%= _('by %s') % link_to(result.author.name, result.author.url) %>
6   - </span>
7   - <span class="extra-info">
8   - <%= (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %>
9   - </span>
10   - </div>
11   -</li>
app/views/browse/_display_results.rhtml
... ... @@ -1,19 +0,0 @@
1   -<div id="browse-results">
2   -
3   - <div class='browse-results-innerbox'>
4   - <% if @results.empty? %>
5   - <div class="browse-results-type-empty">
6   - <div> <%= _('None') %> </div>
7   - </div><!-- end class="browse-results-innerbox" -->
8   - <% end %>
9   - <ul class='common-profile-list-block'>
10   - <% @results.each do |result| %>
11   - <%= render :partial => partial_for_class(result.class), :locals => {:result => result} %>
12   - <% end %>
13   - </ul>
14   - <br style='clear: both;'>
15   - </div>
16   -
17   - <br style="clear:both" />
18   -</div><!-- end id="browse-results" -->
19   -
app/views/browse/_person.rhtml
... ... @@ -1,3 +0,0 @@
1   -<%= profile_image_link result, :portrait, 'li',
2   - "<span class='adr'>#{result.city}</span>" +
3   - (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %>
app/views/browse/_profile.rhtml
... ... @@ -1 +0,0 @@
1   -<%= profile_image_link result, :portrait, 'li', @filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label') %>
app/views/browse/_search_form.rhtml
... ... @@ -1,10 +0,0 @@
1   -<% form_tag( { :controller => 'browse', :action => action}, :method => 'get', :class => 'search_form' ) do %>
2   -
3   - <div class="search-field">
4   - <span class="formfield">
5   - <%= text_field_tag 'query', @query, :size => 50 %>
6   - </span>
7   - <%= submit_button(:search, _('Search')) %>
8   - </div>
9   -
10   -<% end %>
app/views/browse/communities.rhtml
... ... @@ -1,9 +0,0 @@
1   -<%= search_page_title( @title, { :query => @query} ) %>
2   -
3   -<%= render :partial => 'search_form', :locals => {:action => 'communities'} %>
4   -
5   -<%= render :partial => 'display_results' %>
6   -
7   -<%= pagination_links @results %>
8   -
9   -<br style="clear:both" />
app/views/browse/contents.rhtml
... ... @@ -1,9 +0,0 @@
1   -<%= search_page_title( @title, { :query => @query} ) %>
2   -
3   -<%= render :partial => 'search_form', :locals => {:action => 'contents'} %>
4   -
5   -<%= render :partial => 'display_results' %>
6   -
7   -<%= pagination_links @results %>
8   -
9   -<br style="clear:both" />
app/views/browse/people.rhtml
... ... @@ -1,9 +0,0 @@
1   -<%= search_page_title( @title, { :query => @query} ) %>
2   -
3   -<%= render :partial => 'search_form', :locals => {:action => 'people'} %>
4   -
5   -<%= render :partial => 'display_results' %>
6   -
7   -<%= pagination_links @results %>
8   -
9   -<br style="clear:both" />
app/views/events/events.rhtml
1   -<h1 id='agenda-title'>
2   - <div id='agenda-toolbar'>
3   - <%= button :back, _('Back to %s') % profile.name, profile.url %>
4   - <% if user && user.has_permission?('post_content', profile) %>
5   - <%= button :new, _('New event'), myprofile_url(:controller => 'cms', :action => 'new', :type => 'Event') %>
6   - <% end %>
7   - </div>
8   - <%= _("%s's events") % profile.name %>
9   -</h1>
  1 +<h1 id='agenda-title'><%= _("%s's events") % profile.name %></h1>
  2 +
  3 +<div id='agenda-toolbar'>
  4 + <%= button :back, _('Back to %s') % profile.name, profile.url %>
  5 + <% if user && user.has_permission?('post_content', profile) %>
  6 + <%= button :new, _('New event'), myprofile_url(:controller => 'cms', :action => 'new', :type => 'Event') %>
  7 + <% end %>
  8 +</div>
  9 +
  10 +<div style="clear: both"></div>
10 11  
11 12 <%= render :partial => 'agenda' %>
... ...
app/views/layouts/_javascript.rhtml
1   -<%= javascript_include_tag :defaults, 'jquery-latest.js', 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery.cookie', 'reflection', 'add-and-join', 'jquery.tokeninput', 'report-abuse','colorbox', 'jquery-validation/jquery.validate', 'catalog', 'manage-products', :cache => 'cache-general' %>
  1 +<%= javascript_include_tag :defaults, 'jquery-latest.js',
  2 +'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox',
  3 +'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js',
  4 +'jquery.cookie', 'reflection', 'add-and-join', 'jquery.tokeninput',
  5 +'jquery.ba-bbq.min.js', 'report-abuse','colorbox', 'jquery-validation/jquery.validate', 'catalog',
  6 +'manage-products', :cache => 'cache-general' %>
2 7 <% language = FastGettext.locale %>
3   -<%= javascript_include_tag 'jquery-validation/localization/messages_'+language, 'jquery-validation/localization/methods_'+language %>
  8 +<%= javascript_include_tag 'jquery-validation/localization/messages_'+language,
  9 + 'jquery-validation/localization/methods_'+language %>
... ...
app/views/layouts/application-ng.rhtml
... ... @@ -12,6 +12,8 @@
12 12 <%= stylesheet_link_tag template_stylesheet_path %>
13 13 <%= stylesheet_link_tag icon_theme_stylesheet_path %>
14 14 <%= stylesheet_link_tag jquery_ui_theme_stylesheet_path %>
  15 + <%= stylesheet_link_tag "token-input" %>
  16 + <%= stylesheet_link_tag "token-input-facet" %>
15 17 <% @plugins.enabled_plugins.each do |plugin| %>
16 18 <% if plugin.stylesheet? %>
17 19 <%= stylesheet_tag plugin.class.public_path('style.css'), {} %>
... ... @@ -63,16 +65,10 @@
63 65 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %>
64 66 </div>
65 67 </span>
66   - <form action="/search" class="search_form clean" method="get" id="top-search">
67   - <input name="query" size="15" value="<%=_('Search...')%>"
68   - onfocus="this.form.className='focused';
69   - if(this.value=='<%=_('Search...')%>'){this.value=''}"
70   - onblur="this.form.className='';
71   - if(/^\s*$/.test(this.value)){
72   - this.value='<%=_('Search...')%>';
73   - this.form.className='clean'
74   - }" />
  68 + <form action="/search" class="search_form" method="get" class="clean">
  69 + <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" />
75 70 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div>
  71 + <%= javascript_tag 'jQuery("#user form input").hint();' %>
76 72 </form>
77 73 </div><!-- end id="user" -->
78 74  
... ...
app/views/map_balloon/product.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<div id="balloon">
  2 + <%= render :partial => 'search/product', :locals => {:product => @product} %>
  3 +</div>
... ...
app/views/map_balloon/profile.rhtml 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +<div id="balloon">
  2 + <table class='profile-info'>
  3 + <tr>
  4 + <td><div class='profile-info-picture'><%= profile_image(@profile, :thumb) %></div></td>
  5 + <td>
  6 + <strong><%= link_to(@profile.name, url_for(@profile.url)) %></strong><br/>
  7 + <% unless @profile.contact_email.nil? %>
  8 + <strong><%= _('E-Mail: ') + @profile.contact_email %></strong><br/>
  9 + <% end %>
  10 + <% unless @profile.contact_phone.nil? %>
  11 + <strong><%= _('Phone(s): ') + @profile.contact_phone %></strong><br/>
  12 + <% end %>
  13 + <% unless @profile.region.nil? %>
  14 + <strong><%= _('Location: ') + @profile.region.name %></strong><br/>
  15 + <% end %>
  16 + <% unless @profile.address.nil? %>
  17 + <strong><%= _('Address: ') + @profile.address %></strong><br/>
  18 + <% end %>
  19 + <% unless @profile.products.empty? %>
  20 + <strong><%= _('Products/Services: ') + @profile.products.map{|i| link_to(i.name, :controller => 'manage_products', :profile => @profile.identifier, :action => 'show', :id => i.id)}.join(', ') %></strong><br/>
  21 + <% end %>
  22 + <% if @profile.respond_to?(:distance) and !@profile.distance.nil? %>
  23 + <strong><%= _('Distance: ') + "%.2f%" % @profile.distance %></strong><br/>
  24 + <% end %>
  25 + </td>
  26 + </tr>
  27 + </table>
  28 +</div>
... ...
app/views/search/_article.rhtml
1   -<li class="<%= icon_for_article(article) %>">
2   - <strong><%= link_to(article.title, article.view_url) %></strong>
3   - <div class="item_meta">
4   - <% if article.last_changed_by %>
5   - <span class="cat_item_by">
6   - <%= _('by %s') % link_to(article.last_changed_by.name, article.last_changed_by.url) %>
7   - </span>
8   - <% end %>
9   - <span class="cat_item_update"><%= _('Last update: %s.') % show_date(article.updated_at) %></span>
  1 +<li class="search-article-item article-item">
  2 + <%= link_to(article.title, article.url, :class => "search-result-title") %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => article %>
10 5 </div>
  6 + <div class="search-content-second-column">
  7 +
  8 + </div>
  9 +
  10 + <div style="clear:both"></div>
11 11 </li>
... ...
app/views/search/_article_author.rhtml 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<% article = article_author %>
  2 +
  3 +<div class="search-article-author">
  4 + <div class="search-article-author-name">
  5 + <span class="search-field-label"><%= _("Author") %></span>
  6 + <%= link_to_profile article.profile.name, article.profile.identifier %>
  7 + </div>
  8 +</div>
... ...
app/views/search/_article_categories.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<div class="search-article-categories">
  2 + <span class="search-field-label"><%= _('Categories') %></span>
  3 + <span class="search-article-categories-container">
  4 + <% article_categories.each do |category| %>
  5 + <%= link_to_category category, false, :class => "search-article-category" %>
  6 + <% end %>
  7 + <span class="search-field-none"><%= _('None') if article_categories.empty? %></span>
  8 + </span>
  9 +</div>
... ...
app/views/search/_article_common.rhtml 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<% article = article_common %>
  2 +<% show_description = true if show_description.nil? %>
  3 +
  4 +<%= render :partial => 'article_author', :object => article %>
  5 +<%= render :partial => 'article_description', :object => article if show_description %>
  6 +<%= render :partial => 'article_tags', :object => article.tags %>
  7 +<%= render :partial => 'article_categories', :object => article.categories %>
  8 +<%= render :partial => 'article_last_change', :object => article %>
... ...
app/views/search/_article_description.rhtml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<% article = article_description %>
  2 +
  3 +<div class="search-article-description">
  4 + <span class="search-field-label"><%= _("Description") %></span>
  5 + <% if !article.body.blank? %>
  6 + <% body_stripped = strip_tags(article.body.to_s) %>
  7 + <span class="search-article-body"><%= excerpt(body_stripped, body_stripped.first(3), 200) %></span>
  8 + <% else %>
  9 + <span class="search-field-none"><%= _('None') %></span>
  10 + <% end %>
  11 +</div>
... ...
app/views/search/_article_last_change.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<% article = article_last_change %>
  2 +
  3 +<div class="search-article-author-changes">
  4 + <% if article.last_changed_by && article.last_changed_by != article.profile %>
  5 + <span><%= _('by %s') % link_to(article.last_changed_by.name, article.last_changed_by.url) %>&nbsp<%= _(' at %s.') % show_date(article.updated_at) %></span>
  6 + <% else %>
  7 + <span><%= _('Last update: %s.') % show_date(article.updated_at) %></span>
  8 + <% end %>
  9 +</div>
... ...
app/views/search/_article_tags.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<div class="search-article-tags">
  2 + <span class="search-field-label"><%= _('Tags') %></span>
  3 + <span class="search-article-tags-container">
  4 + <% article_tags.each do |tag| %>
  5 + <%= link_to_tag tag, :class => "search-article-tag" %>
  6 + <% end %>
  7 + <span class="search-field-none"><%= _('None') if article_tags.empty? %></span>
  8 + </span>
  9 +</div>
... ...
app/views/search/_blog.rhtml 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +<li class="search-blog article-item">
  2 + <%= link_to blog.title, blog.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => blog %>
  5 + </div>
  6 + <div class="search-content-second-column">
  7 + <div class="search-blog-items">
  8 + <span class="search-field-label"><%= _("Last posts") %></span>
  9 + <% r = blog.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %>
  10 + <% r.each do |a| %>
  11 + <%= link_to a.title, a.view_url, :class => 'search-blog-sample-item '+icon_for_article(a) %>
  12 + <% end %>
  13 + <span class="search-field-none"><%= _('None') if r.empty? %></span>
  14 + </div>
  15 +
  16 + <%= render :partial => 'article_author', :object => blog %>
  17 + <%= render :partial => 'article_tags', :object => blog.tags %>
  18 + <%= render :partial => 'article_categories', :object => blog.categories %>
  19 + <%= render :partial => 'article_last_change', :object => blog %>
  20 + </div>
  21 +
  22 + <div style="clear: both;"/></div>
  23 +</li>
... ...
app/views/search/_content.rhtml 0 → 120000
... ... @@ -0,0 +1 @@
  1 +_article.rhtml
0 2 \ No newline at end of file
... ...
app/views/search/_display_results.rhtml
1 1 <div id="search-results" class="<%= 'only-one-result-box' if @results.size == 1 %>">
2 2  
3   -<%
4   - pos2 = :odd # allow to format in a two columns layout
5   - pos3 = 3 # allow to format in a thre columns layout
6   -%>
7   -<% @order.each do |name| %>
8   - <% results = @results[name] %>
9   - <%
10   - pos3 += 1; pos3 = 1 if pos3 > 3
11   - pos2==:odd ? pos2=:even : pos2=:odd
12   - %>
13   - <% if !results.nil? and !results.empty? %>
14   - <div class="search-results-<%= name %> search-results-box <%= pos2 %> <%= 'col%s_of3' % pos3.to_s %>">
15   - <% if @controller.action_name != 'assets' %>
16   - <% if @results.size != 1 %>
17   - <h3>
18   - <%= @names[name] %>
19   - </h3>
20   - <% end %>
21   - <%# FIXME: don't hardcode an asset like this %>
22   - <% if name == :most_commented_articles %>
23   - <%= link_to( results.respond_to?(:total_entries) ? _('see all (%d)') % results.total_entries : _('see all...'),
24   - params.merge(:action => 'index',
25   - :asset => 'articles' ),
26   - :class => 'see-more' ) if @results.size > 1 %>
27   -
28   - <% else %>
29   - <%= link_to( results.respond_to?(:total_entries) ? _('see all (%d)') % results.total_entries : _('see all...'),
30   - params.merge(:action => 'index',
31   - :asset => name ),
32   - :class => 'see-more' ) if @results.size > 1 %>
  3 + <% @order.each do |name| %>
  4 + <% results = @results[name] %>
  5 + <% if !results.nil? and !results.empty? %>
  6 + <div class="search-results-<%= name %> search-results-box">
  7 +
  8 + <% if @results.size > 1 %>
  9 + <h3><%= @names[name] %></h3>
  10 + <%= link_to(results.respond_to?(:total_entries) ? _('see all (%d)') % results.total_entries : _('see all...'),
  11 + params.merge(:action => name), :class => 'see-more' ) %>
33 12 <% end %>
34   - <% end %>
35   - <% partial = partial_for_class results.first.class %>
36   - <div class="search-results-innerbox search-results-type-<%= partial %> <%= 'common-profile-list-block' if partial == 'profile' %>">
37   - <div class="search-results-innerbox2"><!-- the innerbox2 is a workarround for MSIE -->
38   - <ul>
39   - <% hit_pos = 0 %>
40   - <% results.each do |hit| %>
41   - <% next if hit.respond_to?(:visible) && !hit.visible? %>
42   - <%= render :partial => partial_for_class(hit.class),
43   -
44   - :object => hit,
45   - :locals => { :pos => ( hit_pos += 1 ) } %>
46   - <% end %>
47   - </ul>
48   - <hr />
49   - </div><!-- end class="search-results-innerbox2" -->
50   - </div><!-- end class="search-results-innerbox" -->
51   - </div><!-- end class="search-results-<%= name %>" -->
52   - <% else %>
53   - <div class="search-results-<%= name %> search-results-empty search-results-box <%= pos2 %> <%= 'col%s_of3' % pos3.to_s %>">
54   - <% if @controller.action_name != 'assets' %>
55   - <% if @results.size != 1 %>
  13 +
  14 + <% partial = partial_for_class(results.first.class.class_name.constantize) %>
  15 + <div class="search-results-innerbox search-results-type-<%= partial %> <%= 'common-profile-list-block' if partial == 'profile' %>">
  16 + <div class="search-results-innerbox2"><!-- the innerbox2 is a workarround for MSIE -->
  17 + <ul>
  18 + <% results.each do |hit| %>
  19 + <%= render :partial => partial_for_class(hit.class), :object => hit %>
  20 + <% end %>
  21 + </ul>
  22 + <hr />
  23 + </div><!-- end class="search-results-innerbox2" -->
  24 + </div><!-- end class="search-results-innerbox" -->
  25 +
  26 + </div><!-- end class="search-results-<%= name %>" -->
  27 + <% else %>
  28 + <div class="search-results-<%= name %> search-results-empty search-results-box">
  29 + <% if @results.size > 1 %>
56 30 <h3><%= @names[name] %></h3>
57 31 <% end %>
58   - <% end %>
59   - <div class="search-results-innerbox search-results-type-empty">
60   - <div> <%= _('None') %> </div>
61   - <hr />
62   - </div><!-- end class="search-results-innerbox" -->
63   - </div><!-- end class="search-results-<%= name %>" -->
  32 + <div class="search-results-innerbox search-results-type-empty">
  33 + <div> <%= _('None') %> </div>
  34 + <hr />
  35 + </div><!-- end class="search-results-innerbox" -->
  36 + </div><!-- end class="search-results-<%= name %>" -->
  37 + <% end %>
64 38 <% end %>
65   -<% end %>
66   -
67   -<br style="clear:both" />
  39 +
  40 + <div style="clear:both"></div>
68 41 </div><!-- end id="search-results" -->
69 42  
... ...
app/views/search/_enterprise_homepage.rhtml 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => 'text_article', :object => enterprise_homepage %>
... ...
app/views/search/_event.rhtml
1   -<li class="<%= icon_for_article(event) %>">
2   - <strong><%= link_to(event.title, event.url) %></strong>
3   - <div class="item_meta">
4   - <%= show_period(event.start_date, event.end_date) %>
  1 +<li class="search-event-item article-item">
  2 + <%= link_to(event.title, event.url, :class => "search-result-title") %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => event %>
5 5 </div>
  6 + <div class="search-content-second-column">
  7 + <% if event.start_date %>
  8 + <div class="searc-article-event-date">
  9 + <span class="search-field-label"><%= _('Start date') %></span>
  10 + <span class="article-item-date"><%= event.start_date %></span>
  11 + </div>
  12 + <% end %>
  13 + <% if event.end_date %>
  14 + <div class="searc-article-event-date">
  15 + <span class="search-field-label"><%= _('End date') %></span>
  16 + <span class="article-item-date"><%= event.end_date %></span>
  17 + </div>
  18 + <% end %>
  19 +
  20 + <%= render :partial => 'article_common', :object => event %>
  21 + </div>
  22 +
  23 + <div style="clear: both;"/></div>
6 24 </li>
... ...
app/views/search/_facets_menu.rhtml 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<% less_options_limit = 8 %>
  2 +
  3 +<div id="facets-menu">
  4 + <% @asset_class.map_facets_for(environment).each do |facet| %>
  5 +
  6 + <div id="facet-menu-<%= facet[:id].to_s %>" class="facet-menu">
  7 + <div class="facet-menu-label">
  8 + <%= @asset_class.facet_label(facet) %>
  9 + </div>
  10 +
  11 + <% results = @asset_class.map_facet_results(facet, params[:facet], @facets, @all_facets, :limit => less_options_limit) %>
  12 + <% facet_count = results.total_entries %>
  13 +
  14 + <% if facet_count > 0 %>
  15 + <div class="facet-menu-options facet-menu-more-options" style="display: none">
  16 + </div>
  17 +
  18 + <div class="facet-menu-options facet-menu-less-options">
  19 + <% results.each do |id, label, count| %>
  20 + <%= facet_link_html(facet, params, id, label, count) %><br />
  21 + <% end %>
  22 + </div> <br />
  23 +
  24 + <% if facet_count > less_options_limit %>
  25 + <%= link_to_function _("Options"),
  26 + "facet_options_toggle('#{facet[:id].to_s}', '#{url_for(params.merge(:action => 'facets_browse', :facet_id => facet[:id], :asset => @asset, :escape => false))}'); " +
  27 + "jQuery(this).toggleClass('facet-less-options')", :class => "facet-options-toggle" %>
  28 + <br />
  29 + <% end %>
  30 +
  31 + <% else %>
  32 + <span class="facet-any-result-found"><%= _("No filter available") %></span>
  33 + <% end %>
  34 + </div>
  35 + <% end %>
  36 +</div>
... ...
app/views/search/_facets_unselect_menu.rhtml 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<div class="facets-applied">
  2 + <% if params[:facet] and params[:facet].count > 0 %>
  3 + <span class="facets-applied-label"><%= _("Applied filters") %> </span>
  4 + <%= facet_selecteds_html_for(environment, asset_class(@asset), params) %>
  5 + <% end %>
  6 +</div>
... ...
app/views/search/_folder.rhtml 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +<li class="search-folder-item article-item">
  2 + <%= link_to folder.title, folder.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => folder %>
  5 + </div>
  6 + <div class="search-content-second-column">
  7 + <div class="search-folder-items">
  8 + <span class="search-field-label"><%= _("Last items") %></span>
  9 + <% r = folder.children.last(3) %>
  10 + <% r.each do |a| %>
  11 + <%= link_to a.title, a.view_url, :class => 'search-folder-sample-item '+icon_for_article(a) %>
  12 + <% end %>
  13 + <span class="search-field-none"><%= _('None') if r.empty? %></span>
  14 + </div>
  15 +
  16 + <%= render :partial => 'article_common', :object => folder %>
  17 + </div>
  18 +
  19 + <div style="clear:both"></div>
  20 +</li>
... ...
app/views/search/_forum.rhtml 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +<li class="search-forum-item article-item">
  2 + <%= link_to forum.title, forum.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => forum %>
  5 + </div>
  6 + <div class="search-content-second-column">
  7 + <div class="search-forum-items">
  8 + <span class="search-field-label"><%= _("Last topics") %></span>
  9 + <% r = forum.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %>
  10 + <% r.each do |a| %>
  11 + <%= link_to a.title, a.view_url, :class => 'search-forum-sample-item '+icon_for_article(a) %>
  12 + <% end %>
  13 + <span class="search-field-none"><%= _('None') if r.empty? %></span>
  14 + </div>
  15 +
  16 + <%= render :partial => 'article_common', :object => forum %>
  17 + </div>
  18 +
  19 + <div style="clear:both"></div>
  20 +</li>
... ...
app/views/search/_gallery.rhtml 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +<li class="search-gallery article-item">
  2 + <%= link_to gallery.title, gallery.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => gallery %>
  5 + </div>
  6 + <div class="search-content-second-column">
  7 + <%= render :partial => 'article_common', :object => gallery %>
  8 + </div>
  9 +
  10 + <div style="clear: both"></div>
  11 +</li>
  12 +
... ...
app/views/search/_google_maps.rhtml
1   -<%= content_tag('script', '', :src => GoogleMaps.api_url(environment.default_hostname), :type => 'text/javascript') %>
2   -
3   -
  1 +<div style="clear: both;"/></div>
4 2 <div style='text-align: center;'>
5 3 <div id="map"></div>
6 4 </div>
7 5  
8   -<script type='text/javascript'>
9   -var points = {};
10   -
11   -function putMarker(lat, lng, title, summary) {
12   - var point_str = lat + ":" + lng;
13   -
14   - if (points[point_str]) {
15   - lng += (Math.random() - 0.5) * 0.02;
16   - lat += (Math.random() - 0.5) * 0.02;
17   - } else {
18   - points[point_str] = true;
19   - }
20   -
21   - var point = new GLatLng(lat, lng);
22   - var options = { 'title' : title, 'icon' : icon };
23   - var marker = new GMarker(point, options);
24   - map.addOverlay(marker);
25   - GEvent.addListener(marker, 'click', function() {
26   - map.openInfoWindowHtml(point, summary);
27   - });
28   - bounds.extend(point);
29   -}
30   -
31   -window.unload = function() {
32   - GUnload();
33   -};
34   -
35   -if (GBrowserIsCompatible()) {
36   - var map = new GMap2(document.getElementById("map"));
37   -
38   - new GKeyboardHandler(map);
39   - map.addControl(new GLargeMapControl());
40   - map.addControl(new GMapTypeControl());
41   -
42   - centerPoint = new GLatLng(-15.0, -50.1419);
43   - map.setCenter(centerPoint, <%= GoogleMaps.initial_zoom.to_json %>);
44   - var bounds = new GLatLngBounds();
45   -
46   - var baseIcon = new GIcon();
47   - baseIcon.iconSize=new GSize(32,32);
48   - baseIcon.shadowSize=new GSize(36,32);
49   - baseIcon.iconAnchor=new GPoint(16,32);
50   - baseIcon.infoWindowAnchor=new GPoint(16,0);
51   - <%
52   - icon = default_or_themed_icon("/images/icons-map/enterprise.png")
53   - icon_shadow = default_or_themed_icon("/images/icons-map/enterprise_shadow.png")
54   - %>
55   - var icon = new GIcon(baseIcon, "<%= icon %>", null, "<%= icon_shadow %>");
  6 +<%= content_tag('script', '', :src => GoogleMaps.api_url(environment.default_hostname), :type => 'text/javascript') %>
  7 +<%= javascript_include_tag('google_maps') %>
56 8  
57 9 <%
58   - @results.each do |name,results|
59   - results.each do |item|
60   - if item.lat && item.lng
61   - %>
62   - putMarker(<%= item.lat.to_json %>, <%= item.lng.to_json %>, <%= item.name.to_json %>, <%= display_item_map_info(item).to_json %>);
63   - <%
64   - end
65   - end
66   - end
  10 + icon = default_or_themed_icon("/images/icons-map/enterprise.png")
  11 + icon_shadow = default_or_themed_icon("/images/icons-map/enterprise_shadow.png")
67 12 %>
68   -}
69 13  
70   - map.setZoom(map.getBoundsZoomLevel(bounds));
71   - map.setCenter(bounds.getCenter());
  14 +<script type='text/javascript'>
  15 + mapLoad(<%= GoogleMaps.initial_zoom.to_json %>);
  16 +
  17 + mapBaseIcon = new GIcon();
  18 + mapBaseIcon.iconSize=new GSize(32,32);
  19 + mapBaseIcon.shadowSize=new GSize(36,32);
  20 + mapBaseIcon.iconAnchor=new GPoint(16,32);
  21 + mapBaseIcon.infoWindowAnchor=new GPoint(16,0);
  22 + icon = new GIcon(mapBaseIcon, "<%= icon %>", null, "<%= icon_shadow %>");
  23 +
  24 + <% @results.each do |name,results| %>
  25 + <% results.each do |item| %>
  26 + <% if item.lat && item.lng %>
  27 + mapPutMarker(<%= item.lat.to_json %>, <%= item.lng.to_json %>, <%= item.name.to_json %>, icon,
  28 + '<%= url_for(:controller => :map_balloon, :action => name.to_s.singularize, :id => item.id) %>');
  29 + <% end %>
  30 + <% end %>
  31 + <% end %>
  32 +
  33 + mapCenter();
72 34 </script>
... ...
app/views/search/_image.rhtml 0 → 100644
... ... @@ -0,0 +1,47 @@
  1 +<div class="search-image-container">
  2 +
  3 + <% if image.is_a? UploadedFile %>
  4 + <% extension = image.filename[(image.filename.rindex('.')+1)..-1].downcase %>
  5 + <% if ['jpg', 'jpeg', 'gif', 'png', 'tiff', 'svg'].include? extension %>
  6 + <%= link_to '', image.view_url, :class => "search-image-pic", :style => 'background-image: url(%s)'% image.public_filename(:thumb) %>
  7 + <% if image.width && image.height %>
  8 + <% javascript_tag do %>
  9 + image = jQuery('script').last().parent().find('.search-image-pic');
  10 + des_width = parseInt(image.css('width'));
  11 + des_height = parseInt(image.css('height'));
  12 +
  13 + width = <%= image.width %>;
  14 + height = <%= image.height %>;
  15 + scale_factor = width > height ? des_width/width : des_height/height;
  16 +
  17 + image.css({'width' : scale_factor*width +'px', 'height' : scale_factor*height+'px'});
  18 + <% end %>
  19 + <% end %>
  20 + <% elsif ['pdf'].include? extension %>
  21 + <%= link_to '', image.view_url, :class => 'search-image-pic icon-application-pdf' %>
  22 + <% elsif ['doc', 'docx', 'odt', 'rtf', 'txt', 'html', 'htm'].include? extension %>
  23 + <%= link_to '', image.view_url, :class => 'search-image-pic icon-application-vnd-oasis-opendocument-text' %>
  24 + <% elsif ['xls', 'xlsx', 'ods', 'csv', 'tsv', 'tab'].include? extension %>
  25 + <%= link_to '', image.view_url, :class => 'search-image-pic icon-application-vnd-oasis-opendocument-spreadsheet' %>
  26 + <% end %>
  27 + <% elsif image.is_a? Gallery %>
  28 + <div class="search-gallery-items">
  29 + <% r = image.children.find(:all, :order => :updated_at, :conditions => ['type = ?', 'UploadedFile']).last(3) %>
  30 + <% if r.length > 0 %>
  31 + <% r.each do |i| %>
  32 + <%= link_to '', i.view_url, :class => "search-image-pic", :style => 'background-image: url(%s)'% i.public_filename(:thumb) %>
  33 + <% end %>
  34 + <% else %>
  35 + <div class="search-no-image"><%= _('No image') %></div>
  36 + <% end %>
  37 + </div>
  38 + <% elsif image.is_a? Product %>
  39 + <% if image.image %>
  40 + <%= link_to '', product_path(image), :class => "search-image-pic", :style => 'background-image: url(%s)'% image.default_image(:thumb) %>
  41 + <% else %>
  42 + <div class="search-no-image"><%= _('No image') %></div>
  43 + <% end %>
  44 + <% else %>
  45 + <div class="search-content-type-icon icon-content-<%=image.class.to_s.underscore.dasherize%>"></div>
  46 + <% end %>
  47 +</div>
... ...
app/views/search/_product.rhtml
1   -<%# FIXME add more information %>
2   -
3   -<%
4   -product_item_pos = 0 if ! product_item_pos
5   -product_item_pos += 1
6   -%>
7   -
8 1 <% extra_content = @plugins.map(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
9 2 <% extra_properties = @plugins.map(:asset_product_properties, product)%>
10 3  
11   -<li class="product-item <%= ( pos % 2 == 0 ) ? 'odd' : 'even' %>">
12   - <%= link_to_product product, :class => 'product-pic', :style => 'background-image:url(%s)' % product.default_image(:minor) %>
13   - <strong>
14   - <%= link_to_product product %>
15   - </strong>
16   - <ul>
17   - <li> <%= _('Price: %s') % (product.price ? product.price : _('Not informed')) %> </li>
18   - <% if product.enterprise && product.display_supplier_on_search? %>
19   - <li> <%= _('Supplier: %s') % link_to_homepage(product.enterprise.name, product.enterprise.identifier) %> </li>
20   - <% end %>
  4 +<li class="search-product-item">
  5 + <div class="search-product-item-first-column">
  6 + <%= render :partial => 'image', :object => product %>
21 7  
22   - <li> <%= _('Category:') + ' ' + link_to_product_category(product.product_category) %> </li>
  8 + <% if product.available %>
  9 + <% if product.price && product.price > 0 %>
  10 + <% has_discount = product.discount && product.discount > 0 %>
  11 + <% if product.price %>
  12 + <span class="search-product-price-textid"><%=_("from") if has_discount %></span><%= price_span(product.price, :class => "search-product-price " + (has_discount ? 'with-discount' : '')) %>
  13 + <% if has_discount %>
  14 + <span class="search-product-price-textid"><%=_("by")%></span><%= price_span(product.price_with_discount, :class => "search-product-price") %>
  15 + <% end %>
  16 + <% if product.unit %>
  17 + <span class="search-product-unit">&nbsp;<%= _('/') %>&nbsp;<%= product.unit.name %></span>
  18 + <% end %>
  19 + <% end %>
  20 + <div class="search-product-inputs-info">
  21 + <% if product.inputs.count > product.inputs.collect(&:is_from_solidarity_economy).count(nil) %>
  22 + <% se_i = t_i = 0 %>
  23 + <% product.inputs.each{ |i| t_i += 1; se_i += 1 if i.is_from_solidarity_economy } %>
  24 + <% p = case (se_i.to_f/t_i)*100 when 0..24.999 then ["0", _("0%")]; when 25..49.999 then ["25", _("25%")]; when 50..74.999 then ["50", _("50%")]; when 75..100 then ["75", _("100%")]; end %>
  25 + <div class="search-product-percentage-from-solidarity-economy search-product-ecosol-percentage-icon-<%=p[0]%>" title="<%=_('Percentage of inputs from solidarity economy')%>">
  26 + <%= p[1] %>
  27 + </div>
  28 + <% end %>
23 29  
24   - <% extra_properties.each do |property| %>
25   - <li><%= property[:name] + ': ' + instance_eval(&property[:content]) %></li>
  30 + <% if product.inputs.count == product.inputs.collect(&:has_price_details?).count(true) %>
  31 + <% title = product.inputs.map{ |i|
  32 + '<div class="search-product-input-dots-to-price">' +
  33 + '<div class="search-product-input-name">' + i.product_category.name + '</div>' +
  34 + price_span(i.price_per_unit*i.amount_used, :class => 'search-product-input-price') +
  35 + '</div>' }.join('') %>
  36 + <%= link_to_function _("Open Price"), '', :title => title, :class => "search-product-price-details" %>
  37 + <% end %>
  38 + </div>
  39 + <% end %>
  40 + <% else %>
  41 + <span class="product-not-available"><%= _('Not available') %></div>
26 42 <% end %>
27   - </ul>
  43 +
  44 + </div>
  45 + <div class="search-product-item-second-column">
  46 + <%= link_to_product product, :class => 'search-result-title' %>
  47 + <div class="search-product-supplier">
  48 + <span class="search-field-label"><%= _('Supplier') %> </span><%= link_to_homepage(product.enterprise.name, product.enterprise.identifier) %>
  49 + </div>
  50 + <div class="search-product-description">
  51 + <% if product.description %>
  52 + <% desc_stripped = strip_tags(product.description) %>
  53 + <span class="search-field-label"><%= _('Description') %> </span><%= excerpt(desc_stripped, desc_stripped.first(3), 300) %>
  54 + <% end %>
  55 + </div>
  56 + </div>
  57 + <div class="search-product-item-third-column">
  58 + <div class="search-product-region">
  59 + <% if product.enterprise.region %>
  60 + <span class="search-field-label"><%= _('City') %></span>
  61 + <br /><%= city_with_state(product.enterprise.region) %>
  62 + <% end %>
  63 + </div>
  64 + <div class="search-product-qualifiers">
  65 + <% if product.product_qualifiers.count > 0 %>
  66 + <span class="search-field-label"><%= _('Qualifiers') %></span>
  67 + <% product.product_qualifiers.each do |pq| %>
  68 + <% if pq.qualifier %>
  69 + <span class="search-product-qualifier"><%= pq.qualifier.name + (pq.certifier.nil? ? _(";") : '') %></span>
  70 + <% end %>
  71 + <% if pq.certifier %>
  72 + <span class="search-product-certifier">&nbsp;<%= _('cert. ') + pq.certifier.name + _(";") %></span>
  73 + <% end %>
  74 + <% end %>
  75 + <% end %>
  76 + </div>
  77 + </div>
28 78  
29 79 <%= extra_content.join('\n') %>
  80 + <% extra_properties.each do |property| %>
  81 + <div><%= property[:name] + ': ' + instance_eval(&property[:content]) %></div>
  82 + <% end %>
30 83  
  84 + <br /><br />
31 85 </li>
... ...
app/views/search/_product_categories_menu.rhtml
... ... @@ -1,42 +0,0 @@
1   -<% if @product_category %>
2   - <h3 class="current-cat-path">
3   - <%= @product_category.hierarchy.map {|cat| ((cat == @product_category) ? content_tag('span', cat.name) : link_to((cat.name), params.merge({:product_category => cat.id}))) }.join(' &rarr; ') %>
4   - </h3>
5   -<% end %>
6   -
7   -<% if product_categories_menu %>
8   -
9   -<div id="product-categories-menu">
10   - <ul>
11   - <% if product_categories_menu.empty? %>
12   - <% if @product_category %>
13   - <li class="cat-empty"> <%= _('There is no sub-categories for %s.') % @product_category.name %> </li>
14   - <% else %>
15   - <li class="cat-empty"> <%= _('There is no categories.') %> </li>
16   - <% end %>
17   - <% end %>
18   - <% product_categories_menu.each do |cat, hits, childs| %>
19   - <li class="cat-parent" >
20   - <%= link_to(
21   - cat.name + " " + content_tag('small', "(#{hits})"),
22   - params.merge({:product_category => cat.id})
23   - ) %>
24   - <% if !childs.blank? %>
25   - <div>
26   - <ul>
27   - <% childs.each do |child, child_hits| %>
28   - <li class="cat-child"> <%= link_to(
29   - child.name + " " + content_tag('small', "(#{child_hits})"),
30   - params.merge({:product_category => child.id})
31   - ) %> </li>
32   - <% end %>
33   - </ul>
34   - </div>
35   - <% end %>
36   - </li>
37   - <% end %>
38   - </ul>
39   -</div>
40   -
41   -<% end %>
42   -
app/views/search/_profile.rhtml
1   -<%= profile_image_link profile, :portrait %>
  1 +<li class="search-profile-item">
  2 + <% if @empty_query or @results.size > 1 or !profile.enterprise? %>
  3 + <%= profile_image_link profile, :portrait, 'div' %>
  4 + <% else %>
  5 + <div class="search-enterprise-item">
  6 + <div class="search-enterprise-item-column-left">
  7 + <%= profile_image_link profile, :portrait, 'div' %>
  8 + </div>
  9 + <div class="search-enterprise-item-column-right">
  10 + <%= link_to_homepage(profile.name, profile.identifier, :class => "search-result-title") %>
  11 + <div class="search-enterprise-description">
  12 + <% if profile.description %>
  13 + <% body_stripped = strip_tags(profile.description) %>
  14 + <% elsif profile.home_page and profile.home_page.body %>
  15 + <% body_stripped = strip_tags(profile.home_page.body) %>
  16 + <% end %>
  17 + <%= excerpt(body_stripped, body_stripped.first(3), 200) if body_stripped %>
  18 + </div>
  19 + <div class="search-enterprise-region">
  20 + <span class="search-enterprise-region-label"><%= _("City") %></span>
  21 + <% if profile.region %>
  22 + <span class="search-enterprise-region-name"><%= city_with_state(profile.region) %></span>
  23 + <% else %>
  24 + <% end %>
  25 + </div>
  26 + <% if !profile.description.blank? %>
  27 + <div><%= profile.description %></div><br />
  28 + <% end %>
  29 +
  30 + <div class="search-enterprise-categorization">
  31 + <% profile.top_level_categorization.each do |parent, children| %>
  32 + <div class="search-enterprise-category-<%=parent.id%> search-enterprise-category">
  33 + <span class="search-enterprise-categorization-parent"><%= parent.name %></span>
  34 + <span class="search-enterprise-categorization-children"><%= children.collect(&:name).join(', ') %></span>
  35 + </div>
  36 + <% end %>
  37 + </div>
  38 + </div>
  39 + <hr class="clearfix" />
  40 + </div>
  41 + <% end %>
  42 +</li>
... ...
app/views/search/_results_header.rhtml 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +<div class="search-results-header <%= "search-no-results" if @results[@asset].nil? or @results[@asset].length == 0 %>">
  2 + <% if !@empty_query %>
  3 + <div class="search-results-header-information">
  4 + <%= label_total_found(@asset, @results[@asset].total_entries) %>
  5 + <% if params[:display] != 'map' %>
  6 + <span class="current-page"><%= _("Showing page %s of %s") % [@results[@asset].current_page, @results[@asset].total_pages] %></span>
  7 + <% end %>
  8 + </div>
  9 +
  10 + <div class="search-results-header-facets-order-by">
  11 + <%= facets_unselect_menu(@asset) %>
  12 + <%= order_by(@asset) if params[:display] != 'map' %>
  13 + </div>
  14 + <% else %>
  15 + <div id='search-filter-title'><%= @filter_title if @filter_title %></div>
  16 + <% end %>
  17 +
  18 + <div style="clear: both"></div>
  19 +</div>
... ...
app/views/search/_search_form.rhtml
1 1 <div class='search-form'>
2   -<% simple_search = false unless defined? simple_search %>
3 2  
4   -<% form_tag( { :controller => 'search', :action => 'index', :asset => nil, :category_path => ( @category ? @category.explode_path : [] ) },
5   - :method => 'get', :class => 'search_form' ) do %>
6   - <%= '<h3>%s</h3>' % form_title if defined? form_title %>
7   -
8   - <%= hidden_field_tag :display, params[:display] %>
9   -
10   - <%= hidden_field_tag :asset, params[:asset] %>
11   -
12   - <div class="search-field">
13   - <span class="formfield">
14   - <%= text_field_tag 'query', @query, :id => ( lightbox? ? 'popup-search-input' : '' ), :size => 50 %>
15   - <%= javascript_tag 'setTimeout("$(\"popup-search-input\").focus()", 10 )' if lightbox? %>
16   - </span>
17   - <%= submit_button(:search, _('Search'), :name => :search_whole_site_no) %>
18   - <% if @category %>
19   - <%= submit_button(:search, _('Search in whole site'), :name => :search_whole_site_yes) %>
20   - <% end %>
21   - </div>
22   -
23   - <div id='advanced-search-options' style="display: <%= simple_search ? 'none' : 'block' %>">
24   - <div class="search-options search-within">
25   - <h4><%= _('Search within:') %></h4>
26   - <br style='clear:left'/>
27   - <div>
28   - <span class="formfield">
29   - <%= select_city %>
30   - </span>
31   - <span class="formfield">
32   - <%= labelled_select(_('Distance:'), 'radius', :first, :last, nil, [15, 30, 50, 100, 150, 200, 300, 400, 500, 1000].map{|n|[n, n.to_s + 'km']}) %>
33   - </span>
  3 + <% form_tag( { :controller => 'search', :action => @asset ? @asset : 'index', :asset => nil, :category_path => ( @category ? @category.explode_path : [] ) },
  4 + :method => 'get', :class => 'search_form' ) do %>
  5 +
  6 + <%= hidden_field_tag :display, params[:display] %>
  7 +
  8 + <div class="search-field">
  9 + <span class="formfield">
  10 + <%= text_field_tag 'query', @query, :id => 'search-input', :size => 50 %>
  11 + <%= javascript_tag "jQuery('#search-input').attr('title', \"#{hint}\").hint()" if defined?(hint) %>
  12 + <%= javascript_tag "jQuery('.search_form').submit(function() {
  13 + if (jQuery('#search-input').val().length < 3) {
  14 + jQuery('#search-empty-query-error').slideDown(200).delay(2500).slideUp(200);
  15 + return false;
  16 + }
  17 + });" %>
  18 + </span>
  19 +
  20 + <%= submit_button(:search, _('Search')) %>
  21 +
  22 + <div id="search-empty-query-error">
  23 + <%= _("Type more than 2 characters to start a search") %>
34 24 </div>
35   - </div><!-- fim class="search-options" -->
36   -
37   - <div class="search-options search-for">
38   - <h4><%= _('Search for:') %></h4>
39   - <ul>
40   - <% @search_in.map { |t,n| [t,getterm(n)] } .
41   - sort_by(&:last).each do |thing, name| %>
42   - <li>
43   - <%= labelled_check_box name, 'find_in[]', thing.to_s, @searching[thing.to_sym] %>
44   - </li>
45   - <% end %>
46   - </ul>
47   - <br style="clear:both" />
48   - </div><!-- fim class="search-options" -->
49   - </div><!-- end id="advanced-search-options" -->
  25 + </div>
  26 +
  27 + <% if @empty_query %>
  28 + <% hint = environment.search_hints[@asset] %>
  29 + <% if hint and !hint.blank? %>
  30 + <span class="search-hint"><%= hint %></span>
  31 + <% end %>
  32 + <% end %>
50 33  
51   - <% if simple_search %>
52   - <%= link_to_function(_('More options'), nil, :id => 'advanced_search_link') do |page|
53   - page['advanced_search_link'].hide
54   - page['advanced-search-options'].toggle
55   - end %>
56 34 <% end %>
57 35  
58   - <% if lightbox?; button_bar do %>
59   - <%= lightbox_close_button _('Close') %>
60   - <% end; end %>
61   -
62   -<% end %>
63   -
64   -</div> <!-- id="search-form" -->
  36 + <div style="clear: both"></div>
  37 +</div>
... ...
app/views/search/_text_article.rhtml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<li class="search-text-article-item article-item">
  2 + <%= link_to(text_article.title, text_article.url, :class => "search-result-title") %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => text_article %>
  5 + </div>
  6 + <div class="search-content-second-column">
  7 + <%= render :partial => 'article_common', :object => text_article %>
  8 + </div>
  9 +
  10 + <div style="clear: both"></div>
  11 +</li>
... ...
app/views/search/_uploaded_file.rhtml 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +<li class="search-uploaded-file-item article-item">
  2 + <%= link_to uploaded_file.filename, uploaded_file.view_url, :class => 'search-result-title' %>
  3 + <hr class="clear" />
  4 +
  5 + <div class="search-content-first-column">
  6 + <%= render :partial => 'image', :object => uploaded_file %>
  7 + </div>
  8 +
  9 + <div class="search-uploaded-file-second-column">
  10 + <%= render :partial => 'article_author', :object => uploaded_file %>
  11 +
  12 + <div class="search-uploaded-file-description">
  13 + <% if !uploaded_file.body.blank? %>
  14 + <span class="search-field-label"><%= _("Description") %></span>
  15 + <% body = strip_tags(uploaded_file.body.to_s) %>
  16 + <%= excerpt(body, body.first(3), 200) %>
  17 + <% end %>
  18 + </div>
  19 +
  20 + <div class="search-uploaded-file-parent">
  21 + <% if uploaded_file.parent && uploaded_file.parent.published? %>
  22 + <% if uploaded_file.parent.gallery? %>
  23 + <span class="search-field-label"><%= _("Gallery") %></span>
  24 + <% else %>
  25 + <span class="search-field-label"><%= _("Folder") %></span>
  26 + <% end %>
  27 + <%= link_to uploaded_file.parent.name, {:controller => 'content_viewer', :profile => uploaded_file.profile.identifier, :action => 'view_page', :page => [uploaded_file.parent.slug]} %>
  28 + <% end %>
  29 + </div>
  30 +
  31 + <%= render :partial => 'article_tags', :object => uploaded_file.tags %>
  32 + <%= render :partial => 'article_categories', :object => uploaded_file.categories %>
  33 + <%= render :partial => 'article_last_change', :object => uploaded_file %>
  34 + </div>
  35 +
  36 + <div style="clear:both"></div>
  37 +</li>
... ...
app/views/search/articles.rhtml
1   -<%= search_page_title( _('Articles'), { :query => @query,
2   - :category => @category ? @category.name : nil,
3   - :total_results => @total_results,
4   - :region => @region ? @region.name : nil,
5   - :distance => @radius } ) %>
  1 +<%= search_page_title( @titles[:articles], @category ) %>
6 2  
7   -<%= search_page_link_to_all( { :asset => params[:asset],
8   - :category => @category }) %>
  3 +<div id="search-column-left">
  4 + <% if !@empty_query %>
  5 + <%= facets_menu(:articles, @facets) %>
  6 + <% end %>
  7 +</div>
9 8  
10   -<%= render :partial => 'search_form', :locals => { :form_title => @query.blank? ? _('Search') : _("Refine your search"), :simple_search => true } %>
  9 +<div id="search-column-right">
  10 + <%= render :partial => 'search_form', :locals => { :hint => _('Type the title, author or content desired') } %>
  11 + <%= render :partial => 'results_header' %>
11 12  
12   -<%# FIXME ARMENGUE %>
13   -<%= display_results(false) %>
  13 + <% if !@empty_query or @filter %>
  14 + <%= display_results %>
  15 + <%= pagination_links @results.values.first %>
  16 + <% end %>
  17 +</div>
14 18  
15   -<%= pagination_links @results.values.first %>
16   -
17   -<br style="clear:both" />
  19 +<div style="clear: both"></div>
... ...
app/views/search/category_index.rhtml
... ... @@ -1,23 +0,0 @@
1   -<div id="view-category">
2   -
3   - <div id="category-image"><%= image_tag(@category.image.public_filename(:thumb), :id => 'category-image') if @category.image %></div>
4   - <h1 id="category-name"><%= @category.name %></h1>
5   -
6   - <%= render :partial => 'display_results' %>
7   -
8   - <div id="category-childs">
9   - <h2> <%= _('Sub-categories') %> </h2>
10   - <% if @category.children.empty? %>
11   - <strong id="cat-no-child"><%= _('No sub-categories') %></strong>
12   - <% else %>
13   - <ul>
14   - <% @category.children.each do |c| %>
15   - <li> <%= link_to_category c, false %> </li>
16   - <% end %>
17   - </ul>
18   - <% end %>
19   - </div><!-- end id="child-categories" -->
20   -
21   -<br style="clear:both" />
22   -</div><!-- end id="view-category" -->
23   -
app/views/search/category_index.rhtml 0 → 120000
... ... @@ -0,0 +1 @@
  1 +index.rhtml
0 2 \ No newline at end of file
... ...
app/views/search/cities.rhtml
... ... @@ -1,7 +0,0 @@
1   -<% if !@cities.empty? %>
2   - <% @cities.each do |city| %>
3   - <option value=<%= city.id.to_s.inspect %>><%= city.name %></option>
4   - <% end %>
5   -<% else %>
6   - <option value=''><%= _('No City') %></option>
7   -<% end%>
app/views/search/communities.rhtml
1   -<%= search_page_title( __('Communities'), { :query => @query,
2   - :category => @category ? @category.name : nil,
3   - :total_results => @total_results,
4   - :region => @region ? @region.name : nil,
5   - :distance => @radius } ) %>
  1 +<%= search_page_title( @titles[:communities], @category ) %>
6 2  
7   -<%= search_page_link_to_all( { :asset => params[:asset],
8   - :category => @category }) %>
9   -<%= render :partial => 'search_form', :locals => { :form_title => @query.blank? ? _('Search') : _("Refine your search"), :simple_search => true } %>
  3 +<div id='search-column-left'>
  4 + <% if logged_in? %>
  5 + <% button_bar do %>
  6 + <%# FIXME shouldn't the user create the community in the current environment instead of going to its home environment? %>
  7 + <%= button(:add, __('New community'), user.url.merge(:controller => 'memberships', :action => 'new_community')) %>
  8 + <% end %>
  9 + <% end %>
10 10  
11   -<% if logged_in? %>
12   - <% button_bar do %>
13   - <%# FIXME shouldn't the user create the community in the current environment instead of going to its home environment? %>
14   - <%= button(:add, __('New community'), user.url.merge(:controller => 'memberships', :action => 'new_community')) %>
  11 + <% if !@empty_query %>
  12 + <%= facets_menu(:communities, @facets) %>
15 13 <% end %>
16   -<% end %>
  14 +</div>
17 15  
18   -<div id='search-results-and-pages'>
19   - <%# FIXME ARMENGUE %>
20   - <%= display_results(false) %>
  16 +<div id='search-column-right'>
  17 + <%= render :partial => 'search_form', :locals => { :hint => _("Type words about the community you're looking for") } %>
  18 + <%= render :partial => 'results_header' %>
21 19  
  20 + <%= display_results %>
22 21 <%= pagination_links @results.values.first %>
23 22 </div>
24 23  
25   -<br style="clear:both" />
  24 +<div style="clear: both"></div>
26 25  
... ...
app/views/search/complete_region.rhtml
... ... @@ -1,7 +0,0 @@
1   -<% unless @regions.empty? %>
2   - <ul>
3   - <% for region in @regions %>
4   - <li><%= region.name %></li>
5   - <% end %>
6   - </ul>
7   -<% end%>
app/views/search/contents.rhtml 0 → 120000
... ... @@ -0,0 +1 @@
  1 +articles.rhtml
0 2 \ No newline at end of file
... ...
app/views/search/enterprises.rhtml
1   -<h1>
2   - <% if !@query.blank? %>
3   - <%=h @category ? (__('Enterprise results for "%{query}" in "%{category}"') % { :query => @query, :category => @category.name}) : (__('Enterprise results for "%s"') % @query) %>
4   - <% else %>
5   - <%=h @category ? (__('Enterprises in "%s"') % @category.name) : __('Enterprises') %>
6   - <% end %>
7   -</h1>
8   -
9   -<% if @radius && @region %>
10   - <h2><%=h (_('Within %s km from %s') % [@radius, @region.name]) %><h2>
11   -<% end %>
12   -
13   -<%= search_page_link_to_all( { :asset => params[:asset],
14   - :category => @category }) %>
15   -
16   -<%= render :partial => 'search_form', :locals => { :form_title => _("Refine your search"), :simple_search => true } %>
  1 +<%= search_page_title( @titles[:enterprises], @category ) %>
17 2  
18   -<% if logged_in? && environment.enabled?('enterprise_registration') %>
19   - <% button_bar do %>
20   - <%= button(:add, __('New enterprise'), {:controller => 'enterprise_registration'}) %>
  3 +<div id="search-column-left">
  4 + <% if logged_in? && environment.enabled?('enterprise_registration') %>
  5 + <% button_bar do %>
  6 + <%= button(:add, __('New enterprise'), {:controller => 'enterprise_registration'}) %>
  7 + <% end %>
21 8 <% end %>
22   -<% end %>
23 9  
24   -<% if @categories_menu %>
25   -<div class="has_cat_list">
26   -<% end %>
27   -
28   -<% cache(:action => 'assets', :asset => 'enterprises', :category_path => params[:category_path], :query => @query, :product_category => @product_category, :region => @region, :radius => params[:radius]) do %>
29   - <%= product_categories_menu(:enterprises, @product_category, @result_ids) %>
30   -<% end %>
31   -
32   -<%= display_results %>
  10 + <% if !@empty_query %>
  11 + <% button_bar do %>
  12 + <%= display_map_list_button %>
  13 + <% end %>
  14 + <%= facets_menu(:enterprises, @facets) %>
  15 + <% end %>
  16 +</div>
33 17  
34   -<% if @categories_menu %>
35   -</div><!-- class="has_cat_list" -->
36   -<% end %>
  18 +<div id="search-column-right">
  19 + <%= render :partial => 'search_form', :locals => { :hint => _("Type words about the enterprise you're looking for") } %>
  20 + <%= render :partial => 'results_header' %>
37 21  
38   -<%= pagination_links @results[:enterprises] %>
  22 + <%= display_results(true) %>
  23 + <% if params[:display] != 'map' %>
  24 + <%= pagination_links @results[:enterprises] %>
  25 + <% end %>
  26 +</div>
39 27  
40   -<br style="clear:both" />
  28 +<div style="clear: both"></div>
... ...
app/views/search/events.rhtml
1   -<h1>
2   - <%= _("%s's events") % @environment.name %>
3   - <% if @category %>
4   - :<small><%= @category.name %></small>
5   - <% end %>
6   -</h1>
  1 +<%= search_page_title( @titles[:events], @category ) %>
7 2  
8 3 <%= render :partial => 'events/agenda' %>
... ...
app/views/search/facets_browse.rhtml 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<% results = @asset_class.map_facet_results(@facet, params[:facet], @facets, @all_facets) %>
  2 +
  3 +<% array = [] %>
  4 +<% @asset_class.facet_result_sort(@facet, results, :alphabetically).each do |id, label, count| %>
  5 + <% array << {:id => id, :name => facet_link_html(@facet, params.merge(:controller => 'search', :action => @asset), id, label, count)} %>
  6 +<% end %>
  7 +
  8 +<%= facet_javascript('facet-input-'+@facet[:id].to_s, @facet, array) %>
... ...
app/views/search/index.rhtml
1   -<div id="search-page">
  1 +<div id="search-page" class="<%= "view-category" if @category %>">
2 2  
3   -<%= search_page_title(_('Search Results'), :query => CGI.escapeHTML(@query), :category => @category ? @category.name : nil, :total_results => @total_results) %>
  3 + <% if @category %>
  4 + <div id="category-image"><%= image_tag(@category.image.public_filename(:thumb), :id => 'category-image') if @category.image %></div>
  5 + <% end %>
4 6  
5   -<%= render :partial => 'search_form', :locals => { :form_title => _("Refine your search"), :simple_search => true } %>
  7 + <%= search_page_title(_('Search Results'), @category) %>
  8 + <%= render :partial => 'search_form', :locals => { :hint => '' } %>
  9 + <%= category_context(@category, params) %>
  10 + <%= display_results %>
  11 +
  12 + <div id="category-childs">
  13 + <% if @category %>
  14 + <h2> <%= _('Sub-categories') %> </h2>
  15 + <% if @category.children.empty? %>
  16 + <strong id="cat-no-child"><%= _('No sub-categories') %></strong>
  17 + <% else %>
  18 + <ul>
  19 + <% @category.children.each do |c| %>
  20 + <li> <%= link_to_category c, false %> </li>
  21 + <% end %>
  22 + </ul>
  23 + <% end %>
  24 + <% end %>
  25 + </div>
6 26  
7   -<%= render :partial => 'display_results' %>
  27 +</div>
8 28  
9   -</div><!-- end id="search-page" -->
10   -<br style="clear:both" />
  29 +<div style="clear: both"></div>
... ...
app/views/search/people.rhtml
1   -<%= search_page_title( _('People'), { :query => @query,
2   - :category => @category ? @category.name : nil,
3   - :total_results => @total_results,
4   - :region => @region ? @region.name : nil,
5   - :distance => @radius } ) %>
  1 +<%= search_page_title( @titles[:people], @category ) %>
6 2  
7   -<%= search_page_link_to_all( { :asset => params[:asset],
8   - :category => @category }) %>
  3 +<div id='search-column-left'>
  4 + <% if !@empty_query %>
  5 + <%= facets_menu(:people, @facets) %>
  6 + <% end %>
  7 +</div>
9 8  
10   -<%= render :partial => 'search_form', :locals => { :form_title => @query.blank? ? _('Search') : _("Refine your search"), :simple_search => true } %>
  9 +<div id='search-column-right'>
  10 + <%= render :partial => 'search_form', :locals => { :hint => _("Type words about the person you're looking for") } %>
  11 + <%= render :partial => 'results_header' %>
11 12  
12   -<%# FIXME ARMENGUE %>
13   -<%= display_results(false) %>
  13 + <%= display_results %>
  14 + <% if params[:display] != 'map' %>
  15 + <%= pagination_links @results.values.first %>
  16 + <% end %>
  17 +</div>
14 18  
15   -<%= pagination_links @results.values.first %>
16   -
17   -<br style="clear:both" />
  19 +<div style="clear: both"></div>
... ...
app/views/search/popup.rhtml
... ... @@ -1,9 +0,0 @@
1   -<div id="search-popup">
2   -
3   -<%= render :partial => 'search_form', :locals => { :form_title => _('Search %s') % @environment.name } %>
4   -
5   -<script type='text/javascript'>
6   - $('query').focus();
7   -</script>
8   -
9   -</div><!-- fim id="search-popup" -->
app/views/search/products.rhtml
1   -<%= search_page_title( _('Products and Services'), { :query => @query,
2   - :category => @category ? @category.name : nil,
3   - :total_results => @total_results,
4   - :region => @region ? @region.name : nil,
5   - :distance => @radius } ) %>
6   -
7   -<%= search_page_link_to_all( { :asset => params[:asset],
8   - :category => @category }) %>
9   -
10   -<%= render :partial => 'search_form', :locals => { :form_title => _("Refine your search"), :simple_search => true } %>
11   -
12   -<% if @categories_menu %>
13   -<div class="has_cat_list">
14   -<% end %>
15   -
16   -<% cache(:action => 'assets', :asset => 'products', :category_path => params[:category_path], :query => @query, :product_category => @product_category, :region => @region, :radius => params[:radius]) do %>
17   - <%= product_categories_menu(:products, @product_category, @result_ids) %>
18   -<% end %>
19   -
20   -<%= display_results %>
21   -
22   -<% if @categories_menu %>
23   -</div><!-- class="has_cat_list" -->
24   -<% end %>
25   -
26   -<%= pagination_links @results[:products] %>
27   -
28   -<br style="clear:both" />
  1 +<%= search_page_title( @titles[:products], @category ) %>
  2 +
  3 +<div id="search-column-left">
  4 + <% if !@empty_query %>
  5 + <% button_bar do %>
  6 + <%= display_map_list_button %>
  7 + <% end %>
  8 + <%= facets_menu(:products, @facets) %>
  9 + <% end %>
  10 +</div>
  11 +
  12 +<div id="search-column-right">
  13 + <%= render :partial => 'search_form', :locals => { :hint => _('Type the product, service, city or qualifier desired') } %>
  14 + <%= render :partial => 'results_header' %>
  15 +
  16 + <% if !@empty_query %>
  17 + <%= display_results(true) %>
  18 + <% if params[:display] != 'map' %>
  19 + <%= pagination_links @results[:products] %>
  20 + <% end %>
  21 + <% end %>
  22 +</div>
  23 +
  24 +<div style="clear: both"></div>
... ...
app/views/search/tag.rhtml
... ... @@ -18,4 +18,5 @@
18 18 </div>
19 19 <%= pagination_links @tagged, :param_name => 'npage' %>
20 20  
  21 + <div style="clear: both"></div>
21 22 <% end %>
... ...
config/initializers/dependencies.rb
1 1 # locally-developed modules
  2 +require 'acts_as_faceted'
2 3 require 'acts_as_filesystem'
3 4 require 'acts_as_having_settings'
4 5 require 'acts_as_searchable'
... ...
config/routes.rb
... ... @@ -52,10 +52,6 @@ ActionController::Routing::Routes.draw do |map|
52 52 # search
53 53 map.connect 'search/:action/*category_path', :controller => 'search'
54 54  
55   - # Browse
56   - map.connect 'browse/:action/:filter', :controller => 'browse'
57   - map.connect 'browse/:action', :controller => 'browse'
58   -
59 55 # events
60 56 map.events 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/
61 57 map.events 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/
... ... @@ -85,6 +81,9 @@ ActionController::Routing::Routes.draw do |map|
85 81 # contact
86 82 map.contact 'contact/:profile/:action/:id', :controller => 'contact', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/
87 83  
  84 + # map balloon
  85 + map.contact 'map_balloon/:action/:id', :controller => 'map_balloon', :id => /.*/
  86 +
88 87 # chat
89 88 map.chat 'chat/:action/:id', :controller => 'chat'
90 89 map.chat 'chat/:action', :controller => 'chat'
... ...
db/migrate/20110810123648_add_acronym_and_abbreviation_to_category.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class AddAcronymAndAbbreviationToCategory < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :categories, :acronym, :string
  4 + add_column :categories, :abbreviation, :string
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :categories, :abbreviation
  9 + remove_column :categories, :acronym
  10 + end
  11 +end
... ...
db/migrate/20110814172228_drop_product_categorization.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class DropProductCategorization < ActiveRecord::Migration
  2 + def self.up
  3 + drop_table :product_categorizations
  4 + end
  5 +
  6 + def self.down
  7 + say "this migration can't be reverted"
  8 + end
  9 +end
... ...
db/schema.rb
... ... @@ -175,6 +175,8 @@ ActiveRecord::Schema.define(:version =&gt; 20111004184104) do
175 175 t.integer "children_count", :default => 0
176 176 t.boolean "accept_products", :default => true
177 177 t.integer "image_id"
  178 + t.string "acronym"
  179 + t.string "abbreviation"
178 180 end
179 181  
180 182 create_table "categories_profiles", :id => false, :force => true do |t|
... ... @@ -452,9 +454,9 @@ ActiveRecord::Schema.define(:version =&gt; 20111004184104) do
452 454  
453 455 create_table "roles", :force => true do |t|
454 456 t.string "name"
  457 + t.text "permissions"
455 458 t.string "key"
456 459 t.boolean "system", :default => false
457   - t.text "permissions"
458 460 t.integer "environment_id"
459 461 end
460 462  
... ...
lib/acts_as_faceted.rb 0 → 100644
... ... @@ -0,0 +1,219 @@
  1 +module ActsAsFaceted
  2 +
  3 + module ClassMethods
  4 + end
  5 +
  6 + module ActsMethods
  7 + # Example:
  8 + #
  9 + #acts_as_faceted :fields => {
  10 + # :f_type => {:label => _('Type'), :proc => proc{|klass| f_type_proc(klass)}},
  11 + # :f_published_at => {:type => :date, :label => _('Published date'), :queries => {'[* TO NOW-1YEARS/DAY]' => _("Older than one year"),
  12 + # '[NOW-1YEARS TO NOW/DAY]' => _("Last year"), '[NOW-1MONTHS TO NOW/DAY]' => _("Last month"), '[NOW-7DAYS TO NOW/DAY]' => _("Last week"), '[NOW-1DAYS TO NOW/DAY]' => _("Last day")}},
  13 + # :f_profile_type => {:label => _('Author'), :proc => proc{|klass| f_profile_type_proc(klass)}},
  14 + # :f_category => {:label => _('Categories')}},
  15 + # :order => [:f_type, :f_published_at, :f_profile_type, :f_category]
  16 + #
  17 + #acts_as_searchable :additional_fields => [ {:name => {:type => :string, :as => :name_sort, :boost => 5.0}} ] + facets_fields_for_solr,
  18 + # :exclude_fields => [:setting],
  19 + # :include => [:profile],
  20 + # :facets => facets_option_for_solr,
  21 + # :if => proc{|a| ! ['RssFeed'].include?(a.class.name)}
  22 + def acts_as_faceted(options)
  23 + extend ClassMethods
  24 + extend ActsAsSolr::CommonMethods
  25 +
  26 + cattr_accessor :facets
  27 + cattr_accessor :facets_order
  28 + cattr_accessor :to_solr_fields_names
  29 + cattr_accessor :facets_results_containers
  30 + cattr_accessor :solr_fields_names
  31 + cattr_accessor :facets_option_for_solr
  32 + cattr_accessor :facets_fields_for_solr
  33 + cattr_accessor :facet_category_query
  34 +
  35 + self.facets = options[:fields]
  36 + self.facets_order = options[:order] || facets
  37 + self.facets_results_containers = {:fields => 'facet_fields', :queries => 'facet_queries', :ranges => 'facet_ranges'}
  38 + self.facets_option_for_solr = Hash[facets.select{ |id,data| ! data.has_key?(:queries) }].keys
  39 + self.facets_fields_for_solr = facets.map{ |id,data| {id => data[:type] || :facet} }
  40 + self.solr_fields_names = facets.map{ |id,data| id.to_s + '_' + get_solr_field_type(data[:type] || :facet) }
  41 + self.facet_category_query = options[:category_query]
  42 +
  43 + # A hash to retrieve the field key for the solr facet string returned
  44 + # :field_name => "field_name_facet"
  45 + self.to_solr_fields_names = Hash[facets.keys.zip(solr_fields_names)]
  46 +
  47 + def facet_by_id(id)
  48 + {:id => id}.merge(facets[id]) if facets[id]
  49 + end
  50 +
  51 + def map_facets_for(environment)
  52 + list = facets_order ? facets_order : facets.keys
  53 + list.map do |id|
  54 + facet = facet_by_id(id)
  55 + next if facet[:type_if] and !facet[:type_if].call(self.new)
  56 +
  57 + if facet[:multi]
  58 + facet[:label].call(environment).map do |label_id, label|
  59 + facet.merge({:id => facet[:id].to_s+'_'+label_id.to_s, :solr_field => facet[:id], :label_id => label_id, :label => label})
  60 + end
  61 + else
  62 + facet.merge(:id => facet[:id].to_s, :solr_field => facet[:id])
  63 + end
  64 + end.compact.flatten
  65 + end
  66 +
  67 + def map_facet_results(facet, facet_params, facets_data, unfiltered_facets_data = {}, options = {})
  68 + facets_data ||= {}
  69 + solr_facet = to_solr_fields_names[facet[:solr_field]]
  70 +
  71 + if facet[:queries]
  72 + container = facets_data[facets_results_containers[:queries]]
  73 + facet_data = (container.nil? or container.empty?) ? [] : container.select{ |k,v| k.starts_with? solr_facet }
  74 + container = unfiltered_facets_data[facets_results_containers[:queries]]
  75 + unfiltered_facet_data = (container.nil? or container.empty?) ? [] : container.select{ |k,v| k.starts_with? solr_facet }
  76 + else
  77 + container = facets_data[facets_results_containers[:fields]]
  78 + facet_data = (container.nil? or container.empty?) ? [] : container[solr_facet] || []
  79 + container = unfiltered_facets_data[facets_results_containers[:fields]]
  80 + unfiltered_facet_data = (container.nil? or container.empty?) ? [] : container[solr_facet] || []
  81 + end
  82 +
  83 + if !unfiltered_facets_data.blank? and !facet_params.blank?
  84 + f = Hash[Array(facet_data)]
  85 + zeros = []
  86 + facet_data = unfiltered_facet_data.map do |id, count|
  87 + count = f[id]
  88 + if count.nil?
  89 + zeros.push [id, 0]
  90 + nil
  91 + else
  92 + [id, count]
  93 + end
  94 + end.compact + zeros
  95 + end
  96 +
  97 + facet_count = facet_data.length
  98 +
  99 + if facet[:queries]
  100 + result = facet_data.map do |id, count|
  101 + q = id[id.index(':')+1,id.length]
  102 + label = facet_result_name(facet, q)
  103 + [q, label, count] if count > 0
  104 + end.compact
  105 + result = facet[:queries_order].map{ |id| result.detect{ |rid, label, count| rid == id } }.compact if facet[:queries_order]
  106 + elsif facet[:proc]
  107 + if facet[:label_id]
  108 + result = facet_data.map do |id, count|
  109 + name = facet_result_name(facet, id)
  110 + [id, name, count] if name
  111 + end.compact
  112 + # FIXME limit is NOT improving performance in this case :(
  113 + facet_count = result.length
  114 + result = result.first(options[:limit]) if options[:limit]
  115 + else
  116 + facet_data = facet_data.first(options[:limit]) if options[:limit]
  117 + result = facet_data.map { |id, count| [id, facet_result_name(facet, id), count] }
  118 + end
  119 + else
  120 + facet_data = facet_data.first(options[:limit]) if options[:limit]
  121 + result = facet_data.map { |id, count| [id, facet_result_name(facet, id), count] }
  122 + end
  123 +
  124 + sorted = facet_result_sort(facet, result, options[:sort])
  125 +
  126 + # length can't be used if limit option is given;
  127 + # total_entries comes to help
  128 + sorted.class.send(:define_method, :total_entries, proc { facet_count })
  129 +
  130 + sorted
  131 + end
  132 +
  133 + def facet_result_sort(facet, facets_data, sort_by = nil)
  134 + if facet[:queries_order]
  135 + facets_data
  136 + elsif sort_by == :alphabetically
  137 + facets_data.sort{ |a,b| a[1] <=> b[1] }
  138 + elsif sort_by == :count
  139 + facets_data.sort{ |a,b| -1*(a[2] <=> b[2]) }
  140 + else
  141 + facets_data
  142 + end
  143 + end
  144 +
  145 + def facet_result_name(facet, data)
  146 + if facet[:queries]
  147 + gettext(facet[:queries][data])
  148 + elsif facet[:proc]
  149 + if facet[:multi]
  150 + facet[:label_id] ||= 0
  151 + facet[:proc].call(facet, data)
  152 + else
  153 + gettext(facet[:proc].call(data))
  154 + end
  155 + else
  156 + data
  157 + end
  158 + end
  159 +
  160 + def facet_label(facet)
  161 + _ facet[:label]
  162 + end
  163 +
  164 + def facets_find_options(facets_selected = {}, options = {})
  165 + browses = []
  166 + facets_selected ||= {}
  167 + facets_selected.map do |id, value|
  168 + if value.kind_of?(Hash)
  169 + value.map do |label_id, value|
  170 + value.to_a.each do |value|
  171 + browses << id.to_s + ':' + (facets[id.to_sym][:queries] ? value : '"'+value.to_s+'"')
  172 + end
  173 + end
  174 + else
  175 + browses << id.to_s + ':' + (facets[id.to_sym][:queries] ? value : '"'+value.to_s+'"')
  176 + end
  177 + end.flatten
  178 +
  179 + {:facets => {:zeros => false, :sort => :count,
  180 + :fields => facets_option_for_solr,
  181 + :browse => browses,
  182 + :query => facets.map { |f, options| options[:queries].keys.map { |q| f.to_s + ':' + q } if options[:queries] }.compact.flatten,
  183 + }
  184 + }
  185 + end
  186 + end
  187 + end
  188 +
  189 +end
  190 +
  191 +ActiveRecord::Base.extend ActsAsFaceted::ActsMethods
  192 +
  193 +# from https://github.com/rubyworks/facets/blob/master/lib/core/facets/enumerable/graph.rb
  194 +module Enumerable
  195 + def graph(&yld)
  196 + if yld
  197 + h = {}
  198 + each do |*kv|
  199 + r = yld[*kv]
  200 + case r
  201 + when Hash
  202 + nk, nv = *r.to_a[0]
  203 + when Range
  204 + nk, nv = r.first, r.last
  205 + else
  206 + nk, nv = *r
  207 + end
  208 + h[nk] = nv
  209 + end
  210 + h
  211 + else
  212 + Enumerator.new(self,:graph)
  213 + end
  214 + end
  215 +
  216 + # Alias for #graph, which stands for "map hash".
  217 + alias_method :mash, :graph
  218 +end
  219 +
... ...
lib/acts_as_searchable.rb
... ... @@ -4,16 +4,16 @@ module ActsAsSearchable
4 4 ACTS_AS_SEARCHABLE_ENABLED = true unless defined? ACTS_AS_SEARCHABLE_ENABLED
5 5  
6 6 def acts_as_searchable(options = {})
7   - if ACTS_AS_SEARCHABLE_ENABLED
8   - if (!options[:fields])
9   - options[:additional_fields] |= [{:schema_name => :string}]
10   - else
11   - options[:fields] << {:schema_name => :string}
12   - end
13   - acts_as_solr options
14   - extend FindByContents
15   - send :include, InstanceMethods
  7 + return if !ACTS_AS_SEARCHABLE_ENABLED
  8 +
  9 + if (!options[:fields])
  10 + options[:additional_fields] |= [{:schema_name => :string}]
  11 + else
  12 + options[:fields] << {:schema_name => :string}
16 13 end
  14 + acts_as_solr options
  15 + extend FindByContents
  16 + send :include, InstanceMethods
17 17 end
18 18  
19 19 module InstanceMethods
... ... @@ -31,14 +31,20 @@ module ActsAsSearchable
31 31 def find_by_contents(query, pg_options = {}, options = {}, db_options = {})
32 32 pg_options[:page] ||= 1
33 33 pg_options[:per_page] ||= 20
34   - options[:limit] = pg_options[:per_page].to_i*pg_options[:page].to_i
35   - options[:scores] = true;
36   -
  34 + options[:limit] ||= pg_options[:per_page].to_i*pg_options[:page].to_i
  35 + options[:scores] ||= true;
  36 + all_facets_enabled = options.delete(:all_facets)
37 37 query = !schema_name.empty? ? "+schema_name:\"#{schema_name}\" AND #{query}" : query
  38 + results = []
  39 + facets = all_facets = {}
  40 +
38 41 solr_result = find_by_solr(query, options)
39   - if solr_result.nil?
40   - results = facets = []
41   - else
  42 + if all_facets_enabled
  43 + options[:facets][:browse] = nil
  44 + all_facets = find_by_solr(query, options.merge(:limit => 0)).facets
  45 + end
  46 +
  47 + if !solr_result.nil?
42 48 facets = options.include?(:facets) ? solr_result.facets : []
43 49  
44 50 if db_options.empty?
... ... @@ -61,7 +67,7 @@ module ActsAsSearchable
61 67 results = results.paginate(pg_options.merge(:total_entries => solr_result.total))
62 68 end
63 69  
64   - {:results => results, :facets => facets}
  70 + {:results => results, :facets => facets, :all_facets => all_facets}
65 71 end
66 72 end
67 73 end
... ...
public/designs/themes/base/navigation.rhtml
1 1 <li>
2   - <%= browse_people_menu %>
  2 + <%= search_people_menu %>
3 3 </li>
4 4 <li>
5   - <%= browse_communities_menu %>
  5 + <%= search_communities_menu %>
6 6 </li>
7 7 <li>
8   - <%= browse_contents_menu %>
  8 + <%= search_contents_menu %>
9 9 </li>
10   -<li><a href="/assets/events"><span class='icon-menu-events'><%= _('Events') %></span></a></li>
  10 +<li><a href="/search/events"><span class='icon-menu-events'><%= _('Events') %></span></a></li>
... ...
public/designs/themes/base/style.css
1 1 /* ==> button.css <== */
2 2  
3   -
4 3 .button {
5 4 -moz-border-radius: 3px;
6 5 -webkit-border-radius: 3px;
... ... @@ -108,14 +107,14 @@ body, th, td, input {
108 107 margin-left: 20px;
109 108 }
110 109  
111   -#user form input {
  110 +#user form input .blur {
112 111 width: 160px;
113 112 border: 1px solid #BBB;
114 113 -moz-border-radius: 3px;
115 114 -webkit-border-radius: 3px;
116 115 color: #CCC;
117 116 }
118   -#user form.focused input {
  117 +#user form input {
119 118 border: 1px solid #888;
120 119 color: #555;
121 120 }
... ... @@ -502,11 +501,11 @@ div#notice {
502 501 }
503 502  
504 503 #content .profile-list li a,
505   -#content .common-profile-list-block li a {
  504 +#content .common-profile-list-block .vcard li a {
506 505 color: #555;
507 506 }
508 507 #content .profile-list li a:hover,
509   -#content .common-profile-list-block li a:hover {
  508 +#content .common-profile-list-block .vcard li a:hover {
510 509 color: #000;
511 510 text-decoration: none;
512 511 }
... ...