Commit 78a0bcf54fcd1a5721915b4b3dbd086daf85ea92
Exists in
master
and in
29 other branches
Merge branch 'master' into AI2371
Conflicts: app/controllers/my_profile/cms_controller.rb
Showing
571 changed files
with
133905 additions
and
22540 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 571 files displayed.
HACKING
... | ... | @@ -40,6 +40,8 @@ commands and make sure you understand what you are doing): |
40 | 40 | cp config/database.yml.sqlite3 config/database.yml |
41 | 41 | # create tmp directory if it doesn't exist |
42 | 42 | mkdir tmp |
43 | + # start Solr | |
44 | + rake solr:start | |
43 | 45 | # create the development database |
44 | 46 | rake db:schema:load |
45 | 47 | # run pending migrations | ... | ... |
INSTALL
... | ... | @@ -13,8 +13,7 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or |
13 | 13 | Debian-based systems, all of these packages are available through the Debian |
14 | 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-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 | 18 | On other systems, they may or may not be available through your regular package |
20 | 19 | management system. Below are the links to their homepages. |
... | ... | @@ -24,7 +23,7 @@ management system. Below are the links to their homepages. |
24 | 23 | * po4a: http://po4a.alioth.debian.org/ |
25 | 24 | * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby |
26 | 25 | * rcov: http://eigenclass.org/hiki/rcov |
27 | -* Ferret: http://ferret.davebalmain.com/trac | |
26 | +* Solr: http://lucene.apache.org/solr | |
28 | 27 | * RMagick: http://rmagick.rubyforge.org/ |
29 | 28 | * RedCloth: http://redcloth.org/ |
30 | 29 | * will_paginate: http://github.com/mislav/will_paginate/wikis |
... | ... | @@ -104,7 +103,7 @@ $ tar -zxvf noosfero-0.35.0.tar.gz |
104 | 103 | $ ln -s noosfero-0.35.0 current |
105 | 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 | 107 | probably not need to customize this configuration, but have a look at it. |
109 | 108 | |
110 | 109 | Create the thin configuration file: |
... | ... | @@ -242,6 +241,10 @@ Compile the translations: |
242 | 241 | |
243 | 242 | $ RAILS_ENV=production rake noosfero:translations:compile |
244 | 243 | |
244 | +Run Solr: | |
245 | + | |
246 | +$ rake solr:start | |
247 | + | |
245 | 248 | Now we have to create some initial data. To create your default environment |
246 | 249 | (the first one), run the command below: |
247 | 250 | ... | ... |
Rakefile
app/controllers/my_profile/cms_controller.rb
... | ... | @@ -265,9 +265,10 @@ class CmsController < MyProfileController |
265 | 265 | |
266 | 266 | def search |
267 | 267 | query = params[:q] |
268 | - results = query.blank? ? [] : profile.files.published.find_by_contents(query) | |
268 | + results = query.blank? ? [] : profile.files.published.find_by_contents(query)[:results] | |
269 | 269 | render :text => article_list_to_json(results), :content_type => 'application/json' |
270 | 270 | end |
271 | + | |
271 | 272 | def media_upload |
272 | 273 | files_uploaded = [] |
273 | 274 | parent = check_parent(params[:parent_id]) | ... | ... |
app/controllers/my_profile/maps_controller.rb
... | ... | @@ -6,16 +6,48 @@ class MapsController < MyProfileController |
6 | 6 | @profile_data = profile |
7 | 7 | if request.post? |
8 | 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 | 17 | Profile.transaction do |
10 | 18 | if profile.update_attributes!(params[:profile_data]) |
11 | 19 | session[:notice] = _('Address was updated successfully!') |
12 | 20 | redirect_to :action => 'edit_location' |
13 | 21 | end |
14 | 22 | end |
15 | - rescue | |
16 | - flash[:error] = _('Address could not be saved.') | |
23 | + rescue Exception => exc | |
24 | + flash[:error] = exc.message | |
17 | 25 | end |
18 | 26 | end |
19 | 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 | 53 | end | ... | ... |
app/controllers/public/browse_controller.rb
... | ... | @@ -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 @@ |
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_controller.rb
... | ... | @@ -3,17 +3,15 @@ class ProfileController < PublicController |
3 | 3 | needs_profile |
4 | 4 | before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] |
5 | 5 | before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse] |
6 | - before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report] | |
6 | + before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity] | |
7 | 7 | |
8 | 8 | helper TagsHelper |
9 | 9 | |
10 | 10 | def index |
11 | - @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) | |
12 | - @wall_items = [] | |
13 | - @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) : [] | |
11 | + @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) : [] | |
14 | 12 | if logged_in? && current_person.follows?(@profile) |
15 | - @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) if @network_activities.empty? | |
16 | - @wall_items = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) | |
13 | + @network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) if @network_activities.empty? | |
14 | + @activities = @profile.activities.paginate(:per_page => 15, :page => params[:page]) | |
17 | 15 | end |
18 | 16 | @tags = profile.article_tags |
19 | 17 | unless profile.display_info_to?(user) |
... | ... | @@ -180,22 +178,33 @@ class ProfileController < PublicController |
180 | 178 | @scrap.receiver= receiver |
181 | 179 | @tab_action = params[:tab_action] |
182 | 180 | @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.") |
183 | - @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if params[:not_load_scraps].nil? | |
184 | - render :partial => 'leave_scrap' | |
181 | + activities = @profile.activities.paginate(:per_page => 15, :page => params[:page]) if params[:not_load_scraps].nil? | |
182 | + render :partial => 'profile_activities_list', :locals => {:activities => activities} | |
185 | 183 | end |
186 | 184 | |
187 | - def view_more_scraps | |
188 | - @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) | |
189 | - render :partial => 'profile_scraps', :locals => {:scraps => @scraps} | |
185 | + def leave_comment_on_activity | |
186 | + @comment = Comment.new(params[:comment]) | |
187 | + @comment.author = user | |
188 | + @activity = ActionTracker::Record.find(params[:source_id]) | |
189 | + @comment.source_type, @comment.source_id = (@activity.target_type == 'Article' ? ['Article', @activity.target_id] : [@activity.class.to_s, @activity.id]) | |
190 | + @tab_action = params[:tab_action] | |
191 | + @message = @comment.save ? _("Comment successfully added.") : _("You can't leave an empty comment.") | |
192 | + if @tab_action == 'wall' | |
193 | + activities = @profile.activities.paginate(:per_page => 15, :page => params[:page]) if params[:not_load_scraps].nil? | |
194 | + render :partial => 'profile_activities_list', :locals => {:activities => activities} | |
195 | + else | |
196 | + network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) | |
197 | + render :partial => 'profile_network_activities', :locals => {:network_activities => network_activities} | |
198 | + end | |
190 | 199 | end |
191 | 200 | |
192 | 201 | def view_more_activities |
193 | - @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) | |
194 | - render :partial => 'profile_activities', :locals => {:activities => @activities} | |
202 | + @activities = @profile.activities.paginate(:per_page => 10, :page => params[:page]) | |
203 | + render :partial => 'profile_activities_list', :locals => {:activities => @activities} | |
195 | 204 | end |
196 | 205 | |
197 | 206 | def view_more_network_activities |
198 | - @activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) | |
207 | + @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page]) | |
199 | 208 | render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} |
200 | 209 | end |
201 | 210 | |
... | ... | @@ -213,7 +222,11 @@ class ProfileController < PublicController |
213 | 222 | begin |
214 | 223 | raise if !can_edit_profile |
215 | 224 | activity = ActionTracker::Record.find(params[:activity_id]) |
216 | - activity.destroy | |
225 | + if params[:only_hide] | |
226 | + activity.update_attribute(:visible, false) | |
227 | + else | |
228 | + activity.destroy | |
229 | + end | |
217 | 230 | render :text => _('Activity successfully removed.') |
218 | 231 | rescue |
219 | 232 | render :text => _('You could not remove this activity') |
... | ... | @@ -285,6 +298,16 @@ class ProfileController < PublicController |
285 | 298 | end |
286 | 299 | end |
287 | 300 | |
301 | + def remove_comment | |
302 | + #FIXME Check whether these permissions are enough | |
303 | + @comment = Comment.find(params[:comment_id]) | |
304 | + if (user == @comment.author || user == profile || user.has_permission?(:moderate_comments, profile)) | |
305 | + @comment.destroy | |
306 | + session[:notice] = _('Comment successfully deleted') | |
307 | + end | |
308 | + redirect_to :action => :index | |
309 | + end | |
310 | + | |
288 | 311 | protected |
289 | 312 | |
290 | 313 | def check_access_to_profile | ... | ... |
app/controllers/public/profile_search_controller.rb
... | ... | @@ -8,11 +8,10 @@ class ProfileSearchController < PublicController |
8 | 8 | def index |
9 | 9 | @q = params[:q] |
10 | 10 | unless @q.blank? |
11 | - @filtered_query = remove_stop_words(@q) | |
12 | 11 | if params[:where] == 'environment' |
13 | 12 | redirect_to :controller => 'search', :query => @q |
14 | 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 | 15 | end |
17 | 16 | end |
18 | 17 | end | ... | ... |
app/controllers/public/search_controller.rb
1 | 1 | class SearchController < PublicController |
2 | 2 | |
3 | 3 | helper TagsHelper |
4 | + include SearchHelper | |
5 | + include ActionView::Helpers::NumberHelper | |
4 | 6 | |
5 | 7 | before_filter :load_category |
6 | - before_filter :prepare_filter | |
7 | - before_filter :check_search_whole_site | |
8 | 8 | before_filter :load_search_assets |
9 | - before_filter :check_valid_assets, :only => [ :assets ] | |
9 | + before_filter :load_query | |
10 | 10 | |
11 | 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 | 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 | 29 | else |
27 | - @noosfero_finder = EnvironmentFinder.new(@environment) | |
30 | + @results[@asset] = @environment.articles.public.send(@filter).paginate(paginate_options) | |
28 | 31 | end |
29 | 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 | 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 | 43 | end |
43 | 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 | 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 | 63 | end |
59 | 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 | 65 | end |
67 | 66 | |
68 | - def people | |
69 | - #nothing, just to enable | |
70 | - end | |
71 | 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 | 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 | 81 | end |
94 | - | |
95 | 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 | 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 | 102 | else |
118 | - (searching.size == 1) ? 20 : 6 | |
103 | + @results[@asset] = date_range ? environment.events.by_range(date_range) : environment.events | |
119 | 104 | end |
120 | - end | |
121 | - | |
122 | - public | |
123 | - | |
124 | - include SearchHelper | |
125 | - | |
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 | 105 | |
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 | 110 | end |
149 | 111 | |
150 | 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 | 113 | @results = {} |
161 | 114 | @order = [] |
162 | 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 | 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 | 123 | @names[key] = getterm(description) |
168 | 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 | 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 | 137 | # view the summary of one category |
188 | 138 | def category_index |
189 | 139 | @results = {} |
190 | 140 | @order = [] |
191 | 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 | 155 | end |
205 | 156 | end |
206 | - attr_reader :category | |
207 | 157 | |
208 | 158 | def tags |
209 | 159 | @tags_cache_key = "tags_env_#{environment.id.to_s}" |
... | ... | @@ -216,25 +166,137 @@ class SearchController < PublicController |
216 | 166 | @tag = params[:tag] |
217 | 167 | @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}" |
218 | 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 | 170 | end |
221 | 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 | 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 | 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 | 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 viewed 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 | 300 | end |
239 | 301 | |
240 | 302 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -4,7 +4,7 @@ require 'redcloth' |
4 | 4 | # application. |
5 | 5 | module ApplicationHelper |
6 | 6 | |
7 | - include PermissionName | |
7 | + include PermissionNameHelper | |
8 | 8 | |
9 | 9 | include LightboxHelper |
10 | 10 | |
... | ... | @@ -574,7 +574,7 @@ module ApplicationHelper |
574 | 574 | end |
575 | 575 | extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) |
576 | 576 | links = links_for_balloon(profile) |
577 | - content_tag tag, | |
577 | + content_tag('div', content_tag(tag, | |
578 | 578 | (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") + |
579 | 579 | link_to( |
580 | 580 | content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + |
... | ... | @@ -584,7 +584,7 @@ module ApplicationHelper |
584 | 584 | :class => 'profile_link url', |
585 | 585 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, |
586 | 586 | :title => profile.name ), |
587 | - :class => 'vcard' | |
587 | + :class => 'vcard'), :class => 'common-profile-list-block') | |
588 | 588 | end |
589 | 589 | |
590 | 590 | def gravatar_url_for(email, options = {}) |
... | ... | @@ -890,22 +890,6 @@ module ApplicationHelper |
890 | 890 | result |
891 | 891 | end |
892 | 892 | |
893 | - def search_page_title(title, options={}) | |
894 | - title = "<h1>" + title + "</h1>" | |
895 | - title += "<h2 class='query'>" + _("Searched for '%s'") % options[:query] + "</h2>" if !options[:query].blank? | |
896 | - title += "<h2 class='query'>" + _("In category %s") % options[:category] + "</h2>" if !options[:category].blank? | |
897 | - title += "<h2 class='query'>" + _("within %d km from %s") % [options[:distance], options[:region]] + "</h2>" if !options[:distance].blank? && !options[:region].blank? | |
898 | - title += "<h2 class='query'>" + _("%d results found") % options[:total_results] + "</h2>" if !options[:total_results].blank? | |
899 | - title | |
900 | - end | |
901 | - | |
902 | - def search_page_link_to_all(options={}) | |
903 | - if options[:category] | |
904 | - title = "<div align='center'>" + _('In all categories') + "</div>" | |
905 | - link_to title, :action => 'assets', :asset => options[:asset], :category_path => [] | |
906 | - end | |
907 | - end | |
908 | - | |
909 | 893 | def template_stylesheet_path |
910 | 894 | if profile.nil? |
911 | 895 | "/designs/templates/#{environment.layout_template}/stylesheets/style.css" |
... | ... | @@ -982,6 +966,7 @@ module ApplicationHelper |
982 | 966 | def noosfero_stylesheets |
983 | 967 | [ |
984 | 968 | 'application', |
969 | + 'search', | |
985 | 970 | 'thickbox', |
986 | 971 | 'lightbox', |
987 | 972 | 'colorpicker', |
... | ... | @@ -1001,7 +986,7 @@ module ApplicationHelper |
1001 | 986 | end |
1002 | 987 | |
1003 | 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 | 990 | end |
1006 | 991 | |
1007 | 992 | def noosfero_layout_features |
... | ... | @@ -1100,35 +1085,51 @@ module ApplicationHelper |
1100 | 1085 | ") : '') |
1101 | 1086 | end |
1102 | 1087 | |
1103 | - def browse_people_menu | |
1088 | + def search_contents_menu | |
1089 | + links = [ | |
1090 | + {s_('contents|More Recent') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_recent'})}}, | |
1091 | + {s_('contents|More Viewed') => {: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 | + alias :browse_contents_menu :search_contents_menu | |
1101 | + | |
1102 | + def search_people_menu | |
1104 | 1103 | 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'})}} | |
1104 | + {s_('people|More Recent') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_recent'})}}, | |
1105 | + {s_('people|More Active') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_active'})}}, | |
1106 | + {s_('people|More Popular') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_popular'})}} | |
1108 | 1107 | ] |
1109 | 1108 | if logged_in? |
1110 | 1109 | links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})}) |
1111 | 1110 | links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})}) |
1112 | 1111 | end |
1113 | 1112 | |
1114 | - link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') + | |
1113 | + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "search", :action => 'people', :category_path => ''}, :id => 'submenu-people') + | |
1115 | 1114 | 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 | 1115 | end |
1116 | + alias :browse_people_menu :search_people_menu | |
1117 | 1117 | |
1118 | - def browse_communities_menu | |
1118 | + def search_communities_menu | |
1119 | 1119 | 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'})}} | |
1120 | + {s_('communities|More Recent') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_recent'})}}, | |
1121 | + {s_('communities|More Active') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_active'})}}, | |
1122 | + {s_('communities|More Popular') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_popular'})}} | |
1123 | 1123 | ] |
1124 | 1124 | if logged_in? |
1125 | 1125 | links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})}) |
1126 | 1126 | links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})}) |
1127 | 1127 | end |
1128 | 1128 | |
1129 | - link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') + | |
1129 | + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "search", :action => 'communities'}, :id => 'submenu-communities') + | |
1130 | 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') |
1131 | 1131 | end |
1132 | + alias :browse_communities_menu :search_communities_menu | |
1132 | 1133 | |
1133 | 1134 | def browse_contents_menu |
1134 | 1135 | links = [ |
... | ... | @@ -1217,15 +1218,12 @@ module ApplicationHelper |
1217 | 1218 | else _('1 minute') |
1218 | 1219 | end |
1219 | 1220 | |
1220 | - when 2..44 then _('%{distance} minutes') % { :distance => distance_in_minutes } | |
1221 | - when 45..89 then _('about 1 hour') | |
1222 | - when 90..1439 then _('about %{distance} hours') % { :distance => (distance_in_minutes.to_f / 60.0).round } | |
1223 | - when 1440..2879 then _('1 day') | |
1224 | - when 2880..43199 then _('%{distance} days') % { :distance => (distance_in_minutes / 1440).round } | |
1225 | - when 43200..86399 then _('about 1 month') | |
1226 | - when 86400..525599 then _('%{distance} months') % { :distance => (distance_in_minutes / 43200).round } | |
1227 | - when 525600..1051199 then _('about 1 year') | |
1228 | - else _('over %{distance} years') % { :distance => (distance_in_minutes / 525600).round } | |
1221 | + when 2..44 then _('%{distance} minutes ago') % { :distance => distance_in_minutes } | |
1222 | + when 45..89 then _('about 1 hour ago') | |
1223 | + when 90..1439 then _('about %{distance} hours ago') % { :distance => (distance_in_minutes.to_f / 60.0).round } | |
1224 | + when 1440..2879 then _('1 day ago') | |
1225 | + when 2880..10079 then _('%{distance} days ago') % { :distance => (distance_in_minutes / 1440).round } | |
1226 | + else show_time(from_time) | |
1229 | 1227 | end |
1230 | 1228 | end |
1231 | 1229 | |
... | ... | @@ -1330,6 +1328,10 @@ module ApplicationHelper |
1330 | 1328 | end |
1331 | 1329 | end |
1332 | 1330 | |
1331 | + def jquery_token_input_messages_json(hintText = _('Type in an keyword'), noResultsText = _('No results'), searchingText = _('Searching...')) | |
1332 | + "hintText: '#{hintText}', noResultsText: '#{noResultsText}', searchingText: '#{searchingText}'" | |
1333 | + end | |
1334 | + | |
1333 | 1335 | def delete_article_message(article) |
1334 | 1336 | if article.folder? |
1335 | 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 | 26 | product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url |
27 | 27 | end |
28 | 28 | |
29 | - def link_to_category(category, full = true) | |
29 | + def link_to_tag(tag, html_options = {}) | |
30 | + link_to tag.name, {:controller => 'search', :action => 'tag', :tag => tag.name}, html_options | |
31 | + end | |
32 | + | |
33 | + def link_to_category(category, full = true, html_options = {}) | |
30 | 34 | return _('Uncategorized product') unless category |
31 | 35 | name = full ? category.full_name(' → ') : category.name |
32 | - link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }) | |
36 | + link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }), html_options | |
33 | 37 | end |
34 | 38 | |
35 | 39 | def link_to_product_category(category) |
36 | 40 | if category |
37 | - link_to(category.name, :controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id, :host => category.environment.default_hostname) | |
41 | + link_to(category.name, :controller => 'search', :action => 'products', :category_path => category.explode_path) | |
38 | 42 | else |
39 | 43 | _('Uncategorized product') |
40 | 44 | end | ... | ... |
app/helpers/forum_helper.rb
... | ... | @@ -42,9 +42,9 @@ module ForumHelper |
42 | 42 | def last_topic_update(article) |
43 | 43 | info = article.info_from_last_update |
44 | 44 | if info[:author_url] |
45 | - time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url]) | |
45 | + time_ago_as_sentence(info[:date]) + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url]) | |
46 | 46 | else |
47 | - time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + info[:author_name] | |
47 | + time_ago_as_sentence(info[:date]) + ' ' + _('by') + ' ' + info[:author_name] | |
48 | 48 | end |
49 | 49 | end |
50 | 50 | ... | ... |
app/helpers/lightbox_helper.rb
... | ... | @@ -22,10 +22,7 @@ module LightboxHelper |
22 | 22 | def lightbox_options(options, lightbox_type = 'lbOn') |
23 | 23 | the_class = lightbox_type |
24 | 24 | the_class << " #{options[:class]}" if options.has_key?(:class) |
25 | - options.merge( | |
26 | - :class => the_class, | |
27 | - :onclick => 'alert("%s"); return false' % _('Please, try again when the page loading completes.') | |
28 | - ) | |
25 | + options.merge(:class => the_class) | |
29 | 26 | end |
30 | 27 | |
31 | 28 | def lightbox? | ... | ... |
app/helpers/manage_products_helper.rb
... | ... | @@ -241,6 +241,10 @@ module ManageProductsHelper |
241 | 241 | end |
242 | 242 | end |
243 | 243 | |
244 | + def remove_qualifier_button | |
245 | + button_to_function(:delete, content_tag('span', _('Delete qualifier')), "jQuery(this).parents('tr').remove()") | |
246 | + end | |
247 | + | |
244 | 248 | def select_unit(object) |
245 | 249 | collection_select(object.class.name.downcase, :unit_id, environment.units, :id, :singular, {:include_blank => _('Select the unit')}) |
246 | 250 | end |
... | ... | @@ -278,7 +282,7 @@ module ManageProductsHelper |
278 | 282 | error_msg = _('Something went wrong. Please, try again') |
279 | 283 | select_tag('price_details[][production_cost_id]', |
280 | 284 | '<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), | |
285 | + options_for_select(product.available_production_costs.map {|item| [truncate(item.name, {:length => 10, :omission => '...'}), item.id]} + [[_('Other cost'), '']], selected), | |
282 | 286 | {:class => 'production-cost-selection', |
283 | 287 | :onchange => "productionCostTypeChange(this, '#{url}', '#{prompt_msg}', '#{error_msg}')"}) |
284 | 288 | end | ... | ... |
app/helpers/product_category_viewer_helper.rb
app/helpers/search_helper.rb
1 | 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 | 45 | # FIXME remove it after search_controler refactored |
4 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 164 | end |
121 | - [cat, hits, childs] | |
122 | - end.select{|cat, hits| hits } | |
165 | + end | |
166 | + | |
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 | |
123 | 202 | |
124 | - render(:partial => 'product_categories_menu', :object => product_categories_menu) | |
203 | + def asset_table(asset) | |
204 | + asset_class(asset).table_name | |
125 | 205 | end |
126 | 206 | |
127 | 207 | end | ... | ... |
app/models/action_tracker_notification.rb
... | ... | @@ -3,6 +3,8 @@ class ActionTrackerNotification < ActiveRecord::Base |
3 | 3 | belongs_to :profile |
4 | 4 | belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id' |
5 | 5 | |
6 | + has_many :comments, :through => :action_tracker, :class_name => 'Comment', :foreign_key => 'source_id' | |
7 | + | |
6 | 8 | validates_presence_of :profile_id, :action_tracker_id |
7 | 9 | validates_uniqueness_of :action_tracker_id, :scope => :profile_id |
8 | 10 | ... | ... |
app/models/article.rb
... | ... | @@ -2,9 +2,13 @@ require 'hpricot' |
2 | 2 | |
3 | 3 | class Article < ActiveRecord::Base |
4 | 4 | |
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 | |
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 | |
7 | - track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target | |
5 | + # use for internationalizable human type names in search facets | |
6 | + # reimplement on subclasses | |
7 | + def self.type_name | |
8 | + _('Content') | |
9 | + end | |
10 | + | |
11 | + track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? } | |
8 | 12 | |
9 | 13 | # xss_terminate plugin can't sanitize array fields |
10 | 14 | before_save :sanitize_tag_list |
... | ... | @@ -17,11 +21,14 @@ class Article < ActiveRecord::Base |
17 | 21 | |
18 | 22 | belongs_to :last_changed_by, :class_name => 'Person', :foreign_key => 'last_changed_by_id' |
19 | 23 | |
20 | - has_many :comments, :dependent => :destroy, :order => 'created_at asc' | |
24 | + has_many :comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc' | |
21 | 25 | |
22 | 26 | has_many :article_categorizations, :conditions => [ 'articles_categories.virtual = ?', false ] |
23 | 27 | has_many :categories, :through => :article_categorizations |
24 | 28 | |
29 | + has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization', :dependent => :destroy | |
30 | + has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category | |
31 | + | |
25 | 32 | acts_as_having_settings :field => :setting |
26 | 33 | |
27 | 34 | settings_items :display_hits, :type => :boolean, :default => true |
... | ... | @@ -47,7 +54,7 @@ class Article < ActiveRecord::Base |
47 | 54 | xss_terminate :only => [ :name ], :on => 'validation', :with => 'white_list' |
48 | 55 | |
49 | 56 | named_scope :in_category, lambda { |category| |
50 | - {:include => 'categories', :conditions => { 'categories.id' => category.id }} | |
57 | + {:include => 'categories_including_virtual', :conditions => { 'categories.id' => category.id }} | |
51 | 58 | } |
52 | 59 | |
53 | 60 | named_scope :by_range, lambda { |range| { |
... | ... | @@ -65,7 +72,7 @@ class Article < ActiveRecord::Base |
65 | 72 | validate :translation_must_have_language |
66 | 73 | |
67 | 74 | def is_trackable? |
68 | - self.published? && self.notifiable? && self.advertise? | |
75 | + self.published? && self.notifiable? && self.advertise? && self.profile.public_profile | |
69 | 76 | end |
70 | 77 | |
71 | 78 | def external_link=(link) |
... | ... | @@ -96,11 +103,13 @@ class Article < ActiveRecord::Base |
96 | 103 | @pending_categorizations ||= [] |
97 | 104 | end |
98 | 105 | |
99 | - def add_category(c) | |
100 | - if self.id | |
101 | - ArticleCategorization.add_category_to_article(c, self) | |
102 | - else | |
106 | + def add_category(c, reload=false) | |
107 | + if new_record? | |
103 | 108 | pending_categorizations << c |
109 | + else | |
110 | + ArticleCategorization.add_category_to_article(c, self) | |
111 | + self.categories(reload) | |
112 | + self.solr_save | |
104 | 113 | end |
105 | 114 | end |
106 | 115 | |
... | ... | @@ -109,6 +118,7 @@ class Article < ActiveRecord::Base |
109 | 118 | ids.uniq.each do |item| |
110 | 119 | add_category(Category.find(item)) unless item.to_i.zero? |
111 | 120 | end |
121 | + self.categories(true) | |
112 | 122 | end |
113 | 123 | |
114 | 124 | after_create :create_pending_categorizations |
... | ... | @@ -116,6 +126,8 @@ class Article < ActiveRecord::Base |
116 | 126 | pending_categorizations.each do |item| |
117 | 127 | ArticleCategorization.add_category_to_article(item, self) |
118 | 128 | end |
129 | + self.categories(true) | |
130 | + self.solr_save | |
119 | 131 | pending_categorizations.clear |
120 | 132 | end |
121 | 133 | |
... | ... | @@ -126,8 +138,6 @@ class Article < ActiveRecord::Base |
126 | 138 | |
127 | 139 | acts_as_versioned |
128 | 140 | |
129 | - acts_as_searchable :additional_fields => [ :comment_data ] | |
130 | - | |
131 | 141 | def comment_data |
132 | 142 | comments.map {|item| [item.title, item.body].join(' ') }.join(' ') |
133 | 143 | end |
... | ... | @@ -135,20 +145,40 @@ class Article < ActiveRecord::Base |
135 | 145 | before_update do |article| |
136 | 146 | article.advertise = true |
137 | 147 | end |
138 | - | |
148 | + | |
139 | 149 | # retrieves all articles belonging to the given +profile+ that are not |
140 | 150 | # sub-articles of any other article. |
141 | 151 | named_scope :top_level_for, lambda { |profile| |
142 | 152 | {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]} |
143 | 153 | } |
144 | 154 | |
155 | + named_scope :join_profile, :joins => [:profile] | |
156 | + | |
157 | + named_scope :public, | |
158 | + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ?", true, true, true, true ] | |
159 | + | |
160 | + named_scope :more_recent, | |
161 | + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ? AND | |
162 | + ((articles.type != ?) OR articles.type is NULL)", | |
163 | + true, true, true, true, 'RssFeed' | |
164 | + ], | |
165 | + :order => 'articles.published_at desc, articles.id desc' | |
166 | + | |
167 | + # retrives the most commented articles, sorted by the comment count (largest | |
168 | + # first) | |
169 | + def self.most_commented(limit) | |
170 | + paginate(:order => 'comments_count DESC', :page => 1, :per_page => limit) | |
171 | + end | |
172 | + | |
173 | + named_scope :more_popular, :order => 'hits DESC' | |
174 | + | |
145 | 175 | # retrieves the latest +limit+ articles, sorted from the most recent to the |
146 | 176 | # oldest. |
147 | 177 | # |
148 | 178 | # Only includes articles where advertise == true |
149 | - def self.recent(limit, extra_conditions = {}) | |
179 | + def self.recent(limit = nil, extra_conditions = {}) | |
150 | 180 | # FIXME this method is a horrible hack |
151 | - options = { :limit => limit, | |
181 | + options = { :page => 1, :per_page => limit, | |
152 | 182 | :conditions => [ |
153 | 183 | "advertise = ? AND |
154 | 184 | published = ? AND |
... | ... | @@ -166,20 +196,14 @@ class Article < ActiveRecord::Base |
166 | 196 | options.delete(:include) |
167 | 197 | end |
168 | 198 | if extra_conditions == {} |
169 | - self.find(:all, options) | |
199 | + self.paginate(options) | |
170 | 200 | else |
171 | 201 | with_scope :find => {:conditions => extra_conditions} do |
172 | - self.find(:all, options) | |
202 | + self.paginate(options) | |
173 | 203 | end |
174 | 204 | end |
175 | 205 | end |
176 | 206 | |
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 | 207 | # produces the HTML code that is to be displayed as this article's contents. |
184 | 208 | # |
185 | 209 | # The implementation in this class just provides the +body+ attribute as the |
... | ... | @@ -421,7 +445,7 @@ class Article < ActiveRecord::Base |
421 | 445 | end |
422 | 446 | |
423 | 447 | def comments_updated |
424 | - ferret_update | |
448 | + solr_save | |
425 | 449 | end |
426 | 450 | |
427 | 451 | def accept_category?(cat) |
... | ... | @@ -571,6 +595,114 @@ class Article < ActiveRecord::Base |
571 | 595 | _('Created at: ') |
572 | 596 | end |
573 | 597 | |
598 | + def activity | |
599 | + ActionTracker::Record.find_by_target_type_and_target_id 'Article', self.id | |
600 | + end | |
601 | + | |
602 | + def create_activity | |
603 | + if is_trackable? && !image? | |
604 | + save_action_for_verb 'create_article', [:name, :url, :lead, :first_image], Proc.new{}, :author | |
605 | + end | |
606 | + end | |
607 | + | |
608 | + def first_image | |
609 | + img = Hpricot(self.lead.to_s).search('img[@src]').first || Hpricot(self.body.to_s).search('img').first | |
610 | + img.nil? ? '' : img.attributes['src'] | |
611 | + end | |
612 | + | |
613 | + private | |
614 | + | |
615 | + # FIXME: workaround for development env. | |
616 | + # Subclasses aren't (re)loaded, and acts_as_solr | |
617 | + # depends on subclasses method to search | |
618 | + # see http://stackoverflow.com/questions/4138957/activerecordsubclassnotfound-error-when-using-sti-in-rails/4139245 | |
619 | + UploadedFile | |
620 | + TextArticle | |
621 | + TinyMceArticle | |
622 | + TextileArticle | |
623 | + Folder | |
624 | + EnterpriseHomepage | |
625 | + Gallery | |
626 | + Blog | |
627 | + Forum | |
628 | + Event | |
629 | + | |
630 | + def self.f_type_proc(klass) | |
631 | + klass.constantize.type_name | |
632 | + end | |
633 | + | |
634 | + def self.f_profile_type_proc(klass) | |
635 | + klass.constantize.type_name | |
636 | + end | |
637 | + | |
638 | + def f_type | |
639 | + #join common types | |
640 | + case self.class.name | |
641 | + when 'TinyMceArticle', 'TextileArticle' | |
642 | + TextArticle.name | |
643 | + else | |
644 | + self.class.name | |
645 | + end | |
646 | + end | |
647 | + | |
648 | + def f_profile_type | |
649 | + self.profile.class.name | |
650 | + end | |
651 | + | |
652 | + def f_published_at | |
653 | + self.published_at | |
654 | + end | |
655 | + | |
656 | + def f_category | |
657 | + self.categories.collect(&:name) | |
658 | + end | |
659 | + | |
660 | + delegate :region, :region_id, :environment, :environment_id, :to => :profile | |
661 | + def name_sortable # give a different name for solr | |
662 | + name | |
663 | + end | |
664 | + | |
665 | + def public | |
666 | + self.public? | |
667 | + end | |
668 | + | |
669 | + def category_filter | |
670 | + categories_including_virtual_ids | |
671 | + end | |
672 | + | |
673 | + public | |
674 | + | |
675 | + acts_as_faceted :fields => { | |
676 | + :f_type => {:label => _('Type'), :proc => proc{|klass| f_type_proc(klass)}}, | |
677 | + :f_published_at => {:type => :date, :label => _('Published date'), :queries => {'[* TO NOW-1YEARS/DAY]' => _("Older than one year"), | |
678 | + '[NOW-1YEARS TO NOW/DAY]' => _("In the last year"), '[NOW-1MONTHS TO NOW/DAY]' => _("In the last month"), '[NOW-7DAYS TO NOW/DAY]' => _("In the last week"), '[NOW-1DAYS TO NOW/DAY]' => _("In the last day")}, | |
679 | + :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]']}, | |
680 | + :f_profile_type => {:label => _('Profile'), :proc => proc{|klass| f_profile_type_proc(klass)}}, | |
681 | + :f_category => {:label => _('Categories')}, | |
682 | + }, :category_query => proc { |c| "category_filter:\"#{c.id}\"" }, | |
683 | + :order => [:f_type, :f_published_at, :f_profile_type, :f_category] | |
684 | + | |
685 | + acts_as_searchable :fields => facets_fields_for_solr + [ | |
686 | + # searched fields | |
687 | + {:name => {:type => :text, :boost => 2.0}}, | |
688 | + {:slug => :text}, {:body => :text}, | |
689 | + {:abstract => :text}, {:filename => :text}, | |
690 | + # filtered fields | |
691 | + {:public => :boolean}, {:environment_id => :integer}, | |
692 | + {:profile_id => :integer}, :language, | |
693 | + {:category_filter => :integer}, | |
694 | + # ordered/query-boosted fields | |
695 | + {:name_sortable => :string}, :last_changed_by_id, :published_at, :is_image, | |
696 | + :updated_at, :created_at, | |
697 | + ], :include => [ | |
698 | + {:profile => {:fields => [:name, :identifier, :address, :nickname, :region_id, :lat, :lng]}}, | |
699 | + {:comments => {:fields => [:title, :body, :author_name, :author_email]}}, | |
700 | + {:categories => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}}, | |
701 | + ], :facets => facets_option_for_solr, | |
702 | + :boost => proc { |a| 10 if a.profile && a.profile.enabled }, | |
703 | + :if => proc{ |a| ! ['RssFeed'].include?(a.class.name) } | |
704 | + handle_asynchronously :solr_save | |
705 | + | |
574 | 706 | private |
575 | 707 | |
576 | 708 | def sanitize_tag_list | ... | ... |
app/models/blog.rb
app/models/category.rb
... | ... | @@ -15,13 +15,14 @@ class Category < ActiveRecord::Base |
15 | 15 | |
16 | 16 | acts_as_filesystem |
17 | 17 | |
18 | - has_many :article_categorizations | |
18 | + has_many :article_categorizations, :dependent => :destroy | |
19 | 19 | has_many :articles, :through => :article_categorizations |
20 | 20 | has_many :comments, :through => :articles |
21 | 21 | |
22 | 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 | 26 | has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise' |
26 | 27 | has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' |
27 | 28 | has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community' |
... | ... | @@ -36,18 +37,38 @@ class Category < ActiveRecord::Base |
36 | 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 | 56 | def recent_articles(limit = 10) |
40 | 57 | self.articles.recent(limit) |
41 | 58 | end |
42 | 59 | |
43 | 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 | 62 | end |
46 | 63 | |
47 | 64 | def most_commented_articles(limit = 10) |
48 | 65 | self.articles.most_commented(limit) |
49 | 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 | 72 | def display_in_menu? |
52 | 73 | display_in_menu |
53 | 74 | end |
... | ... | @@ -69,4 +90,23 @@ class Category < ActiveRecord::Base |
69 | 90 | self.children.find(:all, :conditions => {:display_in_menu => true}).empty? |
70 | 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 | 112 | end | ... | ... |
app/models/category_finder.rb
... | ... | @@ -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 | 2 | |
3 | 3 | belongs_to :environment |
4 | 4 | |
5 | - has_many :qualifier_certifiers | |
5 | + has_many :qualifier_certifiers, :dependent => :destroy | |
6 | 6 | has_many :qualifiers, :through => :qualifier_certifiers |
7 | 7 | |
8 | + has_many :product_qualifiers | |
9 | + has_many :products, :through => :product_qualifiers, :source => :product | |
10 | + | |
8 | 11 | validates_presence_of :environment_id |
9 | 12 | validates_presence_of :name |
10 | 13 | |
14 | + def destroy | |
15 | + product_qualifiers.each { |pq| pq.update_attributes! :certifier => nil } | |
16 | + super | |
17 | + end | |
18 | + | |
11 | 19 | def link |
12 | 20 | self[:link] || '' |
13 | 21 | end |
... | ... | @@ -16,4 +24,6 @@ class Certifier < ActiveRecord::Base |
16 | 24 | self.name.downcase.transliterate <=> b.name.downcase.transliterate |
17 | 25 | end |
18 | 26 | |
27 | + after_save_reindex [:products], :with => :delayed_job | |
28 | + | |
19 | 29 | end | ... | ... |
app/models/comment.rb
1 | 1 | class Comment < ActiveRecord::Base |
2 | 2 | |
3 | - track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"], :custom_target => :action_tracker_target | |
4 | - | |
5 | 3 | validates_presence_of :body |
6 | - belongs_to :article, :counter_cache => true | |
4 | + | |
5 | + belongs_to :source, :counter_cache => true, :polymorphic => true | |
6 | + alias :article :source | |
7 | + alias :article= :source= | |
8 | + | |
7 | 9 | belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' |
8 | 10 | has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy |
9 | 11 | belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' |
... | ... | @@ -70,13 +72,25 @@ class Comment < ActiveRecord::Base |
70 | 72 | after_save :notify_article |
71 | 73 | after_destroy :notify_article |
72 | 74 | def notify_article |
73 | - article.comments_updated | |
75 | + article.comments_updated if article.kind_of?(Article) | |
74 | 76 | end |
75 | 77 | |
76 | 78 | after_create do |comment| |
77 | - if comment.article.notify_comments? && !comment.article.profile.notification_emails.empty? | |
79 | + if comment.source.kind_of?(Article) && comment.article.notify_comments? && !comment.article.profile.notification_emails.empty? | |
78 | 80 | Comment::Notifier.deliver_mail(comment) |
79 | 81 | end |
82 | + | |
83 | + if comment.source.kind_of?(Article) | |
84 | + comment.article.create_activity if comment.article.activity.nil? | |
85 | + if comment.article.activity | |
86 | + comment.article.activity.increment!(:comments_count) | |
87 | + comment.article.activity.update_attribute(:visible, true) | |
88 | + end | |
89 | + end | |
90 | + end | |
91 | + | |
92 | + after_destroy do |comment| | |
93 | + comment.article.activity.decrement!(:comments_count) if comment.source.kind_of?(Article) && comment.article.activity | |
80 | 94 | end |
81 | 95 | |
82 | 96 | def replies | ... | ... |
app/models/communities_block.rb
... | ... | @@ -21,7 +21,7 @@ class CommunitiesBlock < ProfileListBlock |
21 | 21 | end |
22 | 22 | when Environment |
23 | 23 | lambda do |
24 | - link_to s_('communities|View all'), :controller => 'browse', :action => 'communities' | |
24 | + link_to s_('communities|View all'), :controller => 'search', :action => 'communities' | |
25 | 25 | end |
26 | 26 | else |
27 | 27 | '' | ... | ... |
app/models/community.rb
1 | 1 | class Community < Organization |
2 | + | |
3 | + def self.type_name | |
4 | + _('Community') | |
5 | + end | |
6 | + | |
2 | 7 | N_('Community') |
3 | 8 | N_('Language') |
4 | 9 | |
5 | 10 | settings_items :language |
6 | 11 | settings_items :zip_code, :city, :state, :country |
7 | 12 | |
13 | + extend SetProfileRegionFromCityState::ClassMethods | |
14 | + set_profile_region_from_city_state | |
15 | + | |
8 | 16 | before_create do |community| |
9 | 17 | community.moderated_articles = true if community.environment.enabled?('organizations_are_moderated_by_default') |
10 | 18 | end |
... | ... | @@ -79,4 +87,8 @@ class Community < Organization |
79 | 87 | {:title => __('Community Info and settings'), :icon => 'edit-profile-group'} |
80 | 88 | end |
81 | 89 | |
90 | + def activities | |
91 | + Scrap.find_by_sql("SELECT id, updated_at, '#{Scrap.to_s}' AS klass FROM #{Scrap.table_name} WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} WHERE action_tracker.target_id = #{self.id} UNION SELECT at.id, at.updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} at INNER JOIN articles a ON at.target_id = a.id WHERE a.profile_id = #{self.id} AND at.target_type = 'Article' ORDER BY updated_at DESC") | |
92 | + end | |
93 | + | |
82 | 94 | end | ... | ... |
app/models/domain.rb
... | ... | @@ -8,9 +8,9 @@ class Domain < ActiveRecord::Base |
8 | 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 | 15 | # checks validations that could not be expressed using Rails' predefined |
16 | 16 | # validations. In particular: | ... | ... |
app/models/enterprise.rb
... | ... | @@ -2,6 +2,10 @@ |
2 | 2 | # only enterprises can offer products and services. |
3 | 3 | class Enterprise < Organization |
4 | 4 | |
5 | + def self.type_name | |
6 | + _('Enterprise') | |
7 | + end | |
8 | + | |
5 | 9 | N_('Enterprise') |
6 | 10 | |
7 | 11 | has_many :products, :dependent => :destroy, :order => 'name ASC' |
... | ... | @@ -10,12 +14,19 @@ class Enterprise < Organization |
10 | 14 | |
11 | 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 | 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 | 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 | 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 | 30 | before_save do |enterprise| |
20 | 31 | enterprise.organization_website = enterprise.maybe_add_http(enterprise.organization_website) |
21 | 32 | end |
... | ... | @@ -67,22 +78,6 @@ class Enterprise < Organization |
67 | 78 | environment ? environment.signup_enterprise_fields : [] |
68 | 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 | 81 | def closed? |
87 | 82 | true |
88 | 83 | end |
... | ... | @@ -167,6 +162,10 @@ class Enterprise < Organization |
167 | 162 | end |
168 | 163 | end |
169 | 164 | |
165 | + def control_panel_settings_button | |
166 | + {:title => __('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'} | |
167 | + end | |
168 | + | |
170 | 169 | settings_items :enable_contact_us, :type => :boolean, :default => true |
171 | 170 | |
172 | 171 | def enable_contact? |
... | ... | @@ -181,4 +180,8 @@ class Enterprise < Organization |
181 | 180 | true |
182 | 181 | end |
183 | 182 | |
183 | + def activities | |
184 | + Scrap.find_by_sql("SELECT id, updated_at, 'Scrap' AS klass FROM scraps WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, 'ActionTracker::Record' AS klass FROM action_tracker WHERE action_tracker.target_id = #{self.id} UNION SELECT action_tracker.id, action_tracker.updated_at, 'ActionTracker::Record' AS klass FROM action_tracker INNER JOIN articles ON action_tracker.target_id = articles.id WHERE articles.profile_id = #{self.id} AND action_tracker.target_type = 'Article' ORDER BY action_tracker.updated_at DESC") | |
185 | + end | |
186 | + | |
184 | 187 | end | ... | ... |
app/models/enterprise_homepage.rb
app/models/environment.rb
... | ... | @@ -248,6 +248,10 @@ class Environment < ActiveRecord::Base |
248 | 248 | |
249 | 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 | 255 | def news_amount_by_folder=(amount) |
252 | 256 | settings[:news_amount_by_folder] = amount.to_i |
253 | 257 | end |
... | ... | @@ -277,6 +281,9 @@ class Environment < ActiveRecord::Base |
277 | 281 | def enabled?(feature) |
278 | 282 | self.settings["#{feature}_enabled".to_sym] == true |
279 | 283 | end |
284 | + def disabled?(feature) | |
285 | + !self.enabled?(feature) | |
286 | + end | |
280 | 287 | |
281 | 288 | def plugin_enabled?(plugin) |
282 | 289 | enabled_plugins.include?(plugin.to_s) | ... | ... |
app/models/environment_finder.rb
... | ... | @@ -1,107 +0,0 @@ |
1 | -class EnvironmentFinder | |
2 | - | |
3 | - def initialize env | |
4 | - @environment = env | |
5 | - end | |
6 | - | |
7 | - def find(asset, query = nil, options={}, finder_method = 'paginate') | |
8 | - @region = Region.find_by_id(options.delete(:region)) if options.has_key?(:region) | |
9 | - if @region && options[:within] | |
10 | - options[:origin] = [@region.lat, @region.lng] | |
11 | - else | |
12 | - options.delete(:within) | |
13 | - end | |
14 | - | |
15 | - product_category = options.delete(:product_category) | |
16 | - | |
17 | - date_range = options.delete(:date_range) | |
18 | - | |
19 | - # FIXME this test is in more than one place | |
20 | - if finder_method == 'paginate' | |
21 | - options = {:page => 1, :per_page => options.delete(:limit)}.merge(options) | |
22 | - end | |
23 | - | |
24 | - if query.blank? | |
25 | - # FIXME this test is in more than one place | |
26 | - if finder_method == 'paginate' | |
27 | - options = {:order => "#{asset_table(asset)}.name"}.merge(options) | |
28 | - end | |
29 | - if product_category && asset == :products | |
30 | - @environment.send(asset).send(finder_method, :all, options.merge(:include => 'product_categorizations', :conditions => ['product_categorizations.category_id = (?)', product_category.id])) | |
31 | - elsif product_category && asset == :enterprises | |
32 | - @environment.send(asset).send(finder_method, :all, options.merge( :order => 'profiles.name', :joins => 'inner join products on (products.enterprise_id = profiles.id) inner join product_categorizations on (product_categorizations.product_id = products.id)', :conditions => ['product_categorizations.category_id = (?)', product_category.id])) | |
33 | - else | |
34 | - if asset == :events | |
35 | - # Ignore pagination for asset events | |
36 | - options.delete(:per_page) | |
37 | - options.delete(:page) | |
38 | - if date_range | |
39 | - @environment.send(asset).send('find', :all, options.merge(:conditions => [ | |
40 | - 'start_date BETWEEN :start_day AND :end_day OR end_date BETWEEN :start_day AND :end_day', | |
41 | - {:start_day => date_range.first, :end_day => date_range.last} | |
42 | - ])) | |
43 | - else | |
44 | - @environment.send(asset).send('find', :all, options) | |
45 | - end | |
46 | - else | |
47 | - @environment.send(asset).send(finder_method, :all, options) | |
48 | - end | |
49 | - end | |
50 | - else | |
51 | - ferret_options = {:page => options.delete(:page), :per_page => options.delete(:per_page)} | |
52 | - if product_category && asset == :products | |
53 | - # SECURITY no risk of SQL injection, since product_category_ids comes from trusted source | |
54 | - @environment.send(asset).find_by_contents(query, ferret_options, options.merge({:include => 'product_categorizations', :conditions => 'product_categorizations.category_id = (%s)' % product_category.id })) | |
55 | - elsif product_category && asset == :enterprises | |
56 | - @environment.send(asset).find_by_contents(query, ferret_options, options.merge(:joins => 'inner join products on products.enterprise_id = profiles.id inner join product_categorizations on (product_categorizations.product_id = products.id)', :conditions => "product_categorizations.category_id = (#{product_category.id})")) | |
57 | - else | |
58 | - @environment.send(asset).find_by_contents(query, ferret_options, options) | |
59 | - end | |
60 | - end | |
61 | - end | |
62 | - | |
63 | - def recent(asset, limit = nil) | |
64 | - find(asset, nil, :limit => limit) | |
65 | - end | |
66 | - | |
67 | - def product_categories_count(asset, product_categories_ids, objects_ids=nil) | |
68 | - conditions = ['product_categorizations.category_id in (?)', product_categories_ids] | |
69 | - | |
70 | - if asset == :products | |
71 | - if objects_ids | |
72 | - conditions[0] += ' and product_categorizations.product_id in (?)' | |
73 | - conditions << objects_ids | |
74 | - end | |
75 | - ProductCategory.find(:all, :select => 'categories.id, count(*) as total', :joins => 'inner join product_categorizations on (product_categorizations.category_id = categories.id)', :group => 'categories.id', :conditions => conditions ) | |
76 | - elsif asset == :enterprises | |
77 | - if objects_ids | |
78 | - conditions[0] += ' and products.enterprise_id in (?)' | |
79 | - conditions << objects_ids | |
80 | - end | |
81 | - ProductCategory.find( | |
82 | - :all, | |
83 | - :select => 'categories.id, count(distinct products.enterprise_id) as total', | |
84 | - :joins => 'inner join product_categorizations on (product_categorizations.category_id = categories.id) inner join products on (products.id = product_categorizations.product_id)', | |
85 | - :group => 'categories.id', | |
86 | - :conditions => conditions | |
87 | - ) | |
88 | - else | |
89 | - raise ArgumentError, 'only products and enterprises supported' | |
90 | - end.inject({}) do |results,pc| | |
91 | - results[pc.id]= pc.total.to_i | |
92 | - results | |
93 | - end | |
94 | - | |
95 | - end | |
96 | - | |
97 | - protected | |
98 | - | |
99 | - def asset_class(asset) | |
100 | - asset.to_s.singularize.camelize.constantize | |
101 | - end | |
102 | - | |
103 | - def asset_table(asset) | |
104 | - asset_class(asset).table_name | |
105 | - end | |
106 | - | |
107 | -end |
app/models/event.rb
1 | 1 | class Event < Article |
2 | 2 | |
3 | + def self.type_name | |
4 | + _('Event') | |
5 | + end | |
6 | + | |
3 | 7 | settings_items :address, :type => :string |
4 | 8 | |
5 | 9 | def link=(value) |
... | ... | @@ -120,6 +124,10 @@ class Event < Article |
120 | 124 | true |
121 | 125 | end |
122 | 126 | |
127 | + def notifiable? | |
128 | + true | |
129 | + end | |
130 | + | |
123 | 131 | include Noosfero::TranslatableContent |
124 | 132 | include MaybeAddHttp |
125 | 133 | ... | ... |
app/models/external_feed.rb
... | ... | @@ -14,7 +14,9 @@ class ExternalFeed < ActiveRecord::Base |
14 | 14 | article = TinyMceArticle.new(:name => title, :profile => blog.profile, :body => content, :published_at => date, :source => link, :profile => blog.profile, :parent => blog) |
15 | 15 | unless blog.children.exists?(:slug => article.slug) |
16 | 16 | article.save! |
17 | + article.delay.create_activity | |
17 | 18 | end |
19 | + article.valid? | |
18 | 20 | end |
19 | 21 | |
20 | 22 | def clear | ... | ... |
app/models/folder.rb
app/models/forum.rb
... | ... | @@ -2,6 +2,10 @@ class Forum < Folder |
2 | 2 | |
3 | 3 | acts_as_having_posts :order => 'updated_at DESC' |
4 | 4 | |
5 | + def self.type_name | |
6 | + _('Forum') | |
7 | + end | |
8 | + | |
5 | 9 | def self.short_description |
6 | 10 | _('Forum') |
7 | 11 | end |
... | ... | @@ -24,4 +28,14 @@ class Forum < Folder |
24 | 28 | def self.icon_name(article = nil) |
25 | 29 | 'forum' |
26 | 30 | end |
31 | + | |
32 | + def notifiable? | |
33 | + true | |
34 | + end | |
35 | + | |
36 | + def first_paragraph | |
37 | + return '' if body.blank? | |
38 | + paragraphs = Hpricot(body).search('p') | |
39 | + paragraphs.empty? ? '' : paragraphs.first.to_html | |
40 | + end | |
27 | 41 | end | ... | ... |
app/models/gallery.rb
app/models/google_maps.rb
1 | 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 | 5 | end |
6 | + | |
28 | 7 | end | ... | ... |
app/models/image.rb
... | ... | @@ -13,7 +13,7 @@ class Image < ActiveRecord::Base |
13 | 13 | :thumbnails => { :big => '150x150', |
14 | 14 | :thumb => '100x100', |
15 | 15 | :portrait => '64x64', |
16 | - :minor => '50x50', | |
16 | + :minor => '50x50>', | |
17 | 17 | :icon => '20x20!' }, |
18 | 18 | :max_size => 5.megabytes # remember to update validate message below |
19 | 19 | ... | ... |
app/models/input.rb
app/models/location_block.rb
... | ... | @@ -17,10 +17,10 @@ class LocationBlock < Block |
17 | 17 | if profile.lat |
18 | 18 | block_title(title) + |
19 | 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 | 21 | ',' + profile.lng.to_s() + '&zoom=' + zoom.to_s() + |
22 | 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 | 24 | :class => 'the-localization-map' ) |
25 | 25 | else |
26 | 26 | content_tag('i', _('This profile has no geographical position registered.')) | ... | ... |
... | ... | @@ -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
app/models/person.rb
1 | 1 | # A person is the profile of an user holding all relationships with the rest of the system |
2 | 2 | class Person < Profile |
3 | 3 | |
4 | + def self.type_name | |
5 | + _('Person') | |
6 | + end | |
7 | + | |
4 | 8 | acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} |
5 | 9 | acts_as_accessor |
6 | 10 | |
... | ... | @@ -187,6 +191,9 @@ class Person < Profile |
187 | 191 | N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code') |
188 | 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 | 197 | def self.conditions_for_profiles(conditions, person) |
191 | 198 | new_conditions = sanitize_sql(['role_assignments.accessor_id = ?', person]) |
192 | 199 | new_conditions << ' AND ' + sanitize_sql(conditions) unless conditions.blank? |
... | ... | @@ -435,6 +442,10 @@ class Person < Profile |
435 | 442 | user.save! |
436 | 443 | end |
437 | 444 | |
445 | + def activities | |
446 | + Scrap.find_by_sql("SELECT id, updated_at, '#{Scrap.to_s}' AS klass FROM #{Scrap.table_name} WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} WHERE action_tracker.user_id = #{self.id} ORDER BY updated_at DESC") | |
447 | + end | |
448 | + | |
438 | 449 | protected |
439 | 450 | |
440 | 451 | def followed_by?(profile) | ... | ... |
app/models/product.rb
1 | 1 | class Product < ActiveRecord::Base |
2 | 2 | belongs_to :enterprise |
3 | + has_one :region, :through => :enterprise | |
4 | + validates_presence_of :enterprise | |
5 | + | |
3 | 6 | belongs_to :product_category |
4 | - has_many :product_categorizations | |
5 | - has_many :product_qualifiers | |
6 | - has_many :qualifiers, :through => :product_qualifiers | |
7 | + | |
7 | 8 | has_many :inputs, :dependent => :destroy, :order => 'position' |
8 | 9 | has_many :price_details, :dependent => :destroy |
9 | 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 | 16 | validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true |
12 | 17 | validates_presence_of :product_category_id |
13 | 18 | validates_associated :product_category |
... | ... | @@ -15,35 +20,20 @@ class Product < ActiveRecord::Base |
15 | 20 | validates_numericality_of :price, :allow_nil => true |
16 | 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 | 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 | 32 | end |
39 | 33 | |
40 | - acts_as_searchable :fields => [ :name, :description, :category_full_name ] | |
41 | - | |
42 | 34 | xss_terminate :only => [ :name ], :on => 'validation' |
43 | 35 | xss_terminate :only => [ :description ], :with => 'white_list', :on => 'validation' |
44 | 36 | |
45 | - acts_as_mappable | |
46 | - | |
47 | 37 | belongs_to :unit |
48 | 38 | |
49 | 39 | include FloatHelper |
... | ... | @@ -89,18 +79,12 @@ class Product < ActiveRecord::Base |
89 | 79 | self.find(:all, :order => 'id desc', :limit => limit) |
90 | 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 | 82 | def url |
99 | 83 | enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => id) |
100 | 84 | end |
101 | 85 | |
102 | 86 | def public? |
103 | - enterprise.public_profile | |
87 | + enterprise.public? | |
104 | 88 | end |
105 | 89 | |
106 | 90 | def formatted_value(method) |
... | ... | @@ -128,7 +112,6 @@ class Product < ActiveRecord::Base |
128 | 112 | end |
129 | 113 | end |
130 | 114 | |
131 | - # Note: will probably be completely overhauled for AI1413 | |
132 | 115 | def inputs_prices? |
133 | 116 | return false if self.inputs.count <= 0 |
134 | 117 | self.inputs.each do |input| |
... | ... | @@ -155,7 +138,7 @@ class Product < ActiveRecord::Base |
155 | 138 | def qualifiers_list=(qualifiers) |
156 | 139 | self.product_qualifiers.destroy_all |
157 | 140 | qualifiers.each do |qualifier_id, certifier_id| |
158 | - self.product_qualifiers.create(:qualifier_id => qualifier_id, :certifier_id => certifier_id) | |
141 | + self.product_qualifiers.create(:qualifier_id => qualifier_id, :certifier_id => certifier_id) if qualifier_id != 'nil' | |
159 | 142 | end |
160 | 143 | end |
161 | 144 | |
... | ... | @@ -213,4 +196,102 @@ class Product < ActiveRecord::Base |
213 | 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 | 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, _("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 | 297 | end | ... | ... |
app/models/product_categorization.rb
... | ... | @@ -1,13 +0,0 @@ |
1 | -class ProductCategorization < ActiveRecord::Base | |
2 | - belongs_to :product_category, :foreign_key => 'category_id' | |
3 | - belongs_to :product | |
4 | - | |
5 | - extend Categorization | |
6 | - | |
7 | - class << self | |
8 | - alias :add_category_to_product :add_category_to_object | |
9 | - def object_id_column | |
10 | - :product_id | |
11 | - end | |
12 | - end | |
13 | -end |
app/models/product_category.rb
1 | 1 | class ProductCategory < Category |
2 | + # FIXME: do not allow category with products or inputs to be destroyed | |
2 | 3 | has_many :products |
3 | 4 | has_many :inputs |
4 | 5 | |
... | ... | @@ -9,4 +10,7 @@ class ProductCategory < Category |
9 | 10 | def self.menu_categories(top_category, env) |
10 | 11 | top_category ? top_category.children : top_level_for(env).select{|c|c.kind_of?(ProductCategory)} |
11 | 12 | end |
13 | + | |
14 | + after_save_reindex [:products], :with => :delayed_job | |
15 | + | |
12 | 16 | end | ... | ... |
app/models/profile.rb
... | ... | @@ -3,6 +3,12 @@ |
3 | 3 | # which by default is the one returned by Environment:default. |
4 | 4 | class Profile < ActiveRecord::Base |
5 | 5 | |
6 | + # use for internationalizable human type names in search facets | |
7 | + # reimplement on subclasses | |
8 | + def self.type_name | |
9 | + _('Profile') | |
10 | + end | |
11 | + | |
6 | 12 | module Roles |
7 | 13 | def self.admin(env_id) |
8 | 14 | find_role('admin', env_id) |
... | ... | @@ -74,8 +80,6 @@ class Profile < ActiveRecord::Base |
74 | 80 | |
75 | 81 | acts_as_having_boxes |
76 | 82 | |
77 | - acts_as_searchable :additional_fields => [ :extra_data_for_index ] | |
78 | - | |
79 | 83 | acts_as_taggable |
80 | 84 | |
81 | 85 | def self.qualified_column_names |
... | ... | @@ -125,8 +129,6 @@ class Profile < ActiveRecord::Base |
125 | 129 | |
126 | 130 | validates_length_of :description, :maximum => 550, :allow_nil => true |
127 | 131 | |
128 | - acts_as_mappable :default_units => :kms | |
129 | - | |
130 | 132 | # Valid identifiers must match this format. |
131 | 133 | IDENTIFIER_FORMAT = /^#{Noosfero.identifier_format}$/ |
132 | 134 | |
... | ... | @@ -183,8 +185,20 @@ class Profile < ActiveRecord::Base |
183 | 185 | has_many :profile_categorizations, :conditions => [ 'categories_profiles.virtual = ?', false ] |
184 | 186 | has_many :categories, :through => :profile_categorizations |
185 | 187 | |
188 | + has_many :profile_categorizations_including_virtual, :class_name => 'ProfileCategorization' | |
189 | + has_many :categories_including_virtual, :through => :profile_categorizations_including_virtual, :source => :category | |
190 | + | |
186 | 191 | has_many :abuse_complaints, :foreign_key => 'requestor_id' |
187 | 192 | |
193 | + def top_level_categorization | |
194 | + ret = {} | |
195 | + self.profile_categorizations.each do |c| | |
196 | + p = c.category.top_ancestor | |
197 | + ret[p] = (ret[p] || []) + [c.category] | |
198 | + end | |
199 | + ret | |
200 | + end | |
201 | + | |
188 | 202 | def interests |
189 | 203 | categories.select {|item| !item.is_a?(Region)} |
190 | 204 | end |
... | ... | @@ -219,12 +233,15 @@ class Profile < ActiveRecord::Base |
219 | 233 | @pending_categorizations ||= [] |
220 | 234 | end |
221 | 235 | |
222 | - def add_category(c) | |
223 | - if self.id | |
224 | - ProfileCategorization.add_category_to_profile(c, self) | |
225 | - else | |
236 | + def add_category(c, reload=false) | |
237 | + if new_record? | |
226 | 238 | pending_categorizations << c |
239 | + else | |
240 | + ProfileCategorization.add_category_to_profile(c, self) | |
241 | + self.categories(true) | |
242 | + self.solr_save | |
227 | 243 | end |
244 | + self.categories(reload) | |
228 | 245 | end |
229 | 246 | |
230 | 247 | def category_ids=(ids) |
... | ... | @@ -529,6 +546,7 @@ private :generate_url, :url_options |
529 | 546 | other.top_level_articles.each do |a| |
530 | 547 | copy_article_tree a |
531 | 548 | end |
549 | + self.articles.reload | |
532 | 550 | end |
533 | 551 | |
534 | 552 | def copy_article_tree(article, parent=nil) |
... | ... | @@ -824,18 +842,105 @@ private :generate_url, :url_options |
824 | 842 | name |
825 | 843 | end |
826 | 844 | |
827 | - protected | |
845 | + # Override in your subclasses | |
846 | + def activities | |
847 | + [] | |
848 | + end | |
849 | + | |
850 | + private | |
851 | + def self.f_categories_label_proc(environment) | |
852 | + ids = environment.top_level_category_as_facet_ids | |
853 | + r = Category.find(ids) | |
854 | + map = {} | |
855 | + ids.map{ |id| map[id.to_s] = r.detect{|c| c.id == id}.name } | |
856 | + map | |
857 | + end | |
858 | + def self.f_categories_proc(facet, id) | |
859 | + id = id.to_i | |
860 | + return if id.zero? | |
861 | + c = Category.find(id) | |
862 | + c.name if c.top_ancestor.id == facet[:label_id].to_i or facet[:label_id] == 0 | |
863 | + end | |
864 | + def f_categories | |
865 | + category_ids - [region_id] | |
866 | + end | |
828 | 867 | |
829 | - def followed_by?(person) | |
830 | - person.is_member_of?(self) | |
868 | + def f_region | |
869 | + self.region_id | |
870 | + end | |
871 | + def self.f_region_proc(id) | |
872 | + c = Region.find(id) | |
873 | + s = c.parent | |
874 | + if c and c.kind_of?(City) and s and s.kind_of?(State) and s.acronym | |
875 | + [c.name, ', ' + s.acronym] | |
876 | + else | |
877 | + c.name | |
831 | 878 | end |
879 | + end | |
832 | 880 | |
833 | - def display_private_info_to?(user) | |
834 | - if user.nil? | |
835 | - false | |
836 | - else | |
837 | - (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self) | |
838 | - end | |
881 | + def self.f_enabled_proc(enabled) | |
882 | + enabled = enabled == "true" ? true : false | |
883 | + enabled ? _('Enabled') : _('Not enabled') | |
884 | + end | |
885 | + def f_enabled | |
886 | + self.enabled | |
887 | + end | |
888 | + | |
889 | + def name_sortable # give a different name for solr | |
890 | + name | |
891 | + end | |
892 | + def public | |
893 | + self.public? | |
894 | + end | |
895 | + def category_filter | |
896 | + categories_including_virtual_ids | |
897 | + end | |
898 | + public | |
899 | + | |
900 | + acts_as_faceted :fields => { | |
901 | + :f_enabled => {:label => _('Situation'), :type_if => proc { |klass| klass.kind_of?(Enterprise) }, | |
902 | + :proc => proc { |id| f_enabled_proc(id) }}, | |
903 | + :f_region => {:label => _('City'), :proc => proc { |id| f_region_proc(id) }}, | |
904 | + :f_categories => {:multi => true, :proc => proc {|facet, id| f_categories_proc(facet, id)}, | |
905 | + :label => proc { |env| f_categories_label_proc(env) }, :label_abbrev => proc{ |env| f_categories_label_abbrev_proc(env) }}, | |
906 | + }, :category_query => proc { |c| "category_filter:#{c.id}" }, | |
907 | + :order => [:f_region, :f_categories, :f_enabled] | |
908 | + | |
909 | + acts_as_searchable :fields => facets_fields_for_solr + [:extra_data_for_index, | |
910 | + # searched fields | |
911 | + {:name => {:type => :text, :boost => 2.0}}, | |
912 | + {:identifier => :text}, {:address => :text}, {:nickname => :text}, | |
913 | + # filtered fields | |
914 | + {:public => :boolean}, {:environment_id => :integer}, | |
915 | + {:category_filter => :integer}, | |
916 | + # ordered/query-boosted fields | |
917 | + {:name_sortable => :string}, {:user_id => :integer}, | |
918 | + :enabled, :active, :validated, :public_profile, | |
919 | + {:lat => :float}, {:lng => :float}, | |
920 | + :updated_at, :created_at, | |
921 | + ], | |
922 | + :include => [ | |
923 | + {:region => {:fields => [:name, :path, :slug, :lat, :lng]}}, | |
924 | + {:categories => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}}, | |
925 | + ], :facets => facets_option_for_solr, | |
926 | + :boost => proc{ |p| 10 if p.enabled } | |
927 | + after_save_reindex [:articles], :with => :delayed_job | |
928 | + handle_asynchronously :solr_save | |
929 | + | |
930 | + def control_panel_settings_button | |
931 | + {:title => _('Profile Info and settings'), :icon => 'edit-profile'} | |
932 | + end | |
933 | + | |
934 | + def followed_by?(person) | |
935 | + person.is_member_of?(self) | |
936 | + end | |
937 | + | |
938 | + def display_private_info_to?(user) | |
939 | + if user.nil? | |
940 | + false | |
941 | + else | |
942 | + (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self) | |
839 | 943 | end |
944 | + end | |
840 | 945 | |
841 | 946 | end | ... | ... |
app/models/qualifier.rb
... | ... | @@ -2,16 +2,19 @@ class Qualifier < ActiveRecord::Base |
2 | 2 | |
3 | 3 | belongs_to :environment |
4 | 4 | |
5 | - has_many :qualifier_certifiers | |
5 | + has_many :qualifier_certifiers, :dependent => :destroy | |
6 | 6 | has_many :certifiers, :through => :qualifier_certifiers |
7 | 7 | |
8 | + has_many :product_qualifiers, :dependent => :destroy | |
9 | + has_many :products, :through => :product_qualifiers, :source => :product | |
10 | + | |
8 | 11 | validates_presence_of :environment_id |
9 | 12 | validates_presence_of :name |
10 | 13 | |
11 | - has_many :product_qualifiers, :dependent => :destroy | |
12 | - | |
13 | 14 | def <=>(b) |
14 | 15 | self.name.downcase.transliterate <=> b.name.downcase.transliterate |
15 | 16 | end |
16 | 17 | |
18 | + after_save_reindex [:products], :with => :delayed_job | |
19 | + | |
17 | 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 | 5 | require_dependency 'enterprise' # enterprises can also be validators |
6 | 6 | |
7 | 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 | 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 | 11 | end |
13 | 12 | |
14 | 13 | def has_validator? |
15 | 14 | validators.count > 0 |
16 | 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 | 20 | end |
23 | 21 | ... | ... |
app/models/rss_feed.rb
app/models/scrap.rb
... | ... | @@ -12,6 +12,7 @@ class Scrap < ActiveRecord::Base |
12 | 12 | named_scope :not_replies, :conditions => {:scrap_id => nil} |
13 | 13 | |
14 | 14 | track_actions :leave_scrap, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.receiver != s.sender}, :custom_target => :action_tracker_target |
15 | + | |
15 | 16 | track_actions :leave_scrap_to_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.receiver == s.sender} |
16 | 17 | |
17 | 18 | after_create do |scrap| | ... | ... |
app/models/task.rb
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | # |
5 | 5 | # The specific types of tasks <em>must</em> override the #perform method, so |
6 | 6 | # the actual action associated to the type of task can be performed. See the |
7 | -# documentation of the #perform method for details. | |
7 | +# documentation of the #perform method for details. | |
8 | 8 | # |
9 | 9 | # This class has a +data+ field of type <tt>text</tt>, where you can store any |
10 | 10 | # type of data (as serialized Ruby objects) you need for your subclass (which |
... | ... | @@ -64,7 +64,9 @@ class Task < ActiveRecord::Base |
64 | 64 | |
65 | 65 | begin |
66 | 66 | target_msg = task.target_notification_message |
67 | - TaskMailer.deliver_target_notification(task, target_msg) if target_msg | |
67 | + if target_msg && task.target && !task.target.notification_emails.empty? | |
68 | + TaskMailer.deliver_target_notification(task, target_msg) | |
69 | + end | |
68 | 70 | rescue NotImplementedError => ex |
69 | 71 | RAILS_DEFAULT_LOGGER.info ex.to_s |
70 | 72 | end |
... | ... | @@ -192,7 +194,7 @@ class Task < ActiveRecord::Base |
192 | 194 | |
193 | 195 | # The message that will be sent to the *target* of the task when it is |
194 | 196 | # created. The indent of this message is to notify the target about the |
195 | - # request that was just created for him/her. | |
197 | + # request that was just created for him/her. | |
196 | 198 | # |
197 | 199 | # The implementation in this class returns +nil+, what makes the notification |
198 | 200 | # not to be sent. If you want to send a notification to the target upon task |
... | ... | @@ -225,7 +227,9 @@ class Task < ActiveRecord::Base |
225 | 227 | |
226 | 228 | begin |
227 | 229 | target_msg = target_notification_message |
228 | - TaskMailer.deliver_target_notification(self, target_msg) if target_msg | |
230 | + if target_msg && self.target && !self.target.notification_emails.empty? | |
231 | + TaskMailer.deliver_target_notification(self, target_msg) | |
232 | + end | |
229 | 233 | rescue NotImplementedError => ex |
230 | 234 | RAILS_DEFAULT_LOGGER.info ex.to_s |
231 | 235 | end |
... | ... | @@ -253,7 +257,7 @@ class Task < ActiveRecord::Base |
253 | 257 | |
254 | 258 | # sends notification e-mail about a task, if the task has a requestor. |
255 | 259 | # |
256 | - # If | |
260 | + # If | |
257 | 261 | def send_notification(action) |
258 | 262 | if sends_email? |
259 | 263 | if self.requestor | ... | ... |
app/models/text_article.rb
app/models/uploaded_file.rb
... | ... | @@ -4,7 +4,11 @@ |
4 | 4 | # of the file itself is kept. (FIXME?) |
5 | 5 | class UploadedFile < Article |
6 | 6 | |
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? } | |
7 | + def self.type_name | |
8 | + _('File') | |
9 | + end | |
10 | + | |
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? }, :custom_target => :action_tracker_target | |
8 | 12 | |
9 | 13 | include ShortFilename |
10 | 14 | |
... | ... | @@ -25,7 +29,7 @@ class UploadedFile < Article |
25 | 29 | end |
26 | 30 | |
27 | 31 | def thumbnail_path |
28 | - self.image? ? self.full_filename(:thumb).gsub(File.join(RAILS_ROOT, 'public'), '') : nil | |
32 | + self.image? ? self.full_filename(:display).gsub(File.join(RAILS_ROOT, 'public'), '') : nil | |
29 | 33 | end |
30 | 34 | |
31 | 35 | def display_title |
... | ... | @@ -140,4 +144,9 @@ class UploadedFile < Article |
140 | 144 | def uploaded_file? |
141 | 145 | true |
142 | 146 | end |
147 | + | |
148 | + def action_tracker_target | |
149 | + self | |
150 | + end | |
151 | + | |
143 | 152 | end | ... | ... |
app/views/account/signup.rhtml
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | <p><%= _("Firstly, some tips for getting started:") %></p> |
6 | 6 | <h4><%= _("Confirm your account!") %></h4> |
7 | 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 | 9 | <h4><%= _("What to do next?") %></h4> |
10 | 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 | 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 | -<li class="<%= 'browse-results-type-content ' + icon_for_article(result) %>"> | |
2 | - <strong><%= link_to(result.title, result.view_url) %></strong> | |
3 | - <div class="item_meta"> | |
4 | - <span class="item_by"> | |
5 | - <%= _('by %s') % link_to(result.author.name, result.author.url) %> | |
6 | - </span> | |
7 | - <span class="extra-info"> | |
8 | - <%= (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %> | |
9 | - </span> | |
10 | - </div> | |
11 | -</li> |
app/views/browse/_display_results.rhtml
... | ... | @@ -1,19 +0,0 @@ |
1 | -<div id="browse-results"> | |
2 | - | |
3 | - <div class='browse-results-innerbox'> | |
4 | - <% if @results.empty? %> | |
5 | - <div class="browse-results-type-empty"> | |
6 | - <div> <%= _('None') %> </div> | |
7 | - </div><!-- end class="browse-results-innerbox" --> | |
8 | - <% end %> | |
9 | - <ul class='common-profile-list-block'> | |
10 | - <% @results.each do |result| %> | |
11 | - <%= render :partial => partial_for_class(result.class), :locals => {:result => result} %> | |
12 | - <% end %> | |
13 | - </ul> | |
14 | - <br style='clear: both;'> | |
15 | - </div> | |
16 | - | |
17 | - <br style="clear:both" /> | |
18 | -</div><!-- end id="browse-results" --> | |
19 | - |
app/views/browse/_person.rhtml
app/views/browse/_profile.rhtml
... | ... | @@ -1 +0,0 @@ |
1 | -<%= profile_image_link result, :portrait, 'li', @filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label') %> |
app/views/browse/_search_form.rhtml
... | ... | @@ -1,10 +0,0 @@ |
1 | -<% form_tag( { :controller => 'browse', :action => action}, :method => 'get', :class => 'search_form' ) do %> | |
2 | - | |
3 | - <div class="search-field"> | |
4 | - <span class="formfield"> | |
5 | - <%= text_field_tag 'query', @query, :size => 50 %> | |
6 | - </span> | |
7 | - <%= submit_button(:search, _('Search')) %> | |
8 | - </div> | |
9 | - | |
10 | -<% end %> |
app/views/browse/communities.rhtml
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 | 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 | 7 | <% language = FastGettext.locale %> |
4 | 8 | <% %w{messages methods}.each do |type| %> | ... | ... |
app/views/layouts/application-ng.rhtml
... | ... | @@ -61,16 +61,10 @@ |
61 | 61 | <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> |
62 | 62 | </div> |
63 | 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 | 66 | <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div> |
67 | + <%= javascript_tag 'jQuery("#user form input").hint();' %> | |
74 | 68 | </form> |
75 | 69 | </div><!-- end id="user" --> |
76 | 70 | ... | ... |
app/views/manage_products/_certifiers_for_selection.rhtml
1 | -<%= select_certifiers(@qualifier) %> | |
2 | -<%= button_to_function(:delete, '', "jQuery(this).parents('tr').remove()") %> | |
1 | +<%= select_certifiers(@qualifier) + remove_qualifier_button %> | |
3 | 2 | <% javascript_tag do %> |
4 | 3 | jQuery('#product-qualifiers-list *').removeClass('small-loading') |
5 | 4 | <% end %> | ... | ... |
app/views/manage_products/_display_image.rhtml
1 | 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 | 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 | 11 | <%= edit_product_link_to_remote(@product, 'image', _('Change image')) %> | ... | ... |
app/views/manage_products/_edit_info.rhtml
... | ... | @@ -39,8 +39,7 @@ |
39 | 39 | <%= select_qualifiers(@product, qualifier.id) %> |
40 | 40 | </td> |
41 | 41 | <td id='certifier-area-<%= index %>'> |
42 | - <%= select_certifiers(qualifier, @product) %> | |
43 | - <%= button_to_function(:delete, content_tag('span', 'Delete qualifier'), "jQuery(this).parents('tr').remove()") %> | |
42 | + <%= select_certifiers(qualifier, @product) + remove_qualifier_button %> | |
44 | 43 | </td> |
45 | 44 | </tr> |
46 | 45 | <% end %> |
... | ... | @@ -48,9 +47,9 @@ |
48 | 47 | <%= button_to_function( |
49 | 48 | :add, |
50 | 49 | _('Add new qualifier'), |
51 | - "new_qualifier_row('#product-qualifiers-list', '#{escape_javascript(select_qualifiers(@product))}')" | |
50 | + "new_qualifier_row('#product-qualifiers-list', '#{escape_javascript(select_qualifiers(@product))}', '#{escape_javascript(remove_qualifier_button)}')" | |
52 | 51 | ) %> |
53 | - <%= hidden_field_tag "product[qualifiers_list]" %> | |
52 | + <%= hidden_field_tag "product[qualifiers_list][nil]" %> | |
54 | 53 | <% end %> |
55 | 54 | |
56 | 55 | <%= hidden_field_tag 'info-bar-update-url', @product.price_composition_bar_display_url, :class => 'bar-update-url' %> | ... | ... |
app/views/manage_products/show.rhtml
... | ... | @@ -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 @@ |
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 | -<%= 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 | 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 | 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 | 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 | 16 | <%= submit_button 'save', _('Save') %> |
14 | 17 | <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> |
15 | 18 | <% end %> |
16 | 19 | </div> |
17 | 20 | |
21 | + <p><%= _('Drag the balloon to find the exact location.') %> </p> | |
18 | 22 | |
19 | 23 | <div style='overflow: hidden'> |
20 | 24 | <p><div id="location-map"></div></p> |
21 | 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 %> | |
27 | + <%= f.hidden_field :lat %> | |
28 | + <%= f.hidden_field :lng %> | |
32 | 29 | |
33 | 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') %> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<%= render :partial => 'default_activity', :locals => { :activity => activity, :tab_action => tab_action } %> | ... | ... |
... | ... | @@ -0,0 +1,67 @@ |
1 | +<% Comment %> | |
2 | +<% Profile %> | |
3 | +<% Person %> | |
4 | + | |
5 | +<li class="article-comment" style='border-bottom:none;'> | |
6 | + <div class="article-comment-inner"> | |
7 | + | |
8 | + <div class="comment-content comment-logged-in"> | |
9 | + | |
10 | + <% if comment.author %> | |
11 | + <%= link_to image_tag(profile_icon(comment.author, :minor)), | |
12 | + Person.find(comment.author_id).url, | |
13 | + :class => 'comment-picture', | |
14 | + :title => comment.author_name | |
15 | + %> | |
16 | + <% end %> | |
17 | + | |
18 | + <div class="comment-details"> | |
19 | + <div class="comment-text"> | |
20 | + <%= link_to(comment.author_name, comment.author.url) %> | |
21 | + <% unless comment.title.blank? %> | |
22 | + <span class="comment-title"><%= comment.title %></span><br/> | |
23 | + <% end %> | |
24 | + <%= txt2html comment.body %> | |
25 | + </div> | |
26 | + <div class="profile-activity-time"> | |
27 | + <%= time_ago_as_sentence(comment.created_at) %> | |
28 | + </div> | |
29 | + </div> | |
30 | + | |
31 | + <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %> | |
32 | + <% button_bar(:style => 'float: right; margin-top: 0px;') do %> | |
33 | + <%= icon_button(:delete, _('Remove'), { :action => :remove_comment, :comment_id => comment.id }, :method => :get, :confirm => _('Are you sure you want to remove this comment and all its replies?')) %> | |
34 | + <% end %> | |
35 | + <% end %> | |
36 | + <br style="clear: both;" /> | |
37 | + | |
38 | + <div class="comment_reply post_comment_box closed"> | |
39 | + <% if @comment && @comment.errors.any? && @comment.reply_of_id.to_i == comment.id %> | |
40 | + <%= error_messages_for :comment %> | |
41 | + <script type="text/javascript"> | |
42 | + jQuery(function() { | |
43 | + document.location.href = '#<%= comment.anchor %>'; | |
44 | + add_comment_reply_form('#comment-reply-to-<%= comment.id %>', <%= comment.id %>); | |
45 | + }); | |
46 | + </script> | |
47 | + <% end %> | |
48 | + <%= report_abuse(comment.author, :comment_link, comment) if comment.author %> | |
49 | + <%= link_to_function _('Reply'), | |
50 | + "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id, | |
51 | + :class => 'comment-footer comment-footer-link comment-footer-hide', | |
52 | + :id => 'comment-reply-to-' + comment.id.to_s | |
53 | + %> | |
54 | + </div> | |
55 | + | |
56 | + </div> | |
57 | + | |
58 | + <% unless comment.replies.blank? %> | |
59 | + <ul class="comment-replies"> | |
60 | + <% comment.replies.each do |reply| %> | |
61 | + <%= render :partial => 'comment', :locals => { :comment => reply } %> | |
62 | + <% end %> | |
63 | + </ul> | |
64 | + <% end %> | |
65 | + | |
66 | + </div> | |
67 | +</li> | ... | ... |
... | ... | @@ -0,0 +1,22 @@ |
1 | +<div class='profile-activity-image'> | |
2 | + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> | |
3 | +</div> | |
4 | +<div class='profile-activity-description profile-activity-article-<%= activity.target.class.icon_name %>'> | |
5 | + <p class='profile-activity-text'> | |
6 | + <%= link_to activity.user.short_name(20), activity.user.url %> | |
7 | + <%= _("on community %s") % link_to(activity.target.profile.short_name(20), activity.target.profile.url) if activity.target.profile.is_a?(Community) %> | |
8 | + </p> | |
9 | + <div class='profile-activity-lead'> | |
10 | + <div class='article-name'><%= link_to(activity.params['name'], activity.params['url']) %></div> | |
11 | + <span title='<%= activity.target.class.short_description %>' class='profile-activity-icon icon-new icon-new<%= activity.target.class.icon_name %>'></span> | |
12 | + <%= image_tag(activity.params['first_image']) unless activity.params['first_image'].blank? %><%= strip_tags(truncate(activity.params['lead'], :length => 1000, :ommision => '...')).gsub(/(\xA0|\xC2|\s)+/, ' ').gsub(/^\s+/, '') %> <small><%= link_to(_('See more'), activity.params['url']) unless activity.get_lead.blank? %></small> | |
13 | + </div> | |
14 | + <%= content_tag(:p, link_to(_('See complete forum'), activity.get_url), :class => 'see-forum') if activity.target.is_a?(Forum) %> | |
15 | + <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p> | |
16 | + <div class='profile-wall-actions'> | |
17 | + <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %> | |
18 | + <%= link_to_remote(content_tag(:span, _('Remove')), :url =>{:action => 'remove_activity', :activity_id => activity.id, :only_hide => true}, :confirm => _('Are you sure?'), :update => "profile-activity-item-#{activity.id}") if logged_in? && current_person == @profile %> | |
19 | + </div> | |
20 | +</div> | |
21 | + | |
22 | +<%= render :partial => 'profile_comments', :locals => { :activity => activity, :tab_action => tab_action } %> | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<div class='profile-activity-image'> | |
2 | + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> | |
3 | +</div> | |
4 | +<div class='profile-activity-description'> | |
5 | + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p> | |
6 | + <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p> | |
7 | + <div class='profile-wall-actions'> | |
8 | + <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %> | |
9 | + <%= link_to_remote(content_tag(:span, _('Remove')), :confirm => _('Are you sure?'), :url =>{:action => 'remove_activity', :activity_id => activity.id}, :update => "profile-activity-item-#{activity.id}") if logged_in? && current_person == @profile %> | |
10 | + </div> | |
11 | +</div> | |
12 | + | |
13 | +<%= render :partial => 'profile_comments', :locals => { :activity => activity, :tab_action => tab_action } %> | ... | ... |
app/views/profile/_leave_scrap.rhtml
1 | -<%= @message %> | |
2 | -<% unless @scraps.nil? %> | |
3 | - <%= render :partial => 'profile_scraps', :locals => {:scraps => @scraps} %> | |
4 | -<% end %> | |
1 | +<div class='profile-activity-image'> | |
2 | + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> | |
3 | +</div> | |
4 | +<div class='profile-activity-description'> | |
5 | + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p> | |
6 | + <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p> | |
7 | + <div class='profile-wall-actions'> | |
8 | + <%= link_to_remote(content_tag(:span, _('Remove')), :confirm => _('Are you sure?'), :url =>{:action => 'remove_activity', :activity_id => activity.id}, :update => "profile-activity-item-#{activity.id}") if logged_in? && current_person == @profile %> | |
9 | + </div> | |
10 | +</div> | |
11 | + | |
12 | +<br/> | ... | ... |
app/views/profile/_profile.rhtml
No preview for this file type
app/views/profile/_profile_activities.rhtml
... | ... | @@ -1,18 +0,0 @@ |
1 | -<% activities.each do |activity| %> | |
2 | - <li class='profile-activity-item <%= activity.verb %>' id='profile-activity-item-<%= activity.id %>'> | |
3 | - <div class='profile-activity-image'> | |
4 | - <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> | |
5 | - </div> | |
6 | - <div class='profile-activity-description'> | |
7 | - <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) + ' ' + _('ago') %></p> | |
8 | - <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p> | |
9 | - <%= button_to_remote(:delete, content_tag(:span, _('Remove')), :url =>{:action => 'remove_activity', :activity_id => activity.id}, :update => "profile-activity-item-#{activity.id}") if can_edit_profile %> | |
10 | - </div> | |
11 | - <hr /> | |
12 | - </li> | |
13 | -<% end %> | |
14 | -<% if activities.current_page < activities.total_pages %> | |
15 | - <div id='profile_activities_page_<%= activities.current_page %>'> | |
16 | - <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1)}, :update => "profile_activities_page_#{activities.current_page}" %> | |
17 | - </div> | |
18 | -<% end %> |
... | ... | @@ -0,0 +1,16 @@ |
1 | +<% unless activities.nil? %> | |
2 | + <% activities.each do |a| %> | |
3 | + <% activity = a.klass.constantize.find(a.id) %> | |
4 | + <% if activity.kind_of?(ActionTracker::Record) %> | |
5 | + <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'wall' } if activity.visible? %> | |
6 | + <% else %> | |
7 | + <%= render :partial => 'profile_scrap', :locals => {:scrap => activity } %> | |
8 | + <% end %> | |
9 | + <% end %> | |
10 | +<% end %> | |
11 | + | |
12 | +<% if activities.current_page < activities.total_pages %> | |
13 | + <div id='profile_activities_page_<%= activities.current_page %>'> | |
14 | + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1)}, :update => "profile_activities_page_#{activities.current_page}" %> | |
15 | + </div> | |
16 | +<% end %> | ... | ... |
app/views/profile/_profile_activity.rhtml
1 | -<div id='profile-activity'> | |
2 | - <h3><%= _("%s's activity") % @profile.name %></h3> | |
3 | - <ul> | |
4 | - <%= render :partial => 'profile_activities', :locals => {:activities => @activities} %> | |
5 | - </ul> | |
6 | -</div> | |
1 | +<li class='profile-activity-item <%= activity.verb %>' id='profile-activity-item-<%= activity.id %>'> | |
2 | + <%= render :partial => activity.verb, :locals => { :activity => activity, :tab_action => tab_action }%> | |
3 | + <hr /> | |
4 | +</li> | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +<div id='profile-wall-reply-<%= activity.id%>'> | |
2 | + <div id='profile-wall-reply-form-<%= activity.id%>'> | |
3 | + <p class='profile-wall-reply'> | |
4 | + <% update_area = tab_action == 'wall' ? 'profile_activities' : 'network-activities' %> | |
5 | + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_comment_on_activity', :tab_action => tab_action}, :html => { :class => 'profile-wall-reply-form', 'data-update' => update_area } do %> | |
6 | + <%= text_area :comment, :body, {:id => "reply_content_#{activity.id}", | |
7 | + :cols => 50, | |
8 | + :rows => 1, | |
9 | + :class => 'submit-with-keypress', | |
10 | + :title => _('Leave your comment'), | |
11 | + :onfocus => ('if(this.value==this.title){this.value="";this.style.color="#000"};this.style.backgroundImage="url(' + profile_icon(current_person, :icon, false) + ')"' if logged_in?), | |
12 | + :onblur => ('if(this.value==""){this.value=this.title;this.style.color="#ccc"};this.style.backgroundImage="none"' if logged_in?), | |
13 | + :value => _('Leave your comment'), | |
14 | + :style => 'color: #ccc' } %> | |
15 | + <%= hidden_field_tag :source_id, activity.id, :id => "activity_id_#{activity.id}" %> | |
16 | + <% end %> | |
17 | + </p> | |
18 | + </div> | |
19 | + <div id='profile-wall-message-response-<%=activity.id%>' class='profile-wall-message-response'></div> | |
20 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<hr /> | |
2 | + | |
3 | +<% if activity.comments_count > 2 %> | |
4 | + <div class='view-all-comments icon-chat'> | |
5 | + <%= link_to(_("View all %s comments") % activity.comments_count, '#') %> | |
6 | + </div> | |
7 | +<% end %> | |
8 | + | |
9 | +<ul class="profile-wall-activities-comments" style="<%= 'display:none;' if (activity.comments_count > 2) %>" > | |
10 | + <%= render :partial => 'comment', :collection => activity.comments_as_thread %> | |
11 | +</ul> | |
12 | + | |
13 | +<%= render :partial => 'profile_comment_form', :locals => { :activity => activity, :tab_action => tab_action } %> | ... | ... |
app/views/profile/_profile_network.rhtml
app/views/profile/_profile_network_activities.rhtml
1 | - <% network_activities.each do |activity| %> | |
2 | - <li class='profile-network-item <%= activity.verb %>' id='profile-network-item-<%= activity.id %>'> | |
3 | - <div class='profile-network-image'> | |
4 | - <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> | |
5 | - <% if logged_in? && current_person.follows?(activity.user) && current_person != activity.user %> | |
6 | - <p class='profile-network-send-message'><%= link_to_function _('Scrap'), "hide_and_show(['#profile-network-message-response-#{activity.id}'],['#profile-network-message-#{activity.id}', '#profile-network-form-#{activity.id}']);$('content_#{activity.id}').value='';return false", :class => "profile-send-message", :title => _("Send a message to %s") % activity.user.name %></p> | |
7 | - <% end %> | |
8 | - </div> | |
9 | - <div class='profile-network-description'> | |
10 | - <p class='profile-network-time'><%= time_ago_as_sentence(activity.created_at) + ' ' + _('ago') %></p> | |
11 | - <p class='profile-network-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p> | |
12 | - <%= button_to_remote(:delete, content_tag(:span, _('Remove')), :url =>{:action => 'remove_notification', :activity_id => activity.id}, :update => "profile-network-item-#{activity.id}") if can_edit_profile %> | |
13 | - <p class='profile-network-where'><%= _('In community %s') % link_to(activity.target.name, activity.target.url) if !profile.is_a?(Community) && activity.target.is_a?(Community) %></p> | |
14 | - </div> | |
15 | - <div id='profile-network-message-<%= activity.id%>' style='display:none;'> | |
16 | - <div id='profile-network-form-<%= activity.id%>' style='display:none;'> | |
17 | - <p class='profile-network-message'> | |
18 | - <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :not_load_scraps => true}, :update => "profile-network-message-response-#{activity.id}", :success =>"hide_and_show(['#profile-network-form-#{activity.id}'],['#profile-network-message-response-#{activity.id}'])" do %> | |
19 | - <%= limited_text_area :scrap, :content, 420, "content_#{activity.id}", :cols => 50, :rows => 2 %> | |
20 | - <%= hidden_field_tag 'receiver_id', activity.user.id %> | |
21 | - <%= submit_button :add, _('Leave a message') %> | |
22 | - <%= button_to_function :cancel, _('Cancel'), "hide_and_show(['#profile-network-message-#{activity.id}'],[]);return false" %> | |
23 | - <% end %> | |
24 | - </p> | |
25 | - </div> | |
26 | - <div id='profile-network-message-response-<%=activity.id%>' class='profile-network-message-response'></div> | |
27 | - </div> | |
28 | - <hr /> | |
29 | - </li> | |
30 | - <% end %> | |
1 | +<% network_activities.each do |activity| %> | |
2 | + <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'network' } if activity.visible? %> | |
3 | +<% end %> | |
31 | 4 | <% if network_activities.current_page < network_activities.total_pages %> |
32 | 5 | <div id='profile_network_activities_page_<%= network_activities.current_page %>'> |
33 | 6 | <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_network_activities', :page => (network_activities.current_page + 1)}, :update => "profile_network_activities_page_#{network_activities.current_page}" %> | ... | ... |
app/views/profile/_profile_scrap.rhtml
1 | -<li class='profile-wall-item' id='profile-wall-item-<%= scrap.id %>'> | |
2 | - <div class='profile-wall-image'> | |
1 | +<li class='profile-activity-item' id='profile-activity-item-<%= scrap.id %>'> | |
2 | + <div class='profile-activity-image'> | |
3 | 3 | <%= link_to(profile_image(scrap.sender, :minor), scrap.sender.url) %> |
4 | - <% if logged_in? && current_person.follows?(scrap.sender) && current_person != scrap.sender %> | |
5 | - <p class='profile-wall-send-message'><%= link_to_function _('Scrap'), "hide_and_show(['#profile-wall-message-response-#{scrap.id}'],['#profile-wall-message-#{scrap.id}', '#profile-wall-form-#{scrap.id}']);$('content_#{scrap.id}').value='';return false", :class => "profile-send-message", :title => _("Send a message to %s") % scrap.sender.name %></p> | |
6 | - <% end %> | |
7 | 4 | </div> |
8 | - <% comment_balloon :class => 'profile-wall-description' do %> | |
9 | - <p class='profile-wall-sender'><%= link_to scrap.sender.name, scrap.sender.url %></p> | |
10 | - <p class='profile-wall-time'><%= time_ago_as_sentence(scrap.created_at) + ' ' + _('ago') %></p> | |
11 | - <p class='profile-wall-text'><%= txt2html scrap.content %></p> | |
12 | - <%= button_to_remote(:delete, content_tag(:span, _('Remove')), :url =>{:action => 'remove_scrap', :scrap_id => scrap.id}, :update => "profile-wall-item-#{scrap.id}") if logged_in? && user.can_control_scrap?(scrap) %> | |
13 | - <% if logged_in? && current_person.follows?(scrap.sender) && scrap.root.nil? %> | |
14 | - <p class='profile-wall-send-reply'><%= link_to_function _('Reply'), "hide_and_show(['#profile-wall-reply-response-#{scrap.id}'],['#profile-wall-reply-#{scrap.id}', '#profile-wall-reply-form-#{scrap.id}']);$('reply_content_#{scrap.id}').value='';$('scrap_id_#{scrap.id}').value='#{scrap.id}';return false", :class => "profile-send-reply icon-reply" %></p> | |
5 | + <div class='profile-activity-description'> | |
6 | + <p class='profile-activity-sender'><%= link_to scrap.sender.name, scrap.sender.url %></p> | |
7 | + <p class='profile-activity-text'><%= txt2html scrap.content %></p> | |
8 | + <p class='profile-activity-time'><%= time_ago_as_sentence(scrap.created_at) %></p> | |
9 | + <div class='profile-wall-actions'> | |
10 | + <% if logged_in? && current_person.follows?(scrap.sender) %> | |
11 | + <span class='profile-activity-send-reply'> | |
12 | + <%= link_to_function s_('profile|Comment'), "hide_and_show(['#profile-wall-message-response-#{scrap.id}'],['#profile-wall-reply-#{scrap.id}', '#profile-wall-reply-form-#{scrap.id}']);$('reply_content_#{scrap.id}').value='';$('reply_content_#{scrap.id}').focus();return false", :class => "profile-send-reply" %> | |
13 | + </span> | |
15 | 14 | <% end %> |
15 | + <%= link_to_remote(content_tag(:span, _('Remove')), :confirm => _('Are you sure?'), :url =>{:action => 'remove_scrap', :scrap_id => scrap.id}, :update => "profile-activity-item-#{scrap.id}") if logged_in? && user.can_control_scrap?(scrap) %> | |
16 | + </div> | |
17 | + </div> | |
18 | + | |
19 | + <% if scrap.replies.count > 2 %> | |
20 | + <div class='view-all-comments icon-chat'> | |
21 | + <%= link_to(_("View all %s comments") % scrap.replies.count, '#') %> | |
22 | + </div> | |
16 | 23 | <% end %> |
17 | - <ul class="profile-wall-scrap-replies"> | |
24 | + | |
25 | + <ul class="profile-wall-activities-comments scrap-replies" style="width: auto; <%= 'display:none;' if (scrap.replies.count > 2) %>" > | |
18 | 26 | <% scrap.replies.map do |reply| %> |
19 | 27 | <%= render :partial => 'profile_scrap', :locals => {:scrap => reply} %> |
20 | 28 | <% end %> |
21 | 29 | </ul> |
22 | - <div id='profile-wall-message-<%= scrap.id%>' style='display:none;'> | |
23 | - <div id='profile-wall-form-<%= scrap.id%>' style='display:none;'> | |
24 | - <p class='profile-wall-message'> | |
25 | - <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :not_load_scraps => true}, :update => "profile-wall-message-response-#{scrap.id}", :success =>"hide_and_show(['#profile-wall-form-#{scrap.id}'],['#profile-wall-message-response-#{scrap.id}'])" do %> | |
26 | - <%= limited_text_area :scrap, :content, 420, "content_#{scrap.id}", :cols => 50, :rows => 2 %> | |
27 | - <%= hidden_field_tag 'receiver_id', scrap.sender.id %> | |
28 | - <%= submit_button :add, _('Leave a scrap') %> | |
29 | - <%= button_to_function :cancel, _('Cancel'), "hide_and_show(['#profile-wall-message-#{scrap.id}'],[]);return false" %> | |
30 | - <% end %> | |
31 | - </p> | |
32 | - </div> | |
33 | - <div id='profile-wall-message-response-<%=scrap.id%>' class='profile-wall-message-response'></div> | |
34 | - </div> | |
35 | - <div id='profile-wall-reply-<%= scrap.id%>' style='display:none;'> | |
36 | - <div id='profile-wall-reply-form-<%= scrap.id%>' style='display:none;'> | |
37 | - <p class='profile-wall-reply'> | |
38 | - <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap'}, :update => "profile_scraps", :success =>"hide_and_show(['#profile-wall-reply-form-#{scrap.id}'],['#profile-wall-reply-response-#{scrap.id}'])" do %> | |
39 | - <%= limited_text_area :scrap, :content, 420, "reply_content_#{scrap.id}", :cols => 50, :rows => 2 %> | |
40 | - <%= hidden_field :scrap, :scrap_id, :id => "scrap_id_#{scrap.id}" %> | |
41 | - <%= hidden_field_tag 'receiver_id', scrap.sender.id %> | |
42 | - <%= submit_button :add, _('Leave a scrap') %> | |
43 | - <%= button_to_function :cancel, _('Cancel'), "hide_and_show(['#profile-wall-reply-#{scrap.id}'],[]);return false" %> | |
44 | - <% end %> | |
45 | - </p> | |
46 | - </div> | |
47 | - <div id='profile-wall-message-response-<%=scrap.id%>' class='profile-wall-message-response'></div> | |
48 | - </div> | |
30 | + <%= render :partial => 'profile_scrap_reply_form', :locals => { :scrap => scrap } %> | |
49 | 31 | <hr /> |
50 | 32 | </li> | ... | ... |
... | ... | @@ -0,0 +1,20 @@ |
1 | +<div id='profile-wall-reply-<%= scrap.id%>' style='display:none'> | |
2 | + <div id='profile-wall-reply-form-<%= scrap.id%>' style='display:none'> | |
3 | + <p class='profile-wall-reply'> | |
4 | + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap'}, :update => "profile_activities", :html => { :class => 'profile-wall-reply-form'} do %> | |
5 | + <%= text_area :scrap, :content, { :id => "reply_content_#{scrap.id}", | |
6 | + :cols => 50, | |
7 | + :rows => 1, | |
8 | + :class => 'submit-with-keypress', | |
9 | + :title => _('Leave your comment'), | |
10 | + :onfocus => ('if(this.value==this.title){this.value="";this.style.color="#000"};this.style.backgroundImage="url(' + profile_icon(current_person, :icon, false) + ')"' if logged_in?), | |
11 | + :onblur => ('if(this.value==""){this.value=this.title;this.style.color="#ccc"};this.style.backgroundImage="none"' if logged_in?), | |
12 | + :value => _('Leave your comment') | |
13 | + } %> | |
14 | + <%= hidden_field_tag 'scrap[scrap_id]', scrap.id %> | |
15 | + <%= hidden_field_tag 'receiver_id', scrap.sender.id %> | |
16 | + <% end %> | |
17 | + </p> | |
18 | + </div> | |
19 | + <div id='profile-wall-message-response-<%=scrap.id%>' class='profile-wall-message-response'></div> | |
20 | +</div> | ... | ... |
app/views/profile/_profile_scraps.rhtml
1 | -<% scraps.map do |scrap| %> | |
2 | - <%= render :partial => 'profile_scrap', :locals => {:scrap => scrap} %> | |
3 | -<% end %> | |
4 | -<% if scraps.current_page < scraps.total_pages %> | |
5 | - <div id='profile_scraps_page_<%= scraps.current_page %>'> | |
6 | - <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_scraps', :page => (scraps.current_page + 1)}, :update => "profile_scraps_page_#{scraps.current_page}" %> | |
7 | - </div> | |
8 | -<% end %> | |
1 | +NÃO DEVE APARECER | ... | ... |
app/views/profile/_profile_wall.rhtml
1 | 1 | <h3><%= _("%s's wall") % @profile.name %></h3> |
2 | 2 | <div id='leave_scrap'> |
3 | 3 | <%= flash[:error] %> |
4 | - <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %> | |
4 | + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "$('leave_scrap_content').value=''" do %> | |
5 | 5 | <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> |
6 | - <%= submit_button :scrap, _('Leave a scrap') %> | |
6 | + <%= submit_button :new, _('Share') %> | |
7 | 7 | <% end %> |
8 | 8 | </div> |
9 | 9 | <div id='leave_scrap_response'></div> |
10 | -<ul id='profile_scraps'> | |
11 | - <%= render :partial => 'profile_scraps', :locals => {:scraps => @wall_items} %> | |
10 | +<ul id='profile_activities' class='profile-activities'> | |
11 | + <%= render :partial => 'profile_activities_list', :locals => {:activities => @activities} %> | |
12 | 12 | </ul> | ... | ... |