Commit 03c856bbddd762c9f7f77e7b320eb1f3e10a221e

Authored by Daniela Feitosa
2 parents 93103d85 420c09be

Merge commit 'refs/merge-requests/173' of git://gitorious.org/noosfero/noosfero …

…into merge-requests/173
Showing 471 changed files with 122499 additions and 14444 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 471 files displayed.

@@ -40,6 +40,8 @@ commands and make sure you understand what you are doing): @@ -40,6 +40,8 @@ commands and make sure you understand what you are doing):
40 cp config/database.yml.sqlite3 config/database.yml 40 cp config/database.yml.sqlite3 config/database.yml
41 # create tmp directory if it doesn't exist 41 # create tmp directory if it doesn't exist
42 mkdir tmp 42 mkdir tmp
  43 + # start Solr
  44 + rake solr:start
43 # create the development database 45 # create the development database
44 rake db:schema:load 46 rake db:schema:load
45 # run pending migrations 47 # run pending migrations
@@ -13,8 +13,7 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or @@ -13,8 +13,7 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or
13 Debian-based systems, all of these packages are available through the Debian 13 Debian-based systems, all of these packages are available through the Debian
14 archive. You can install them with the following command: 14 archive. You can install them with the following command:
15 15
16 - # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby thin tango-icon-theme libhpricot-ruby  
17 - 16 + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby openjdk-6-jre libdaemons-ruby thin tango-icon-theme libhpricot-ruby
18 17
19 On other systems, they may or may not be available through your regular package 18 On other systems, they may or may not be available through your regular package
20 management system. Below are the links to their homepages. 19 management system. Below are the links to their homepages.
@@ -24,7 +23,7 @@ management system. Below are the links to their homepages. @@ -24,7 +23,7 @@ management system. Below are the links to their homepages.
24 * po4a: http://po4a.alioth.debian.org/ 23 * po4a: http://po4a.alioth.debian.org/
25 * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby 24 * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby
26 * rcov: http://eigenclass.org/hiki/rcov 25 * rcov: http://eigenclass.org/hiki/rcov
27 -* Ferret: http://ferret.davebalmain.com/trac 26 +* Solr: http://lucene.apache.org/solr
28 * RMagick: http://rmagick.rubyforge.org/ 27 * RMagick: http://rmagick.rubyforge.org/
29 * RedCloth: http://redcloth.org/ 28 * RedCloth: http://redcloth.org/
30 * will_paginate: http://github.com/mislav/will_paginate/wikis 29 * will_paginate: http://github.com/mislav/will_paginate/wikis
@@ -104,7 +103,7 @@ $ tar -zxvf noosfero-0.35.0.tar.gz @@ -104,7 +103,7 @@ $ tar -zxvf noosfero-0.35.0.tar.gz
104 $ ln -s noosfero-0.35.0 current 103 $ ln -s noosfero-0.35.0 current
105 $ cd current 104 $ cd current
106 105
107 -Copy config/ferret_server.yml.dist to config/ferret_server.yml. You will 106 +Copy config/solr.yml.dist to config/solr.yml. You will
108 probably not need to customize this configuration, but have a look at it. 107 probably not need to customize this configuration, but have a look at it.
109 108
110 Create the thin configuration file: 109 Create the thin configuration file:
@@ -242,6 +241,10 @@ Compile the translations: @@ -242,6 +241,10 @@ Compile the translations:
242 241
243 $ RAILS_ENV=production rake noosfero:translations:compile 242 $ RAILS_ENV=production rake noosfero:translations:compile
244 243
  244 +Run Solr:
  245 +
  246 +$ rake solr:start
  247 +
245 Now we have to create some initial data. To create your default environment 248 Now we have to create some initial data. To create your default environment
246 (the first one), run the command below: 249 (the first one), run the command below:
247 250
@@ -7,4 +7,6 @@ require 'rake' @@ -7,4 +7,6 @@ require 'rake'
7 require 'rake/testtask' 7 require 'rake/testtask'
8 require 'rake/rdoctask' 8 require 'rake/rdoctask'
9 9
  10 +ACTS_AS_SEARCHABLE_ENABLED = false if Rake.application.top_level_tasks.detect{|t| t == 'db:data:minimal'}
  11 +
10 require 'tasks/rails' 12 require 'tasks/rails'
app/controllers/application.rb
1 require 'application_controller' 1 require 'application_controller'
  2 +
app/controllers/my_profile/cms_controller.rb
@@ -265,9 +265,10 @@ class CmsController < MyProfileController @@ -265,9 +265,10 @@ class CmsController < MyProfileController
265 265
266 def search 266 def search
267 query = params[:q] 267 query = params[:q]
268 - results = query.blank? ? [] : profile.articles.published.find_by_contents(query) 268 + results = query.blank? ? [] : profile.articles.published.find_by_contents(query)[:results]
269 render :text => article_list_to_json(results), :content_type => 'application/json' 269 render :text => article_list_to_json(results), :content_type => 'application/json'
270 end 270 end
  271 +
271 def media_upload 272 def media_upload
272 files_uploaded = [] 273 files_uploaded = []
273 parent = check_parent(params[:parent_id]) 274 parent = check_parent(params[:parent_id])
app/controllers/my_profile/maps_controller.rb
@@ -6,16 +6,48 @@ class MapsController < MyProfileController @@ -6,16 +6,48 @@ class MapsController < MyProfileController
6 @profile_data = profile 6 @profile_data = profile
7 if request.post? 7 if request.post?
8 begin 8 begin
  9 + country = params[:profile_data][:country]
  10 + city = params[:profile_data][:city]
  11 + state = params[:profile_data][:state]
  12 + nregion = NationalRegion.validate!(city, state, country)
  13 + unless nregion.blank?
  14 + params[:profile_data][:national_region_code] = nregion.national_region_code
  15 + end
  16 +
9 Profile.transaction do 17 Profile.transaction do
10 if profile.update_attributes!(params[:profile_data]) 18 if profile.update_attributes!(params[:profile_data])
11 session[:notice] = _('Address was updated successfully!') 19 session[:notice] = _('Address was updated successfully!')
12 redirect_to :action => 'edit_location' 20 redirect_to :action => 'edit_location'
13 end 21 end
14 end 22 end
15 - rescue  
16 - flash[:error] = _('Address could not be saved.') 23 + rescue Exception => exc
  24 + flash[:error] = exc.message
17 end 25 end
18 end 26 end
19 end 27 end
20 28
  29 + def google_map
  30 + render :partial => 'google_map.js'
  31 + end
  32 +
  33 + def search_city
  34 +
  35 + term = params[:term];
  36 +
  37 + regions = NationalRegion.search_city(term + "%", true).map {|r|{ :label => r.city , :category => r.state}}
  38 +
  39 + render :json => regions
  40 +
  41 + end
  42 +
  43 + def search_state
  44 +
  45 + term = params[:term];
  46 +
  47 + regions = NationalRegion.search_state(term + "%", true).map {|r|{ :label => r.state}}
  48 +
  49 + render :json => regions
  50 +
  51 + end
  52 +
21 end 53 end
app/controllers/public/browse_controller.rb
@@ -1,77 +0,0 @@ @@ -1,77 +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])  
25 - end  
26 - @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])  
27 - end  
28 -  
29 - def communities  
30 - @filter = filter  
31 - @title = self.filter_description(params[:action] + '_' + @filter )  
32 -  
33 - @results = @environment.communities.visible.send(@filter)  
34 -  
35 - if !params[:query].blank?  
36 - @results = @results.find_by_contents(params[:query])  
37 - end  
38 - @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])  
39 - end  
40 -  
41 - def contents  
42 - @filter = filter  
43 - @title = self.filter_description(params[:action] + '_' + @filter )  
44 -  
45 - @results = @environment.articles.published.text_articles.send(@filter)  
46 -  
47 - if !params[:query].blank?  
48 - @results = @results.find_by_contents(params[:query])  
49 - end  
50 - @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])  
51 - end  
52 -  
53 - protected  
54 -  
55 - def filter  
56 - if FILTERS.include?(params[:filter])  
57 - params[:filter]  
58 - else  
59 - 'more_recent'  
60 - end  
61 - end  
62 -  
63 - def filter_description(str)  
64 - {  
65 - 'people_more_recent' => _('More recent people'),  
66 - 'people_more_active' => _('More active people'),  
67 - 'people_more_popular' => _('More popular people'),  
68 - 'communities_more_recent' => _('More recent communities'),  
69 - 'communities_more_active' => _('More active communities'),  
70 - 'communities_more_popular' => _('More popular communities'),  
71 - 'contents_more_recent' => _('More recent contents'),  
72 - 'contents_more_views' => _('Most viewed contents'),  
73 - 'contents_more_comments' => _('Most commented contents'),  
74 - }[str] || str  
75 - end  
76 -  
77 -end  
app/controllers/public/map_balloon_controller.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +class MapBalloonController < PublicController
  2 +
  3 + helper SearchHelper
  4 +
  5 + before_filter :load_profile, :only => [:person, :enterprise, :community]
  6 +
  7 + def product
  8 + @product = Product.find(params[:id])
  9 + render :action => 'product', :layout => false
  10 + end
  11 +
  12 + def person
  13 + render :action => 'profile', :layout => false
  14 + end
  15 +
  16 + def enterprise
  17 + render :action => 'profile', :layout => false
  18 + end
  19 +
  20 + def community
  21 + render :action => 'profile', :layout => false
  22 + end
  23 +
  24 + protected
  25 +
  26 + def load_profile
  27 + @profile = Profile.find(params[:id])
  28 + end
  29 +
  30 +end
app/controllers/public/profile_search_controller.rb
@@ -8,11 +8,10 @@ class ProfileSearchController &lt; PublicController @@ -8,11 +8,10 @@ class ProfileSearchController &lt; PublicController
8 def index 8 def index
9 @q = params[:q] 9 @q = params[:q]
10 unless @q.blank? 10 unless @q.blank?
11 - @filtered_query = remove_stop_words(@q)  
12 if params[:where] == 'environment' 11 if params[:where] == 'environment'
13 redirect_to :controller => 'search', :query => @q 12 redirect_to :controller => 'search', :query => @q
14 else 13 else
15 - @results = profile.articles.published.find_by_contents(@filtered_query).paginate(:per_page => 10, :page => params[:page]) 14 + @results = profile.articles.published.find_by_contents(@q, {:per_page => 10, :page => params[:page]})[:results]
16 end 15 end
17 end 16 end
18 end 17 end
app/controllers/public/search_controller.rb
1 class SearchController < PublicController 1 class SearchController < PublicController
2 2
3 helper TagsHelper 3 helper TagsHelper
  4 + include SearchHelper
  5 + include ActionView::Helpers::NumberHelper
4 6
5 before_filter :load_category 7 before_filter :load_category
6 - before_filter :prepare_filter  
7 - before_filter :check_search_whole_site  
8 before_filter :load_search_assets 8 before_filter :load_search_assets
9 - before_filter :check_valid_assets, :only => [ :assets ] 9 + before_filter :load_query
10 10
11 no_design_blocks 11 no_design_blocks
12 12
13 - protected 13 + def facets_browse
  14 + @asset = params[:asset]
  15 + @asset_class = asset_class(@asset)
14 16
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)  
20 - end 17 + @facets_only = true
  18 + send(@asset)
  19 +
  20 + @facet = @asset_class.map_facets_for(environment).find { |facet| facet[:id] == params[:facet_id] }
  21 + raise 'Facet not found' if @facet.nil?
  22 +
  23 + render :layout => false
21 end 24 end
22 25
23 - def prepare_filter  
24 - if @category  
25 - @noosfero_finder = CategoryFinder.new(@category) 26 + def articles
  27 + if !@empty_query
  28 + full_text_search ['public:true']
26 else 29 else
27 - @noosfero_finder = EnvironmentFinder.new(@environment) 30 + @results[@asset] = @environment.articles.public.send(@filter).paginate(paginate_options)
28 end 31 end
29 end 32 end
30 33
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)  
34 - end 34 + def contents
  35 + redirect_to params.merge(:action => :articles)
35 end 36 end
36 37
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 38 + def people
  39 + if !@empty_query
  40 + full_text_search ['public:true']
  41 + else
  42 + @results[@asset] = @environment.people.visible.send(@filter).paginate(paginate_options)
42 end 43 end
43 end 44 end
44 45
45 - def events  
46 - @category_id = @category ? @category.id : nil  
47 -  
48 - @selected_day = nil  
49 - @events_of_the_day = []  
50 - date = build_date(params[:year], params[:month], params[:day])  
51 -  
52 - if params[:day] || !params[:year] && !params[:month]  
53 - @selected_day = date  
54 - if @category_id and Category.exists?(@category_id)  
55 - @events_of_the_day = environment.events.by_day(@selected_day).in_category(Category.find(@category_id)) 46 + def products
  47 + public_filters = ['public:true', 'enabled:true']
  48 + if !@empty_query
  49 + full_text_search public_filters
  50 + else
  51 + @one_page = true
  52 + @geosearch = logged_in? && current_user.person.lat && current_user.person.lng
  53 +
  54 + extra_limit = LIST_SEARCH_LIMIT*5
  55 + sql_options = {:limit => LIST_SEARCH_LIMIT, :order => 'random()'}
  56 + if @geosearch
  57 + full_text_search public_filters, :sql_options => sql_options, :extra_limit => extra_limit,
  58 + :alternate_query => "{!boost b=recip(geodist(),#{"%e" % (1.to_f/DistBoost)},1,1)}",
  59 + :radius => DistFilt, :latitude => current_user.person.lat, :longitude => current_user.person.lng
56 else 60 else
57 - @events_of_the_day = environment.events.by_day(@selected_day) 61 + full_text_search public_filters, :sql_options => sql_options, :extra_limit => extra_limit,
  62 + :boost_functions => ['recip(ms(NOW/HOUR,updated_at),1.3e-10,1,1)']
58 end 63 end
59 end 64 end
60 -  
61 - events = @results[:events]  
62 -  
63 - @calendar = populate_calendar(date, events)  
64 - @previous_calendar = populate_calendar(date - 1.month, events)  
65 - @next_calendar = populate_calendar(date + 1.month, events)  
66 end 65 end
67 66
68 - def people  
69 - #nothing, just to enable  
70 - end  
71 def enterprises 67 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 68 + if !@empty_query
  69 + full_text_search ['public:true']
  70 + else
  71 + @filter_title = _('Enterprises from network')
  72 + @results[@asset] = @environment.enterprises.visible.paginate(paginate_options)
  73 + end
85 end 74 end
86 75
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 - @result_ids = @noosfero_finder.find(asset, @filtered_query, calculate_find_options(asset, nil, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]).merge({:limit => :all})) 76 + def communities
  77 + if !@empty_query
  78 + full_text_search ['public:true']
  79 + else
  80 + @results[@asset] = @environment.communities.visible.send(@filter).paginate(paginate_options)
93 end 81 end
94 -  
95 end 82 end
96 83
97 - def calculate_find_options(asset, limit, page, product_category, region, radius, year, month)  
98 - result = { :product_category => product_category, :per_page => limit, :page => page }  
99 - if [:enterprises, :people, :products].include?(asset) && region  
100 - result.merge!(:within => radius, :region => region.id)  
101 - end 84 + def events
  85 + year = (params[:year] ? params[:year].to_i : Date.today.year)
  86 + month = (params[:month] ? params[:month].to_i : Date.today.month)
  87 + day = (params[:day] ? params[:day].to_i : Date.today.day)
  88 + date = build_date(params[:year], params[:month], params[:day])
  89 + date_range = (date - 1.month)..(date + 1.month).at_end_of_month
102 90
103 - if month || year  
104 - date = Date.new(year.to_i, month.to_i, 1)  
105 - result[:date_range] = (date - 1.month)..(date + 1.month).at_end_of_month 91 + @selected_day = nil
  92 + @events_of_the_day = []
  93 + if params[:day] || !params[:year] && !params[:month]
  94 + @selected_day = date
  95 + @events_of_the_day = @category ?
  96 + environment.events.by_day(@selected_day).in_category(Category.find(@category_id)) :
  97 + environment.events.by_day(@selected_day)
106 end 98 end
107 99
108 - result  
109 - end  
110 -  
111 - # limit the number of results per page  
112 - # TODO: dont hardcore like this  
113 - def limit  
114 - searching = @searching.values.select{|v|v}  
115 - if params[:display] == 'map'  
116 - 2000 100 + if !@empty_query
  101 + full_text_search
117 else 102 else
118 - (searching.size == 1) ? 20 : 6 103 + @results[@asset] = date_range ? environment.events.by_range(date_range) : environment.events
119 end 104 end
120 - end  
121 -  
122 - public  
123 -  
124 - include SearchHelper  
125 105
126 - ######################################################  
127 -  
128 - def where_to_search  
129 - [  
130 - [ :articles, N_('Articles') ],  
131 - [ :enterprises, N_('Enterprises') ],  
132 - [ :people, N_('People') ],  
133 - [ :communities, N_('Communities') ],  
134 - [ :products, N_('Products') ],  
135 - [ :events, N_('Events') ]  
136 - ].select {|key, name| !environment.enabled?('disable_asset_' + key.to_s) }  
137 - end  
138 -  
139 - def cities  
140 - @cities = City.find(:all, :order => 'name', :conditions => ['parent_id = ? and lat is not null and lng is not null', params[:state_id]])  
141 - render :action => 'cities', :layout => false  
142 - end  
143 -  
144 - def complete_region  
145 - # FIXME this logic should be in the model  
146 - @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 + '%' ])  
147 - render :action => 'complete_region', :layout => false 106 + events = @results[@asset]
  107 + @calendar = populate_calendar(date, events)
  108 + @previous_calendar = populate_calendar(date - 1.month, events)
  109 + @next_calendar = populate_calendar(date + 1.month, events)
148 end 110 end
149 111
150 def index 112 def index
151 - @query = params[:query] || ''  
152 - @filtered_query = remove_stop_words(@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 = {} 113 @results = {}
161 @order = [] 114 @order = []
162 @names = {} 115 @names = {}
  116 + @results_only = true
163 117
164 - where_to_search.select { |key,description| @searching[key] }.each do |key, description| 118 + @enabled_searches.select { |key,description| @searching[key] }.each do |key, description|
  119 + load_query
  120 + @asset = key
  121 + send(key)
165 @order << key 122 @order << key
166 - @results[key] = @noosfero_finder.find(key, @filtered_query, calculate_find_options(key, limit, params[:page], @product_category, @region, params[:radius], params[:year], params[:month]))  
167 @names[key] = getterm(description) 123 @names[key] = getterm(description)
168 end 124 end
  125 + @asset = nil
  126 + @facets = {}
169 127
170 - if @results.keys.size == 1  
171 - specific_action = @results.keys.first  
172 - if respond_to?(specific_action)  
173 - @asset_name = getterm(@names[@results.keys.first])  
174 - send(specific_action)  
175 - render :action => specific_action  
176 - return  
177 - end  
178 - end  
179 -  
180 - render :action => 'index' 128 + render :action => @results.keys.first if @results.keys.size == 1
181 end 129 end
182 130
183 - alias :assets :index  
184 -  
185 - ####################################################### 131 + # keep old URLs workings
  132 + def assets
  133 + params[:action] = params[:asset].is_a?(Array) ? :index : params.delete(:asset)
  134 + redirect_to params
  135 + end
186 136
187 # view the summary of one category 137 # view the summary of one category
188 def category_index 138 def category_index
189 @results = {} 139 @results = {}
190 @order = [] 140 @order = []
191 @names = {} 141 @names = {}
  142 + limit = MULTIPLE_SEARCH_LIMIT
192 [ 143 [
193 - [ :people, _('People'), @noosfero_finder.recent('people', limit) ],  
194 - [ :enterprises, __('Enterprises'), @noosfero_finder.recent('enterprises', limit) ],  
195 - [ :products, _('Products'), @noosfero_finder.recent('products', limit) ],  
196 - [ :events, _('Upcoming events'), @noosfero_finder.upcoming_events({:per_page => limit}) ],  
197 - [ :communities, __('Communities'), @noosfero_finder.recent('communities', limit) ],  
198 - [ :most_commented_articles, _('Most commented articles'), @noosfero_finder.most_commented_articles(limit) ],  
199 - [ :articles, _('Articles'), @noosfero_finder.recent('text_articles', limit) ]  
200 - ].each do |key, name, list|  
201 - @order << key  
202 - @results[key] = list  
203 - @names[key] = name 144 + [ :people, _('People'), :recent_people ],
  145 + [ :enterprises, _('Enterprises'), :recent_enterprises ],
  146 + [ :products, _('Products'), :recent_products ],
  147 + [ :events, _('Upcoming events'), :upcoming_events ],
  148 + [ :communities, _('Communities'), :recent_communities ],
  149 + [ :articles, _('Contents'), :recent_articles ]
  150 + ].each do |asset, name, filter|
  151 + @order << asset
  152 + @results[asset] = @category.send(filter, limit)
  153 + raise "No total_entries for: #{asset}" unless @results[asset].respond_to?(:total_entries)
  154 + @names[asset] = name
204 end 155 end
205 end 156 end
206 - attr_reader :category  
207 157
208 def tags 158 def tags
209 @tags_cache_key = "tags_env_#{environment.id.to_s}" 159 @tags_cache_key = "tags_env_#{environment.id.to_s}"
@@ -216,25 +166,137 @@ class SearchController &lt; PublicController @@ -216,25 +166,137 @@ class SearchController &lt; PublicController
216 @tag = params[:tag] 166 @tag = params[:tag]
217 @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}" 167 @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}"
218 if is_cache_expired?(@tag_cache_key) 168 if is_cache_expired?(@tag_cache_key)
219 - @tagged = environment.articles.find_tagged_with(@tag).paginate(:per_page => 10, :page => params[:npage]) 169 + @results[@asset] = environment.articles.find_tagged_with(@tag).paginate(paginate_options)
220 end 170 end
221 end 171 end
222 172
  173 + def events_by_day
  174 + @selected_day = build_date(params[:year], params[:month], params[:day])
  175 + @events_of_the_day = environment.events.by_day(@selected_day)
  176 + render :partial => 'events/events_by_day'
  177 + end
  178 +
223 ####################################################### 179 #######################################################
  180 + protected
  181 +
  182 + def load_query
  183 + @asset = params[:action].to_sym
  184 + @order ||= [@asset]
  185 + @results ||= {}
  186 + @filter = filter
  187 + @filter_title = filter_description(@asset, @filter)
224 188
225 - def popup  
226 - @regions = Region.find(:all).select{|r|r.lat && r.lng}  
227 - render :action => 'popup', :layout => false 189 + @query = params[:query] || ''
  190 + @empty_query = @category.nil? && @query.blank?
228 end 191 end
229 192
230 - def events_by_day  
231 - @selected_day = build_date(params[:year], params[:month], params[:day])  
232 - if params[:category_id] and Category.exists?(params[:category_id])  
233 - @events_of_the_day = environment.events.by_day(@selected_day).in_category(Category.find(params[:category_id])) 193 + def load_category
  194 + if params[:category_path].blank?
  195 + render_not_found if params[:action] == 'category_index'
234 else 196 else
235 - @events_of_the_day = environment.events.by_day(@selected_day) 197 + path = params[:category_path].join('/')
  198 + @category = environment.categories.find_by_path(path)
  199 + if @category.nil?
  200 + render_not_found(path)
  201 + else
  202 + @category_id = @category.id
  203 + end
236 end 204 end
237 - render :partial => 'events/events_by_day' 205 + end
  206 +
  207 + FILTERS = %w(
  208 + more_recent
  209 + more_active
  210 + more_popular
  211 + )
  212 + def filter
  213 + if FILTERS.include?(params[:filter])
  214 + params[:filter]
  215 + else
  216 + 'more_recent'
  217 + end
  218 + end
  219 +
  220 + def filter_description(asset, filter)
  221 + {
  222 + 'articles_more_recent' => _('More recent contents from network'),
  223 + 'articles_more_popular' => _('More read contents from network'),
  224 + 'people_more_recent' => _('More recent people from network'),
  225 + 'people_more_active' => _('More active people from network'),
  226 + 'people_more_popular' => _('More popular people from network'),
  227 + 'communities_more_recent' => _('More recent communities from network'),
  228 + 'communities_more_active' => _('More active communities from network'),
  229 + 'communities_more_popular' => _('More popular communities from network'),
  230 + 'products_more_recent' => _('Highlights'),
  231 + }[asset.to_s + '_' + filter]
  232 + end
  233 +
  234 + def load_search_assets
  235 + if Searches.keys.include?(params[:action].to_sym) and environment.enabled?("disable_asset_#{params[:action]}")
  236 + render_not_found
  237 + return
  238 + end
  239 +
  240 + @enabled_searches = Searches.select {|key, name| environment.disabled?("disable_asset_#{params[:action]}") }
  241 + @searching = {}
  242 + @titles = {}
  243 + @enabled_searches.each do |key, name|
  244 + @titles[key] = name
  245 + @searching[key] = params[:action] == 'index' || params[:action] == key.to_s
  246 + end
  247 + @names = @titles if @names.nil?
  248 + end
  249 +
  250 + def limit
  251 + searching = @searching.values.select{ |v| v }
  252 + if params[:display] == 'map'
  253 + MAP_SEARCH_LIMIT
  254 + elsif searching.size <= 1
  255 + if [:people, :communities].include? @asset
  256 + BLOCKS_SEARCH_LIMIT
  257 + elsif @asset == :enterprises and @empty_query
  258 + BLOCKS_SEARCH_LIMIT
  259 + else
  260 + LIST_SEARCH_LIMIT
  261 + end
  262 + else
  263 + MULTIPLE_SEARCH_LIMIT
  264 + end
  265 + end
  266 +
  267 + def paginate_options(page = params[:page])
  268 + { :per_page => limit, :page => page }
  269 + end
  270 +
  271 + def full_text_search(filters = [], options = {})
  272 + paginate_options = paginate_options(params[:page])
  273 + asset_class = asset_class(@asset)
  274 +
  275 + solr_options = options
  276 + if !@results_only and asset_class.respond_to? :facets
  277 + solr_options.merge! asset_class.facets_find_options(params[:facet])
  278 + solr_options[:all_facets] = true
  279 + solr_options[:limit] = 0 if @facets_only
  280 + end
  281 + solr_options[:filter_queries] ||= []
  282 + solr_options[:filter_queries] += filters
  283 + solr_options[:filter_queries] << "environment_id:#{environment.id}"
  284 + solr_options[:filter_queries] << asset_class.facet_category_query.call(@category) if @category
  285 +
  286 + solr_options[:boost_functions] ||= []
  287 + params[:order_by] = nil if params[:order_by] == 'none'
  288 + if params[:order_by]
  289 + order = SortOptions[@asset][params[:order_by].to_sym]
  290 + raise "Unknown order by" if order.nil?
  291 + order[:solr_opts].each do |opt, value|
  292 + solr_options[opt] = value.is_a?(Proc) ? instance_eval(&value) : value
  293 + end
  294 + end
  295 +
  296 + ret = asset_class.find_by_contents(@query, paginate_options, solr_options)
  297 + @results[@asset] = ret[:results]
  298 + @facets = ret[:facets]
  299 + @all_facets = ret[:all_facets]
238 end 300 end
239 301
240 end 302 end
app/helpers/application_helper.rb
@@ -4,7 +4,7 @@ require &#39;redcloth&#39; @@ -4,7 +4,7 @@ require &#39;redcloth&#39;
4 # application. 4 # application.
5 module ApplicationHelper 5 module ApplicationHelper
6 6
7 - include PermissionName 7 + include PermissionNameHelper
8 8
9 include LightboxHelper 9 include LightboxHelper
10 10
@@ -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',
@@ -1001,7 +986,7 @@ module ApplicationHelper @@ -1001,7 +986,7 @@ module ApplicationHelper
1001 end 986 end
1002 987
1003 def tokeninput_stylesheets 988 def tokeninput_stylesheets
1004 - ['token-input', 'token-input-facebook', 'token-input-mac'] 989 + ['token-input', 'token-input-facebook', 'token-input-mac', 'token-input-facet']
1005 end 990 end
1006 991
1007 def noosfero_layout_features 992 def noosfero_layout_features
@@ -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-articles'), {: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,6 +1328,10 @@ module ApplicationHelper @@ -1330,6 +1328,10 @@ 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 def delete_article_message(article) 1335 def delete_article_message(article)
1334 if article.folder? 1336 if article.folder?
1335 _("Are you sure that you want to remove the folder \"#{article.name}\"? Note that all the items inside it will also be removed!") 1337 _("Are you sure that you want to remove the folder \"#{article.name}\"? Note that all the items inside it will also be removed!")
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/manage_products_helper.rb
@@ -278,7 +278,7 @@ module ManageProductsHelper @@ -278,7 +278,7 @@ module ManageProductsHelper
278 error_msg = _('Something went wrong. Please, try again') 278 error_msg = _('Something went wrong. Please, try again')
279 select_tag('price_details[][production_cost_id]', 279 select_tag('price_details[][production_cost_id]',
280 '<option value="" disabled="disabled">' + _('Select...') + '</option>' + 280 '<option value="" disabled="disabled">' + _('Select...') + '</option>' +
281 - options_for_select(product.available_production_costs.map {|item| [truncate(item.name, 10, '...'), item.id]} + [[_('Other cost'), '']], selected), 281 + options_for_select(product.available_production_costs.map {|item| [truncate(item.name, {:length => 10, :omission => '...'}), item.id]} + [[_('Other cost'), '']], selected),
282 {:class => 'production-cost-selection', 282 {:class => 'production-cost-selection',
283 :onchange => "productionCostTypeChange(this, '#{url}', '#{prompt_msg}', '#{error_msg}')"}) 283 :onchange => "productionCostTypeChange(this, '#{url}', '#{prompt_msg}', '#{error_msg}')"})
284 end 284 end
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
1 module SearchHelper 1 module SearchHelper
2 2
  3 + MAP_SEARCH_LIMIT = 2000
  4 + LIST_SEARCH_LIMIT = 20
  5 + BLOCKS_SEARCH_LIMIT = 18
  6 + MULTIPLE_SEARCH_LIMIT = 8
  7 + DistFilt = 200
  8 + DistBoost = 50
  9 +
  10 + Searches = ActiveSupport::OrderedHash[
  11 + :articles, _('Contents'),
  12 + :enterprises, _('Enterprises'),
  13 + :people, _('People'),
  14 + :communities, _('Communities'),
  15 + :products, _('Products and Services'),
  16 + :events, _('Events'),
  17 + ]
  18 +
  19 + SortOptions = {
  20 + :products => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  21 + :more_recent, {:label => _('More Recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}},
  22 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  23 + :closest, {:label => _('Closest to me'), :if => proc{ logged_in? && (profile=current_user.person).lat && profile.lng },
  24 + :solr_opts => {:sort => "geodist() asc",
  25 + :latitude => proc{ current_user.person.lat }, :longitude => proc{ current_user.person.lng }}},
  26 + ],
  27 + :events => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  28 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  29 + ],
  30 + :articles => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  31 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  32 + :more_recent, {:label => _('More recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}},
  33 + ],
  34 + :enterprises => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  35 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  36 + ],
  37 + :people => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  38 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  39 + ],
  40 + :communities => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  41 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  42 + ],
  43 + }
  44 +
3 # FIXME remove it after search_controler refactored 45 # FIXME remove it after search_controler refactored
4 include EventsHelper 46 include EventsHelper
5 47
6 - STOP_WORDS = {  
7 - 'pt_BR' => Ferret::Analysis::FULL_PORTUGUESE_STOP_WORDS,  
8 - 'en' => Ferret::Analysis::FULL_ENGLISH_STOP_WORDS,  
9 - }  
10 -  
11 - def relevance_for(hit)  
12 - n = (hit.ferret_score if hit.respond_to?(:ferret_score))  
13 - n ||= 1.0  
14 - (n * 100.0).round 48 + def search_page_title(title, category = nil)
  49 + title = "<h1>" + title
  50 + title += '<small>' + category.name + '</small>' if category
  51 + title + "</h1>"
15 end 52 end
16 53
17 - def remove_stop_words(query)  
18 - (query.downcase.scan(/"[^"]*"?|'[^']*'?|[^'"\s]+/) - (STOP_WORDS[locale] || [])).join(' ') 54 + def category_context(category, url)
  55 + content_tag('div', category.full_name + _(', ') +
  56 + link_to(_('search in all categories'),
  57 + url.merge(:category_path => [], :action => (params[:action] == 'category_index' ? 'index' : params[:action]) )),
  58 + :align => 'center', :class => 'search-category-context') if category
19 end 59 end
20 60
21 - def display_results(use_map = true)  
22 -  
23 - unless use_map && GoogleMaps.enabled?(environment.default_hostname)  
24 - return render(:partial => 'display_results') 61 + def display_results(use_map = false)
  62 + if params[:display] == 'map' && use_map
  63 + partial = 'google_maps'
  64 + klass = 'map'
  65 + else
  66 + partial = 'display_results'
  67 + klass = 'list'
25 end 68 end
26 69
27 - data =  
28 - if params[:display] == 'map'  
29 - {  
30 - :partial => 'google_maps',  
31 - :toggle => button(:search, _('Display in list'), params.merge(:display => 'list'), :class => "map-toggle-button" ),  
32 - :class => 'map' ,  
33 - } 70 + content_tag('div', render(:partial => partial), :class => "map-or-list-search-results #{klass}")
  71 + end
  72 +
  73 + def display_map_list_button
  74 + button(:search, params[:display] == 'map' ? _('Display in list') : _('Display in map'),
  75 + params.merge(:display => (params[:display] == 'map' ? 'list' : 'map')),
  76 + :class => "map-toggle-button" )
  77 + end
  78 +
  79 + def city_with_state(city)
  80 + if city and city.kind_of?(City)
  81 + s = city.parent
  82 + if s and s.kind_of?(State) and s.acronym
  83 + city.name + ', ' + s.acronym
34 else 84 else
35 - {  
36 - :partial => 'display_results',  
37 - :toggle => button(:search, _('Display in map'), params.merge(:display => 'map'), :class => "map-toggle-button" ),  
38 - :class => 'list' ,  
39 - } 85 + city.name
40 end 86 end
  87 + else
  88 + nil
  89 + end
  90 + end
41 91
42 - content_tag('div', data[:toggle] + (render :partial => data[:partial]), :class => "map-or-list-search-results #{data[:class]}") 92 + def facets_menu(asset, _facets)
  93 + @asset_class = asset_class(asset)
  94 + @facets = _facets
  95 + render(:partial => 'facets_menu')
43 end 96 end
44 97
45 - def display_item_map_info(item)  
46 - if item.kind_of?(Profile)  
47 - display_profile_info(item)  
48 - elsif item.kind_of?(Product)  
49 - display_product_info(item)  
50 - end 98 + def facets_unselect_menu(asset)
  99 + @asset_class = asset_class(asset)
  100 + render(:partial => 'facets_unselect_menu')
51 end 101 end
52 102
53 - def display_profile_info(profile)  
54 - data = ''  
55 - unless profile.contact_email.nil?  
56 - data << content_tag('strong', _('E-Mail: ')) + profile.contact_email + '<br/>'  
57 - end  
58 - unless profile.contact_phone.nil?  
59 - data << content_tag('strong', _('Phone(s): ')) + profile.contact_phone + '<br/>'  
60 - end  
61 - unless profile.region.nil?  
62 - data << content_tag('strong', _('Location: ')) + profile.region.name + '<br/>'  
63 - end  
64 - unless profile.address.nil?  
65 - data << content_tag('strong', _('Address: ')) + profile.address + '<br/>'  
66 - end  
67 - unless profile.products.empty?  
68 - 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/>'  
69 - end  
70 - if profile.respond_to?(:distance) and !profile.distance.nil?  
71 - data << content_tag('strong', _('Distance: ')) + "%.2f%" % profile.distance + '<br/>'  
72 - end  
73 - content_tag('table',  
74 - content_tag('tr',  
75 - content_tag('td', content_tag('div', profile_image(profile, :thumb), :class => 'profile-info-picture')) +  
76 - content_tag('td', content_tag('strong', link_to(profile.name, url_for(profile.url))) + '<br/>' + data  
77 - )  
78 - ),  
79 - :class => 'profile-info'  
80 - ) 103 + def facet_javascript(input_id, facet, array)
  104 + array = [] if array.nil?
  105 + hintText = _('Type in an option')
  106 + text_field_tag('facet['+input_id+']', '', :id => input_id) +
  107 + javascript_tag("jQuery.TokenList(jQuery('##{input_id}'), #{array.to_json},
  108 + {searchDelay: 0, permanentDropdown: true, theme: 'facet', dontAdd: true, preventDuplicates: true,
  109 + #{jquery_token_input_messages_json(hintText)}});")
81 end 110 end
82 111
83 - def display_product_info(product)  
84 - data = ''  
85 - unless product.price.nil?  
86 - data << content_tag('strong', _('Price: ')) + product.price + '<br/>'  
87 - end  
88 - unless product.enterprise.nil?  
89 - data << content_tag('strong', _('Provider: ')) + link_to_profile(product.enterprise.name, product.enterprise.identifier)  
90 - end  
91 - unless product.product_category.nil?  
92 - data << content_tag('strong', _('Category: ')) + link_to(product.product_category.name, :controller => 'search', :action => 'assets', :asset => 'products', :product_category => product.product_category.id) 112 + def facet_link_html(facet, params, value, label, count)
  113 + params = params ? params.dup : {}
  114 + has_extra = label.kind_of?(Array)
  115 + link_label = has_extra ? label[0] : label
  116 + id = facet[:solr_field].to_s
  117 + params[:facet] ||= {}
  118 + params[:facet][id] ||= {}
  119 + params[:page] = {} if params[:page]
  120 +
  121 + selected = facet[:label_id].nil? ? params[:facet][id] == value : params[:facet][id][facet[:label_id]].to_a.include?(value)
  122 +
  123 + if count > 0
  124 + url = params.merge(:facet => params[:facet].merge(
  125 + id => facet[:label_id].nil? ? value : params[:facet][id].merge( facet[:label_id] => params[:facet][id][facet[:label_id]].to_a | [value] )
  126 + ))
  127 + else
  128 + # preserve others filters and change this filter
  129 + url = params.merge(:facet => params[:facet].merge(
  130 + id => facet[:label_id].nil? ? value : { facet[:label_id] => value }
  131 + ))
93 end 132 end
94 - content_tag('table',  
95 - content_tag('tr',  
96 - 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')) +  
97 - content_tag('td', content_tag('strong', link_to(product.name, :controller => 'catalog', :profile => product.enterprise.identifier, :action => 'show', :id => product)) + '<br/>' + data)  
98 - ), :class => 'profile-info') 133 +
  134 + content_tag 'div', link_to(link_label, url, :class => 'facet-result-link-label') +
  135 + content_tag('span', (has_extra ? label[1] : ''), :class => 'facet-result-extra-label') +
  136 + (count > 0 ? content_tag('span', " (#{count})", :class => 'facet-result-count') : ''),
  137 + :class => 'facet-menu-item' + (selected ? ' facet-result-link-selected' : '')
99 end 138 end
100 139
101 - def product_categories_menu(asset, product_category, object_ids = nil)  
102 - cats = ProductCategory.menu_categories(@product_category, environment)  
103 - cats += cats.select { |c| c.children_count > 0 }.map(&:children).flatten  
104 - product_categories_ids = cats.map(&:id)  
105 -  
106 - counts = @noosfero_finder.product_categories_count(asset, product_categories_ids, object_ids)  
107 -  
108 - product_categories_menu = ProductCategory.menu_categories(product_category, environment).map do |cat|  
109 - hits = counts[cat.id]  
110 - childs = []  
111 - if hits  
112 - if cat.children_count > 0  
113 - childs = cat.children.map do |child|  
114 - child_hits = counts[child.id]  
115 - [child, child_hits]  
116 - end.select{|child, child_hits| child_hits }  
117 - else  
118 - childs = [] 140 + def facet_selecteds_html_for(environment, klass, params)
  141 + def name_with_extra(klass, facet, value)
  142 + name = klass.facet_result_name(facet, value)
  143 + name = name[0] + name[1] if name.kind_of?(Array)
  144 + name
  145 + end
  146 +
  147 + ret = []
  148 + params = params.dup
  149 + params[:facet].each do |id, value|
  150 + facet = klass.facet_by_id(id.to_sym)
  151 + if value.kind_of?(Hash)
  152 + label_hash = facet[:label].call(environment)
  153 + value.each do |label_id, value|
  154 + facet[:label_id] = label_id
  155 + facet[:label] = label_hash[label_id]
  156 + value.to_a.each do |value|
  157 + ret << [facet[:label], name_with_extra(klass, facet, value),
  158 + params.merge(:facet => params[:facet].merge(id => params[:facet][id].merge(label_id => params[:facet][id][label_id].to_a.reject{ |v| v == value })))]
  159 + end
119 end 160 end
  161 + else
  162 + ret << [klass.facet_label(facet), name_with_extra(klass, facet, value),
  163 + params.merge(:facet => params[:facet].reject{ |k,v| k == id })]
120 end 164 end
121 - [cat, hits, childs]  
122 - end.select{|cat, hits| hits } 165 + end
123 166
124 - render(:partial => 'product_categories_menu', :object => product_categories_menu) 167 + ret.map do |label, name, url|
  168 + content_tag('div', content_tag('span', label, :class => 'facet-selected-label') +
  169 + content_tag('span', name, :class => 'facet-selected-name') +
  170 + link_to('', url, :class => 'facet-selected-remove', :title => 'remove facet'), :class => 'facet-selected')
  171 + end.join
  172 + end
  173 +
  174 + def order_by(asset)
  175 + options = SortOptions[asset].map do |name, options|
  176 + next if options[:if] and ! instance_eval(&options[:if])
  177 + [_(options[:label]), name.to_s]
  178 + end.compact
  179 +
  180 + content_tag('div', _('Sort results by ') +
  181 + select_tag(asset.to_s + '[order]', options_for_select(options, params[:order_by] || 'none'),
  182 + {:onchange => "window.location = jQuery.param.querystring(window.location.href, { 'order_by' : this.options[this.selectedIndex].value})"}),
  183 + :class => "search-ordering")
  184 + end
  185 +
  186 + def label_total_found(asset, total_found)
  187 + labels = {
  188 + :products => _("%s products offers found"),
  189 + :articles => _("%s articles found"),
  190 + :events => _("%s events found"),
  191 + :people => _("%s people found"),
  192 + :enterprises => _("%s enterprises found"),
  193 + :communities => _("%s communities found"),
  194 + }
  195 + content_tag('span', labels[asset] % total_found,
  196 + :class => "total-pages-found") if labels[asset]
  197 + end
  198 +
  199 + def asset_class(asset)
  200 + asset.to_s.singularize.camelize.constantize
  201 + end
  202 +
  203 + def asset_table(asset)
  204 + asset_class(asset).table_name
125 end 205 end
126 206
127 end 207 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
@@ -22,6 +28,9 @@ class Article &lt; ActiveRecord::Base @@ -22,6 +28,9 @@ class Article &lt; ActiveRecord::Base
22 has_many :article_categorizations, :conditions => [ 'articles_categories.virtual = ?', false ] 28 has_many :article_categorizations, :conditions => [ 'articles_categories.virtual = ?', false ]
23 has_many :categories, :through => :article_categorizations 29 has_many :categories, :through => :article_categorizations
24 30
  31 + has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization', :dependent => :destroy
  32 + has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category
  33 +
25 acts_as_having_settings :field => :setting 34 acts_as_having_settings :field => :setting
26 35
27 settings_items :display_hits, :type => :boolean, :default => true 36 settings_items :display_hits, :type => :boolean, :default => true
@@ -47,7 +56,7 @@ class Article &lt; ActiveRecord::Base @@ -47,7 +56,7 @@ class Article &lt; ActiveRecord::Base
47 xss_terminate :only => [ :name ], :on => 'validation', :with => 'white_list' 56 xss_terminate :only => [ :name ], :on => 'validation', :with => 'white_list'
48 57
49 named_scope :in_category, lambda { |category| 58 named_scope :in_category, lambda { |category|
50 - {:include => 'categories', :conditions => { 'categories.id' => category.id }} 59 + {:include => 'categories_including_virtual', :conditions => { 'categories.id' => category.id }}
51 } 60 }
52 61
53 named_scope :by_range, lambda { |range| { 62 named_scope :by_range, lambda { |range| {
@@ -96,11 +105,13 @@ class Article &lt; ActiveRecord::Base @@ -96,11 +105,13 @@ class Article &lt; ActiveRecord::Base
96 @pending_categorizations ||= [] 105 @pending_categorizations ||= []
97 end 106 end
98 107
99 - def add_category(c)  
100 - if self.id  
101 - ArticleCategorization.add_category_to_article(c, self)  
102 - else 108 + def add_category(c, reload=false)
  109 + if new_record?
103 pending_categorizations << c 110 pending_categorizations << c
  111 + else
  112 + ArticleCategorization.add_category_to_article(c, self)
  113 + self.categories(reload)
  114 + self.solr_save
104 end 115 end
105 end 116 end
106 117
@@ -109,6 +120,7 @@ class Article &lt; ActiveRecord::Base @@ -109,6 +120,7 @@ class Article &lt; ActiveRecord::Base
109 ids.uniq.each do |item| 120 ids.uniq.each do |item|
110 add_category(Category.find(item)) unless item.to_i.zero? 121 add_category(Category.find(item)) unless item.to_i.zero?
111 end 122 end
  123 + self.categories(true)
112 end 124 end
113 125
114 after_create :create_pending_categorizations 126 after_create :create_pending_categorizations
@@ -116,6 +128,8 @@ class Article &lt; ActiveRecord::Base @@ -116,6 +128,8 @@ class Article &lt; ActiveRecord::Base
116 pending_categorizations.each do |item| 128 pending_categorizations.each do |item|
117 ArticleCategorization.add_category_to_article(item, self) 129 ArticleCategorization.add_category_to_article(item, self)
118 end 130 end
  131 + self.categories(true)
  132 + self.solr_save
119 pending_categorizations.clear 133 pending_categorizations.clear
120 end 134 end
121 135
@@ -126,8 +140,6 @@ class Article &lt; ActiveRecord::Base @@ -126,8 +140,6 @@ class Article &lt; ActiveRecord::Base
126 140
127 acts_as_versioned 141 acts_as_versioned
128 142
129 - acts_as_searchable :additional_fields => [ :comment_data ]  
130 -  
131 def comment_data 143 def comment_data
132 comments.map {|item| [item.title, item.body].join(' ') }.join(' ') 144 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
133 end 145 end
@@ -142,13 +154,33 @@ class Article &lt; ActiveRecord::Base @@ -142,13 +154,33 @@ class Article &lt; ActiveRecord::Base
142 {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]} 154 {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]}
143 } 155 }
144 156
  157 + named_scope :join_profile, :joins => [:profile]
  158 +
  159 + named_scope :public,
  160 + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ?", true, true, true, true ]
  161 +
  162 + named_scope :more_recent,
  163 + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ? AND
  164 + ((articles.type != ?) OR articles.type is NULL)",
  165 + true, true, true, true, 'RssFeed'
  166 + ],
  167 + :order => 'articles.published_at desc, articles.id desc'
  168 +
  169 + # retrives the most commented articles, sorted by the comment count (largest
  170 + # first)
  171 + def self.most_commented(limit)
  172 + paginate(:order => 'comments_count DESC', :page => 1, :per_page => limit)
  173 + end
  174 +
  175 + named_scope :more_popular, :order => 'hits DESC'
  176 +
145 # retrieves the latest +limit+ articles, sorted from the most recent to the 177 # retrieves the latest +limit+ articles, sorted from the most recent to the
146 # oldest. 178 # oldest.
147 # 179 #
148 # Only includes articles where advertise == true 180 # Only includes articles where advertise == true
149 - def self.recent(limit, extra_conditions = {}) 181 + def self.recent(limit = nil, extra_conditions = {})
150 # FIXME this method is a horrible hack 182 # FIXME this method is a horrible hack
151 - options = { :limit => limit, 183 + options = { :page => 1, :per_page => limit,
152 :conditions => [ 184 :conditions => [
153 "advertise = ? AND 185 "advertise = ? AND
154 published = ? AND 186 published = ? AND
@@ -166,20 +198,14 @@ class Article &lt; ActiveRecord::Base @@ -166,20 +198,14 @@ class Article &lt; ActiveRecord::Base
166 options.delete(:include) 198 options.delete(:include)
167 end 199 end
168 if extra_conditions == {} 200 if extra_conditions == {}
169 - self.find(:all, options) 201 + self.paginate(options)
170 else 202 else
171 with_scope :find => {:conditions => extra_conditions} do 203 with_scope :find => {:conditions => extra_conditions} do
172 - self.find(:all, options) 204 + self.paginate(options)
173 end 205 end
174 end 206 end
175 end 207 end
176 208
177 - # retrives the most commented articles, sorted by the comment count (largest  
178 - # first)  
179 - def self.most_commented(limit)  
180 - find(:all, :order => 'comments_count DESC', :limit => limit)  
181 - end  
182 -  
183 # produces the HTML code that is to be displayed as this article's contents. 209 # produces the HTML code that is to be displayed as this article's contents.
184 # 210 #
185 # The implementation in this class just provides the +body+ attribute as the 211 # The implementation in this class just provides the +body+ attribute as the
@@ -421,7 +447,7 @@ class Article &lt; ActiveRecord::Base @@ -421,7 +447,7 @@ class Article &lt; ActiveRecord::Base
421 end 447 end
422 448
423 def comments_updated 449 def comments_updated
424 - ferret_update 450 + solr_save
425 end 451 end
426 452
427 def accept_category?(cat) 453 def accept_category?(cat)
@@ -573,6 +599,99 @@ class Article &lt; ActiveRecord::Base @@ -573,6 +599,99 @@ class Article &lt; ActiveRecord::Base
573 599
574 private 600 private
575 601
  602 + # FIXME: workaround for development env.
  603 + # Subclasses aren't (re)loaded, and acts_as_solr
  604 + # depends on subclasses method to search
  605 + # see http://stackoverflow.com/questions/4138957/activerecordsubclassnotfound-error-when-using-sti-in-rails/4139245
  606 + UploadedFile
  607 + TextArticle
  608 + TinyMceArticle
  609 + TextileArticle
  610 + Folder
  611 + EnterpriseHomepage
  612 + Gallery
  613 + Blog
  614 + Forum
  615 + Event
  616 +
  617 + def self.f_type_proc(klass)
  618 + klass.constantize.type_name
  619 + end
  620 +
  621 + def self.f_profile_type_proc(klass)
  622 + klass.constantize.type_name
  623 + end
  624 +
  625 + def f_type
  626 + #join common types
  627 + case self.class.name
  628 + when 'TinyMceArticle', 'TextileArticle'
  629 + TextArticle.name
  630 + else
  631 + self.class.name
  632 + end
  633 + end
  634 +
  635 + def f_profile_type
  636 + self.profile.class.name
  637 + end
  638 +
  639 + def f_published_at
  640 + self.published_at
  641 + end
  642 +
  643 + def f_category
  644 + self.categories.collect(&:name)
  645 + end
  646 +
  647 + delegate :region, :region_id, :environment, :environment_id, :to => :profile
  648 + def name_sortable # give a different name for solr
  649 + name
  650 + end
  651 +
  652 + def public
  653 + self.public?
  654 + end
  655 +
  656 + def category_filter
  657 + categories_including_virtual_ids
  658 + end
  659 +
  660 + public
  661 +
  662 + acts_as_faceted :fields => {
  663 + :f_type => {:label => _('Type'), :proc => proc{|klass| f_type_proc(klass)}},
  664 + :f_published_at => {:type => :date, :label => _('Published date'), :queries => {'[* TO NOW-1YEARS/DAY]' => _("Older than one year"),
  665 + '[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")},
  666 + :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]']},
  667 + :f_profile_type => {:label => _('Profile'), :proc => proc{|klass| f_profile_type_proc(klass)}},
  668 + :f_category => {:label => _('Categories')},
  669 + }, :category_query => proc { |c| "category_filter:\"#{c.id}\"" },
  670 + :order => [:f_type, :f_published_at, :f_profile_type, :f_category]
  671 +
  672 + acts_as_searchable :fields => facets_fields_for_solr + [
  673 + # searched fields
  674 + {:name => {:type => :text, :boost => 2.0}},
  675 + {:slug => :text}, {:body => :text},
  676 + {:abstract => :text}, {:filename => :text},
  677 + # filtered fields
  678 + {:public => :boolean}, {:environment_id => :integer},
  679 + {:profile_id => :integer}, :language,
  680 + {:category_filter => :integer},
  681 + # ordered/query-boosted fields
  682 + {:name_sortable => :string}, :last_changed_by_id, :published_at, :is_image,
  683 + :updated_at, :created_at,
  684 + ], :include => [
  685 + {:profile => {:fields => [:name, :identifier, :address, :nickname, :region_id, :lat, :lng]}},
  686 + {:comments => {:fields => [:title, :body, :author_name, :author_email]}},
  687 + {:categories => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}},
  688 + ], :facets => facets_option_for_solr,
  689 + :boost => proc { |a| 10 if a.profile && a.profile.enabled },
  690 + :if => proc{ |a| ! ['RssFeed'].include?(a.class.name) }
  691 + handle_asynchronously :solr_save
  692 +
  693 + private
  694 +
576 def sanitize_tag_list 695 def sanitize_tag_list
577 sanitizer = HTML::FullSanitizer.new 696 sanitizer = HTML::FullSanitizer.new
578 self.tag_list.names.map!{|i| strip_tag_name sanitizer.sanitize(i) } 697 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
@@ -15,13 +15,14 @@ class Category &lt; ActiveRecord::Base @@ -15,13 +15,14 @@ class Category &lt; ActiveRecord::Base
15 15
16 acts_as_filesystem 16 acts_as_filesystem
17 17
18 - has_many :article_categorizations 18 + has_many :article_categorizations, :dependent => :destroy
19 has_many :articles, :through => :article_categorizations 19 has_many :articles, :through => :article_categorizations
20 has_many :comments, :through => :articles 20 has_many :comments, :through => :articles
21 21
22 has_many :events, :through => :article_categorizations, :class_name => 'Event', :source => :article 22 has_many :events, :through => :article_categorizations, :class_name => 'Event', :source => :article
23 23
24 - has_many :profile_categorizations 24 + has_many :profile_categorizations, :dependent => :destroy
  25 + has_many :profiles, :through => :profile_categorizations, :source => :profile
25 has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise' 26 has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise'
26 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' 27 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
27 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community' 28 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community'
@@ -36,18 +37,38 @@ class Category &lt; ActiveRecord::Base @@ -36,18 +37,38 @@ class Category &lt; ActiveRecord::Base
36 { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] } 37 { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] }
37 } 38 }
38 39
  40 + def recent_people(limit = 10)
  41 + self.people.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  42 + end
  43 +
  44 + def recent_enterprises(limit = 10)
  45 + self.enterprises.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  46 + end
  47 +
  48 + def recent_communities(limit = 10)
  49 + self.communities.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  50 + end
  51 +
  52 + def recent_products(limit = 10)
  53 + self.products.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  54 + end
  55 +
39 def recent_articles(limit = 10) 56 def recent_articles(limit = 10)
40 self.articles.recent(limit) 57 self.articles.recent(limit)
41 end 58 end
42 59
43 def recent_comments(limit = 10) 60 def recent_comments(limit = 10)
44 - comments.find(:all, :order => 'created_at DESC, comments.id DESC', :limit => limit) 61 + comments.paginate(:all, :order => 'created_at DESC, comments.id DESC', :page => 1, :per_page => limit)
45 end 62 end
46 63
47 def most_commented_articles(limit = 10) 64 def most_commented_articles(limit = 10)
48 self.articles.most_commented(limit) 65 self.articles.most_commented(limit)
49 end 66 end
50 67
  68 + def upcoming_events(limit = 10)
  69 + self.events.paginate(:conditions => [ 'start_date >= ?', Date.today ], :order => 'start_date', :page => 1, :per_page => limit)
  70 + end
  71 +
51 def display_in_menu? 72 def display_in_menu?
52 display_in_menu 73 display_in_menu
53 end 74 end
@@ -69,4 +90,23 @@ class Category &lt; ActiveRecord::Base @@ -69,4 +90,23 @@ class Category &lt; ActiveRecord::Base
69 self.children.find(:all, :conditions => {:display_in_menu => true}).empty? 90 self.children.find(:all, :conditions => {:display_in_menu => true}).empty?
70 end 91 end
71 92
  93 + private
  94 + def name_sortable # give a different name for solr
  95 + name
  96 + end
  97 + public
  98 +
  99 + acts_as_searchable :fields => [
  100 + # searched fields
  101 + {:name => {:type => :text, :boost => 2.0}},
  102 + {:path => :text}, {:slug => :text},
  103 + {:abbreviation => :text}, {:acronym => :text},
  104 + # filtered fields
  105 + :parent_id,
  106 + # ordered/query-boosted fields
  107 + {:name_sortable => :string},
  108 + ]
  109 + after_save_reindex [:articles, :profiles], :with => :delayed_job
  110 + handle_asynchronously :solr_save
  111 +
72 end 112 end
app/models/category_finder.rb
@@ -1,144 +0,0 @@ @@ -1,144 +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 - asset_class(asset).send(finder_method, :all, options_for_find(asset_class(asset), {:order => "#{asset_table(asset)}.name"}.merge(options), date_range))  
32 - else  
33 - ferret_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}  
34 - asset_class(asset).find_by_contents(query, ferret_options, options_for_find(asset_class(asset), options, date_range))  
35 - end  
36 - end  
37 -  
38 - def recent(asset, limit = nil)  
39 - find(asset, nil, :limit => limit, :order => 'created_at DESC, id DESC')  
40 - end  
41 -  
42 - def most_commented_articles(limit=10, options={})  
43 - options = {:page => 1, :per_page => limit, :order => 'comments_count DESC'}.merge(options)  
44 - Article.paginate(:all, options_for_find(Article, options))  
45 - end  
46 -  
47 - def current_events(year, month, options={})  
48 - options.delete(:page)  
49 - options.delete(:per_page)  
50 -  
51 - range = Event.date_range(year, month)  
52 -  
53 - Event.find(:all, {:include => :categories, :conditions => { 'categories.id' => category_id, :start_date => range }}.merge(options))  
54 - end  
55 -  
56 - def upcoming_events(options = {})  
57 - options.delete(:page)  
58 - options.delete(:per_page)  
59 -  
60 - Event.find(:all, {:include => :categories, :conditions => [ 'categories.id = ? and start_date >= ?', category_id, Date.today ], :order => 'start_date' }.merge(options))  
61 - end  
62 -  
63 - def product_categories_count(asset, product_categories_ids, objects_ids=nil)  
64 - conditions = [ "product_categorizations.category_id in (?) and #{ProfileCategorization.table_name}.category_id = ?", product_categories_ids, category_id]  
65 -  
66 - if asset == :products  
67 - if objects_ids  
68 - conditions[0] += ' and product_categorizations.product_id in (?)'  
69 - conditions << objects_ids  
70 - end  
71 - ProductCategory.find(  
72 - :all,  
73 - :select => 'categories.id, count(*) as total',  
74 - :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)",  
75 - :group => 'categories.id',  
76 - :conditions => conditions  
77 - )  
78 - elsif asset == :enterprises  
79 - if objects_ids  
80 - conditions[0] += ' and products.enterprise_id in (?)'  
81 - conditions << objects_ids  
82 - end  
83 - ProductCategory.find(  
84 - :all,  
85 - :select => 'categories.id, count(distinct products.enterprise_id) as total',  
86 - :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)",  
87 - :group => 'categories.id',  
88 - :conditions => conditions  
89 - )  
90 - else  
91 - raise ArgumentError, 'only products and enterprises supported'  
92 - end.inject({}) do |results,pc|  
93 - results[pc.id]= pc.total.to_i  
94 - results  
95 - end  
96 - end  
97 -  
98 - protected  
99 -  
100 - def options_for_find(klass, options={}, date_range = nil)  
101 - if defined? options[:product_category]  
102 - prod_cat = options.delete(:product_category)  
103 - end  
104 -  
105 - case klass.name  
106 - when 'Comment'  
107 - {:joins => 'inner join articles_categories on articles_categories.article_id = comments.article_id', :conditions => ['articles_categories.category_id = (?)', category_id]}.merge!(options)  
108 - when 'Product'  
109 - if prod_cat  
110 - {: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)  
111 - else  
112 - {:joins => 'inner join categories_profiles on products.enterprise_id = categories_profiles.profile_id', :conditions => ['categories_profiles.category_id = (?)', category_id]}.merge!(options)  
113 - end  
114 - when 'Article', 'TextArticle'  
115 - {:joins => 'inner join articles_categories on (articles_categories.article_id = articles.id)', :conditions => ['articles_categories.category_id = (?)', category_id]}.merge!(options)  
116 - when 'Event'  
117 - conditions =  
118 - if date_range  
119 - ['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} ]  
120 - else  
121 - ['articles_categories.category_id = (?) ', category_id ]  
122 - end  
123 - {:joins => 'inner join articles_categories on (articles_categories.article_id = articles.id)', :conditions => conditions}.merge!(options)  
124 - when 'Enterprise'  
125 - if prod_cat  
126 - {: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)  
127 - else  
128 - {:joins => 'inner join categories_profiles on (categories_profiles.profile_id = profiles.id)', :conditions => ['categories_profiles.category_id = (?)', category_id]}.merge!(options)  
129 - end  
130 - when 'Person', 'Community'  
131 - {:joins => 'inner join categories_profiles on (categories_profiles.profile_id = profiles.id)', :conditions => ['categories_profiles.category_id = (?)', category_id]}.merge!(options)  
132 - else  
133 - raise "unreconized class #{klass.name}"  
134 - end  
135 - end  
136 -  
137 - def asset_class(asset)  
138 - asset.to_s.singularize.camelize.constantize  
139 - end  
140 -  
141 - def asset_table(asset)  
142 - asset_class(asset).table_name  
143 - end  
144 -end  
app/models/certifier.rb
@@ -2,12 +2,20 @@ class Certifier &lt; ActiveRecord::Base @@ -2,12 +2,20 @@ 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
  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
  14 + def destroy
  15 + product_qualifiers.each { |pq| pq.update_attributes! :certifier => nil }
  16 + super
  17 + end
  18 +
11 def link 19 def link
12 self[:link] || '' 20 self[:link] || ''
13 end 21 end
@@ -16,4 +24,6 @@ class Certifier &lt; ActiveRecord::Base @@ -16,4 +24,6 @@ class Certifier &lt; ActiveRecord::Base
16 self.name.downcase.transliterate <=> b.name.downcase.transliterate 24 self.name.downcase.transliterate <=> b.name.downcase.transliterate
17 end 25 end
18 26
  27 + after_save_reindex [:products], :with => :delayed_job
  28 +
19 end 29 end
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
5 settings_items :language 10 settings_items :language
6 settings_items :zip_code, :city, :state, :country 11 settings_items :zip_code, :city, :state, :country
7 12
  13 + extend SetProfileRegionFromCityState::ClassMethods
  14 + set_profile_region_from_city_state
  15 +
8 before_create do |community| 16 before_create do |community|
9 community.moderated_articles = true if community.environment.enabled?('organizations_are_moderated_by_default') 17 community.moderated_articles = true if community.environment.enabled?('organizations_are_moderated_by_default')
10 end 18 end
app/models/domain.rb
@@ -8,9 +8,9 @@ class Domain &lt; ActiveRecord::Base @@ -8,9 +8,9 @@ class Domain &lt; ActiveRecord::Base
8 # validations 8 # validations
9 ############# 9 #############
10 10
11 - # <tt>name</tt> must be a sequence of word characters (a to z, plus 0 to 9,  
12 - # plus '_'). Letters must be lowercase  
13 - validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('%{fn} must be composed only of lowercase latters (a to z), numbers (0 to 9), "_" and "-"').fix_i18n 11 + # <tt>name</tt> must be sequences of alphanumeric characters (a to z,
  12 + # 0 to 9), plus '_' or '-', separated by dots. Letters must be lowercase.
  13 + validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('%{fn} must be composed of sequences of lowercase letters (a to z), numbers (0 to 9), "_" and "-", separated by dots.').fix_i18n
14 14
15 # checks validations that could not be expressed using Rails' predefined 15 # checks validations that could not be expressed using Rails' predefined
16 # validations. In particular: 16 # validations. In particular:
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'
@@ -10,12 +14,19 @@ class Enterprise &lt; Organization @@ -10,12 +14,19 @@ class Enterprise &lt; Organization
10 14
11 has_and_belongs_to_many :fans, :class_name => 'Person', :join_table => 'favorite_enteprises_people' 15 has_and_belongs_to_many :fans, :class_name => 'Person', :join_table => 'favorite_enteprises_people'
12 16
  17 + after_save_reindex [:products], :with => :delayed_job
13 extra_data_for_index :product_categories 18 extra_data_for_index :product_categories
  19 + def product_categories
  20 + products.map{|p| p.category_full_name}.compact
  21 + end
14 22
15 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code') 23 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code')
16 24
17 settings_items :organization_website, :historic_and_current_context, :activities_short_description, :zip_code, :city, :state, :country 25 settings_items :organization_website, :historic_and_current_context, :activities_short_description, :zip_code, :city, :state, :country
18 26
  27 + extend SetProfileRegionFromCityState::ClassMethods
  28 + set_profile_region_from_city_state
  29 +
19 before_save do |enterprise| 30 before_save do |enterprise|
20 enterprise.organization_website = enterprise.maybe_add_http(enterprise.organization_website) 31 enterprise.organization_website = enterprise.maybe_add_http(enterprise.organization_website)
21 end 32 end
@@ -67,22 +78,6 @@ class Enterprise &lt; Organization @@ -67,22 +78,6 @@ class Enterprise &lt; Organization
67 environment ? environment.signup_enterprise_fields : [] 78 environment ? environment.signup_enterprise_fields : []
68 end 79 end
69 80
70 - def product_categories  
71 - products.map{|p| p.category_full_name}.compact  
72 - end  
73 -  
74 - def product_updated  
75 - ferret_update  
76 - end  
77 -  
78 - after_save do |e|  
79 - e.delay.update_products_position  
80 - end  
81 -  
82 - def update_products_position  
83 - products.each{ |p| p.enterprise_updated(self) }  
84 - end  
85 -  
86 def closed? 81 def closed?
87 true 82 true
88 end 83 end
@@ -167,6 +162,10 @@ class Enterprise &lt; Organization @@ -167,6 +162,10 @@ class Enterprise &lt; Organization
167 end 162 end
168 end 163 end
169 164
  165 + def control_panel_settings_button
  166 + {:title => __('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'}
  167 + end
  168 +
170 settings_items :enable_contact_us, :type => :boolean, :default => true 169 settings_items :enable_contact_us, :type => :boolean, :default => true
171 170
172 def enable_contact? 171 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
@@ -248,6 +248,10 @@ class Environment &lt; ActiveRecord::Base @@ -248,6 +248,10 @@ class Environment &lt; ActiveRecord::Base
248 248
249 settings_items :enabled_plugins, :type => Array, :default => [] 249 settings_items :enabled_plugins, :type => Array, :default => []
250 250
  251 + settings_items :search_hints, :type => Hash, :default => {}
  252 +
  253 + settings_items :top_level_category_as_facet_ids, :type => Array, :default => []
  254 +
251 def news_amount_by_folder=(amount) 255 def news_amount_by_folder=(amount)
252 settings[:news_amount_by_folder] = amount.to_i 256 settings[:news_amount_by_folder] = amount.to_i
253 end 257 end
@@ -277,6 +281,9 @@ class Environment &lt; ActiveRecord::Base @@ -277,6 +281,9 @@ class Environment &lt; ActiveRecord::Base
277 def enabled?(feature) 281 def enabled?(feature)
278 self.settings["#{feature}_enabled".to_sym] == true 282 self.settings["#{feature}_enabled".to_sym] == true
279 end 283 end
  284 + def disabled?(feature)
  285 + !self.enabled?(feature)
  286 + end
280 287
281 def plugin_enabled?(plugin) 288 def plugin_enabled?(plugin)
282 enabled_plugins.include?(plugin.to_s) 289 enabled_plugins.include?(plugin.to_s)
@@ -358,11 +365,11 @@ class Environment &lt; ActiveRecord::Base @@ -358,11 +365,11 @@ class Environment &lt; ActiveRecord::Base
358 end 365 end
359 366
360 def terminology 367 def terminology
361 - if self.settings[:terminology]  
362 - self.settings[:terminology].constantize.instance  
363 - else 368 + #if self.settings[:terminology]
  369 + #self.settings[:terminology].constantize.instance
  370 + #else
364 Noosfero.terminology 371 Noosfero.terminology
365 - end 372 + #end
366 end 373 end
367 374
368 def terminology=(value) 375 def terminology=(value)
app/models/environment_finder.rb
@@ -1,107 +0,0 @@ @@ -1,107 +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 - # FIXME this test is in more than one place  
26 - if finder_method == 'paginate'  
27 - options = {:order => "#{asset_table(asset)}.name"}.merge(options)  
28 - end  
29 - if product_category && asset == :products  
30 - @environment.send(asset).send(finder_method, :all, options.merge(:include => 'product_categorizations', :conditions => ['product_categorizations.category_id = (?)', product_category.id]))  
31 - elsif product_category && asset == :enterprises  
32 - @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]))  
33 - else  
34 - if asset == :events  
35 - # Ignore pagination for asset events  
36 - options.delete(:per_page)  
37 - options.delete(:page)  
38 - if date_range  
39 - @environment.send(asset).send('find', :all, options.merge(:conditions => [  
40 - 'start_date BETWEEN :start_day AND :end_day OR end_date BETWEEN :start_day AND :end_day',  
41 - {:start_day => date_range.first, :end_day => date_range.last}  
42 - ]))  
43 - else  
44 - @environment.send(asset).send('find', :all, options)  
45 - end  
46 - else  
47 - @environment.send(asset).send(finder_method, :all, options)  
48 - end  
49 - end  
50 - else  
51 - ferret_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)}  
52 - if product_category && asset == :products  
53 - # SECURITY no risk of SQL injection, since product_category_ids comes from trusted source  
54 - @environment.send(asset).find_by_contents(query, ferret_options, options.merge({:include => 'product_categorizations', :conditions => 'product_categorizations.category_id = (%s)' % product_category.id }))  
55 - elsif product_category && asset == :enterprises  
56 - @environment.send(asset).find_by_contents(query, ferret_options, options.merge(: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})"))  
57 - else  
58 - @environment.send(asset).find_by_contents(query, ferret_options, options)  
59 - end  
60 - end  
61 - end  
62 -  
63 - def recent(asset, limit = nil)  
64 - find(asset, nil, :limit => limit)  
65 - end  
66 -  
67 - def product_categories_count(asset, product_categories_ids, objects_ids=nil)  
68 - conditions = ['product_categorizations.category_id in (?)', product_categories_ids]  
69 -  
70 - if asset == :products  
71 - if objects_ids  
72 - conditions[0] += ' and product_categorizations.product_id in (?)'  
73 - conditions << objects_ids  
74 - end  
75 - 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 )  
76 - elsif asset == :enterprises  
77 - if objects_ids  
78 - conditions[0] += ' and products.enterprise_id in (?)'  
79 - conditions << objects_ids  
80 - end  
81 - ProductCategory.find(  
82 - :all,  
83 - :select => 'categories.id, count(distinct products.enterprise_id) as total',  
84 - :joins => 'inner join product_categorizations on (product_categorizations.category_id = categories.id) inner join products on (products.id = product_categorizations.product_id)',  
85 - :group => 'categories.id',  
86 - :conditions => conditions  
87 - )  
88 - else  
89 - raise ArgumentError, 'only products and enterprises supported'  
90 - end.inject({}) do |results,pc|  
91 - results[pc.id]= pc.total.to_i  
92 - results  
93 - end  
94 -  
95 - end  
96 -  
97 - protected  
98 -  
99 - def asset_class(asset)  
100 - asset.to_s.singularize.camelize.constantize  
101 - end  
102 -  
103 - def asset_table(asset)  
104 - asset_class(asset).table_name  
105 - end  
106 -  
107 -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/google_maps.rb
1 class GoogleMaps 1 class GoogleMaps
2 2
3 - extend ActionView::Helpers::TagHelper  
4 -  
5 - class << self  
6 -  
7 - include ApplicationHelper  
8 -  
9 - def enabled?(domain)  
10 - domain = Domain.find_by_name(domain)  
11 - domain ? !domain.google_maps_key.nil? : false  
12 - end  
13 -  
14 - def key(domainname)  
15 - domain = Domain.find_by_name(domainname)  
16 - domain && domain.google_maps_key || ''  
17 - end  
18 -  
19 - def initial_zoom  
20 - NOOSFERO_CONF['googlemaps_initial_zoom'] || 4  
21 - end  
22 -  
23 - def api_url(domain)  
24 - "http://maps.google.com/maps?file=api&amp;v=2&amp;key=#{key(domain)}"  
25 - end  
26 - 3 + def self.initial_zoom
  4 + NOOSFERO_CONF['googlemaps_initial_zoom'] || 4
27 end 5 end
  6 +
28 end 7 end
app/models/input.rb
@@ -59,4 +59,7 @@ class Input &lt; ActiveRecord::Base @@ -59,4 +59,7 @@ class Input &lt; ActiveRecord::Base
59 return 0 if self.amount_used.blank? || self.price_per_unit.blank? 59 return 0 if self.amount_used.blank? || self.price_per_unit.blank?
60 self.amount_used * self.price_per_unit 60 self.amount_used * self.price_per_unit
61 end 61 end
  62 +
  63 + alias_method :price, :cost
  64 +
62 end 65 end
app/models/location_block.rb
@@ -17,10 +17,10 @@ class LocationBlock &lt; Block @@ -17,10 +17,10 @@ class LocationBlock &lt; Block
17 if profile.lat 17 if profile.lat
18 block_title(title) + 18 block_title(title) +
19 content_tag('div', 19 content_tag('div',
20 - '<img src="http://maps.google.com/staticmap?center=' + profile.lat.to_s() + 20 + '<img src="http://maps.google.com/maps/api/staticmap?center=' + profile.lat.to_s() +
21 ',' + profile.lng.to_s() + '&zoom=' + zoom.to_s() + 21 ',' + profile.lng.to_s() + '&zoom=' + zoom.to_s() +
22 '&size=190x250&maptype=' + map_type + '&markers=' + profile.lat.to_s() + ',' + 22 '&size=190x250&maptype=' + map_type + '&markers=' + profile.lat.to_s() + ',' +
23 - profile.lng.to_s() + ',green&key=' + GoogleMaps::key(profile.default_hostname) + '&sensor=false"/>', 23 + profile.lng.to_s() + ',green' + '&sensor=false"/>',
24 :class => 'the-localization-map' ) 24 :class => 'the-localization-map' )
25 else 25 else
26 content_tag('i', _('This profile has no geographical position registered.')) 26 content_tag('i', _('This profile has no geographical position registered.'))
app/models/national_region.rb 0 → 100644
@@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
  1 +class NationalRegion < ActiveRecord::Base
  2 +
  3 + def self.search_city(city_name, like = false, state = nil)
  4 +
  5 + operator = "="
  6 + find_return = :first
  7 + adtional_contions = "";
  8 +
  9 + if like
  10 + operator = "like"
  11 + find_return = :all
  12 + end
  13 +
  14 + if state
  15 + adtional_contions = " AND nr.name = :state "
  16 + end
  17 +
  18 +
  19 + conditions = ["national_regions.name #{operator} :name AND
  20 + national_regions.national_region_type_id = :type" + adtional_contions,
  21 + {:name => city_name ,
  22 + :type => NationalRegionType::CITY,
  23 + :state => state}];
  24 +
  25 + region = NationalRegion.find(find_return,
  26 + :select => "national_regions.name as city, nr.name as state, national_regions.national_region_code",
  27 + :conditions => conditions,
  28 + :joins => "LEFT JOIN national_regions as nr ON national_regions.parent_national_region_code = nr.national_region_code",
  29 + :limit => 10
  30 + )
  31 + return region
  32 + end
  33 +
  34 + def self.search_state(state_name, like = false)
  35 + operator = "="
  36 + find_return = :first
  37 +
  38 + if like
  39 + operator = "like"
  40 + find_return = :all
  41 + end
  42 +
  43 + conditions = ["national_regions.name #{operator} :name AND
  44 + national_regions.national_region_type_id = :type",
  45 + {:name => state_name,
  46 + :type => NationalRegionType::STATE}];
  47 +
  48 + region = NationalRegion.find(find_return,
  49 + :select => "national_regions.name as state, national_regions.national_region_code",
  50 + :conditions => conditions,
  51 + :limit => 10
  52 + )
  53 + return region
  54 + end
  55 +
  56 + def self.validate!(city, state, country)
  57 +
  58 + country_region = NationalRegion.find_by_national_region_code(country,
  59 + :conditions => ["national_region_type_id = :type",
  60 + {:type => NationalRegionType::COUNTRY}])
  61 +
  62 + if(country_region)
  63 +
  64 + nregion = NationalRegion.search_city(city, false, state);
  65 +
  66 + if nregion == nil
  67 + raise _('Invalid city or state name.')
  68 + end
  69 +
  70 + end
  71 +
  72 + return nregion
  73 +
  74 + end
  75 +
  76 +end
app/models/national_region_type.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class NationalRegionType < ActiveRecord::Base
  2 + COUNTRY = 1
  3 + STATE = 2
  4 + CITY = 3
  5 +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
@@ -187,6 +191,9 @@ class Person &lt; Profile @@ -187,6 +191,9 @@ class Person &lt; Profile
187 N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code') 191 N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code')
188 settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code 192 settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code
189 193
  194 + extend SetProfileRegionFromCityState::ClassMethods
  195 + set_profile_region_from_city_state
  196 +
190 def self.conditions_for_profiles(conditions, person) 197 def self.conditions_for_profiles(conditions, person)
191 new_conditions = sanitize_sql(['role_assignments.accessor_id = ?', person]) 198 new_conditions = sanitize_sql(['role_assignments.accessor_id = ?', person])
192 new_conditions << ' AND ' + sanitize_sql(conditions) unless conditions.blank? 199 new_conditions << ' AND ' + sanitize_sql(conditions) unless conditions.blank?
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 + validates_presence_of :enterprise
  5 +
3 belongs_to :product_category 6 belongs_to :product_category
4 - has_many :product_categorizations  
5 - has_many :product_qualifiers  
6 - has_many :qualifiers, :through => :product_qualifiers 7 +
7 has_many :inputs, :dependent => :destroy, :order => 'position' 8 has_many :inputs, :dependent => :destroy, :order => 'position'
8 has_many :price_details, :dependent => :destroy 9 has_many :price_details, :dependent => :destroy
9 has_many :production_costs, :through => :price_details 10 has_many :production_costs, :through => :price_details
10 11
  12 + has_many :product_qualifiers, :dependent => :destroy
  13 + has_many :qualifiers, :through => :product_qualifiers
  14 + has_many :certifiers, :through => :product_qualifiers
  15 +
11 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true 16 validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true
12 validates_presence_of :product_category_id 17 validates_presence_of :product_category_id
13 validates_associated :product_category 18 validates_associated :product_category
@@ -15,35 +20,20 @@ class Product &lt; ActiveRecord::Base @@ -15,35 +20,20 @@ class Product &lt; ActiveRecord::Base
15 validates_numericality_of :price, :allow_nil => true 20 validates_numericality_of :price, :allow_nil => true
16 validates_numericality_of :discount, :allow_nil => true 21 validates_numericality_of :discount, :allow_nil => true
17 22
18 - after_update :save_image 23 + named_scope :more_recent, :order => "created_at DESC"
19 24
20 - before_create do |p|  
21 - if p.enterprise  
22 - p['lat'] = p.enterprise.lat  
23 - p['lng'] = p.enterprise.lng  
24 - end  
25 - end 25 + after_update :save_image
26 26
27 - after_save do |p|  
28 - p.enterprise.product_updated if p.enterprise 27 + def lat
  28 + self.enterprise.lat
29 end 29 end
30 -  
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 30 + def lng
  31 + self.enterprise.lng
38 end 32 end
39 33
40 - acts_as_searchable :fields => [ :name, :description, :category_full_name ]  
41 -  
42 xss_terminate :only => [ :name ], :on => 'validation' 34 xss_terminate :only => [ :name ], :on => 'validation'
43 xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation' 35 xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation'
44 36
45 - acts_as_mappable  
46 -  
47 belongs_to :unit 37 belongs_to :unit
48 38
49 include FloatHelper 39 include FloatHelper
@@ -89,18 +79,12 @@ class Product &lt; ActiveRecord::Base @@ -89,18 +79,12 @@ class Product &lt; ActiveRecord::Base
89 self.find(:all, :order => 'id desc', :limit => limit) 79 self.find(:all, :order => 'id desc', :limit => limit)
90 end 80 end
91 81
92 - def enterprise_updated(e)  
93 - self.lat = e.lat  
94 - self.lng = e.lng  
95 - save!  
96 - end  
97 -  
98 def url 82 def url
99 enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => id) 83 enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => id)
100 end 84 end
101 85
102 def public? 86 def public?
103 - enterprise.public_profile 87 + enterprise.public?
104 end 88 end
105 89
106 def formatted_value(method) 90 def formatted_value(method)
@@ -128,7 +112,6 @@ class Product &lt; ActiveRecord::Base @@ -128,7 +112,6 @@ class Product &lt; ActiveRecord::Base
128 end 112 end
129 end 113 end
130 114
131 - # Note: will probably be completely overhauled for AI1413  
132 def inputs_prices? 115 def inputs_prices?
133 return false if self.inputs.count <= 0 116 return false if self.inputs.count <= 0
134 self.inputs.each do |input| 117 self.inputs.each do |input|
@@ -213,4 +196,102 @@ class Product &lt; ActiveRecord::Base @@ -213,4 +196,102 @@ 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)) 196 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 197 end
215 198
  199 + def percentage_from_solidarity_economy
  200 + se_i = t_i = 0
  201 + self.inputs(true).each{ |i| t_i += 1; se_i += 1 if i.is_from_solidarity_economy }
  202 + t_i = 1 if t_i == 0 # avoid division by 0
  203 + p = case (se_i.to_f/t_i)*100
  204 + when 0..24.999 then [0, _("")];
  205 + when 25..49.999 then [25, _("25%")];
  206 + when 50..74.999 then [50, _("50%")];
  207 + when 75..99.999 then [75, _("75%")];
  208 + when 100 then [100, _("100%")];
  209 + end
  210 + end
  211 +
  212 + private
  213 + def f_category
  214 + self.product_category.name
  215 + end
  216 + def f_region
  217 + self.enterprise.region.id if self.enterprise.region
  218 + end
  219 + def self.f_region_proc(id)
  220 + c = Region.find(id)
  221 + s = c.parent
  222 + if c and c.kind_of?(City) and s and s.kind_of?(State) and s.acronym
  223 + [c.name, ', ' + s.acronym]
  224 + else
  225 + c.name
  226 + end
  227 + end
  228 + def self.f_qualifier_proc(ids)
  229 + array = ids.split
  230 + qualifier = Qualifier.find_by_id array[0]
  231 + certifier = Certifier.find_by_id array[1]
  232 + certifier ? [qualifier.name, _(' cert. ') + certifier.name] : qualifier.name
  233 + end
  234 + def f_qualifier
  235 + product_qualifiers.map do |pq|
  236 + "#{pq.qualifier_id} #{pq.certifier_id}"
  237 + end
  238 + end
  239 +
  240 + alias_method :name_sortable, :name
  241 + delegate :enabled, :region, :region_id, :environment, :environment_id, :to => :enterprise
  242 + def name_sortable # give a different name for solr
  243 + name
  244 + end
  245 + def public
  246 + self.public?
  247 + end
  248 + def price_sortable
  249 + (price.nil? or price.zero?) ? nil : price
  250 + end
  251 + def category_filter
  252 + enterprise.categories_including_virtual_ids << product_category_id
  253 + end
  254 + public
  255 +
  256 + acts_as_faceted :fields => {
  257 + :f_category => {:label => _('Related products')},
  258 + :f_region => {:label => _('City'), :proc => proc { |id| f_region_proc(id) }},
  259 + :f_qualifier => {:label => _('Qualifiers'), :proc => proc { |id| f_qualifier_proc(id) }},
  260 + }, :category_query => proc { |c| "category_filter:#{c.id}" },
  261 + :order => [:f_category, :f_region, :f_qualifier]
  262 +
  263 + Boosts = [
  264 + [:image, 0.55, proc{ |p| p.image ? 1 : 0}],
  265 + [:qualifiers, 0.45, proc{ |p| p.product_qualifiers.count > 0 ? 1 : 0}],
  266 + [:open_price, 0.45, proc{ |p| p.price_described? ? 1 : 0}],
  267 + [:solidarity, 0.45, proc{ |p| p.percentage_from_solidarity_economy[0].to_f/100 }],
  268 + [:available, 0.35, proc{ |p| p.available ? 1 : 0}],
  269 + [:price, 0.35, proc{ |p| (!p.price.nil? and p.price > 0) ? 1 : 0}],
  270 + [:new_product, 0.35, proc{ |p| (p.updated_at.to_i - p.created_at.to_i) < 24*3600 ? 1 : 0}],
  271 + [:description, 0.3, proc{ |p| !p.description.blank? ? 1 : 0}],
  272 + [:enabled, 0.2, proc{ |p| p.enterprise.enabled ? 1 : 0}],
  273 + ]
  274 +
  275 + acts_as_searchable :fields => facets_fields_for_solr + [
  276 + # searched fields
  277 + {:name => {:type => :text, :boost => 2.0}},
  278 + {:description => :text}, {:category_full_name => :text},
  279 + # filtered fields
  280 + {:public => :boolean}, {:environment_id => :integer},
  281 + {:enabled => :boolean}, {:category_filter => :integer},
  282 + # ordered/query-boosted fields
  283 + {:price_sortable => :decimal}, {:name_sortable => :string},
  284 + {:lat => :float}, {:lng => :float},
  285 + :updated_at, :created_at,
  286 + ], :include => [
  287 + {:product_category => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}},
  288 + {:region => {:fields => [:name, :path, :slug, :lat, :lng]}},
  289 + {:enterprise => {:fields => [:name, :identifier, :address, :nickname, :lat, :lng]}},
  290 + {:qualifiers => {:fields => [:name]}},
  291 + {:certifiers => {:fields => [:name]}},
  292 + ], :facets => facets_option_for_solr,
  293 + :boost => proc{ |p| boost = 1; Boosts.each{ |b| boost = boost * (1 - ((1 - b[2].call(p)) * b[1])) }; boost}
  294 + handle_asynchronously :solr_save
  295 + after_save_reindex [:enterprise], :with => :delayed_job
  296 +
216 end 297 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/product_category.rb
1 class ProductCategory < Category 1 class ProductCategory < Category
  2 + # FIXME: do not allow category with products or inputs to be destroyed
2 has_many :products 3 has_many :products
3 has_many :inputs 4 has_many :inputs
4 5
@@ -9,4 +10,7 @@ class ProductCategory &lt; Category @@ -9,4 +10,7 @@ class ProductCategory &lt; Category
9 def self.menu_categories(top_category, env) 10 def self.menu_categories(top_category, env)
10 top_category ? top_category.children : top_level_for(env).select{|c|c.kind_of?(ProductCategory)} 11 top_category ? top_category.children : top_level_for(env).select{|c|c.kind_of?(ProductCategory)}
11 end 12 end
  13 +
  14 + after_save_reindex [:products], :with => :delayed_job
  15 +
12 end 16 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)
@@ -74,8 +80,6 @@ class Profile &lt; ActiveRecord::Base @@ -74,8 +80,6 @@ class Profile &lt; ActiveRecord::Base
74 80
75 acts_as_having_boxes 81 acts_as_having_boxes
76 82
77 - acts_as_searchable :additional_fields => [ :extra_data_for_index ]  
78 -  
79 acts_as_taggable 83 acts_as_taggable
80 84
81 def self.qualified_column_names 85 def self.qualified_column_names
@@ -125,8 +129,6 @@ class Profile &lt; ActiveRecord::Base @@ -125,8 +129,6 @@ class Profile &lt; ActiveRecord::Base
125 129
126 validates_length_of :description, :maximum => 550, :allow_nil => true 130 validates_length_of :description, :maximum => 550, :allow_nil => true
127 131
128 - acts_as_mappable :default_units => :kms  
129 -  
130 # Valid identifiers must match this format. 132 # Valid identifiers must match this format.
131 IDENTIFIER_FORMAT = /^#{Noosfero.identifier_format}$/ 133 IDENTIFIER_FORMAT = /^#{Noosfero.identifier_format}$/
132 134
@@ -181,8 +183,20 @@ class Profile &lt; ActiveRecord::Base @@ -181,8 +183,20 @@ class Profile &lt; ActiveRecord::Base
181 has_many :profile_categorizations, :conditions => [ 'categories_profiles.virtual = ?', false ] 183 has_many :profile_categorizations, :conditions => [ 'categories_profiles.virtual = ?', false ]
182 has_many :categories, :through => :profile_categorizations 184 has_many :categories, :through => :profile_categorizations
183 185
  186 + has_many :profile_categorizations_including_virtual, :class_name => 'ProfileCategorization'
  187 + has_many :categories_including_virtual, :through => :profile_categorizations_including_virtual, :source => :category
  188 +
184 has_many :abuse_complaints, :foreign_key => 'requestor_id' 189 has_many :abuse_complaints, :foreign_key => 'requestor_id'
185 190
  191 + def top_level_categorization
  192 + ret = {}
  193 + self.profile_categorizations.each do |c|
  194 + p = c.category.top_ancestor
  195 + ret[p] = (ret[p] || []) + [c.category]
  196 + end
  197 + ret
  198 + end
  199 +
186 def interests 200 def interests
187 categories.select {|item| !item.is_a?(Region)} 201 categories.select {|item| !item.is_a?(Region)}
188 end 202 end
@@ -217,12 +231,15 @@ class Profile &lt; ActiveRecord::Base @@ -217,12 +231,15 @@ class Profile &lt; ActiveRecord::Base
217 @pending_categorizations ||= [] 231 @pending_categorizations ||= []
218 end 232 end
219 233
220 - def add_category(c)  
221 - if self.id  
222 - ProfileCategorization.add_category_to_profile(c, self)  
223 - else 234 + def add_category(c, reload=false)
  235 + if new_record?
224 pending_categorizations << c 236 pending_categorizations << c
  237 + else
  238 + ProfileCategorization.add_category_to_profile(c, self)
  239 + self.categories(true)
  240 + self.solr_save
225 end 241 end
  242 + self.categories(reload)
226 end 243 end
227 244
228 def category_ids=(ids) 245 def category_ids=(ids)
@@ -527,6 +544,7 @@ private :generate_url, :url_options @@ -527,6 +544,7 @@ private :generate_url, :url_options
527 other.top_level_articles.each do |a| 544 other.top_level_articles.each do |a|
528 copy_article_tree a 545 copy_article_tree a
529 end 546 end
  547 + self.articles.reload
530 end 548 end
531 549
532 def copy_article_tree(article, parent=nil) 550 def copy_article_tree(article, parent=nil)
@@ -822,18 +840,100 @@ private :generate_url, :url_options @@ -822,18 +840,100 @@ private :generate_url, :url_options
822 name 840 name
823 end 841 end
824 842
825 - protected 843 + private
  844 + def self.f_categories_label_proc(environment)
  845 + ids = environment.top_level_category_as_facet_ids
  846 + r = Category.find(ids)
  847 + map = {}
  848 + ids.map{ |id| map[id.to_s] = r.detect{|c| c.id == id}.name }
  849 + map
  850 + end
  851 + def self.f_categories_proc(facet, id)
  852 + id = id.to_i
  853 + return if id.zero?
  854 + c = Category.find(id)
  855 + c.name if c.top_ancestor.id == facet[:label_id].to_i or facet[:label_id] == 0
  856 + end
  857 + def f_categories
  858 + category_ids - [region_id]
  859 + end
826 860
827 - def followed_by?(person)  
828 - person.is_member_of?(self) 861 + def f_region
  862 + self.region_id
  863 + end
  864 + def self.f_region_proc(id)
  865 + c = Region.find(id)
  866 + s = c.parent
  867 + if c and c.kind_of?(City) and s and s.kind_of?(State) and s.acronym
  868 + [c.name, ', ' + s.acronym]
  869 + else
  870 + c.name
829 end 871 end
  872 + end
830 873
831 - def display_private_info_to?(user)  
832 - if user.nil?  
833 - false  
834 - else  
835 - (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)  
836 - end 874 + def self.f_enabled_proc(enabled)
  875 + enabled = enabled == "true" ? true : false
  876 + enabled ? _('Enabled') : _('Not enabled')
  877 + end
  878 + def f_enabled
  879 + self.enabled
  880 + end
  881 +
  882 + def name_sortable # give a different name for solr
  883 + name
  884 + end
  885 + def public
  886 + self.public?
  887 + end
  888 + def category_filter
  889 + categories_including_virtual_ids
  890 + end
  891 + public
  892 +
  893 + acts_as_faceted :fields => {
  894 + :f_enabled => {:label => _('Situation'), :type_if => proc { |klass| klass.kind_of?(Enterprise) },
  895 + :proc => proc { |id| f_enabled_proc(id) }},
  896 + :f_region => {:label => _('City'), :proc => proc { |id| f_region_proc(id) }},
  897 + :f_categories => {:multi => true, :proc => proc {|facet, id| f_categories_proc(facet, id)},
  898 + :label => proc { |env| f_categories_label_proc(env) }, :label_abbrev => proc{ |env| f_categories_label_abbrev_proc(env) }},
  899 + }, :category_query => proc { |c| "category_filter:#{c.id}" },
  900 + :order => [:f_region, :f_categories, :f_enabled]
  901 +
  902 + acts_as_searchable :fields => facets_fields_for_solr + [:extra_data_for_index,
  903 + # searched fields
  904 + {:name => {:type => :text, :boost => 2.0}},
  905 + {:identifier => :text}, {:address => :text}, {:nickname => :text},
  906 + # filtered fields
  907 + {:public => :boolean}, {:environment_id => :integer},
  908 + {:category_filter => :integer},
  909 + # ordered/query-boosted fields
  910 + {:name_sortable => :string}, {:user_id => :integer},
  911 + :enabled, :active, :validated, :public_profile,
  912 + {:lat => :float}, {:lng => :float},
  913 + :updated_at, :created_at,
  914 + ],
  915 + :include => [
  916 + {:region => {:fields => [:name, :path, :slug, :lat, :lng]}},
  917 + {:categories => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}},
  918 + ], :facets => facets_option_for_solr,
  919 + :boost => proc{ |p| 10 if p.enabled }
  920 + after_save_reindex [:articles], :with => :delayed_job
  921 + handle_asynchronously :solr_save
  922 +
  923 + def control_panel_settings_button
  924 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  925 + end
  926 +
  927 + def followed_by?(person)
  928 + person.is_member_of?(self)
  929 + end
  930 +
  931 + def display_private_info_to?(user)
  932 + if user.nil?
  933 + false
  934 + else
  935 + (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)
837 end 936 end
  937 + end
838 938
839 end 939 end
app/models/qualifier.rb
@@ -2,16 +2,19 @@ class Qualifier &lt; ActiveRecord::Base @@ -2,16 +2,19 @@ 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
16 17
  18 + after_save_reindex [:products], :with => :delayed_job
  19 +
17 end 20 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/region.rb
@@ -5,19 +5,17 @@ class Region &lt; Category @@ -5,19 +5,17 @@ class Region &lt; Category
5 require_dependency 'enterprise' # enterprises can also be validators 5 require_dependency 'enterprise' # enterprises can also be validators
6 6
7 # searches for organizations that could become validators for this region. 7 # searches for organizations that could become validators for this region.
8 - # <tt>search</tt> is passed as is to ferret's find_by_contents on Organizatio  
9 - # find_by_contents on Organization class. 8 + # <tt>search</tt> is passed as is to find_by_contents on Organization.
10 def search_possible_validators(search) 9 def search_possible_validators(search)
11 - Organization.find_by_contents(search).reject {|item| self.validator_ids.include?(item.id) } 10 + Organization.find_by_contents(search)[:results].docs.reject {|item| self.validator_ids.include?(item.id) }
12 end 11 end
13 12
14 def has_validator? 13 def has_validator?
15 validators.count > 0 14 validators.count > 0
16 end 15 end
17 16
18 - def self.with_validators  
19 - Region.find(:all, :joins => 'INNER JOIN region_validators on (region_validators.region_id = categories.id)', :select => "distinct #{table_name}.*")  
20 - end 17 + named_scope :with_validators, :group => 'id',
  18 + :joins => 'INNER JOIN region_validators on (region_validators.region_id = categories.id)'
21 19
22 end 20 end
23 21
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/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/account/signup.rhtml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <p><%= _("Firstly, some tips for getting started:") %></p> 5 <p><%= _("Firstly, some tips for getting started:") %></p>
6 <h4><%= _("Confirm your account!") %></h4> 6 <h4><%= _("Confirm your account!") %></h4>
7 <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p> 7 <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p>
8 - <p><%= _("You won't appear as %s until your account is confirmed.") % link_to(_('user'), {:controller => :browse, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p> 8 + <p><%= _("You won't appear as %s until your account is confirmed.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
9 <h4><%= _("What to do next?") %></h4> 9 <h4><%= _("What to do next?") %></h4>
10 <p><%= _("%s. Upload an avatar and let your friends find you easily :)") % link_to(_('Customize your profile'), {:controller => 'doc', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank') %></p> 10 <p><%= _("%s. Upload an avatar and let your friends find you easily :)") % link_to(_('Customize your profile'), {:controller => 'doc', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank') %></p>
11 <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p> 11 <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p>
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', 'colorbox',
  3 +'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
  4 +'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput',
  5 +'add-and-join', 'report-abuse', 'catalog', 'manage-products', :cache => 'cache-general' %>
2 6
3 <% language = FastGettext.locale %> 7 <% language = FastGettext.locale %>
4 <% %w{messages methods}.each do |type| %> 8 <% %w{messages methods}.each do |type| %>
app/views/layouts/application-ng.rhtml
@@ -61,16 +61,10 @@ @@ -61,16 +61,10 @@
61 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> 61 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %>
62 </div> 62 </div>
63 </span> 63 </span>
64 - <form action="/search" class="search_form clean" method="get" id="top-search">  
65 - <input name="query" size="15" value="<%=_('Search...')%>"  
66 - onfocus="this.form.className='focused';  
67 - if(this.value=='<%=_('Search...')%>'){this.value=''}"  
68 - onblur="this.form.className='';  
69 - if(/^\s*$/.test(this.value)){  
70 - this.value='<%=_('Search...')%>';  
71 - this.form.className='clean'  
72 - }" /> 64 + <form action="/search" class="search_form" method="get" class="clean">
  65 + <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" />
73 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div> 66 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div>
  67 + <%= javascript_tag 'jQuery("#user form input").hint();' %>
74 </form> 68 </form>
75 </div><!-- end id="user" --> 69 </div><!-- end id="user" -->
76 70
app/views/manage_products/_display_image.rhtml
1 <div id='display-product-image'> 1 <div id='display-product-image'>
2 - <%= image_tag (@product.reload.default_image('thumb')), :class => 'product-pic' %> 2 + <%= image_tag (@product.reload.default_image('big')), :class => 'product-pic' %>
3 </div> 3 </div>
4 4
  5 +<% if @product.image %>
  6 + <%= link_to content_tag(:span, _('Zoom in')), @product.image.public_filename,
  7 + :class => 'zoomify-image' %>
  8 +<% end %>
  9 +<%= add_zoom_to_images %>
  10 +
5 <%= edit_product_link_to_remote(@product, 'image', _('Change image')) %> 11 <%= edit_product_link_to_remote(@product, 'image', _('Change image')) %>
app/views/manage_products/show.rhtml
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 </div> 9 </div>
10 10
11 <div id='product-details'> 11 <div id='product-details'>
12 - <div id='product-image'> 12 + <div id='product-image' class="zoomable-image">
13 <%= render :partial => 'manage_products/display_image' %> 13 <%= render :partial => 'manage_products/display_image' %>
14 </div> 14 </div>
15 <div id='product-extra-content'> 15 <div id='product-extra-content'>
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 + <% if @profile.respond_to?(:products) and !@profile.products.blank? %>
  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/maps/_google_map.js.erb 0 → 100644
@@ -0,0 +1,252 @@ @@ -0,0 +1,252 @@
  1 +
  2 +var geocoder;
  3 +var map;
  4 +var marker;
  5 +var center;
  6 +var move = true;
  7 +var previousCenter;
  8 +
  9 +function getAddress(latlng) {
  10 + $('location-fields').addClassName("loading");
  11 +
  12 + if (latlng != null) {
  13 + geocoder.geocode( {'latLng': latlng}, showAddress);
  14 + }
  15 +}
  16 +
  17 +function codeAddress() {
  18 + $('location-fields').addClassName("loading");
  19 +
  20 + var country_option = $('profile_data_country').value;
  21 + var address = $('profile_data_address').value + "-" + $('profile_data_zip_code').value + "," + $('profile_data_city').value+ "-" + $('profile_data_state').value + "," + country_option;
  22 +
  23 + if (geocoder) {
  24 + geocoder.geocode( { 'address': address}, function(results, status) {
  25 + if (status == google.maps.GeocoderStatus.OK) {
  26 + map.setCenter(results[0].geometry.location);
  27 + marker.setPosition(results[0].geometry.location);
  28 + getAddress(marker.getPosition());
  29 +
  30 + $('profile_data_lat').value = results[0].geometry.location.lat();
  31 + $('profile_data_lng').value = results[0].geometry.location.lng();
  32 + $('location-fields').removeClassName("loading");
  33 + enable_save();
  34 + } else {
  35 + $('location-fields').removeClassName("loading");
  36 + alert('<%=_("Address not found, reason:")%>' + translate_status(status));
  37 + }
  38 + });
  39 + }
  40 +
  41 + map.setZoom(11);
  42 +
  43 + return false;
  44 +}
  45 +
  46 +function translate_status(status)
  47 +{
  48 + var translated_status = '';
  49 +
  50 + if (google.maps.GeocoderStatus.INVALID_REQUEST == status)
  51 + translated_status = '<%= _('Invalid address') %>';
  52 + else if (google.maps.GeocoderStatus.REQUEST_DENIED == status)
  53 + translated_status = '<%= _('Request denied') %>';
  54 + else if (google.maps.GeocoderStatus.OVER_QUERY_LIMIT == status)
  55 + translated_status = '<%= _('Over query limit') %>';
  56 + else if (google.maps.GeocoderStatus.ZERO_RESULTS == status)
  57 + translated_status = "<%= _('Address do not exist') %>";
  58 +
  59 + return translated_status;
  60 +}
  61 +
  62 +function getAddressData() {
  63 + var text = '';
  64 + var fields = [
  65 + 'profile_data_country',
  66 + 'profile_data_state',
  67 + 'profile_data_city',
  68 + 'profile_data_address',
  69 + 'profile_data_zip_code'
  70 + ];
  71 + for (var i = 0; i < fields.length; i++) {
  72 + var field = fields[i];
  73 + if ($(field)) {
  74 + text += $(field).value + " ";
  75 + }
  76 + }
  77 + return text;
  78 +}
  79 +
  80 +function showAddress(results, status) {
  81 +
  82 + if (status == google.maps.GeocoderStatus.OK) {
  83 + map.setCenter(results[0].geometry.location);
  84 + updateFields(results[0]);
  85 +
  86 + } else {
  87 + alert("<%=_("Address not found, reason:")%>" + translate_status(status));
  88 + }
  89 +
  90 +}
  91 +
  92 +function updateFields(place) {
  93 + var position = marker.getPosition();
  94 + $('profile_data_lat').value = position.lat();
  95 + $('profile_data_lng').value = position.lng();
  96 + $('location-fields').removeClassName("loading");
  97 +
  98 + form = jQuery('#location-form')[0];
  99 + form.lat = marker.getPosition().lat();
  100 + form.lng = marker.getPosition().lng();
  101 +
  102 + var components_len = place.address_components.size();
  103 +
  104 + if(components_len < 2)
  105 + {
  106 + return false;
  107 + }
  108 +
  109 + var components = place.address_components;
  110 + var address = "";
  111 + var zip_code = "";
  112 + var city = "";
  113 + var state = "";
  114 + var country_code = "";
  115 + var i = 0;
  116 +
  117 + for( i =0 ; i < components_len; i ++)
  118 + {
  119 +
  120 + if (components[i].types[0] == 'country')
  121 + country_code = components[i].short_name;
  122 + else if (components[i].types[0] == 'administrative_area_level_1')
  123 + state = components[i].long_name;
  124 + else if (components[i].types[0] == 'locality')
  125 + city = components[i].long_name;
  126 + else if (components[i].types[0] == 'sublocality')
  127 + address = components[i].long_name + "-" + address;
  128 + else if (components[i].types[0] == "route")
  129 + address = components[i].long_name + address;
  130 + else if (components[i].types[0] == "street_number")
  131 + address = address + "," + components[i].short_name ;
  132 + else if (components[i].types[0] == 'postal_code')
  133 + zip_code = components[i].short_name;
  134 + }
  135 +
  136 + $('profile_data_country').value = country_code;
  137 + $('profile_data_state').value = state;
  138 + $('profile_data_address').value = address;
  139 + $('profile_data_city').value = city;
  140 + $('profile_data_zip_code').value = zip_code;
  141 +}
  142 +
  143 +
  144 +function initialize_map() {
  145 + geocoder = new google.maps.Geocoder();
  146 +
  147 + var lat = <%= profile.lat || 'false' %>;
  148 + var lng = <%= profile.lng || 'false' %>;
  149 +
  150 + if ( !(lat && lng) ) {
  151 + lat = -15.7605361485013;
  152 + lng = -47.933349609375;
  153 + }
  154 +
  155 + var latlng = new google.maps.LatLng(lat,lng);
  156 +
  157 + var myOptions = {
  158 + zoom: 8,
  159 + center: latlng,
  160 + mapTypeId: google.maps.MapTypeId.ROADMAP
  161 + }
  162 +
  163 + center = latlng;
  164 +
  165 + map = new google.maps.Map(document.getElementById("location-map"), myOptions);
  166 +
  167 + continueLoadMapV3()
  168 +}
  169 +
  170 +function continueLoadMapV3() {
  171 +
  172 + marker = new google.maps.Marker({
  173 + position: center,
  174 + map: map,
  175 + draggable: true
  176 + });
  177 +
  178 + google.maps.event.addListener(marker, "dragend", function() {
  179 + move = false;
  180 + getAddress(marker.getPosition());
  181 + enable_save();
  182 + });
  183 +
  184 +}
  185 +
  186 +window.onload = initialize_map;
  187 +
  188 +var delay_autocomplete = 500;
  189 +
  190 +jQuery.noConflict();
  191 +jQuery(document).ready(function (){
  192 +
  193 + jQuery.widget( "custom.catcomplete",jQuery.ui.autocomplete, {
  194 + _renderMenu: function( ul, items ) {
  195 + var self = this,
  196 + currentCategory = "";
  197 + jQuery.each( items, function( index, item ) {
  198 + if ( item.category != currentCategory ) {
  199 + ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
  200 + currentCategory = item.category;
  201 + }
  202 + self._renderItem( ul, item );
  203 + });
  204 + }
  205 + });
  206 +
  207 +
  208 + jQuery("#profile_data_city").catcomplete({
  209 + source: "../maps/search_city",
  210 + minLength: 3,
  211 + delay: delay_autocomplete,
  212 + select: function( event, ui ) { $('profile_data_state').value =( ui.item ? ui.item.category : this.value ); }
  213 + });
  214 +
  215 + jQuery("#profile_data_state").autocomplete({
  216 + source: "../maps/search_state",
  217 + minLength: 3,
  218 + delay: delay_autocomplete
  219 + });
  220 +
  221 + jQuery("#profile_data_city").keyup(function(){
  222 +
  223 + disable_save();
  224 +
  225 + });
  226 +
  227 + jQuery("#profile_data_state").keyup(function(){
  228 +
  229 + disable_save();
  230 +
  231 + });
  232 +
  233 + jQuery("#profile_data_country").change(function(){
  234 +
  235 + disable_save();
  236 +
  237 + });
  238 +
  239 +});
  240 +
  241 +function disable_save()
  242 +{
  243 + jQuery('input[type="submit"]').attr("disabled", "true");
  244 + jQuery('input[type="submit"]').val('<%=_("Localize before save")%>');
  245 + jQuery('input[type="submit"]').addClass('disabled');
  246 +}
  247 +function enable_save()
  248 +{
  249 + jQuery('input[type="submit"]').removeAttr("disabled");
  250 + jQuery('input[type="submit"]').val('<%=_("Save")%>');
  251 + jQuery('input[type="submit"]').removeClass('disabled');
  252 +}
app/views/maps/_google_map.rhtml
@@ -1,136 +0,0 @@ @@ -1,136 +0,0 @@
1 -<%= content_tag('script', '', :src => GoogleMaps.api_url(profile.default_hostname), :type => 'text/javascript') %>  
2 -  
3 -<script type="text/javascript" >  
4 - var geocoder;  
5 - var map;  
6 - var marker;  
7 - var center;  
8 - var move = true;  
9 - var previousCenter;  
10 -  
11 - function getAddress(overlay, latlng) {  
12 - $('location-fields').addClassName("loading");  
13 - if (latlng != null) {  
14 - geocoder.getLocations(latlng, showAddress);  
15 - }  
16 - }  
17 -  
18 - function getAddressData() {  
19 - var text = '';  
20 - var fields = [  
21 - 'profile_data_country',  
22 - 'profile_data_state',  
23 - 'profile_data_city',  
24 - 'profile_data_address',  
25 - 'profile_data_zip_code'  
26 - ];  
27 - for (var i = 0; i < fields.length; i++) {  
28 - var field = fields[i];  
29 - if ($(field)) {  
30 - text += $(field).value + " ";  
31 - }  
32 - }  
33 - return text;  
34 - }  
35 -  
36 - function showAddress(response) {  
37 - var message;  
38 - place = geoCodeAddress(response);  
39 - if ( place ) {  
40 - if ( move ) {  
41 - center = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);  
42 - marker.setLatLng(center);  
43 - } else {  
44 - move = true;  
45 - }  
46 - message = showMessage(place);  
47 - updateFields(place);  
48 - } else {  
49 - message = showNotFoundMessage();  
50 - }  
51 - map.addOverlay(marker);  
52 - map.setCenter(marker.getLatLng());  
53 - marker.openInfoWindowHtml(message, {maxWidth:300});  
54 - }  
55 -  
56 - function geoCodeAddress(response) {  
57 - if (!response || (response && response.Status.code != '200')) {  
58 - return false;  
59 - } else {  
60 - place = response.Placemark[0];  
61 - return place;  
62 - }  
63 - }  
64 -  
65 - function showMessage(place) {  
66 - var message = '<b><%= _('Address:') %></b> ' + place.address + '<br>' +  
67 - '<b><%= _('Coordinates:') %></b> ' + place.Point.coordinates[0] + "," + place.Point.coordinates[1] + '<br>' +  
68 - '<b><%= _('Country code:') %></b> ' + place.AddressDetails.Country.CountryNameCode + '<br>';  
69 - return message;  
70 - }  
71 -  
72 - function showNotFoundMessage() {  
73 - var message = '<%= _('Address not found') %>' + '<br>' +  
74 - '<b><%= _('Coordinates:') %></b> ' + marker.getLatLng().lng() + "," + marker.getLatLng().lat();  
75 - return message;  
76 - }  
77 -  
78 - function updateFields(response) {  
79 - var position = marker.getLatLng();  
80 - $('profile_data_lat').value = position.lat();  
81 - $('profile_data_lng').value = position.lng();  
82 - $('location-fields').removeClassName("loading");  
83 - }  
84 -  
85 - function loadMap() {  
86 - if (GBrowserIsCompatible()) {  
87 - map = new GMap2(document.getElementById("location-map"));  
88 - geocoder = new GClientGeocoder();  
89 - var lat = <%= profile.lat || 'false' %>;  
90 - var lng = <%= profile.lng || 'false' %>;  
91 - if ( lat && lng ) {  
92 - center = new GLatLng( lat, lng );  
93 - continueLoadMap();  
94 - } else {  
95 - geocoder.getLocations('<%= profile.geolocation %>', loadAddress);  
96 - }  
97 - }  
98 - }  
99 -  
100 - function loadAddress(response) {  
101 - place = geoCodeAddress(response);  
102 - if ( move ) {  
103 - center = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);  
104 - }  
105 - continueLoadMap();  
106 - }  
107 -  
108 - function continueLoadMap() {  
109 - marker = new GMarker(center, {draggable: true});  
110 - map.setCenter(center, 4);  
111 -  
112 - map.addControl(new GLargeMapControl());  
113 - map.addControl(new GScaleControl());  
114 - map.addControl(new GMapTypeControl());  
115 -  
116 - GEvent.addListener(marker, "dragstart", function() {  
117 - previousCenter = marker.getLatLng();  
118 - map.closeInfoWindow();  
119 - });  
120 -  
121 - GEvent.addListener(marker, "dragend", function() {  
122 - move = false;  
123 - getAddress(overlay, marker.getLatLng());  
124 - });  
125 -  
126 - GEvent.addListener(marker, "click", function() {  
127 - move = false;  
128 - getAddress(overlay, marker.getLatLng());  
129 - });  
130 -  
131 - map.addOverlay(marker);  
132 - }  
133 -  
134 - window.onload = loadMap;  
135 - window.unload = GUnload();  
136 -</script>  
app/views/maps/edit_location.rhtml
1 <h1><%= _('Location') %></h1> 1 <h1><%= _('Location') %></h1>
  2 +<div class="error">
  3 + <%= flash[:error] %>
  4 +</div>
2 5
3 -<% form_for :profile_data, :url => {:action => 'edit_location'} do |f| %> 6 +<% form_for :profile_data, :url => {:action => 'edit_location'}, :html => {:id => 'location-form'} do |f| %>
4 7
5 <div id='location-fields'> 8 <div id='location-fields'>
6 - <%= optional_field(profile, 'country', select_country(_('Country'), 'profile_data', 'country', {:class => 'type-select'})) %>  
7 - <%= optional_field(profile, 'state', labelled_form_field(_('State'), f.text_field(:state))) %>  
8 - <%= optional_field(profile, 'city', labelled_form_field(_('City'), f.text_field(:city))) %>  
9 - <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code))) %>  
10 - <%= optional_field(profile, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address))) %> 9 + <%= select_country _('Country'), 'profile_data', 'country', {:class => 'type-select'} %>
  10 + <%= labelled_form_field _('State'), f.text_field(:state) %>
  11 + <%= labelled_form_field _('City'), f.text_field(:city) %>
  12 + <%= labelled_form_field _('ZIP code'), text_field(:profile_data, :zip_code) %>
  13 + <%= labelled_form_field _('Address (street and number)'), text_field(:profile_data, :address) %>
11 <% button_bar do %> 14 <% button_bar do %>
12 - <%= button_to_function :search, _('Locate in the map'), "getAddress(null, getAddressData())", :title => _("Locate the address informed above in the map below (note that you'll probably need to adjust the marker to get a precise position)") %> 15 + <%= button_to_function :search, _('Locate in the map'), "codeAddress()", :title => _("Locate the address informed above in the map below (note that you'll probably need to adjust the marker to get a precise position)") %>
13 <%= submit_button 'save', _('Save') %> 16 <%= submit_button 'save', _('Save') %>
14 <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> 17 <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>
15 <% end %> 18 <% end %>
16 </div> 19 </div>
17 20
  21 + <p><%= _('Drag the balloon to find the exact location.') %> </p>
18 22
19 <div style='overflow: hidden'> 23 <div style='overflow: hidden'>
20 <p><div id="location-map"></div></p> 24 <p><div id="location-map"></div></p>
21 </div> 25 </div>
22 26
23 - <%= f.hidden_field(:lat) %>  
24 - <%= f.hidden_field(:lng) %>  
25 -  
26 -  
27 -  
28 - <% button_bar do %>  
29 - <%= submit_button 'save', _('Save') %>  
30 - <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>  
31 - <% end %>  
32 - 27 + <%= f.hidden_field :lat %>
  28 + <%= f.hidden_field :lng %>
  29 +
33 <% end %> 30 <% end %>
34 31
35 -<%= render :partial => 'google_map'%> 32 +<%= content_tag('script', '', :src => "http://maps.googleapis.com/maps/api/js?sensor=false", :type => 'text/javascript') %>
  33 +<%= content_tag('script', '', :src => url_for(:controller => :maps, :action => :google_map), :type => 'text/javascript') %>
  34 +
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 + <table class="noborder search-content-second-column">
  7 + <%= render :partial => 'article_common', :object => article %>
  8 + </table>
  9 + <%= render :partial => 'article_last_change', :object => article %>
  10 +
  11 + <div style="clear:both"></div>
11 </li> 12 </li>
app/views/search/_article_author.rhtml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +<% article = article_author %>
  2 +
  3 +<tr class="search-article-author search-article-author-name">
  4 + <td class="search-field-label"><%= _("Author") %></td>
  5 + <td>
  6 + <%= link_to_profile article.profile.name, article.profile.identifier %>
  7 + </td>
  8 +</tr>
app/views/search/_article_categories.rhtml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<tr class="search-article-categories">
  2 + <td class="search-field-label"><%= _('Categories') %></td>
  3 + <td class="search-article-categories-container <%= "search-field-none" if article_categories.empty? %>">
  4 + <% article_categories.each do |category| %>
  5 + <%= link_to_category category, false, :class => "search-article-category" %>
  6 + <% end %>
  7 + <%= _('None') if article_categories.empty? %>
  8 + </td>
  9 +</tr>
app/views/search/_article_common.rhtml 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  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 %>
app/views/search/_article_description.rhtml 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +<% article = article_description %>
  2 +
  3 +<tr class="search-article-description">
  4 + <td class="search-field-label"><%= _("Description") %></td>
  5 +
  6 + <% if !article.body.blank? %>
  7 + <% description = strip_tags(article.body.to_s) %>
  8 + <% description.gsub!(/\s{2,}/, ' ') %>
  9 + <% description = excerpt(description, description.first(3), 200) %>
  10 +
  11 + <td class="search-article-body"><%= description %></td>
  12 + <% else %>
  13 + <td class="search-field-none"><%= _('None') %></td>
  14 + <% end %>
  15 +</tr>
app/views/search/_article_last_change.rhtml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +<% article = article_last_change %>
  2 +
  3 +<div class="search-article-author-changes">
  4 + <% if article.last_changed_by and article.last_changed_by != article.profile %>
  5 + <span><%= _('by %{name} at %{date}') % {:name => link_to(article.last_changed_by.name, article.last_changed_by.url),
  6 + :date => show_date(article.updated_at) } %></span>
  7 + <% else %>
  8 + <span><%= _('Last update: %s.') % show_date(article.updated_at) %></span>
  9 + <% end %>
  10 +</div>
app/views/search/_article_tags.rhtml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<tr class="search-article-tags">
  2 + <td class="search-field-label"><%= _('Tags') %></td>
  3 + <td class="search-article-tags-container <%= "search-field-none" if article_tags.empty? %>">
  4 + <% article_tags.each do |tag| %>
  5 + <%= link_to_tag tag, :class => "search-article-tag" %>
  6 + <% end %>
  7 + <%= _('None') if article_tags.empty? %>
  8 + </td>
  9 +</tr>
app/views/search/_blog.rhtml 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  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 + <table class="noborder search-content-second-column">
  7 + <tr class="search-blog-items">
  8 + <td class="search-field-label"><%= _("Last posts") %></td>
  9 +
  10 + <% r = blog.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %>
  11 + <td class="<%= "search-field-none" if r.empty? %>">
  12 + <% r.each do |a| %>
  13 + <%= link_to a.title, a.view_url, :class => 'search-blog-sample-item '+icon_for_article(a) %>
  14 + <% end %>
  15 + <%= _('None') if r.empty? %>
  16 + </td>
  17 + </tr>
  18 +
  19 + <%= render :partial => 'article_common', :object => blog %>
  20 + </table>
  21 + <%= render :partial => 'article_last_change', :object => blog %>
  22 +
  23 + <div style="clear: both;"/></div>
  24 +</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="<%= @results.size == 1 ? 'only-one-result-box' : 'multiple-results-boxes' %>">
  2 + <% @order.each do |name| %>
  3 + <% results = @results[name] %>
  4 + <% empty = results.nil? || results.empty? %>
2 5
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 %> 6 + <div class="search-results-<%= name %> search-results-box <%= "search-results-empty" if empty %>">
  7 + <% if not empty %>
  8 + <% partial = partial_for_class(results.first.class.class_name.constantize) %>
27 9
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 %> 10 + <% if @results.size > 1 %>
  11 + <h3><%= @names[name] %></h3>
  12 + <% if results.total_entries > SearchController::MULTIPLE_SEARCH_LIMIT %>
  13 + <%= link_to(_('see all (%d)') % results.total_entries, params.merge(:action => name), :class => 'see-more' ) %>
  14 + <% end %>
33 <% end %> 15 <% 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 --> 16 +
  17 + <div class="search-results-innerbox search-results-type-<%= partial %> <%= 'common-profile-list-block' if partial == 'profile' %>">
38 <ul> 18 <ul>
39 - <% hit_pos = 0 %>  
40 <% results.each do |hit| %> 19 <% 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 ) } %> 20 + <%= render :partial => partial_for_class(hit.class), :object => hit %>
46 <% end %> 21 <% end %>
47 </ul> 22 </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 %> 23 + </div>
  24 + <% else %>
  25 + <% if @results.size > 1 %>
56 <h3><%= @names[name] %></h3> 26 <h3><%= @names[name] %></h3>
57 <% end %> 27 <% end %>
  28 + <div class="search-results-innerbox search-results-type-empty">
  29 + <div> <%= _('None') %> </div>
  30 + </div>
58 <% end %> 31 <% 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>
64 <% end %> 33 <% end %>
65 -<% end %> 34 +
  35 + <div style="clear:both"></div>
66 36
67 -<br style="clear:both" />  
68 -</div><!-- end id="search-results" --> 37 + <%= add_zoom_to_images %>
  38 +</div>
69 39
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) %>  
5 - </div> 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>
  6 +<table class="noborder search-content-second-column">
  7 + <% if event.start_date %>
  8 + <tr class="search-article-event-date">
  9 + <td class="search-field-label"><%= _('Start date') %></td>
  10 + <td class="article-item-date"><%= event.start_date %></td>
  11 + </tr>
  12 + <% end %>
  13 + <% if event.end_date %>
  14 + <tr class="search-article-event-date">
  15 + <td class="search-field-label"><%= _('End date') %></td>
  16 + <td class="article-item-date"><%= event.end_date %></td>
  17 + </tr>
  18 + <% end %>
  19 +
  20 + <%= render :partial => 'article_common', :object => event %>
  21 +</table>
  22 +<%= render :partial => 'article_last_change', :object => event %>
  23 +
  24 +<div style="clear: both"></div>
6 </li> 25 </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,24 @@ @@ -0,0 +1,24 @@
  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 + <table class="noborder search-content-second-column">
  7 + <tr class="search-folder-items">
  8 + <td class="search-field-label"><%= _("Last items") %></td>
  9 +
  10 + <% r = folder.children.last(3) %>
  11 + <td class="<%= "search-field-none" if r.empty? %>">
  12 + <% r.each do |a| %>
  13 + <%= link_to a.title, a.view_url, :class => 'search-folder-sample-item '+icon_for_article(a) %>
  14 + <% end %>
  15 + <%= _('None') if r.empty? %>
  16 + </td>
  17 + </tr>
  18 +
  19 + <%= render :partial => 'article_common', :object => folder %>
  20 + </table>
  21 + <%= render :partial => 'article_last_change', :object => folder %>
  22 +
  23 + <div style="clear:both"></div>
  24 +</li>
app/views/search/_forum.rhtml 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  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 + <table class="noborder search-content-second-column">
  7 + <tr class="search-forum-items">
  8 + <td class="search-field-label"><%= _("Last topics") %></td>
  9 +
  10 + <% r = forum.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %>
  11 + <td class="<%= "search-field-none" if r.empty? %>">
  12 + <% r.each do |a| %>
  13 + <%= link_to a.title, a.view_url, :class => 'search-forum-sample-item '+icon_for_article(a) %>
  14 + <% end %>
  15 + <%= _('None') if r.empty? %>
  16 + </td>
  17 + </tr>
  18 +
  19 + <%= render :partial => 'article_common', :object => forum %>
  20 + </table>
  21 + <%= render :partial => 'article_last_change', :object => forum %>
  22 +
  23 + <div style="clear:both"></div>
  24 +</li>
app/views/search/_gallery.rhtml 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  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 + <table class="noborder search-content-second-column">
  7 + <%= render :partial => 'article_common', :object => gallery %>
  8 + </table>
  9 + <%= render :partial => 'article_last_change', :object => gallery %>
  10 +
  11 + <div style="clear: both"></div>
  12 +</li>
  13 +
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 => "http://maps.google.com/maps/api/js?sensor=true", :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")
67 %> 11 %>
68 -}  
69 12
70 - map.setZoom(map.getBoundsZoomLevel(bounds));  
71 - map.setCenter(bounds.getCenter()); 13 +<script type='text/javascript'>
  14 + mapLoad(<%= GoogleMaps.initial_zoom.to_json %>);
  15 +
  16 + <% @results.each do |name,results| %>
  17 + <% results.each do |item| %>
  18 + <% if item.lat && item.lng %>
  19 + mapPutMarker(<%= item.lat.to_json %>, <%= item.lng.to_json %>, <%= item.name.to_json %>, '<%= icon %>',
  20 + '<%= url_for(:controller => :map_balloon, :action => name.to_s.singularize, :id => item.id) %>');
  21 + <% end %>
  22 + <% end %>
  23 + <% end %>
  24 +
  25 + mapCenter();
72 </script> 26 </script>
app/views/search/_image.rhtml 0 → 100644
@@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
  1 +<div class="search-image-container">
  2 +
  3 + <% if image.is_a? UploadedFile and image.filename %>
  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"><span><%= _('No image') %></span></div>
  36 + <% end %>
  37 + </div>
  38 + <% elsif image.is_a? Product %>
  39 + <% if image.image %>
  40 + <div class="zoomable-image">
  41 + <%= link_to '', product_path(image), :class => "search-image-pic",
  42 + :style => 'background-image: url(%s)'% image.default_image(:thumb) %>
  43 + <%= link_to content_tag(:span, _('Zoom in')), image.image.public_filename,
  44 + :class => 'zoomify-image' %>
  45 + </div>
  46 + <% else %>
  47 + <div class="search-no-image"><span><%= _('No image') %></span></div>
  48 + <% end %>
  49 + <% else %>
  50 + <div class="search-content-type-icon icon-content-<%=image.class.to_s.underscore.dasherize%>"></div>
  51 + <% end %>
  52 +</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.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %> 1 <% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
9 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%> 2 <% extra_properties = @plugins.dispatch(: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">
21 5
22 - <li> <%= _('Category:') + ' ' + link_to_product_category(product.product_category) %> </li> 6 + <div class="search-product-item-first-column">
  7 + <%= render :partial => 'search/image', :object => product %>
23 8
24 - <% extra_properties.each do |property| %>  
25 - <li><%= property[:name] + ': ' + instance_eval(&property[:content]) %></li> 9 + <% if product.available %>
  10 + <% if product.price && product.price > 0 %>
  11 + <% has_discount = product.discount && product.discount > 0 %>
  12 + <% if product.price %>
  13 + <span class="search-product-price-textid"><%=_("from") if has_discount %></span><%= price_span(product.price, :class => "search-product-price " + (has_discount ? 'with-discount' : '')) %>
  14 + <% if has_discount %>
  15 + <span class="search-product-price-textid"><%=_("by")%></span><%= price_span(product.price_with_discount, :class => "search-product-price") %>
  16 + <% end %>
  17 + <% if product.unit %>
  18 + <span class="search-product-unit">&nbsp;<%= _('/') %>&nbsp;<%= product.unit.name %></span>
  19 + <% end %>
  20 + <% end %>
  21 + <div class="search-product-inputs-info">
  22 + <% if p = product.percentage_from_solidarity_economy %>
  23 + <div class="search-product-percentage-from-solidarity-economy search-product-ecosol-percentage-icon-<%= p[0].to_s %>"
  24 + title="<%=_('Percentage of inputs from solidarity economy')%>">
  25 + <%= p[1] %>
  26 + </div>
  27 + <% end %>
  28 +
  29 + <% if product.price_described? %>
  30 + <% title = (product.inputs + product.price_details).map{ |i|
  31 + '<div class="search-product-input-dots-to-price">' +
  32 + '<div class="search-product-input-name">' + i.product_category.name + '</div>' +
  33 + price_span(i.price, :class => 'search-product-input-price') +
  34 + '</div>' }.join('') %>
  35 + <%= link_to_function _("Open Price"), '', :title => title, :class => "search-product-price-details" %>
  36 + <% end %>
  37 + </div>
  38 + <% end %>
  39 + <% else %>
  40 + <span class="product-not-available"><%= _('Not available') %></div>
26 <% end %> 41 <% end %>
27 - </ul> 42 +
  43 + </div>
  44 + <div class="search-product-item-second-column">
  45 + <%= link_to_product product, :class => 'search-result-title' %>
  46 + <div class="search-product-supplier">
  47 + <span class="search-field-label"><%= _('Supplier') %> </span><%= link_to_homepage(product.enterprise.name, product.enterprise.identifier) %>
  48 + </div>
  49 + <div class="search-product-description">
  50 + <% if product.description %>
  51 + <% desc_stripped = strip_tags(product.description) %>
  52 + <span class="search-field-label"><%= _('Description') %> </span><%= excerpt(desc_stripped, desc_stripped.first(3), 300) %>
  53 + <% end %>
  54 + </div>
  55 + </div>
  56 + <div class="search-product-item-third-column">
  57 + <div class="search-product-region">
  58 + <% if product.enterprise.region %>
  59 + <span class="search-field-label"><%= _('City') %></span>
  60 + <br /><%= city_with_state(product.enterprise.region) %>
  61 + <% end %>
  62 + </div>
  63 + <div class="search-product-qualifiers">
  64 + <% if product.product_qualifiers.count > 0 %>
  65 + <span class="search-field-label"><%= _('Qualifiers') %></span>
  66 + <% product.product_qualifiers.each do |pq| %>
  67 + <% if pq.qualifier %>
  68 + <span class="search-product-qualifier"><%= pq.qualifier.name + (pq.certifier.nil? ? _(";") : '') %></span>
  69 + <% end %>
  70 + <% if pq.certifier %>
  71 + <span class="search-product-certifier">&nbsp;<%= _('cert. ') + pq.certifier.name + _(";") %></span>
  72 + <% end %>
  73 + <% end %>
  74 + <% end %>
  75 + </div>
  76 + </div>
  77 +
  78 + <div style="clear: both"></div>
28 79
29 <%= extra_content.join('\n') %> 80 <%= extra_content.join('\n') %>
  81 + <% extra_properties.each do |property| %>
  82 + <div><%= property[:name] + ': ' + instance_eval(&property[:content]) %></div>
  83 + <% end %>
30 84
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 + <% end %>
  24 + </div>
  25 +
  26 + <div class="search-enterprise-categorization">
  27 + <% profile.top_level_categorization.each do |parent, children| %>
  28 + <% if parent.name != "Territórios" %>
  29 + <div class="search-enterprise-category-<%=parent.id%> search-enterprise-category">
  30 + <span class="search-enterprise-categorization-parent"><%= parent.name %></span>
  31 + <span class="search-enterprise-categorization-children">
  32 + <%= children.collect(&:name).join(', ') %>
  33 + </span>
  34 + </div>
  35 + <% end %>
  36 + <% end %>
  37 + </div>
  38 + </div>
  39 +
  40 + <hr class="clearfix" />
  41 + </div>
  42 +<% end %>
  43 +</li>
app/views/search/_results_header.rhtml 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  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 + <% if @results[@asset].total_entries > 0 %>
  5 + <%= label_total_found(@asset, @results[@asset].total_entries) %>
  6 + <% if params[:display] != 'map' %>
  7 + <span class="current-page"><%= _("Showing page %s of %s") % [@results[@asset].current_page, @results[@asset].total_pages] %></span>
  8 + <% end %>
  9 + <% end %>
  10 + </div>
  11 +
  12 + <div class="search-results-header-facets-order-by">
  13 + <%= facets_unselect_menu(@asset) %>
  14 + <%= order_by(@asset) if params[:display] != 'map' %>
  15 + </div>
  16 + <% else %>
  17 + <div id='search-filter-title'><%= @filter_title if @filter_title %></div>
  18 + <% end %>
  19 +
  20 + <div style="clear: both"></div>
  21 +</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) %> 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 + <% params_uri = CGI::unescape(request.request_uri) %>
  9 + <% if params_uri.index('?') %>
  10 + <% params_uri[(params_uri.index('?')+1)..-1].to_s.split("&").each do |part| %>
  11 + <% if part.start_with? "facet" %>
  12 + <% name_value = part.split("=") %>
  13 + <%= hidden_field_tag name_value[0], name_value[1] %>
  14 + <% end %>
  15 + <% end %>
20 <% end %> 16 <% 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>  
34 - </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" --> 17 +
  18 + <div class="search-field">
  19 + <span class="formfield">
  20 + <%= text_field_tag 'query', @query, :id => 'search-input', :size => 50 %>
  21 + <%= javascript_tag "jQuery('#search-input').attr('title', \"#{hint}\").hint()" if defined?(hint) %>
  22 + </span>
  23 +
  24 + <%= submit_button(:search, _('Search')) %>
  25 + </div>
50 26
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 %> 27 + <% end %>
  28 +
  29 + <% if @empty_query %>
  30 + <% hint = environment.search_hints[@asset] %>
  31 + <% if hint and !hint.blank? %>
  32 + <div class="search-hint"><%= hint %></div>
  33 + <% 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,13 @@ @@ -0,0 +1,13 @@
  1 +<li class="search-text-article-item article-item">
  2 + <%= link_to(text_article.title, text_article.url, :class => "search-result-title") %>
  3 +
  4 + <div class="search-content-first-column">
  5 + <%= render :partial => 'image', :object => text_article %>
  6 + </div>
  7 + <table class="noborder search-content-second-column">
  8 + <%= render :partial => 'article_common', :object => text_article %>
  9 + </table>
  10 + <%= render :partial => 'article_last_change', :object => text_article %>
  11 +
  12 + <div style="clear: both"></div>
  13 +</li>
app/views/search/_uploaded_file.rhtml 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +<li class="search-uploaded-file-item article-item">
  2 + <%= link_to uploaded_file.filename, uploaded_file.view_url, :class => 'search-result-title' %>
  3 +
  4 + <div class="search-content-first-column">
  5 + <%= render :partial => 'image', :object => uploaded_file %>
  6 + </div>
  7 +
  8 + <table class="noborder search-content-second-column">
  9 + <%= render :partial => 'article_author', :object => uploaded_file %>
  10 + <%= render :partial => 'article_description', :object => uploaded_file %>
  11 +
  12 + <% if uploaded_file.parent and uploaded_file.parent.published? %>
  13 + <tr class="search-uploaded-file-parent">
  14 + <td class="search-field-label"><%= uploaded_file.parent.gallery? ? _("Gallery") : _("Folder") %></td>
  15 + <td><%= link_to uploaded_file.parent.name, uploaded_file.parent.url %></td>
  16 + </tr>
  17 + <% end %>
  18 +
  19 + <%= render :partial => 'article_tags', :object => uploaded_file.tags %>
  20 + <%= render :partial => 'article_categories', :object => uploaded_file.categories %>
  21 + </table>
  22 + <%= render :partial => 'article_last_change', :object => uploaded_file %>
  23 +
  24 + <div style="clear:both"></div>
  25 +</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 + <%= display_results %>
  14 + <%= pagination_links @results[:articles] %>
  15 +</div>
14 16
15 -<%= pagination_links @results.values.first %>  
16 -  
17 -<br style="clear:both" /> 17 +<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