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 require 'application_controller' 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,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 @@ @@ -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 class SearchController < PublicController 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 helper TagsHelper 8 helper TagsHelper
  9 + include SearchHelper
  10 + include ActionView::Helpers::NumberHelper
4 11
5 before_filter :load_category 12 before_filter :load_category
6 - before_filter :prepare_filter  
7 - before_filter :check_search_whole_site  
8 before_filter :load_search_assets 13 before_filter :load_search_assets
9 - before_filter :check_valid_assets, :only => [ :assets ] 14 + before_filter :load_query
10 15
11 no_design_blocks 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 end 38 end
21 end 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 else 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 end 57 end
29 end 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 end 66 end
35 end 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 end 74 end
43 end 75 end
44 76
45 def events 77 def events
46 @category_id = @category ? @category.id : nil 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 @selected_day = nil 100 @selected_day = nil
49 @events_of_the_day = [] 101 @events_of_the_day = []
50 date = build_date(params[:year], params[:month], params[:day]) 102 date = build_date(params[:year], params[:month], params[:day])
@@ -58,118 +110,27 @@ class SearchController &lt; PublicController @@ -58,118 +110,27 @@ class SearchController &lt; PublicController
58 end 110 end
59 end 111 end
60 112
61 - events = @results[:events]  
62 - 113 + events = @results[@asset]
63 @calendar = populate_calendar(date, events) 114 @calendar = populate_calendar(date, events)
64 @previous_calendar = populate_calendar(date - 1.month, events) 115 @previous_calendar = populate_calendar(date - 1.month, events)
65 @next_calendar = populate_calendar(date + 1.month, events) 116 @next_calendar = populate_calendar(date + 1.month, events)
66 end 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 def index 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 @results = {} 120 @results = {}
161 - @facets = {}  
162 @order = [] 121 @order = []
163 @names = {} 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 @order << key 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 @names[key] = getterm(description) 130 @names[key] = getterm(description)
172 end 131 end
  132 + @asset = nil
  133 + @facets = {}
173 134
174 if @results.keys.size == 1 135 if @results.keys.size == 1
175 specific_action = @results.keys.first 136 specific_action = @results.keys.first
@@ -180,34 +141,32 @@ class SearchController &lt; PublicController @@ -180,34 +141,32 @@ class SearchController &lt; PublicController
180 return 141 return
181 end 142 end
182 end 143 end
183 -  
184 - render :action => 'index'  
185 end 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 # view the summary of one category 151 # view the summary of one category
192 def category_index 152 def category_index
193 @results = {} 153 @results = {}
194 @order = [] 154 @order = []
195 @names = {} 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 end 168 end
209 end 169 end
210 - attr_reader :category  
211 170
212 def tags 171 def tags
213 @tags_cache_key = "tags_env_#{environment.id.to_s}" 172 @tags_cache_key = "tags_env_#{environment.id.to_s}"
@@ -224,21 +183,123 @@ class SearchController &lt; PublicController @@ -224,21 +183,123 @@ class SearchController &lt; PublicController
224 end 183 end
225 end 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 end 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 else 226 else
239 - @events_of_the_day = environment.events.by_day(@selected_day) 227 + 'more_recent'
240 end 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 end 303 end
243 304
244 end 305 end
app/helpers/application_helper.rb
@@ -574,7 +574,7 @@ module ApplicationHelper @@ -574,7 +574,7 @@ module ApplicationHelper
574 end 574 end
575 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) 575 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
576 links = links_for_balloon(profile) 576 links = links_for_balloon(profile)
577 - content_tag tag, 577 + content_tag('div', content_tag(tag,
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) : "") + 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 link_to( 579 link_to(
580 content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + 580 content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
@@ -584,7 +584,7 @@ module ApplicationHelper @@ -584,7 +584,7 @@ module ApplicationHelper
584 :class => 'profile_link url', 584 :class => 'profile_link url',
585 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, 585 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
586 :title => profile.name ), 586 :title => profile.name ),
587 - :class => 'vcard' 587 + :class => 'vcard'), :class => 'common-profile-list-block')
588 end 588 end
589 589
590 def gravatar_url_for(email, options = {}) 590 def gravatar_url_for(email, options = {})
@@ -890,22 +890,6 @@ module ApplicationHelper @@ -890,22 +890,6 @@ module ApplicationHelper
890 result 890 result
891 end 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 def template_stylesheet_path 893 def template_stylesheet_path
910 if profile.nil? 894 if profile.nil?
911 "/designs/templates/#{environment.layout_template}/stylesheets/style.css" 895 "/designs/templates/#{environment.layout_template}/stylesheets/style.css"
@@ -982,6 +966,7 @@ module ApplicationHelper @@ -982,6 +966,7 @@ module ApplicationHelper
982 def noosfero_stylesheets 966 def noosfero_stylesheets
983 [ 967 [
984 'application', 968 'application',
  969 + 'search',
985 'thickbox', 970 'thickbox',
986 'lightbox', 971 'lightbox',
987 'colorpicker', 972 'colorpicker',
@@ -1100,33 +1085,46 @@ module ApplicationHelper @@ -1100,33 +1085,46 @@ module ApplicationHelper
1100 ") : '') 1085 ") : '')
1101 end 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 links = [ 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 if logged_in? 1107 if logged_in?
1110 links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})}) 1108 links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})})
1111 links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})}) 1109 links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})})
1112 end 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 link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger') 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 end 1114 end
1117 1115
1118 - def browse_communities_menu 1116 + def search_communities_menu
1119 links = [ 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 if logged_in? 1122 if logged_in?
1125 links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})}) 1123 links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})})
1126 links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})}) 1124 links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})})
1127 end 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 link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger') 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 end 1129 end
1132 1130
@@ -1330,4 +1328,8 @@ module ApplicationHelper @@ -1330,4 +1328,8 @@ module ApplicationHelper
1330 end 1328 end
1331 end 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 end 1335 end
app/helpers/display_helper.rb
@@ -26,15 +26,19 @@ module DisplayHelper @@ -26,15 +26,19 @@ module DisplayHelper
26 product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url 26 product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url
27 end 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 return _('Uncategorized product') unless category 34 return _('Uncategorized product') unless category
31 name = full ? category.full_name(' &rarr; ') : category.name 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 end 37 end
34 38
35 def link_to_product_category(category) 39 def link_to_product_category(category)
36 if category 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 else 42 else
39 _('Uncategorized product') 43 _('Uncategorized product')
40 end 44 end
app/helpers/lightbox_helper.rb
@@ -22,10 +22,7 @@ module LightboxHelper @@ -22,10 +22,7 @@ module LightboxHelper
22 def lightbox_options(options, lightbox_type = 'lbOn') 22 def lightbox_options(options, lightbox_type = 'lbOn')
23 the_class = lightbox_type 23 the_class = lightbox_type
24 the_class << " #{options[:class]}" if options.has_key?(:class) 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 end 26 end
30 27
31 def lightbox? 28 def lightbox?
app/helpers/product_category_viewer_helper.rb
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -module ProductCategoryViewerHelper  
2 -end  
app/helpers/search_helper.rb
@@ -3,116 +3,160 @@ module SearchHelper @@ -3,116 +3,160 @@ module SearchHelper
3 # FIXME remove it after search_controler refactored 3 # FIXME remove it after search_controler refactored
4 include EventsHelper 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 end 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 end 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 end 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 end 43 end
42 end 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 end 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 end 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 end 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 end 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 end 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 end 160 end
117 161
118 end 162 end
app/models/article.rb
@@ -2,6 +2,12 @@ require &#39;hpricot&#39; @@ -2,6 +2,12 @@ require &#39;hpricot&#39;
2 2
3 class Article < ActiveRecord::Base 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 track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && !a.image? }, :custom_target => :action_tracker_target 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 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 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 track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target 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,8 +131,6 @@ class Article &lt; ActiveRecord::Base
125 131
126 acts_as_versioned 132 acts_as_versioned
127 133
128 - acts_as_searchable :additional_fields => [ :comment_data ]  
129 -  
130 def comment_data 134 def comment_data
131 comments.map {|item| [item.title, item.body].join(' ') }.join(' ') 135 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
132 end 136 end
@@ -141,13 +145,31 @@ class Article &lt; ActiveRecord::Base @@ -141,13 +145,31 @@ class Article &lt; ActiveRecord::Base
141 {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]} 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 # retrieves the latest +limit+ articles, sorted from the most recent to the 166 # retrieves the latest +limit+ articles, sorted from the most recent to the
145 # oldest. 167 # oldest.
146 # 168 #
147 # Only includes articles where advertise == true 169 # Only includes articles where advertise == true
148 - def self.recent(limit, extra_conditions = {}) 170 + def self.recent(limit = nil, extra_conditions = {})
149 # FIXME this method is a horrible hack 171 # FIXME this method is a horrible hack
150 - options = { :limit => limit, 172 + options = { :page => 1, :per_page => limit,
151 :conditions => [ 173 :conditions => [
152 "advertise = ? AND 174 "advertise = ? AND
153 published = ? AND 175 published = ? AND
@@ -165,20 +187,14 @@ class Article &lt; ActiveRecord::Base @@ -165,20 +187,14 @@ class Article &lt; ActiveRecord::Base
165 options.delete(:include) 187 options.delete(:include)
166 end 188 end
167 if extra_conditions == {} 189 if extra_conditions == {}
168 - self.find(:all, options) 190 + self.paginate(options)
169 else 191 else
170 with_scope :find => {:conditions => extra_conditions} do 192 with_scope :find => {:conditions => extra_conditions} do
171 - self.find(:all, options) 193 + self.paginate(options)
172 end 194 end
173 end 195 end
174 end 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 # produces the HTML code that is to be displayed as this article's contents. 198 # produces the HTML code that is to be displayed as this article's contents.
183 # 199 #
184 # The implementation in this class just provides the +body+ attribute as the 200 # The implementation in this class just provides the +body+ attribute as the
@@ -561,6 +577,81 @@ class Article &lt; ActiveRecord::Base @@ -561,6 +577,81 @@ class Article &lt; ActiveRecord::Base
561 577
562 private 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 def sanitize_tag_list 655 def sanitize_tag_list
565 sanitizer = HTML::FullSanitizer.new 656 sanitizer = HTML::FullSanitizer.new
566 self.tag_list.names.map!{|i| strip_tag_name sanitizer.sanitize(i) } 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,6 +9,10 @@ class Blog &lt; Folder
9 end 9 end
10 alias_method_chain :posts, :no_folders 10 alias_method_chain :posts, :no_folders
11 11
  12 + def self.type_name
  13 + _('Blog')
  14 + end
  15 +
12 def self.short_description 16 def self.short_description
13 _('Blog') 17 _('Blog')
14 end 18 end
app/models/category.rb
@@ -36,18 +36,38 @@ class Category &lt; ActiveRecord::Base @@ -36,18 +36,38 @@ class Category &lt; ActiveRecord::Base
36 { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] } 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 def recent_articles(limit = 10) 55 def recent_articles(limit = 10)
40 self.articles.recent(limit) 56 self.articles.recent(limit)
41 end 57 end
42 58
43 def recent_comments(limit = 10) 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 end 61 end
46 62
47 def most_commented_articles(limit = 10) 63 def most_commented_articles(limit = 10)
48 self.articles.most_commented(limit) 64 self.articles.most_commented(limit)
49 end 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 def display_in_menu? 71 def display_in_menu?
52 display_in_menu 72 display_in_menu
53 end 73 end
app/models/category_finder.rb
@@ -1,146 +0,0 @@ @@ -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,9 +2,12 @@ class Certifier &lt; ActiveRecord::Base
2 2
3 belongs_to :environment 3 belongs_to :environment
4 4
5 - has_many :qualifier_certifiers 5 + has_many :qualifier_certifiers, :dependent => :destroy
6 has_many :qualifiers, :through => :qualifier_certifiers 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 validates_presence_of :environment_id 11 validates_presence_of :environment_id
9 validates_presence_of :name 12 validates_presence_of :name
10 13
app/models/communities_block.rb
@@ -21,7 +21,7 @@ class CommunitiesBlock &lt; ProfileListBlock @@ -21,7 +21,7 @@ class CommunitiesBlock &lt; ProfileListBlock
21 end 21 end
22 when Environment 22 when Environment
23 lambda do 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 end 25 end
26 else 26 else
27 '' 27 ''
app/models/community.rb
1 class Community < Organization 1 class Community < Organization
  2 +
  3 + def self.type_name
  4 + _('Community')
  5 + end
  6 +
2 N_('Community') 7 N_('Community')
3 N_('Language') 8 N_('Language')
4 9
app/models/enterprise.rb
@@ -2,6 +2,10 @@ @@ -2,6 +2,10 @@
2 # only enterprises can offer products and services. 2 # only enterprises can offer products and services.
3 class Enterprise < Organization 3 class Enterprise < Organization
4 4
  5 + def self.type_name
  6 + _('Enterprise')
  7 + end
  8 +
5 N_('Enterprise') 9 N_('Enterprise')
6 10
7 has_many :products, :dependent => :destroy, :order => 'name ASC' 11 has_many :products, :dependent => :destroy, :order => 'name ASC'
@@ -163,6 +167,10 @@ class Enterprise &lt; Organization @@ -163,6 +167,10 @@ class Enterprise &lt; Organization
163 end 167 end
164 end 168 end
165 169
  170 + def control_panel_settings_button
  171 + {:title => __('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'}
  172 + end
  173 +
166 settings_items :enable_contact_us, :type => :boolean, :default => true 174 settings_items :enable_contact_us, :type => :boolean, :default => true
167 175
168 def enable_contact? 176 def enable_contact?
app/models/enterprise_homepage.rb
1 class EnterpriseHomepage < Article 1 class EnterpriseHomepage < Article
2 2
  3 + def self.type_name
  4 + _('Homepage')
  5 + end
  6 +
3 def self.short_description 7 def self.short_description
4 __('Enterprise homepage.') 8 __('Enterprise homepage.')
5 end 9 end
app/models/environment.rb
@@ -247,6 +247,10 @@ class Environment &lt; ActiveRecord::Base @@ -247,6 +247,10 @@ class Environment &lt; ActiveRecord::Base
247 247
248 settings_items :enabled_plugins, :type => Array, :default => [] 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 def news_amount_by_folder=(amount) 254 def news_amount_by_folder=(amount)
251 settings[:news_amount_by_folder] = amount.to_i 255 settings[:news_amount_by_folder] = amount.to_i
252 end 256 end
@@ -353,11 +357,11 @@ class Environment &lt; ActiveRecord::Base @@ -353,11 +357,11 @@ class Environment &lt; ActiveRecord::Base
353 end 357 end
354 358
355 def terminology 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 Noosfero.terminology 363 Noosfero.terminology
360 - end 364 + #end
361 end 365 end
362 366
363 def terminology=(value) 367 def terminology=(value)
app/models/environment_finder.rb
@@ -1,115 +0,0 @@ @@ -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 class Event < Article 1 class Event < Article
2 2
  3 + def self.type_name
  4 + _('Event')
  5 + end
  6 +
3 settings_items :address, :type => :string 7 settings_items :address, :type => :string
4 8
5 def link=(value) 9 def link=(value)
app/models/folder.rb
1 class Folder < Article 1 class Folder < Article
2 2
  3 + def self.type_name
  4 + _('Folder')
  5 + end
  6 +
3 validate :not_belong_to_blog 7 validate :not_belong_to_blog
4 8
5 def not_belong_to_blog 9 def not_belong_to_blog
app/models/forum.rb
@@ -2,6 +2,10 @@ class Forum &lt; Folder @@ -2,6 +2,10 @@ class Forum &lt; Folder
2 2
3 acts_as_having_posts :order => 'updated_at DESC' 3 acts_as_having_posts :order => 'updated_at DESC'
4 4
  5 + def self.type_name
  6 + _('Forum')
  7 + end
  8 +
5 def self.short_description 9 def self.short_description
6 _('Forum') 10 _('Forum')
7 end 11 end
app/models/gallery.rb
1 class Gallery < Folder 1 class Gallery < Folder
2 2
  3 + def self.type_name
  4 + _('Gallery')
  5 + end
  6 +
3 def self.short_description 7 def self.short_description
4 _('Gallery') 8 _('Gallery')
5 end 9 end
app/models/people_block.rb
@@ -18,7 +18,7 @@ class PeopleBlock &lt; ProfileListBlock @@ -18,7 +18,7 @@ class PeopleBlock &lt; ProfileListBlock
18 18
19 def footer 19 def footer
20 lambda do 20 lambda do
21 - link_to _('View all'), :controller => 'browse', :action => 'people' 21 + link_to _('View all'), :controller => 'search', :action => 'people'
22 end 22 end
23 end 23 end
24 24
app/models/person.rb
1 # A person is the profile of an user holding all relationships with the rest of the system 1 # A person is the profile of an user holding all relationships with the rest of the system
2 class Person < Profile 2 class Person < Profile
3 3
  4 + def self.type_name
  5 + _('Person')
  6 + end
  7 +
4 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} 8 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
5 acts_as_accessor 9 acts_as_accessor
6 10
app/models/product.rb
1 class Product < ActiveRecord::Base 1 class Product < ActiveRecord::Base
2 belongs_to :enterprise 2 belongs_to :enterprise
  3 + has_one :region, :through => :enterprise
  4 +
3 belongs_to :product_category 5 belongs_to :product_category
4 - has_many :product_categorizations  
5 - has_many :product_qualifiers  
6 - has_many :qualifiers, :through => :product_qualifiers 6 +
7 has_many :inputs, :dependent => :destroy, :order => 'position' 7 has_many :inputs, :dependent => :destroy, :order => 'position'
8 has_many :price_details, :dependent => :destroy 8 has_many :price_details, :dependent => :destroy
9 has_many :production_costs, :through => :price_details 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 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true 15 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true
12 validates_presence_of :product_category_id 16 validates_presence_of :product_category_id
13 validates_associated :product_category 17 validates_associated :product_category
@@ -28,17 +32,6 @@ class Product &lt; ActiveRecord::Base @@ -28,17 +32,6 @@ class Product &lt; ActiveRecord::Base
28 p.enterprise.product_updated if p.enterprise 32 p.enterprise.product_updated if p.enterprise
29 end 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 xss_terminate :only => [ :name ], :on => 'validation' 35 xss_terminate :only => [ :name ], :on => 'validation'
43 xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation' 36 xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation'
44 37
@@ -90,9 +83,11 @@ class Product &lt; ActiveRecord::Base @@ -90,9 +83,11 @@ class Product &lt; ActiveRecord::Base
90 end 83 end
91 84
92 def enterprise_updated(e) 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 end 91 end
97 92
98 def url 93 def url
@@ -100,7 +95,7 @@ class Product &lt; ActiveRecord::Base @@ -100,7 +95,7 @@ class Product &lt; ActiveRecord::Base
100 end 95 end
101 96
102 def public? 97 def public?
103 - enterprise.public_profile 98 + enterprise.public?
104 end 99 end
105 100
106 def formatted_value(method) 101 def formatted_value(method)
@@ -213,4 +208,64 @@ class Product &lt; ActiveRecord::Base @@ -213,4 +208,64 @@ class Product &lt; ActiveRecord::Base
213 url_for({:host => enterprise.default_hostname, :controller => 'manage_products', :action => 'display_inputs_cost', :profile => enterprise.identifier, :id => self.id }.merge(Noosfero.url_options)) 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 end 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 end 271 end
app/models/product_categorization.rb
@@ -1,13 +0,0 @@ @@ -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,6 +3,12 @@
3 # which by default is the one returned by Environment:default. 3 # which by default is the one returned by Environment:default.
4 class Profile < ActiveRecord::Base 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 module Roles 12 module Roles
7 def self.admin(env_id) 13 def self.admin(env_id)
8 find_role('admin', env_id) 14 find_role('admin', env_id)
@@ -70,8 +76,6 @@ class Profile &lt; ActiveRecord::Base @@ -70,8 +76,6 @@ class Profile &lt; ActiveRecord::Base
70 76
71 acts_as_having_boxes 77 acts_as_having_boxes
72 78
73 - acts_as_searchable :additional_fields => [ :extra_data_for_index ]  
74 -  
75 acts_as_taggable 79 acts_as_taggable
76 80
77 def self.qualified_column_names 81 def self.qualified_column_names
@@ -179,6 +183,15 @@ class Profile &lt; ActiveRecord::Base @@ -179,6 +183,15 @@ class Profile &lt; ActiveRecord::Base
179 183
180 has_many :abuse_complaints, :foreign_key => 'requestor_id' 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 def interests 195 def interests
183 categories.select {|item| !item.is_a?(Region)} 196 categories.select {|item| !item.is_a?(Region)}
184 end 197 end
@@ -818,18 +831,66 @@ private :generate_url, :url_options @@ -818,18 +831,66 @@ private :generate_url, :url_options
818 name 831 name
819 end 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 end 893 end
  894 + end
834 895
835 end 896 end
app/models/qualifier.rb
@@ -2,14 +2,15 @@ class Qualifier &lt; ActiveRecord::Base @@ -2,14 +2,15 @@ class Qualifier &lt; ActiveRecord::Base
2 2
3 belongs_to :environment 3 belongs_to :environment
4 4
5 - has_many :qualifier_certifiers 5 + has_many :qualifier_certifiers, :dependent => :destroy
6 has_many :certifiers, :through => :qualifier_certifiers 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 validates_presence_of :environment_id 11 validates_presence_of :environment_id
9 validates_presence_of :name 12 validates_presence_of :name
10 13
11 - has_many :product_qualifiers, :dependent => :destroy  
12 -  
13 def <=>(b) 14 def <=>(b)
14 self.name.downcase.transliterate <=> b.name.downcase.transliterate 15 self.name.downcase.transliterate <=> b.name.downcase.transliterate
15 end 16 end
app/models/qualifier_certifier.rb
1 class QualifierCertifier < ActiveRecord::Base 1 class QualifierCertifier < ActiveRecord::Base
2 belongs_to :qualifier 2 belongs_to :qualifier
3 belongs_to :certifier 3 belongs_to :certifier
  4 +
  5 + validates_presence_of :qualifier
4 end 6 end
app/models/raw_html_article.rb
1 class RawHTMLArticle < TextArticle 1 class RawHTMLArticle < TextArticle
2 2
  3 + def self.type_name
  4 + _('HTML')
  5 + end
  6 +
3 def self.short_description 7 def self.short_description
4 _('Raw HTML text article.') 8 _('Raw HTML text article.')
5 end 9 end
app/models/rss_feed.rb
1 class RssFeed < Article 1 class RssFeed < Article
2 2
  3 + def self.type_name
  4 + _('RssFeed')
  5 + end
  6 +
3 # i dont know why before filter dont work here 7 # i dont know why before filter dont work here
4 def initialize(*args) 8 def initialize(*args)
5 super(*args) 9 super(*args)
app/models/text_article.rb
@@ -3,6 +3,10 @@ class TextArticle &lt; Article @@ -3,6 +3,10 @@ class TextArticle &lt; Article
3 3
4 xss_terminate :only => [ :name ], :on => 'validation' 4 xss_terminate :only => [ :name ], :on => 'validation'
5 5
  6 + def self.type_name
  7 + _('Article')
  8 + end
  9 +
6 include Noosfero::TranslatableContent 10 include Noosfero::TranslatableContent
7 11
8 def self.icon_name(article = nil) 12 def self.icon_name(article = nil)
app/models/textile_article.rb
1 class TextileArticle < TextArticle 1 class TextileArticle < TextArticle
2 2
  3 + def self.type_name
  4 + _('Article')
  5 + end
  6 +
3 def self.short_description 7 def self.short_description
4 _('Text article with Textile markup language') 8 _('Text article with Textile markup language')
5 end 9 end
app/models/tiny_mce_article.rb
1 class TinyMceArticle < TextArticle 1 class TinyMceArticle < TextArticle
2 2
  3 + def self.type_name
  4 + _('Article')
  5 + end
  6 +
3 def self.short_description 7 def self.short_description
4 _('Text article with visual editor.') 8 _('Text article with visual editor.')
5 end 9 end
app/models/uploaded_file.rb
@@ -4,6 +4,10 @@ @@ -4,6 +4,10 @@
4 # of the file itself is kept. (FIXME?) 4 # of the file itself is kept. (FIXME?)
5 class UploadedFile < Article 5 class UploadedFile < Article
6 6
  7 + def self.type_name
  8 + _('File')
  9 + end
  10 +
7 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? } 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 include ShortFilename 13 include ShortFilename
app/views/browse/_article.rhtml
@@ -1,11 +0,0 @@ @@ -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,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,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 +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,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,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,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,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 <%= render :partial => 'agenda' %> 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 <% language = FastGettext.locale %> 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,6 +12,8 @@
12 <%= stylesheet_link_tag template_stylesheet_path %> 12 <%= stylesheet_link_tag template_stylesheet_path %>
13 <%= stylesheet_link_tag icon_theme_stylesheet_path %> 13 <%= stylesheet_link_tag icon_theme_stylesheet_path %>
14 <%= stylesheet_link_tag jquery_ui_theme_stylesheet_path %> 14 <%= stylesheet_link_tag jquery_ui_theme_stylesheet_path %>
  15 + <%= stylesheet_link_tag "token-input" %>
  16 + <%= stylesheet_link_tag "token-input-facet" %>
15 <% @plugins.enabled_plugins.each do |plugin| %> 17 <% @plugins.enabled_plugins.each do |plugin| %>
16 <% if plugin.stylesheet? %> 18 <% if plugin.stylesheet? %>
17 <%= stylesheet_tag plugin.class.public_path('style.css'), {} %> 19 <%= stylesheet_tag plugin.class.public_path('style.css'), {} %>
@@ -63,16 +65,10 @@ @@ -63,16 +65,10 @@
63 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> 65 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %>
64 </div> 66 </div>
65 </span> 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 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div> 70 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div>
  71 + <%= javascript_tag 'jQuery("#user form input").hint();' %>
76 </form> 72 </form>
77 </div><!-- end id="user" --> 73 </div><!-- end id="user" -->
78 74
app/views/map_balloon/product.rhtml 0 → 100644
@@ -0,0 +1,3 @@ @@ -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 @@ @@ -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 </div> 5 </div>
  6 + <div class="search-content-second-column">
  7 +
  8 + </div>
  9 +
  10 + <div style="clear:both"></div>
11 </li> 11 </li>
app/views/search/_article_author.rhtml 0 → 100644
@@ -0,0 +1,8 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -0,0 +1 @@
  1 +_article.rhtml
0 \ No newline at end of file 2 \ No newline at end of file
app/views/search/_display_results.rhtml
1 <div id="search-results" class="<%= 'only-one-result-box' if @results.size == 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 <% end %> 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 <h3><%= @names[name] %></h3> 30 <h3><%= @names[name] %></h3>
57 <% end %> 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 <% end %> 38 <% end %>
65 -<% end %>  
66 -  
67 -<br style="clear:both" /> 39 +
  40 + <div style="clear:both"></div>
68 </div><!-- end id="search-results" --> 41 </div><!-- end id="search-results" -->
69 42
app/views/search/_enterprise_homepage.rhtml 0 → 100644
@@ -0,0 +1 @@ @@ -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 </div> 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 </li> 24 </li>
app/views/search/_facets_menu.rhtml 0 → 100644
@@ -0,0 +1,36 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 <div style='text-align: center;'> 2 <div style='text-align: center;'>
5 <div id="map"></div> 3 <div id="map"></div>
6 </div> 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 </script> 34 </script>
app/views/search/_image.rhtml 0 → 100644
@@ -0,0 +1,47 @@ @@ -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 <% extra_content = @plugins.map(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %> 1 <% extra_content = @plugins.map(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
9 <% extra_properties = @plugins.map(:asset_product_properties, product)%> 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 <% end %> 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 <%= extra_content.join('\n') %> 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 </li> 85 </li>
app/views/search/_product_categories_menu.rhtml
@@ -1,42 +0,0 @@ @@ -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 @@ @@ -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 <div class='search-form'> 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 </div> 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 <% end %> 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 @@ @@ -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 @@ @@ -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,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 @@ @@ -0,0 +1 @@
  1 +index.rhtml
0 \ No newline at end of file 2 \ No newline at end of file
app/views/search/cities.rhtml
@@ -1,7 +0,0 @@ @@ -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 <% end %> 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 <%= pagination_links @results.values.first %> 21 <%= pagination_links @results.values.first %>
23 </div> 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,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 @@ @@ -0,0 +1 @@
  1 +articles.rhtml
0 \ No newline at end of file 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 <% end %> 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 <%= render :partial => 'events/agenda' %> 3 <%= render :partial => 'events/agenda' %>
app/views/search/facets_browse.rhtml 0 → 100644
@@ -0,0 +1,8 @@ @@ -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,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,4 +18,5 @@
18 </div> 18 </div>
19 <%= pagination_links @tagged, :param_name => 'npage' %> 19 <%= pagination_links @tagged, :param_name => 'npage' %>
20 20
  21 + <div style="clear: both"></div>
21 <% end %> 22 <% end %>
config/initializers/dependencies.rb
1 # locally-developed modules 1 # locally-developed modules
  2 +require 'acts_as_faceted'
2 require 'acts_as_filesystem' 3 require 'acts_as_filesystem'
3 require 'acts_as_having_settings' 4 require 'acts_as_having_settings'
4 require 'acts_as_searchable' 5 require 'acts_as_searchable'
config/routes.rb
@@ -52,10 +52,6 @@ ActionController::Routing::Routes.draw do |map| @@ -52,10 +52,6 @@ ActionController::Routing::Routes.draw do |map|
52 # search 52 # search
53 map.connect 'search/:action/*category_path', :controller => 'search' 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 # events 55 # events
60 map.events 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/ 56 map.events 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/
61 map.events 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/ 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,6 +81,9 @@ ActionController::Routing::Routes.draw do |map|
85 # contact 81 # contact
86 map.contact 'contact/:profile/:action/:id', :controller => 'contact', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/ 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 # chat 87 # chat
89 map.chat 'chat/:action/:id', :controller => 'chat' 88 map.chat 'chat/:action/:id', :controller => 'chat'
90 map.chat 'chat/:action', :controller => 'chat' 89 map.chat 'chat/:action', :controller => 'chat'
db/migrate/20110810123648_add_acronym_and_abbreviation_to_category.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -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 @@ @@ -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
@@ -175,6 +175,8 @@ ActiveRecord::Schema.define(:version =&gt; 20111004184104) do @@ -175,6 +175,8 @@ ActiveRecord::Schema.define(:version =&gt; 20111004184104) do
175 t.integer "children_count", :default => 0 175 t.integer "children_count", :default => 0
176 t.boolean "accept_products", :default => true 176 t.boolean "accept_products", :default => true
177 t.integer "image_id" 177 t.integer "image_id"
  178 + t.string "acronym"
  179 + t.string "abbreviation"
178 end 180 end
179 181
180 create_table "categories_profiles", :id => false, :force => true do |t| 182 create_table "categories_profiles", :id => false, :force => true do |t|
@@ -452,9 +454,9 @@ ActiveRecord::Schema.define(:version =&gt; 20111004184104) do @@ -452,9 +454,9 @@ ActiveRecord::Schema.define(:version =&gt; 20111004184104) do
452 454
453 create_table "roles", :force => true do |t| 455 create_table "roles", :force => true do |t|
454 t.string "name" 456 t.string "name"
  457 + t.text "permissions"
455 t.string "key" 458 t.string "key"
456 t.boolean "system", :default => false 459 t.boolean "system", :default => false
457 - t.text "permissions"  
458 t.integer "environment_id" 460 t.integer "environment_id"
459 end 461 end
460 462
lib/acts_as_faceted.rb 0 → 100644
@@ -0,0 +1,219 @@ @@ -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,16 +4,16 @@ module ActsAsSearchable
4 ACTS_AS_SEARCHABLE_ENABLED = true unless defined? ACTS_AS_SEARCHABLE_ENABLED 4 ACTS_AS_SEARCHABLE_ENABLED = true unless defined? ACTS_AS_SEARCHABLE_ENABLED
5 5
6 def acts_as_searchable(options = {}) 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 end 13 end
  14 + acts_as_solr options
  15 + extend FindByContents
  16 + send :include, InstanceMethods
17 end 17 end
18 18
19 module InstanceMethods 19 module InstanceMethods
@@ -31,14 +31,20 @@ module ActsAsSearchable @@ -31,14 +31,20 @@ module ActsAsSearchable
31 def find_by_contents(query, pg_options = {}, options = {}, db_options = {}) 31 def find_by_contents(query, pg_options = {}, options = {}, db_options = {})
32 pg_options[:page] ||= 1 32 pg_options[:page] ||= 1
33 pg_options[:per_page] ||= 20 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 query = !schema_name.empty? ? "+schema_name:\"#{schema_name}\" AND #{query}" : query 37 query = !schema_name.empty? ? "+schema_name:\"#{schema_name}\" AND #{query}" : query
  38 + results = []
  39 + facets = all_facets = {}
  40 +
38 solr_result = find_by_solr(query, options) 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 facets = options.include?(:facets) ? solr_result.facets : [] 48 facets = options.include?(:facets) ? solr_result.facets : []
43 49
44 if db_options.empty? 50 if db_options.empty?
@@ -61,7 +67,7 @@ module ActsAsSearchable @@ -61,7 +67,7 @@ module ActsAsSearchable
61 results = results.paginate(pg_options.merge(:total_entries => solr_result.total)) 67 results = results.paginate(pg_options.merge(:total_entries => solr_result.total))
62 end 68 end
63 69
64 - {:results => results, :facets => facets} 70 + {:results => results, :facets => facets, :all_facets => all_facets}
65 end 71 end
66 end 72 end
67 end 73 end
public/designs/themes/base/navigation.rhtml
1 <li> 1 <li>
2 - <%= browse_people_menu %> 2 + <%= search_people_menu %>
3 </li> 3 </li>
4 <li> 4 <li>
5 - <%= browse_communities_menu %> 5 + <%= search_communities_menu %>
6 </li> 6 </li>
7 <li> 7 <li>
8 - <%= browse_contents_menu %> 8 + <%= search_contents_menu %>
9 </li> 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 /* ==> button.css <== */ 1 /* ==> button.css <== */
2 2
3 -  
4 .button { 3 .button {
5 -moz-border-radius: 3px; 4 -moz-border-radius: 3px;
6 -webkit-border-radius: 3px; 5 -webkit-border-radius: 3px;
@@ -108,14 +107,14 @@ body, th, td, input { @@ -108,14 +107,14 @@ body, th, td, input {
108 margin-left: 20px; 107 margin-left: 20px;
109 } 108 }
110 109
111 -#user form input { 110 +#user form input .blur {
112 width: 160px; 111 width: 160px;
113 border: 1px solid #BBB; 112 border: 1px solid #BBB;
114 -moz-border-radius: 3px; 113 -moz-border-radius: 3px;
115 -webkit-border-radius: 3px; 114 -webkit-border-radius: 3px;
116 color: #CCC; 115 color: #CCC;
117 } 116 }
118 -#user form.focused input { 117 +#user form input {
119 border: 1px solid #888; 118 border: 1px solid #888;
120 color: #555; 119 color: #555;
121 } 120 }
@@ -502,11 +501,11 @@ div#notice { @@ -502,11 +501,11 @@ div#notice {
502 } 501 }
503 502
504 #content .profile-list li a, 503 #content .profile-list li a,
505 -#content .common-profile-list-block li a { 504 +#content .common-profile-list-block .vcard li a {
506 color: #555; 505 color: #555;
507 } 506 }
508 #content .profile-list li a:hover, 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 color: #000; 509 color: #000;
511 text-decoration: none; 510 text-decoration: none;
512 } 511 }