Commit 03c856bbddd762c9f7f77e7b320eb1f3e10a221e
Exists in
master
and in
28 other branches
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.
HACKING
@@ -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 |
INSTALL
@@ -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 |
Rakefile
@@ -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
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 |
@@ -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 < PublicController | @@ -8,11 +8,10 @@ class ProfileSearchController < 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 < PublicController | @@ -216,25 +166,137 @@ class SearchController < 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 'redcloth' | @@ -4,7 +4,7 @@ require 'redcloth' | ||
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(' → ') : category.name | 35 | name = full ? category.full_name(' → ') : 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
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 'hpricot' | @@ -2,6 +2,12 @@ require 'hpricot' | ||
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 < ActiveRecord::Base | @@ -22,6 +28,9 @@ class Article < 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 < ActiveRecord::Base | @@ -47,7 +56,7 @@ class Article < 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 < ActiveRecord::Base | @@ -96,11 +105,13 @@ class Article < 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 < ActiveRecord::Base | @@ -109,6 +120,7 @@ class Article < 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 < ActiveRecord::Base | @@ -116,6 +128,8 @@ class Article < 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 < ActiveRecord::Base | @@ -126,8 +140,6 @@ class Article < 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 < ActiveRecord::Base | @@ -142,13 +154,33 @@ class Article < 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 < ActiveRecord::Base | @@ -166,20 +198,14 @@ class Article < 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 < ActiveRecord::Base | @@ -421,7 +447,7 @@ class Article < 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 < ActiveRecord::Base | @@ -573,6 +599,99 @@ class Article < 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 < Folder | @@ -9,6 +9,10 @@ class Blog < 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 < ActiveRecord::Base | @@ -15,13 +15,14 @@ class Category < 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 < ActiveRecord::Base | @@ -36,18 +37,38 @@ class Category < 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 < ActiveRecord::Base | @@ -69,4 +90,23 @@ class Category < 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 < ActiveRecord::Base | @@ -2,12 +2,20 @@ class Certifier < 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 < ActiveRecord::Base | @@ -16,4 +24,6 @@ class Certifier < 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 < ProfileListBlock | @@ -21,7 +21,7 @@ class CommunitiesBlock < 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 < ActiveRecord::Base | @@ -8,9 +8,9 @@ class Domain < 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 < Organization | @@ -10,12 +14,19 @@ class Enterprise < 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 < Organization | @@ -67,22 +78,6 @@ class Enterprise < 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 < Organization | @@ -167,6 +162,10 @@ class Enterprise < 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
app/models/environment.rb
@@ -248,6 +248,10 @@ class Environment < ActiveRecord::Base | @@ -248,6 +248,10 @@ class Environment < 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 < ActiveRecord::Base | @@ -277,6 +281,9 @@ class Environment < 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 < ActiveRecord::Base | @@ -358,11 +365,11 @@ class Environment < 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
app/models/folder.rb
app/models/forum.rb
@@ -2,6 +2,10 @@ class Forum < Folder | @@ -2,6 +2,10 @@ class Forum < 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
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&v=2&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 < ActiveRecord::Base | @@ -59,4 +59,7 @@ class Input < 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 < Block | @@ -17,10 +17,10 @@ class LocationBlock < 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.')) |
@@ -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/people_block.rb
@@ -18,7 +18,7 @@ class PeopleBlock < ProfileListBlock | @@ -18,7 +18,7 @@ class PeopleBlock < 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 < Profile | @@ -187,6 +191,9 @@ class Person < 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 < ActiveRecord::Base | @@ -15,35 +20,20 @@ class Product < 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 < ActiveRecord::Base | @@ -89,18 +79,12 @@ class Product < 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 < ActiveRecord::Base | @@ -128,7 +112,6 @@ class Product < 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 < ActiveRecord::Base | @@ -213,4 +196,102 @@ class Product < 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 < Category | @@ -9,4 +10,7 @@ class ProductCategory < 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 < ActiveRecord::Base | @@ -74,8 +80,6 @@ class Profile < 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 < ActiveRecord::Base | @@ -125,8 +129,6 @@ class Profile < 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 < ActiveRecord::Base | @@ -181,8 +183,20 @@ class Profile < 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 < ActiveRecord::Base | @@ -217,12 +231,15 @@ class Profile < 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 < ActiveRecord::Base | @@ -2,16 +2,19 @@ class Qualifier < 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
app/models/raw_html_article.rb
app/models/region.rb
@@ -5,19 +5,17 @@ class Region < Category | @@ -5,19 +5,17 @@ class Region < 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
app/models/text_article.rb
@@ -3,6 +3,10 @@ class TextArticle < Article | @@ -3,6 +3,10 @@ class TextArticle < 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
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
app/views/browse/people.rhtml
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'> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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 %> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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/_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 |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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"> <%= _('/') %> <%= 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"> <%= _('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(' → ') %> | ||
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> |
@@ -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> |
@@ -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> |
@@ -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/cities.rhtml
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 |