Commit 9103171c933a3a92834ea3c48802ed5bd344baf1

Authored by Rafael Martins
2 parents 406c009f 99a41461

Merge branch 'solr-searches' of https://github.com/coletivoEITA/noosfero-ecosol into ecosol-solr

Conflicts:
	app/models/enterprise.rb
Showing 444 changed files with 120699 additions and 14351 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 444 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,7 +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-ruby-data 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
  16 + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby openjdk-6-jre libdaemons-ruby thin tango-icon-theme libhpricot-ruby
17 17  
18 18 On other systems, they may or may not be available through your regular package
19 19 management system. Below are the links to their homepages.
... ... @@ -24,7 +24,7 @@ management system. Below are the links to their homepages.
24 24 * Ruby-GetText: http://www.yotabanana.com/hiki/ruby-gettext.html?ruby-gettext (at least version 1.9.0)
25 25 * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby
26 26 * rcov: http://eigenclass.org/hiki/rcov
27   -* Ferret: http://ferret.davebalmain.com/trac
  27 +* Solr: http://lucene.apache.org/solr
28 28 * RMagick: http://rmagick.rubyforge.org/
29 29 * RedCloth: http://redcloth.org/
30 30 * will_paginate: http://github.com/mislav/will_paginate/wikis
... ... @@ -115,7 +115,7 @@ $ tar -zxvf noosfero-0.27.1.tar.gz
115 115 $ ln -s noosfero-0.27.1 current
116 116 $ cd current
117 117  
118   -Copy config/ferret_server.yml.dist to config/ferret_server.yml. You will
  118 +Copy config/solr.yml.dist to config/solr.yml. You will
119 119 probably not need to customize this configuration, but have a look at it.
120 120  
121 121 Create the thin configuration file:
... ... @@ -241,6 +241,10 @@ Compile the translations:
241 241  
242 242 $ RAILS_ENV=production rake noosfero:translations:compile
243 243  
  244 +Run Solr:
  245 +
  246 +$ rake solr:start
  247 +
244 248 Now we have to create some initial data. To create your default environment
245 249 (the first one), run the command below:
246 250  
... ...
Rakefile
... ... @@ -7,4 +7,6 @@ require 'rake'
7 7 require 'rake/testtask'
8 8 require 'rake/rdoctask'
9 9  
  10 +ACTS_AS_SEARCHABLE_ENABLED = false if Rake.application.top_level_tasks.detect{|t| t == 'db:data:minimal'}
  11 +
10 12 require 'tasks/rails'
... ...
app/controllers/application.rb
1 1 require 'application_controller'
  2 +
  3 +# his is the application's main controller. Features defined here are
  4 +# available in all controllers.
  5 +class ApplicationController < ActionController::Base
  6 +
  7 + before_filter :change_pg_schema
  8 +
  9 + include ApplicationHelper
  10 + layout :get_layout
  11 + def get_layout
  12 + prepend_view_path('public/' + theme_path)
  13 + theme_option(:layout) || 'application'
  14 + end
  15 +
  16 + filter_parameter_logging :password
  17 +
  18 + def log_processing
  19 + super
  20 + return unless ENV['RAILS_ENV'] == 'production'
  21 + if logger && logger.info?
  22 + logger.info(" HTTP Referer: #{request.referer}")
  23 + logger.info(" User Agent: #{request.user_agent}")
  24 + logger.info(" Accept-Language: #{request.headers['HTTP_ACCEPT_LANGUAGE']}")
  25 + end
  26 + end
  27 +
  28 + helper :document
  29 + helper :language
  30 +
  31 + def self.no_design_blocks
  32 + @no_design_blocks = true
  33 + end
  34 + def self.uses_design_blocks?
  35 + !@no_design_blocks
  36 + end
  37 + def uses_design_blocks?
  38 + !@no_design_blocks && self.class.uses_design_blocks?
  39 + end
  40 +
  41 + # Be sure to include AuthenticationSystem in Application Controller instead
  42 + include AuthenticatedSystem
  43 + include PermissionCheck
  44 +
  45 + def self.require_ssl(*options)
  46 + before_filter :check_ssl, *options
  47 + end
  48 + def check_ssl
  49 + return true if (request.ssl? || ENV['RAILS_ENV'] == 'development')
  50 + redirect_to_ssl
  51 + end
  52 + def redirect_to_ssl
  53 + if environment.enable_ssl
  54 + redirect_to(params.merge(:protocol => 'https://', :host => ssl_hostname))
  55 + true
  56 + else
  57 + false
  58 + end
  59 + end
  60 +
  61 + def self.refuse_ssl(*options)
  62 + before_filter :avoid_ssl, *options
  63 + end
  64 + def avoid_ssl
  65 + if (!request.ssl? || ENV['RAILS_ENV'] == 'development')
  66 + true
  67 + else
  68 + redirect_to(params.merge(:protocol => 'http://'))
  69 + false
  70 + end
  71 + end
  72 +
  73 + before_filter :set_locale
  74 + def set_locale
  75 + FastGettext.available_locales = Noosfero.available_locales
  76 + FastGettext.default_locale = Noosfero.default_locale
  77 + FastGettext.set_locale(params[:lang] || session[:lang] || Noosfero.default_locale || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
  78 + if params[:lang]
  79 + session[:lang] = params[:lang]
  80 + end
  81 + end
  82 +
  83 + include NeedsProfile
  84 +
  85 + before_filter :detect_stuff_by_domain
  86 + before_filter :init_noosfero_plugins
  87 + attr_reader :environment
  88 +
  89 + before_filter :load_terminology
  90 +
  91 + # declares that the given <tt>actions</tt> cannot be accessed by other HTTP
  92 + # method besides POST.
  93 + def self.post_only(actions, redirect = { :action => 'index'})
  94 + verify :method => :post, :only => actions, :redirect_to => redirect
  95 + end
  96 +
  97 + helper_method :current_person, :current_person
  98 +
  99 + def change_pg_schema
  100 + if Noosfero::MultiTenancy.on? and ActiveRecord::Base.postgresql?
  101 + Noosfero::MultiTenancy.db_by_host = request.host
  102 + end
  103 + end
  104 +
  105 + protected
  106 +
  107 + def boxes_editor?
  108 + false
  109 + end
  110 +
  111 + def content_editor?
  112 + false
  113 + end
  114 +
  115 + def user
  116 + current_user.person if logged_in?
  117 + end
  118 +
  119 + alias :current_person :user
  120 +
  121 + # TODO: move this logic somewhere else (Domain class?)
  122 + def detect_stuff_by_domain
  123 + @domain = Domain.find_by_name(request.host)
  124 + if @domain.nil?
  125 + @environment = Environment.default
  126 + else
  127 + @environment = @domain.environment
  128 + @profile = @domain.profile
  129 + end
  130 + end
  131 +
  132 + def init_noosfero_plugins
  133 + @plugins = Noosfero::Plugin::Manager.new(self)
  134 + @plugins.enabled_plugins.map(&:class).each do |plugin|
  135 + prepend_view_path(plugin.view_path)
  136 + end
  137 + init_noosfero_plugins_controller_filters
  138 + end
  139 +
  140 + # This is a generic method that initialize any possible filter defined by a
  141 + # plugin to the current controller being initialized.
  142 + def init_noosfero_plugins_controller_filters
  143 + @plugins.enabled_plugins.each do |plugin|
  144 + plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter|
  145 + self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {}))
  146 + self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block])
  147 + end
  148 + end
  149 + end
  150 +
  151 + def load_terminology
  152 + # cache terminology for performance
  153 + @@terminology_cache ||= {}
  154 + @@terminology_cache[environment.id] ||= environment.terminology
  155 + Noosfero.terminology = @@terminology_cache[environment.id]
  156 + end
  157 +
  158 + def render_not_found(path = nil)
  159 + @no_design_blocks = true
  160 + @path ||= request.path
  161 + render :template => 'shared/not_found.rhtml', :status => 404, :layout => get_layout
  162 + end
  163 + alias :render_404 :render_not_found
  164 +
  165 + def render_access_denied(message = nil, title = nil)
  166 + @no_design_blocks = true
  167 + @message = message
  168 + @title = title
  169 + render :template => 'shared/access_denied.rhtml', :status => 403
  170 + end
  171 +
  172 +end
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -286,7 +286,7 @@ class CmsController &lt; MyProfileController
286 286  
287 287 def search
288 288 query = params[:q]
289   - results = query.blank? ? [] : profile.articles.published.find_by_contents(query)
  289 + results = query.blank? ? [] : profile.articles.published.find_by_contents(query)[:results]
290 290 render :text => article_list_to_json(results), :content_type => 'application/json'
291 291 end
292 292 def media_upload
... ...
app/controllers/my_profile/maps_controller.rb
... ... @@ -6,16 +6,48 @@ class MapsController &lt; 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
app/controllers/public/map_balloon_controller.rb 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +class MapBalloonController < PublicController
  2 +
  3 + helper SearchHelper
  4 +
  5 + before_filter :load_profile, :only => [:person, :enterprise, :community]
  6 +
  7 + def product
  8 + @product = Product.find(params[:id])
  9 + render :action => 'product', :layout => false
  10 + end
  11 +
  12 + def person
  13 + render :action => 'profile', :layout => false
  14 + end
  15 +
  16 + def enterprise
  17 + render :action => 'profile', :layout => false
  18 + end
  19 +
  20 + def community
  21 + render :action => 'profile', :layout => false
  22 + end
  23 +
  24 + protected
  25 +
  26 + def load_profile
  27 + @profile = Profile.find(params[:id])
  28 + end
  29 +
  30 +end
... ...
app/controllers/public/profile_search_controller.rb
... ... @@ -8,11 +8,11 @@ class ProfileSearchController &lt; 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 = Article.find_by_contents(@q, {:per_page => 10, :page => params[:page]},
  15 + {:filter_queries => ["profile_id:#{profile.id}", 'public:true']})[:results]
16 16 end
17 17 end
18 18 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 + if !@empty_query
  48 + full_text_search ['public:true']
  49 + else
  50 + @one_page = true
  51 + @geosearch = logged_in? && current_user.person.lat && current_user.person.lng
  52 +
  53 + extra_limit = LIST_SEARCH_LIMIT*5
  54 + sql_options = {:limit => LIST_SEARCH_LIMIT, :order => 'random()'}
  55 + if @geosearch
  56 + full_text_search ['public:true', "{!geofilt}"], :sql_options => sql_options, :extra_limit => extra_limit,
  57 + :alternate_query => "{!boost b=recip(geodist(),#{1/DistBoost},1,1)}",
  58 + :radius => DistFilt, :latitude => current_user.person.lat, :longitude => current_user.person.lng
56 59 else
57   - @events_of_the_day = environment.events.by_day(@selected_day)
  60 + full_text_search ['public:true'], :sql_options => sql_options, :extra_limit => extra_limit,
  61 + :boost_functions => ['recip(ms(NOW/HOUR,updated_at),1.3e-10,1,1)']
58 62 end
59 63 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 64 end
67 65  
68   - def people
69   - #nothing, just to enable
70   - end
71 66 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
  67 + if !@empty_query
  68 + full_text_search ['public:true']
  69 + else
  70 + @filter_title = _('Enterprises from network')
  71 + @results[@asset] = @environment.enterprises.visible.paginate(paginate_options)
  72 + end
85 73 end
86 74  
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}))
  75 + def communities
  76 + if !@empty_query
  77 + full_text_search ['public:true']
  78 + else
  79 + @results[@asset] = @environment.communities.visible.send(@filter).paginate(paginate_options)
93 80 end
94   -
95 81 end
96 82  
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
  83 + def events
  84 + year = (params[:year] ? params[:year].to_i : Date.today.year)
  85 + month = (params[:month] ? params[:month].to_i : Date.today.month)
  86 + day = (params[:day] ? params[:day].to_i : Date.today.day)
  87 + date = build_date(params[:year], params[:month], params[:day])
  88 + date_range = (date - 1.month)..(date + 1.month).at_end_of_month
102 89  
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
  90 + @selected_day = nil
  91 + @events_of_the_day = []
  92 + if params[:day] || !params[:year] && !params[:month]
  93 + @selected_day = date
  94 + @events_of_the_day = @category ?
  95 + environment.events.by_day(@selected_day).in_category(Category.find(@category_id)) :
  96 + environment.events.by_day(@selected_day)
106 97 end
107 98  
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
  99 + if !@empty_query
  100 + full_text_search
117 101 else
118   - (searching.size == 1) ? 20 : 6
  102 + @results[@asset] = date_range ? environment.events.by_range(date_range) : environment.events
119 103 end
120   - end
121   -
122   - public
123   -
124   - include SearchHelper
125 104  
126   - ######################################################
127   -
128   - def where_to_search
129   - [
130   - [ :articles, N_('Articles') ],
131   - [ :enterprises, N_('Enterprises') ],
132   - [ :people, N_('People') ],
133   - [ :communities, N_('Communities') ],
134   - [ :products, N_('Products') ],
135   - [ :events, N_('Events') ]
136   - ].select {|key, name| !environment.enabled?('disable_asset_' + key.to_s) }
137   - end
138   -
139   - def cities
140   - @cities = City.find(:all, :order => 'name', :conditions => ['parent_id = ? and lat is not null and lng is not null', params[:state_id]])
141   - render :action => 'cities', :layout => false
142   - end
143   -
144   - def complete_region
145   - # FIXME this logic should be in the model
146   - @regions = Region.find(:all, :conditions => [ '(name like ? or name like ?) and lat is not null and lng is not null', '%' + params[:region][:name] + '%', '%' + params[:region][:name].capitalize + '%' ])
147   - render :action => 'complete_region', :layout => false
  105 + events = @results[@asset]
  106 + @calendar = populate_calendar(date, events)
  107 + @previous_calendar = populate_calendar(date - 1.month, events)
  108 + @next_calendar = populate_calendar(date + 1.month, events)
148 109 end
149 110  
150 111 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 112 @results = {}
161 113 @order = []
162 114 @names = {}
  115 + @results_only = true
163 116  
164   - where_to_search.select { |key,description| @searching[key] }.each do |key, description|
  117 + @enabled_searchs.select { |key,description| @searching[key] }.each do |key, description|
  118 + load_query
  119 + @asset = key
  120 + send(key)
165 121 @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 122 @names[key] = getterm(description)
168 123 end
  124 + @asset = nil
  125 + @facets = {}
169 126  
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'
  127 + render :action => @results.keys.first if @results.keys.size == 1
181 128 end
182 129  
183   - alias :assets :index
184   -
185   - #######################################################
  130 + # keep old URLs workings
  131 + def assets
  132 + params[:action] = params[:asset].is_a?(Array) ? :index : params.delete(:asset)
  133 + redirect_to params
  134 + end
186 135  
187 136 # view the summary of one category
188 137 def category_index
189 138 @results = {}
190 139 @order = []
191 140 @names = {}
  141 + limit = MULTIPLE_SEARCH_LIMIT
192 142 [
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
  143 + [ :people, _('People'), :recent_people ],
  144 + [ :enterprises, _('Enterprises'), :recent_enterprises ],
  145 + [ :products, _('Products'), :recent_products ],
  146 + [ :events, _('Upcoming events'), :upcoming_events ],
  147 + [ :communities, _('Communities'), :recent_communities ],
  148 + [ :articles, _('Contents'), :recent_articles ]
  149 + ].each do |asset, name, filter|
  150 + @order << asset
  151 + @results[asset] = @category.send(filter, limit)
  152 + raise "nao total #{asset}" unless @results[asset].respond_to?(:total_entries)
  153 + @names[asset] = name
204 154 end
205 155 end
206   - attr_reader :category
207 156  
208 157 def tags
209 158 @tags_cache_key = "tags_env_#{environment.id.to_s}"
... ... @@ -216,25 +165,138 @@ class SearchController &lt; PublicController
216 165 @tag = params[:tag]
217 166 @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}"
218 167 if is_cache_expired?(@tag_cache_key)
219   - @tagged = environment.articles.find_tagged_with(@tag).paginate(:per_page => 10, :page => params[:npage])
  168 + @asset = :articles
  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 + unless params[:category_path].blank?
  195 + path = params[:category_path].join('/')
  196 + @category = environment.categories.find_by_path(path)
  197 + if @category.nil?
  198 + render_not_found(path)
  199 + else
  200 + @category_id = @category.id
  201 + end
  202 + end
  203 + end
  204 +
  205 + FILTERS = %w(
  206 + more_recent
  207 + more_active
  208 + more_popular
  209 + )
  210 + def filter
  211 + if FILTERS.include?(params[:filter])
  212 + params[:filter]
234 213 else
235   - @events_of_the_day = environment.events.by_day(@selected_day)
  214 + 'more_recent'
236 215 end
237   - render :partial => 'events/events_by_day'
  216 + end
  217 +
  218 + def filter_description(asset, filter)
  219 + {
  220 + 'articles_more_recent' => _('More recent contents from network'),
  221 + 'articles_more_popular' => _('More read contents from network'),
  222 + 'people_more_recent' => _('More recent people from network'),
  223 + 'people_more_active' => _('More active people from network'),
  224 + 'people_more_popular' => _('More popular people from network'),
  225 + 'communities_more_recent' => _('More recent communities from network'),
  226 + 'communities_more_active' => _('More active communities from network'),
  227 + 'communities_more_popular' => _('More popular communities from network'),
  228 + 'products_more_recent' => _('Highlights'),
  229 + }[asset.to_s + '_' + filter]
  230 + end
  231 +
  232 + def load_search_assets
  233 + @enabled_searchs = [
  234 + [ :articles, _('Contents') ],
  235 + [ :enterprises, _('Enterprises') ],
  236 + [ :people, _('People') ],
  237 + [ :communities, _('Communities') ],
  238 + [ :products, _('Products and Services') ],
  239 + [ :events, _('Events') ]
  240 + ].select {|key, name| !environment.enabled?('disable_asset_' + key.to_s) }
  241 +
  242 + @searching = {}
  243 + @titles = {}
  244 + @enabled_searchs.each do |key, name|
  245 + @titles[key] = name
  246 + @searching[key] = params[:action] == 'index' || params[:action] == key.to_s
  247 + end
  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 &#39;redcloth&#39;
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,33 +1085,46 @@ module ApplicationHelper
1100 1085 ") : '')
1101 1086 end
1102 1087  
1103   - def browse_people_menu
  1088 + def search_contents_menu
  1089 + links = [
  1090 + {s_('contents|More Recent') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_recent'})}},
  1091 + {s_('contents|More Read') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_popular'})}}
  1092 + ]
  1093 + if logged_in?
  1094 + links.push(_('New Content') => lightbox_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_user.login, :cms => true})}))
  1095 + end
  1096 +
  1097 + link_to(content_tag(:span, _('Contents'), :class => 'icon-menu-articles'), {:controller => "search", :action => 'contents', :category_path => ''}, :id => 'submenu-contents') +
  1098 + link_to(content_tag(:span, _('Contents Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-contents-trigger')
  1099 + end
  1100 +
  1101 + def search_people_menu
1104 1102 links = [
1105   - {s_('people|More Recent') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_recent'})}},
1106   - {s_('people|More Active') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_active'})}},
1107   - {s_('people|More Popular') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_popular'})}}
  1103 + {s_('people|More Recent') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_recent'})}},
  1104 + {s_('people|More Active') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_active'})}},
  1105 + {s_('people|More Popular') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_popular'})}}
1108 1106 ]
1109 1107 if logged_in?
1110 1108 links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})})
1111 1109 links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})})
1112 1110 end
1113 1111  
1114   - link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') +
  1112 + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "search", :action => 'people', :category_path => ''}, :id => 'submenu-people') +
1115 1113 link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger')
1116 1114 end
1117 1115  
1118   - def browse_communities_menu
  1116 + def search_communities_menu
1119 1117 links = [
1120   - {s_('communities|More Recent') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_recent'})}},
1121   - {s_('communities|More Active') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_active'})}},
1122   - {s_('communities|More Popular') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_popular'})}}
  1118 + {s_('communities|More Recent') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_recent'})}},
  1119 + {s_('communities|More Active') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_active'})}},
  1120 + {s_('communities|More Popular') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_popular'})}}
1123 1121 ]
1124 1122 if logged_in?
1125 1123 links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})})
1126 1124 links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})})
1127 1125 end
1128 1126  
1129   - link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') +
  1127 + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "search", :action => 'communities'}, :id => 'submenu-communities') +
1130 1128 link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger')
1131 1129 end
1132 1130  
... ... @@ -1330,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(' &rarr; ') : category.name
32   - link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname })
  36 + link_to name, Noosfero.url_options.merge({:controller => 'search', :action => 'category_index', :category_path => category.path.split('/'),:host => category.environment.default_hostname }), html_options
33 37 end
34 38  
35 39 def link_to_product_category(category)
36 40 if category
37   - link_to(category.name, :controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id, :host => category.environment.default_hostname)
  41 + link_to(category.name, :controller => 'search', :action => 'products', :category_path => category.explode_path)
38 42 else
39 43 _('Uncategorized product')
40 44 end
... ...
app/helpers/lightbox_helper.rb
... ... @@ -22,10 +22,7 @@ module LightboxHelper
22 22 def lightbox_options(options, lightbox_type = 'lbOn')
23 23 the_class = lightbox_type
24 24 the_class << " #{options[:class]}" if options.has_key?(:class)
25   - options.merge(
26   - :class => the_class,
27   - :onclick => 'alert("%s"); return false' % _('Please, try again when the page loading completes.')
28   - )
  25 + options.merge(:class => the_class)
29 26 end
30 27  
31 28 def lightbox?
... ...
app/helpers/product_category_viewer_helper.rb
... ... @@ -1,2 +0,0 @@
1   -module ProductCategoryViewerHelper
2   -end
app/helpers/search_helper.rb
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 + SortOptions = {
  10 + :products => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  11 + :more_recent, {:label => _('More Recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}},
  12 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  13 + :closest, {:label => _('Closest to me'), :if => proc{ logged_in? && (profile=current_user.person).lat && profile.lng },
  14 + :solr_opts => {:sort => "geodist() asc",
  15 + :latitude => proc{ current_user.person.lat }, :longitude => proc{ current_user.person.lng }}},
  16 + ],
  17 + :events => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  18 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  19 + ],
  20 + :articles => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  21 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  22 + :name, {:label => _('Most recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}},
  23 + ],
  24 + :enterprises => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  25 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  26 + ],
  27 + :people => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  28 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  29 + ],
  30 + :communities => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
  31 + :name, {:label => _('Name'), :solr_opts => {:sort => 'name_sortable asc'}},
  32 + ],
  33 + }
  34 +
3 35 # FIXME remove it after search_controler refactored
4 36 include EventsHelper
5 37  
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
  38 + def search_page_title(title, category = nil)
  39 + title = "<h1>" + title
  40 + title += '<small>' + category.name + '</small>' if category
  41 + title + "</h1>"
15 42 end
16 43  
17   - def remove_stop_words(query)
18   - (query.downcase.scan(/"[^"]*"?|'[^']*'?|[^'"\s]+/) - (STOP_WORDS[locale] || [])).join(' ')
  44 + def category_context(category, url)
  45 + content_tag('div', category.full_name + _(', ') +
  46 + link_to(_('search in all categories'),
  47 + url.merge(:category_path => [], :action => (params[:action] == 'category_index' ? 'index' : params[:action]) )),
  48 + :align => 'center', :class => 'search-category-context') if category
19 49 end
20 50  
21   - def display_results(use_map = true)
22   -
23   - unless use_map && GoogleMaps.enabled?(environment.default_hostname)
24   - return render(:partial => 'display_results')
  51 + def display_results(use_map = false)
  52 + if params[:display] == 'map' && use_map
  53 + partial = 'google_maps'
  54 + klass = 'map'
  55 + else
  56 + partial = 'display_results'
  57 + klass = 'list'
25 58 end
26 59  
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   - }
  60 + content_tag('div', render(:partial => partial), :class => "map-or-list-search-results #{klass}")
  61 + end
  62 +
  63 + def display_map_list_button
  64 + button(:search, params[:display] == 'map' ? _('Display in list') : _('Display in map'),
  65 + params.merge(:display => (params[:display] == 'map' ? 'list' : 'map')),
  66 + :class => "map-toggle-button" )
  67 + end
  68 +
  69 + def city_with_state(city)
  70 + if city and city.kind_of?(City)
  71 + s = city.parent
  72 + if s and s.kind_of?(State) and s.acronym
  73 + city.name + ', ' + s.acronym
34 74 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   - }
  75 + city.name
40 76 end
  77 + else
  78 + nil
  79 + end
  80 + end
41 81  
42   - content_tag('div', data[:toggle] + (render :partial => data[:partial]), :class => "map-or-list-search-results #{data[:class]}")
  82 + def facets_menu(asset, _facets)
  83 + @asset_class = asset_class(asset)
  84 + @facets = _facets
  85 + render(:partial => 'facets_menu')
43 86 end
44 87  
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
  88 + def facets_unselect_menu(asset)
  89 + @asset_class = asset_class(asset)
  90 + render(:partial => 'facets_unselect_menu')
51 91 end
52 92  
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   - )
  93 + def facet_javascript(input_id, facet, array)
  94 + array = [] if array.nil?
  95 + hintText = _('Type in an option')
  96 + text_field_tag('facet['+input_id+']', '', :id => input_id) +
  97 + javascript_tag("jQuery.TokenList(jQuery('##{input_id}'), #{array.to_json},
  98 + {searchDelay: 0, permanentDropdown: true, theme: 'facet', dontAdd: true, preventDuplicates: true,
  99 + #{jquery_token_input_messages_json(hintText)}});")
81 100 end
82 101  
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)
  102 + def facet_link_html(facet, params, value, label, count)
  103 + params = params ? params.dup : {}
  104 + has_extra = label.kind_of?(Array)
  105 + link_label = has_extra ? label[0] : label
  106 + id = facet[:solr_field].to_s
  107 + params[:facet] ||= {}
  108 + params[:facet][id] ||= {}
  109 + params[:page] = {} if params[:page]
  110 +
  111 + selected = facet[:label_id].nil? ? params[:facet][id] == value : params[:facet][id][facet[:label_id]].to_a.include?(value)
  112 +
  113 + if count > 0
  114 + url = params.merge(:facet => params[:facet].merge(
  115 + id => facet[:label_id].nil? ? value : params[:facet][id].merge( facet[:label_id] => params[:facet][id][facet[:label_id]].to_a.push(value) )
  116 + ))
  117 + else
  118 + # preserve others filters and change this filter
  119 + url = params.merge(:facet => params[:facet].merge(
  120 + id => facet[:label_id].nil? ? value : { facet[:label_id] => value }
  121 + ))
93 122 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')
  123 +
  124 + content_tag 'div', link_to(link_label, url, :class => 'facet-result-link-label') +
  125 + content_tag('span', (has_extra ? label[1] : ''), :class => 'facet-result-extra-label') +
  126 + (count > 0 ? content_tag('span', " (#{count})", :class => 'facet-result-count') : ''),
  127 + :class => 'facet-menu-item' + (selected ? ' facet-result-link-selected' : '')
99 128 end
100 129  
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 = []
  130 + def facet_selecteds_html_for(environment, klass, params)
  131 + def name_with_extra(klass, facet, value)
  132 + name = klass.facet_result_name(facet, value)
  133 + name = name[0] + name[1] if name.kind_of?(Array)
  134 + name
  135 + end
  136 +
  137 + ret = []
  138 + params = params.dup
  139 + params[:facet].each do |id, value|
  140 + facet = klass.facet_by_id(id.to_sym)
  141 + if value.kind_of?(Hash)
  142 + label_hash = facet[:label].call(environment)
  143 + value.each do |label_id, value|
  144 + facet[:label_id] = label_id
  145 + facet[:label] = label_hash[label_id]
  146 + value.to_a.each do |value|
  147 + ret << [facet[:label], name_with_extra(klass, facet, value),
  148 + params.merge(:facet => params[:facet].merge(id => params[:facet][id].merge(label_id => params[:facet][id][label_id].to_a.reject{ |v| v == value })))]
  149 + end
119 150 end
  151 + else
  152 + ret << [klass.facet_label(facet), name_with_extra(klass, facet, value),
  153 + params.merge(:facet => params[:facet].reject{ |k,v| k == id })]
120 154 end
121   - [cat, hits, childs]
122   - end.select{|cat, hits| hits }
  155 + end
123 156  
124   - render(:partial => 'product_categories_menu', :object => product_categories_menu)
  157 + ret.map do |label, name, url|
  158 + content_tag('div', content_tag('span', label, :class => 'facet-selected-label') +
  159 + content_tag('span', name, :class => 'facet-selected-name') +
  160 + link_to('', url, :class => 'facet-selected-remove'), :class => 'facet-selected')
  161 + end.join
  162 + end
  163 +
  164 + def order_by(asset)
  165 + options = SortOptions[asset].map do |name, options|
  166 + next if options[:if] and ! instance_eval(&options[:if])
  167 + [_(options[:label]), name.to_s]
  168 + end.compact
  169 +
  170 + content_tag('div', _('Sort results by ') +
  171 + select_tag(asset.to_s + '[order]', options_for_select(options, params[:order_by] || 'none'),
  172 + {:onchange => "window.location = jQuery.param.querystring(window.location.href, { 'order_by' : this.options[this.selectedIndex].value})"}),
  173 + :class => "search-ordering")
  174 + end
  175 +
  176 + def label_total_found(asset, total_found)
  177 + labels = {
  178 + :products => _("%s products offers found"),
  179 + :articles => _("%s articles found"),
  180 + :events => _("%s events found"),
  181 + :people => _("%s people found"),
  182 + :enterprises => _("%s enterprises found"),
  183 + :communities => _("%s communities found"),
  184 + }
  185 + content_tag('span', labels[asset] % total_found,
  186 + :class => "total-pages-found") if labels[asset]
  187 + end
  188 +
  189 + def asset_class(asset)
  190 + asset.to_s.singularize.camelize.constantize
  191 + end
  192 +
  193 + def asset_table(asset)
  194 + asset_class(asset).table_name
125 195 end
126 196  
127 197 end
... ...
app/models/article.rb
... ... @@ -2,6 +2,12 @@ require &#39;hpricot&#39;
2 2  
3 3 class Article < ActiveRecord::Base
4 4  
  5 + # use for internationalizable human type names in search facets
  6 + # reimplement on subclasses
  7 + def self.type_name
  8 + _('Content')
  9 + end
  10 +
5 11 track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && !a.image? }, :custom_target => :action_tracker_target
6 12 track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && (a.body_changed? || a.name_changed?) }, :custom_target => :action_tracker_target
7 13 track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target
... ... @@ -22,6 +28,9 @@ class Article &lt; ActiveRecord::Base
22 28 has_many :article_categorizations, :conditions => [ 'articles_categories.virtual = ?', false ]
23 29 has_many :categories, :through => :article_categorizations
24 30  
  31 + has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization'
  32 + has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category
  33 +
25 34 acts_as_having_settings :field => :setting
26 35  
27 36 settings_items :display_hits, :type => :boolean, :default => true
... ... @@ -108,6 +117,7 @@ class Article &lt; ActiveRecord::Base
108 117 ids.uniq.each do |item|
109 118 add_category(Category.find(item)) unless item.to_i.zero?
110 119 end
  120 + self.categories(true)
111 121 end
112 122  
113 123 after_create :create_pending_categorizations
... ... @@ -125,8 +135,6 @@ class Article &lt; ActiveRecord::Base
125 135  
126 136 acts_as_versioned
127 137  
128   - acts_as_searchable :additional_fields => [ :comment_data ]
129   -
130 138 def comment_data
131 139 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
132 140 end
... ... @@ -141,13 +149,31 @@ class Article &lt; ActiveRecord::Base
141 149 {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]}
142 150 }
143 151  
  152 + named_scope :public,
  153 + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ?", true, true, true, true ]
  154 +
  155 + named_scope :more_recent,
  156 + :conditions => [ "advertise = ? AND published = ? AND profiles.visible = ? AND profiles.public_profile = ? AND
  157 + ((articles.type != ?) OR articles.type is NULL)",
  158 + true, true, true, true, 'RssFeed'
  159 + ],
  160 + :order => 'articles.published_at desc, articles.id desc'
  161 +
  162 + # retrives the most commented articles, sorted by the comment count (largest
  163 + # first)
  164 + def self.most_commented(limit)
  165 + paginate(:order => 'comments_count DESC', :page => 1, :per_page => limit)
  166 + end
  167 +
  168 + named_scope :more_popular, :order => 'hits DESC'
  169 +
144 170 # retrieves the latest +limit+ articles, sorted from the most recent to the
145 171 # oldest.
146 172 #
147 173 # Only includes articles where advertise == true
148   - def self.recent(limit, extra_conditions = {})
  174 + def self.recent(limit = nil, extra_conditions = {})
149 175 # FIXME this method is a horrible hack
150   - options = { :limit => limit,
  176 + options = { :page => 1, :per_page => limit,
151 177 :conditions => [
152 178 "advertise = ? AND
153 179 published = ? AND
... ... @@ -165,20 +191,14 @@ class Article &lt; ActiveRecord::Base
165 191 options.delete(:include)
166 192 end
167 193 if extra_conditions == {}
168   - self.find(:all, options)
  194 + self.paginate(options)
169 195 else
170 196 with_scope :find => {:conditions => extra_conditions} do
171   - self.find(:all, options)
  197 + self.paginate(options)
172 198 end
173 199 end
174 200 end
175 201  
176   - # retrives the most commented articles, sorted by the comment count (largest
177   - # first)
178   - def self.most_commented(limit)
179   - find(:all, :order => 'comments_count DESC', :limit => limit)
180   - end
181   -
182 202 # produces the HTML code that is to be displayed as this article's contents.
183 203 #
184 204 # The implementation in this class just provides the +body+ attribute as the
... ... @@ -409,7 +429,7 @@ class Article &lt; ActiveRecord::Base
409 429 end
410 430  
411 431 def comments_updated
412   - ferret_update
  432 + solr_save
413 433 end
414 434  
415 435 def accept_category?(cat)
... ... @@ -561,6 +581,92 @@ class Article &lt; ActiveRecord::Base
561 581  
562 582 private
563 583  
  584 + # FIXME: workaround for development env.
  585 + # Subclasses aren't (re)loaded, and acts_as_solr
  586 + # depends on subclasses method to search
  587 + # see http://stackoverflow.com/questions/4138957/activerecordsubclassnotfound-error-when-using-sti-in-rails/4139245
  588 + UploadedFile
  589 + TextArticle
  590 + TinyMceArticle
  591 + TextileArticle
  592 + Folder
  593 + EnterpriseHomepage
  594 + Gallery
  595 + Blog
  596 + Forum
  597 + Event
  598 +
  599 + def self.f_type_proc(klass)
  600 + klass.constantize.type_name
  601 + end
  602 + def self.f_profile_type_proc(klass)
  603 + klass.constantize.type_name
  604 + end
  605 +
  606 + def f_type
  607 + #join common types
  608 + case self.class.name
  609 + when 'TinyMceArticle', 'TextileArticle'
  610 + TextArticle.name
  611 + else
  612 + self.class.name
  613 + end
  614 + end
  615 + def f_profile_type
  616 + self.profile.class.name
  617 + end
  618 + def f_published_at
  619 + self.published_at
  620 + end
  621 + def f_category
  622 + self.categories.collect(&:name)
  623 + end
  624 +
  625 + delegate :region, :region_id, :environment, :environment_id, :to => :profile
  626 + def name_sortable # give a different name for solr
  627 + name
  628 + end
  629 + def public
  630 + self.public?
  631 + end
  632 + def category_filter
  633 + categories_including_virtual_ids
  634 + end
  635 + public
  636 +
  637 + acts_as_faceted :fields => {
  638 + :f_type => {:label => _('Type'), :proc => proc{|klass| f_type_proc(klass)}},
  639 + :f_published_at => {:type => :date, :label => _('Published date'), :queries => {'[* TO NOW-1YEARS/DAY]' => _("Older than one year"),
  640 + '[NOW-1YEARS TO NOW/DAY]' => _("Last year"), '[NOW-1MONTHS TO NOW/DAY]' => _("Last month"), '[NOW-7DAYS TO NOW/DAY]' => _("Last week"), '[NOW-1DAYS TO NOW/DAY]' => _("Last day")},
  641 + :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]']},
  642 + :f_profile_type => {:label => _('Profile'), :proc => proc{|klass| f_profile_type_proc(klass)}},
  643 + :f_category => {:label => _('Categories')},
  644 + }, :category_query => proc { |c| "category_filter:\"#{c.id}\"" },
  645 + :order => [:f_type, :f_published_at, :f_profile_type, :f_category]
  646 +
  647 + acts_as_searchable :fields => facets_fields_for_solr + [
  648 + # searched fields
  649 + {:name => {:type => :text, :boost => 2.0}},
  650 + {:slug => :text}, {:body => :text},
  651 + {:abstract => :text}, {:filename => :text},
  652 + # filtered fields
  653 + {:public => :boolean}, {:environment_id => :integer},
  654 + {:profile_id => :integer}, :language,
  655 + {:category_filter => :integer},
  656 + # ordered/query-boosted fields
  657 + {:name_sortable => :string}, :last_changed_by_id, :published_at, :is_image,
  658 + :updated_at, :created_at,
  659 + ], :include => [
  660 + {:profile => {:fields => [:name, :identifier, :address, :nickname, :region_id, :lat, :lng]}},
  661 + {:comments => {:fields => [:title, :body, :author_name, :author_email]}},
  662 + {:categories => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}},
  663 + ], :facets => facets_option_for_solr,
  664 + :boost => proc { |a| 10 if a.profile.enabled },
  665 + :if => proc{ |a| ! ['RssFeed'].include?(a.class.name) }
  666 + handle_asynchronously :solr_save
  667 +
  668 + private
  669 +
564 670 def sanitize_tag_list
565 671 sanitizer = HTML::FullSanitizer.new
566 672 self.tag_list.names.map!{|i| strip_tag_name sanitizer.sanitize(i) }
... ...
app/models/blog.rb
... ... @@ -9,6 +9,10 @@ class Blog &lt; Folder
9 9 end
10 10 alias_method_chain :posts, :no_folders
11 11  
  12 + def self.type_name
  13 + _('Blog')
  14 + end
  15 +
12 16 def self.short_description
13 17 _('Blog')
14 18 end
... ...
app/models/category.rb
... ... @@ -15,13 +15,13 @@ class Category &lt; 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 25 has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise'
26 26 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
27 27 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community'
... ... @@ -36,18 +36,38 @@ class Category &lt; ActiveRecord::Base
36 36 { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] }
37 37 }
38 38  
  39 + def recent_people(limit = 10)
  40 + self.people.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  41 + end
  42 +
  43 + def recent_enterprises(limit = 10)
  44 + self.enterprises.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  45 + end
  46 +
  47 + def recent_communities(limit = 10)
  48 + self.communities.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  49 + end
  50 +
  51 + def recent_products(limit = 10)
  52 + self.products.paginate(:order => 'created_at DESC, id DESC', :page => 1, :per_page => limit)
  53 + end
  54 +
39 55 def recent_articles(limit = 10)
40 56 self.articles.recent(limit)
41 57 end
42 58  
43 59 def recent_comments(limit = 10)
44   - comments.find(:all, :order => 'created_at DESC, comments.id DESC', :limit => limit)
  60 + comments.paginate(:all, :order => 'created_at DESC, comments.id DESC', :page => 1, :per_page => limit)
45 61 end
46 62  
47 63 def most_commented_articles(limit = 10)
48 64 self.articles.most_commented(limit)
49 65 end
50 66  
  67 + def upcoming_events(limit = 10)
  68 + self.events.paginate(:conditions => [ 'start_date >= ?', Date.today ], :order => 'start_date', :page => 1, :per_page => limit)
  69 + end
  70 +
51 71 def display_in_menu?
52 72 display_in_menu
53 73 end
... ... @@ -69,4 +89,23 @@ class Category &lt; ActiveRecord::Base
69 89 self.children.find(:all, :conditions => {:display_in_menu => true}).empty?
70 90 end
71 91  
  92 + private
  93 + def name_sortable # give a different name for solr
  94 + name
  95 + end
  96 + public
  97 +
  98 + acts_as_searchable :fields => [
  99 + # searched fields
  100 + {:name => {:type => :text, :boost => 2.0}},
  101 + {:path => :text}, {:slug => :text},
  102 + {:abbreviation => :text}, {:acronym => :text},
  103 + # filtered fields
  104 + :parent_id,
  105 + # ordered/query-boosted fields
  106 + {:name_sortable => :string},
  107 + ]
  108 + after_save_reindex [:articles, :profiles], :with => :delayed_job
  109 + handle_asynchronously :solr_save
  110 +
72 111 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 &lt; ActiveRecord::Base
2 2  
3 3 belongs_to :environment
4 4  
5   - has_many :qualifier_certifiers
  5 + has_many :qualifier_certifiers, :dependent => :destroy
6 6 has_many :qualifiers, :through => :qualifier_certifiers
7 7  
  8 + has_many :product_qualifiers
  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 &lt; 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/communities_block.rb
... ... @@ -21,7 +21,7 @@ class CommunitiesBlock &lt; ProfileListBlock
21 21 end
22 22 when Environment
23 23 lambda do
24   - link_to s_('communities|View all'), :controller => 'browse', :action => 'communities'
  24 + link_to s_('communities|View all'), :controller => 'search', :action => 'communities'
25 25 end
26 26 else
27 27 ''
... ...
app/models/community.rb
1 1 class Community < Organization
  2 +
  3 + def self.type_name
  4 + _('Community')
  5 + end
  6 +
2 7 N_('Community')
3 8 N_('Language')
4 9  
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
... ...
app/models/domain.rb
... ... @@ -8,9 +8,9 @@ class Domain &lt; 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 &lt; 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 &lt; 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 &lt; 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?
... ...
app/models/enterprise_homepage.rb
1 1 class EnterpriseHomepage < Article
2 2  
  3 + def self.type_name
  4 + _('Homepage')
  5 + end
  6 +
3 7 def self.short_description
4 8 __('Enterprise homepage.')
5 9 end
... ...
app/models/environment.rb
... ... @@ -248,6 +248,10 @@ class Environment &lt; 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
... ... @@ -354,11 +358,11 @@ class Environment &lt; ActiveRecord::Base
354 358 end
355 359  
356 360 def terminology
357   - if self.settings[:terminology]
358   - self.settings[:terminology].constantize.instance
359   - else
  361 + #if self.settings[:terminology]
  362 + #self.settings[:terminology].constantize.instance
  363 + #else
360 364 Noosfero.terminology
361   - end
  365 + #end
362 366 end
363 367  
364 368 def terminology=(value)
... ...
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)
... ...
app/models/folder.rb
1 1 class Folder < Article
2 2  
  3 + def self.type_name
  4 + _('Folder')
  5 + end
  6 +
3 7 validate :not_belong_to_blog
4 8  
5 9 def not_belong_to_blog
... ...
app/models/forum.rb
... ... @@ -2,6 +2,10 @@ class Forum &lt; Folder
2 2  
3 3 acts_as_having_posts :order => 'updated_at DESC'
4 4  
  5 + def self.type_name
  6 + _('Forum')
  7 + end
  8 +
5 9 def self.short_description
6 10 _('Forum')
7 11 end
... ...
app/models/gallery.rb
1 1 class Gallery < Folder
2 2  
  3 + def self.type_name
  4 + _('Gallery')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Gallery')
5 9 end
... ...
app/models/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&amp;v=2&amp;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
... ... @@ -23,4 +23,9 @@ class Image &lt; ActiveRecord::Base
23 23  
24 24 postgresql_attachment_fu
25 25  
  26 + alias_method :public_filename_old, :public_filename
  27 + def public_filename(*args)
  28 + "http://cirandas.net#{public_filename_old(args)}"
  29 + end
  30 +
26 31 end
... ...
app/models/location_block.rb
... ... @@ -17,10 +17,10 @@ class LocationBlock &lt; 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.'))
... ...
app/models/national_region.rb 0 → 100644
... ... @@ -0,0 +1,76 @@
  1 +class NationalRegion < ActiveRecord::Base
  2 +
  3 + def self.search_city(city_name, like = false, state = nil)
  4 +
  5 + operator = "="
  6 + find_return = :first
  7 + adtional_contions = "";
  8 +
  9 + if like
  10 + operator = "like"
  11 + find_return = :all
  12 + end
  13 +
  14 + if state
  15 + adtional_contions = " AND nr.name = :state "
  16 + end
  17 +
  18 +
  19 + conditions = ["national_regions.name #{operator} :name AND
  20 + national_regions.national_region_type_id = :type" + adtional_contions,
  21 + {:name => city_name ,
  22 + :type => NationalRegionType::CITY,
  23 + :state => state}];
  24 +
  25 + region = NationalRegion.find(find_return,
  26 + :select => "national_regions.name as city, nr.name as state, national_regions.national_region_code",
  27 + :conditions => conditions,
  28 + :joins => "LEFT JOIN national_regions as nr ON national_regions.parent_national_region_code = nr.national_region_code",
  29 + :limit => 10
  30 + )
  31 + return region
  32 + end
  33 +
  34 + def self.search_state(state_name, like = false)
  35 + operator = "="
  36 + find_return = :first
  37 +
  38 + if like
  39 + operator = "like"
  40 + find_return = :all
  41 + end
  42 +
  43 + conditions = ["national_regions.name #{operator} :name AND
  44 + national_regions.national_region_type_id = :type",
  45 + {:name => state_name,
  46 + :type => NationalRegionType::STATE}];
  47 +
  48 + region = NationalRegion.find(find_return,
  49 + :select => "national_regions.name as state, national_regions.national_region_code",
  50 + :conditions => conditions,
  51 + :limit => 10
  52 + )
  53 + return region
  54 + end
  55 +
  56 + def self.validate!(city, state, country)
  57 +
  58 + country_region = NationalRegion.find_by_national_region_code(country,
  59 + :conditions => ["national_region_type_id = :type",
  60 + {:type => NationalRegionType::COUNTRY}])
  61 +
  62 + if(country_region)
  63 +
  64 + nregion = NationalRegion.search_city(city, false, state);
  65 +
  66 + if nregion == nil
  67 + raise _('Invalid city or state name.')
  68 + end
  69 +
  70 + end
  71 +
  72 + return nregion
  73 +
  74 + end
  75 +
  76 +end
... ...
app/models/national_region_type.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class NationalRegionType < ActiveRecord::Base
  2 + COUNTRY = 1
  3 + STATE = 2
  4 + CITY = 3
  5 +end
... ...
app/models/people_block.rb
... ... @@ -18,7 +18,7 @@ class PeopleBlock &lt; ProfileListBlock
18 18  
19 19 def footer
20 20 lambda do
21   - link_to _('View all'), :controller => 'browse', :action => 'people'
  21 + link_to _('View all'), :controller => 'search', :action => 'people'
22 22 end
23 23 end
24 24  
... ...
app/models/person.rb
1 1 # A person is the profile of an user holding all relationships with the rest of the system
2 2 class Person < Profile
3 3  
  4 + def self.type_name
  5 + _('Person')
  6 + end
  7 +
4 8 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
5 9 acts_as_accessor
6 10  
... ... @@ -173,6 +177,9 @@ class Person &lt; Profile
173 177 N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code')
174 178 settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code
175 179  
  180 + extend SetProfileRegionFromCityState::ClassMethods
  181 + set_profile_region_from_city_state
  182 +
176 183 def self.conditions_for_profiles(conditions, person)
177 184 new_conditions = sanitize_sql(['role_assignments.accessor_id = ?', person])
178 185 new_conditions << ' AND ' + sanitize_sql(conditions) unless conditions.blank?
... ...
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 &lt; 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 => "updated_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 &lt; 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 &lt; 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|
... ... @@ -213,4 +196,100 @@ class Product &lt; 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.each{ |i| t_i += 1; se_i += 1 if i.is_from_solidarity_economy }
  202 + p = case (se_i.to_f/t_i)*100
  203 + when 0..24.999 then [0, _("")];
  204 + when 25..49.999 then [25, _("25%")];
  205 + when 50..74.999 then [50, _("50%")];
  206 + when 75..99.999 then [75, _("75%")];
  207 + when 100 then [100, _("100%")];
  208 + end
  209 + end
  210 +
  211 + private
  212 + def f_category
  213 + self.product_category.name
  214 + end
  215 + def f_region
  216 + self.enterprise.region.id if self.enterprise.region
  217 + end
  218 + def self.f_region_proc(id)
  219 + c = Region.find(id)
  220 + s = c.parent
  221 + if c and c.kind_of?(City) and s and s.kind_of?(State) and s.acronym
  222 + [c.name, ', ' + s.acronym]
  223 + else
  224 + c.name
  225 + end
  226 + end
  227 + def self.f_qualifier_proc(ids)
  228 + array = ids.split ' '
  229 + qualifier = Qualifier.find_by_id array[0]
  230 + certifier = Certifier.find_by_id array[1]
  231 + certifier ? [qualifier.name, _(' cert. ') + certifier.name] : qualifier.name
  232 + end
  233 + def f_qualifier
  234 + product_qualifiers.map do |pq|
  235 + "#{pq.qualifier_id} #{pq.certifier_id}"
  236 + end
  237 + end
  238 +
  239 + alias_method :name_sortable, :name
  240 + delegate :region, :region_id, :environment, :environment_id, :to => :enterprise
  241 + def name_sortable # give a different name for solr
  242 + name
  243 + end
  244 + def public
  245 + self.public?
  246 + end
  247 + def price_sortable
  248 + (price.nil? or price.zero?) ? nil : price
  249 + end
  250 + def category_filter
  251 + enterprise.categories_including_virtual_ids << product_category_id
  252 + end
  253 + public
  254 +
  255 + acts_as_faceted :fields => {
  256 + :f_category => {:label => _('Related products')},
  257 + :f_region => {:label => _('City'), :proc => proc { |id| f_region_proc(id) }},
  258 + :f_qualifier => {:label => _('Qualifiers'), :proc => proc { |id| f_qualifier_proc(id) }},
  259 + }, :category_query => proc { |c| "category_filter:#{c.id}" },
  260 + :order => [:f_category, :f_region, :f_qualifier]
  261 +
  262 + Boosts = [
  263 + [:image, 0.4, proc{ |p| p.image ? 1 : 0}],
  264 + [:qualifiers, 0.3, proc{ |p| p.product_qualifiers.count > 0 ? 1 : 0}],
  265 + [:open_price, 0.3, proc{ |p| p.price_described? ? 1 : 0}],
  266 + [:solidarity, 0.3, proc{ |p| p.inputs.count > 0 ? p.percentage_from_solidarity_economy[0]/100 : 0 }],
  267 + [:available, 0.2, proc{ |p| p.available ? 1 : 0}],
  268 + [:price, 0.2, proc{ |p| (!p.price.nil? and p.price > 0) ? 1 : 0}],
  269 + [:new_product, 0.2, proc{ |p| (p.updated_at.to_i - p.created_at.to_i) < 24*3600 ? 1 : 0}],
  270 + [:description, 0.15, proc{ |p| (!p.description.nil? and !p.description.empty?) ? 1 : 0}],
  271 + [:enabled, 0.05, proc{ |p| p.enterprise.enabled ? 1 : 0}],
  272 + ]
  273 +
  274 + acts_as_searchable :fields => facets_fields_for_solr + [
  275 + # searched fields
  276 + {:name => {:type => :text, :boost => 2.0}},
  277 + {:description => :text},
  278 + # filtered fields
  279 + {:public => :boolean}, {:environment_id => :integer},
  280 + {:category_filter => :integer},
  281 + # ordered/query-boosted fields
  282 + {:price_sortable => :decimal}, {:name_sortable => :string},
  283 + {:lat => :float}, {:lng => :float},
  284 + :updated_at, :created_at,
  285 + ], :include => [
  286 + {:product_category => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}},
  287 + {:region => {:fields => [:name, :path, :slug, :lat, :lng]}},
  288 + {:enterprise => {:fields => [:name, :identifier, :address, :nickname, :lat, :lng]}},
  289 + {:qualifiers => {:fields => [:name]}},
  290 + {:certifiers => {:fields => [:name]}},
  291 + ], :facets => facets_option_for_solr,
  292 + :boost => proc{ |p| boost = 1; Boosts.each{ |b| boost = boost * (1 - ((1 - b[2].call(p)) * b[1])) }; boost}
  293 + handle_asynchronously :solr_save
  294 +
216 295 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 &lt; 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 &lt; 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 &lt; 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  
... ... @@ -181,8 +183,20 @@ class Profile &lt; ActiveRecord::Base
181 183 has_many :profile_categorizations, :conditions => [ 'categories_profiles.virtual = ?', false ]
182 184 has_many :categories, :through => :profile_categorizations
183 185  
  186 + has_many :profile_categorizations_including_virtual, :class_name => 'ProfileCategorization'
  187 + has_many :categories_including_virtual, :through => :profile_categorizations_including_virtual, :source => :category
  188 +
184 189 has_many :abuse_complaints, :foreign_key => 'requestor_id'
185 190  
  191 + def top_level_categorization
  192 + ret = {}
  193 + self.profile_categorizations.each do |c|
  194 + p = c.category.top_ancestor
  195 + ret[p] = (ret[p] || []) + [c.category]
  196 + end
  197 + ret
  198 + end
  199 +
186 200 def interests
187 201 categories.select {|item| !item.is_a?(Region)}
188 202 end
... ... @@ -822,18 +836,89 @@ private :generate_url, :url_options
822 836 name
823 837 end
824 838  
825   - protected
826   -
827   - def followed_by?(person)
828   - person.is_member_of?(self)
  839 + private
  840 + def self.f_categories_label_proc(environment)
  841 + ids = environment.top_level_category_as_facet_ids
  842 + r = Category.find(ids)
  843 + map = {}
  844 + ids.map{ |id| map[id.to_s] = r.detect{|c| c.id == id}.name }
  845 + map
  846 + end
  847 + def self.f_categories_proc(facet, id)
  848 + id = id.to_i
  849 + return if id.zero?
  850 + c = Category.find(id)
  851 + c.name if c.top_ancestor.id == facet[:label_id].to_i or facet[:label_id] == 0
  852 + end
  853 + def f_categories
  854 + category_ids
  855 + end
  856 + def f_region
  857 + self.region_id
  858 + end
  859 + def self.f_region_proc(id)
  860 + c = Region.find(id)
  861 + s = c.parent
  862 + if c and c.kind_of?(City) and s and s.kind_of?(State) and s.acronym
  863 + [c.name, ', ' + s.acronym]
  864 + else
  865 + c.name
829 866 end
  867 + end
830 868  
831   - def display_private_info_to?(user)
832   - if user.nil?
833   - false
834   - else
835   - (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)
836   - end
  869 + def name_sortable # give a different name for solr
  870 + name
  871 + end
  872 + def public
  873 + self.public?
  874 + end
  875 + def category_filter
  876 + categories_including_virtual_ids
  877 + end
  878 + public
  879 +
  880 + acts_as_faceted :fields => {
  881 + :f_region => {:label => _('City'), :proc => proc { |id| f_region_proc(id) }},
  882 + :f_categories => {:multi => true, :proc => proc {|facet, id| f_categories_proc(facet, id)},
  883 + :label => proc { |env| f_categories_label_proc(env) }, :label_abbrev => proc{ |env| f_categories_label_abbrev_proc(env) }},
  884 + }, :category_query => proc { |c| "category_filter:#{c.id}" },
  885 + :order => [:f_region, :f_categories]
  886 +
  887 + acts_as_searchable :fields => facets_fields_for_solr + [:extra_data_for_index,
  888 + # searched fields
  889 + {:name => {:type => :text, :boost => 2.0}},
  890 + {:identifier => :text}, {:address => :text}, {:nickname => :text},
  891 + # filtered fields
  892 + {:public => :boolean}, {:environment_id => :integer},
  893 + {:category_filter => :integer},
  894 + # ordered/query-boosted fields
  895 + {:name_sortable => :string}, {:user_id => :integer},
  896 + :enabled, :active, :validated, :public_profile,
  897 + {:lat => :float}, {:lng => :float},
  898 + :updated_at, :created_at,
  899 + ],
  900 + :include => [
  901 + {:region => {:fields => [:name, :path, :slug, :lat, :lng]}},
  902 + {:categories => {:fields => [:name, :path, :slug, :lat, :lng, :acronym, :abbreviation]}},
  903 + ], :facets => facets_option_for_solr,
  904 + :boost => proc{ |p| 10 if p.enabled }
  905 + after_save_reindex [:articles], :with => :delayed_job
  906 + handle_asynchronously :solr_save
  907 +
  908 + def control_panel_settings_button
  909 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  910 + end
  911 +
  912 + def followed_by?(person)
  913 + person.is_member_of?(self)
  914 + end
  915 +
  916 + def display_private_info_to?(user)
  917 + if user.nil?
  918 + false
  919 + else
  920 + (user == self) || (user.is_admin?(self.environment)) || user.is_admin?(self) || user.memberships.include?(self)
837 921 end
  922 + end
838 923  
839 924 end
... ...
app/models/qualifier.rb
... ... @@ -2,16 +2,19 @@ class Qualifier &lt; ActiveRecord::Base
2 2  
3 3 belongs_to :environment
4 4  
5   - has_many :qualifier_certifiers
  5 + has_many :qualifier_certifiers, :dependent => :destroy
6 6 has_many :certifiers, :through => :qualifier_certifiers
7 7  
  8 + has_many :product_qualifiers, :dependent => :destroy
  9 + has_many :products, :through => :product_qualifiers, :source => :product
  10 +
8 11 validates_presence_of :environment_id
9 12 validates_presence_of :name
10 13  
11   - has_many :product_qualifiers, :dependent => :destroy
12   -
13 14 def <=>(b)
14 15 self.name.downcase.transliterate <=> b.name.downcase.transliterate
15 16 end
16 17  
  18 + after_save_reindex [:products], :with => :delayed_job
  19 +
17 20 end
... ...
app/models/qualifier_certifier.rb
1 1 class QualifierCertifier < ActiveRecord::Base
2 2 belongs_to :qualifier
3 3 belongs_to :certifier
  4 +
  5 + validates_presence_of :qualifier
4 6 end
... ...
app/models/raw_html_article.rb
1 1 class RawHTMLArticle < TextArticle
2 2  
  3 + def self.type_name
  4 + _('HTML')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Raw HTML text article.')
5 9 end
... ...
app/models/region.rb
... ... @@ -5,10 +5,9 @@ class Region &lt; 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].reject {|item| self.validator_ids.include?(item.id) }
12 11 end
13 12  
14 13 def has_validator?
... ...
app/models/rss_feed.rb
1 1 class RssFeed < Article
2 2  
  3 + def self.type_name
  4 + _('RssFeed')
  5 + end
  6 +
3 7 # i dont know why before filter dont work here
4 8 def initialize(*args)
5 9 super(*args)
... ...
app/models/text_article.rb
... ... @@ -3,6 +3,10 @@ class TextArticle &lt; Article
3 3  
4 4 xss_terminate :only => [ :name ], :on => 'validation'
5 5  
  6 + def self.type_name
  7 + _('Article')
  8 + end
  9 +
6 10 include Noosfero::TranslatableContent
7 11  
8 12 def self.icon_name(article = nil)
... ...
app/models/textile_article.rb
1 1 class TextileArticle < TextArticle
2 2  
  3 + def self.type_name
  4 + _('Article')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Text article with Textile markup language')
5 9 end
... ...
app/models/tiny_mce_article.rb
1 1 class TinyMceArticle < TextArticle
2 2  
  3 + def self.type_name
  4 + _('Article')
  5 + end
  6 +
3 7 def self.short_description
4 8 _('Text article with visual editor.')
5 9 end
... ...
app/models/uploaded_file.rb
... ... @@ -4,6 +4,10 @@
4 4 # of the file itself is kept. (FIXME?)
5 5 class UploadedFile < Article
6 6  
  7 + def self.type_name
  8 + _('File')
  9 + end
  10 +
7 11 track_actions :upload_image, :after_create, :keep_params => ["view_url", "thumbnail_path", "parent.url", "parent.name"], :if => Proc.new { |a| a.published? && a.image? && !a.parent.nil? && a.parent.gallery? }
8 12  
9 13 include ShortFilename
... ...
app/views/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
... ... @@ -1,3 +0,0 @@
1   -<%= profile_image_link result, :portrait, 'li',
2   - "<span class='adr'>#{result.city}</span>" +
3   - (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %>
app/views/browse/_profile.rhtml
... ... @@ -1 +0,0 @@
1   -<%= profile_image_link result, :portrait, 'li', @filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label') %>
app/views/browse/_search_form.rhtml
... ... @@ -1,10 +0,0 @@
1   -<% form_tag( { :controller => 'browse', :action => action}, :method => 'get', :class => 'search_form' ) do %>
2   -
3   - <div class="search-field">
4   - <span class="formfield">
5   - <%= text_field_tag 'query', @query, :size => 50 %>
6   - </span>
7   - <%= submit_button(:search, _('Search')) %>
8   - </div>
9   -
10   -<% end %>
app/views/browse/communities.rhtml
... ... @@ -1,9 +0,0 @@
1   -<%= search_page_title( @title, { :query => @query} ) %>
2   -
3   -<%= render :partial => 'search_form', :locals => {:action => 'communities'} %>
4   -
5   -<%= render :partial => 'display_results' %>
6   -
7   -<%= pagination_links @results %>
8   -
9   -<br style="clear:both" />
app/views/browse/contents.rhtml
... ... @@ -1,9 +0,0 @@
1   -<%= search_page_title( @title, { :query => @query} ) %>
2   -
3   -<%= render :partial => 'search_form', :locals => {:action => 'contents'} %>
4   -
5   -<%= render :partial => 'display_results' %>
6   -
7   -<%= pagination_links @results %>
8   -
9   -<br style="clear:both" />
app/views/browse/people.rhtml
... ... @@ -1,9 +0,0 @@
1   -<%= search_page_title( @title, { :query => @query} ) %>
2   -
3   -<%= render :partial => 'search_form', :locals => {:action => 'people'} %>
4   -
5   -<%= render :partial => 'display_results' %>
6   -
7   -<%= pagination_links @results %>
8   -
9   -<br style="clear:both" />
app/views/events/events.rhtml
1   -<h1 id='agenda-title'>
2   - <div id='agenda-toolbar'>
3   - <%= button :back, _('Back to %s') % profile.name, profile.url %>
4   - <% if user && user.has_permission?('post_content', profile) %>
5   - <%= button :new, _('New event'), myprofile_url(:controller => 'cms', :action => 'new', :type => 'Event') %>
6   - <% end %>
7   - </div>
8   - <%= _("%s's events") % profile.name %>
9   -</h1>
  1 +<h1 id='agenda-title'><%= _("%s's events") % profile.name %></h1>
  2 +
  3 +<div id='agenda-toolbar'>
  4 + <%= button :back, _('Back to %s') % profile.name, profile.url %>
  5 + <% if user && user.has_permission?('post_content', profile) %>
  6 + <%= button :new, _('New event'), myprofile_url(:controller => 'cms', :action => 'new', :type => 'Event') %>
  7 + <% end %>
  8 +</div>
  9 +
  10 +<div style="clear: both"></div>
10 11  
11 12 <%= render :partial => 'agenda' %>
... ...
app/views/layouts/_javascript.rhtml
1   -<%= javascript_include_tag :defaults, 'jquery-latest.js', 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery.cookie', 'reflection', 'add-and-join', 'jquery.tokeninput', 'report-abuse','colorbox', 'jquery-validation/jquery.validate', 'catalog', 'manage-products', :cache => 'cache-general' %>
  1 +<%= javascript_include_tag :defaults, 'jquery-latest.js',
  2 +'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox',
  3 +'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js',
  4 +'jquery.cookie', 'reflection', 'add-and-join', 'jquery.tokeninput',
  5 +'jquery.ba-bbq.min.js', 'report-abuse','colorbox', 'jquery-validation/jquery.validate', 'catalog',
  6 +'manage-products', :cache => 'cache-general' %>
2 7 <% language = FastGettext.locale %>
3   -<%= javascript_include_tag 'jquery-validation/localization/messages_'+language, 'jquery-validation/localization/methods_'+language %>
  8 +<%= javascript_include_tag 'jquery-validation/localization/messages_'+language,
  9 + 'jquery-validation/localization/methods_'+language %>
... ...
app/views/layouts/application-ng.rhtml
... ... @@ -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/_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/show.rhtml
... ... @@ -9,7 +9,7 @@
9 9 </div>
10 10  
11 11 <div id='product-details'>
12   - <div id='product-image'>
  12 + <div id='product-image' class="zoomable-image">
13 13 <%= render :partial => 'manage_products/display_image' %>
14 14 </div>
15 15 <div id='product-extra-content'>
... ...
app/views/map_balloon/product.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<div id="balloon">
  2 + <%= render :partial => 'search/product', :locals => {:product => @product} %>
  3 +</div>
... ...
app/views/map_balloon/profile.rhtml 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +<div id="balloon">
  2 + <table class='profile-info'>
  3 + <tr>
  4 + <td><div class='profile-info-picture'><%= profile_image(@profile, :thumb) %></div></td>
  5 + <td>
  6 + <strong><%= link_to(@profile.name, url_for(@profile.url)) %></strong><br/>
  7 + <% unless @profile.contact_email.nil? %>
  8 + <strong><%= _('E-Mail: ') + @profile.contact_email %></strong><br/>
  9 + <% end %>
  10 + <% unless @profile.contact_phone.nil? %>
  11 + <strong><%= _('Phone(s): ') + @profile.contact_phone %></strong><br/>
  12 + <% end %>
  13 + <% unless @profile.region.nil? %>
  14 + <strong><%= _('Location: ') + @profile.region.name %></strong><br/>
  15 + <% end %>
  16 + <% unless @profile.address.nil? %>
  17 + <strong><%= _('Address: ') + @profile.address %></strong><br/>
  18 + <% end %>
  19 + <% unless @profile.products.empty? %>
  20 + <strong><%= _('Products/Services: ') + @profile.products.map{|i| link_to(i.name, :controller => 'manage_products', :profile => @profile.identifier, :action => 'show', :id => i.id)}.join(', ') %></strong><br/>
  21 + <% end %>
  22 + <% if @profile.respond_to?(:distance) and !@profile.distance.nil? %>
  23 + <strong><%= _('Distance: ') + "%.2f%" % @profile.distance %></strong><br/>
  24 + <% end %>
  25 + </td>
  26 + </tr>
  27 + </table>
  28 +</div>
... ...
app/views/maps/_google_map.js.erb 0 → 100644
... ... @@ -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 %>
32   -
  27 + <%= f.hidden_field :lat %>
  28 + <%= f.hidden_field :lng %>
  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') %>
  34 +
... ...
app/views/search/_article.rhtml
1   -<li class="<%= icon_for_article(article) %>">
2   - <strong><%= link_to(article.title, article.view_url) %></strong>
3   - <div class="item_meta">
4   - <% if article.last_changed_by %>
5   - <span class="cat_item_by">
6   - <%= _('by %s') % link_to(article.last_changed_by.name, article.last_changed_by.url) %>
7   - </span>
8   - <% end %>
9   - <span class="cat_item_update"><%= _('Last update: %s.') % show_date(article.updated_at) %></span>
  1 +<li class="search-article-item article-item">
  2 + <%= link_to(article.title, article.url, :class => "search-result-title") %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => article %>
10 5 </div>
  6 + <table class="noborder search-content-second-column">
  7 + <%= render :partial => 'article_common', :object => article %>
  8 + </table>
  9 + <%= render :partial => 'article_last_change', :object => article %>
  10 +
  11 + <div style="clear:both"></div>
11 12 </li>
... ...
app/views/search/_article_author.rhtml 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<% article = article_author %>
  2 +
  3 +<tr class="search-article-author search-article-author-name">
  4 + <td class="search-field-label"><%= _("Author") %></td>
  5 + <td>
  6 + <%= link_to_profile article.profile.name, article.profile.identifier %>
  7 + </td>
  8 +</tr>
... ...
app/views/search/_article_categories.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<tr class="search-article-categories">
  2 + <td class="search-field-label"><%= _('Categories') %></td>
  3 + <td class="search-article-categories-container <%= "search-field-none" if article_categories.empty? %>">
  4 + <% article_categories.each do |category| %>
  5 + <%= link_to_category category, false, :class => "search-article-category" %>
  6 + <% end %>
  7 + <%= _('None') if article_categories.empty? %>
  8 + </td>
  9 +</tr>
... ...
app/views/search/_article_common.rhtml 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<% article = article_common %>
  2 +<% show_description = true if show_description.nil? %>
  3 +
  4 +<%= render :partial => 'article_author', :object => article %>
  5 +<%= render :partial => 'article_description', :object => article if show_description %>
  6 +<%= render :partial => 'article_tags', :object => article.tags %>
  7 +<%= render :partial => 'article_categories', :object => article.categories %>
... ...
app/views/search/_article_description.rhtml 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<% article = article_description %>
  2 +
  3 +<tr class="search-article-description">
  4 + <td class="search-field-label"><%= _("Description") %></td>
  5 +
  6 + <% if !article.body.blank? %>
  7 + <% description = strip_tags(article.body.to_s) %>
  8 + <% description = excerpt(description, description.first(3), 200).gsub!(/\s{2,}/, ' ') %>
  9 + <td class="search-article-body"><%= description %></td>
  10 + <% else %>
  11 + <td class="search-field-none"><%= _('None') %></td>
  12 + <% end %>
  13 +</tr>
... ...
app/views/search/_article_last_change.rhtml 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<% article = article_last_change %>
  2 +
  3 +<div class="search-article-author-changes">
  4 + <% if article.last_changed_by and article.last_changed_by != article.profile %>
  5 + <span><%= _('by %{name} at %{date}') % {:name => link_to(article.last_changed_by.name, article.last_changed_by.url),
  6 + :date => show_date(article.updated_at) } %></span>
  7 + <% else %>
  8 + <span><%= _('Last update: %s.') % show_date(article.updated_at) %></span>
  9 + <% end %>
  10 +</div>
... ...
app/views/search/_article_tags.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<tr class="search-article-tags">
  2 + <td class="search-field-label"><%= _('Tags') %></td>
  3 + <td class="search-article-tags-container <%= "search-field-none" if article_tags.empty? %>">
  4 + <% article_tags.each do |tag| %>
  5 + <%= link_to_tag tag, :class => "search-article-tag" %>
  6 + <% end %>
  7 + <%= _('None') if article_tags.empty? %></td>
  8 + </td>
  9 +</tr>
... ...
app/views/search/_blog.rhtml 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +<li class="search-blog article-item">
  2 + <%= link_to blog.title, blog.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => blog %>
  5 + </div>
  6 + <table class="noborder search-content-second-column">
  7 + <tr class="search-blog-items">
  8 + <td class="search-field-label"><%= _("Last posts") %></td>
  9 +
  10 + <% r = blog.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %>
  11 + <td class="<%= "search-field-none" if r.empty? %>">
  12 + <% r.each do |a| %>
  13 + <%= link_to a.title, a.view_url, :class => 'search-blog-sample-item '+icon_for_article(a) %>
  14 + <% end %>
  15 + <%= _('None') if r.empty? %>
  16 + </td>
  17 + </tr>
  18 +
  19 + <%= render :partial => 'article_common', :object => blog %>
  20 + </table>
  21 + <%= render :partial => 'article_last_change', :object => blog %>
  22 +
  23 + <div style="clear: both;"/></div>
  24 +</li>
... ...
app/views/search/_content.rhtml 0 → 120000
... ... @@ -0,0 +1 @@
  1 +_article.rhtml
0 2 \ No newline at end of file
... ...
app/views/search/_display_results.rhtml
1   -<div id="search-results" class="<%= 'only-one-result-box' if @results.size == 1 %>">
  1 +<div id="search-results" class="<%= @results.size == 1 ? 'only-one-result-box' : 'multiple-results-boxes' %>">
  2 + <% @order.each do |name| %>
  3 + <% results = @results[name] %>
  4 + <% empty = results.nil? || results.empty? %>
2 5  
3   -<%
4   - pos2 = :odd # allow to format in a two columns layout
5   - pos3 = 3 # allow to format in a thre columns layout
6   -%>
7   -<% @order.each do |name| %>
8   - <% results = @results[name] %>
9   - <%
10   - pos3 += 1; pos3 = 1 if pos3 > 3
11   - pos2==:odd ? pos2=:even : pos2=:odd
12   - %>
13   - <% if !results.nil? and !results.empty? %>
14   - <div class="search-results-<%= name %> search-results-box <%= pos2 %> <%= 'col%s_of3' % pos3.to_s %>">
15   - <% if @controller.action_name != 'assets' %>
16   - <% if @results.size != 1 %>
17   - <h3>
18   - <%= @names[name] %>
19   - </h3>
20   - <% end %>
21   - <%# FIXME: don't hardcode an asset like this %>
22   - <% if name == :most_commented_articles %>
23   - <%= link_to( results.respond_to?(:total_entries) ? _('see all (%d)') % results.total_entries : _('see all...'),
24   - params.merge(:action => 'index',
25   - :asset => 'articles' ),
26   - :class => 'see-more' ) if @results.size > 1 %>
  6 + <div class="search-results-<%= name %> search-results-box <%= "search-results-empty" if empty %>">
  7 + <% if not empty %>
  8 + <% partial = partial_for_class(results.first.class.class_name.constantize) %>
27 9  
28   - <% else %>
29   - <%= link_to( results.respond_to?(:total_entries) ? _('see all (%d)') % results.total_entries : _('see all...'),
30   - params.merge(:action => 'index',
31   - :asset => name ),
32   - :class => 'see-more' ) if @results.size > 1 %>
  10 + <% if @results.size > 1 %>
  11 + <h3><%= @names[name] %></h3>
  12 + <% if results.total_entries > SearchController::MULTIPLE_SEARCH_LIMIT %>
  13 + <%= link_to(_('see all (%d)') % results.total_entries, params.merge(:action => name), :class => 'see-more' ) %>
  14 + <% end %>
33 15 <% end %>
34   - <% end %>
35   - <% partial = partial_for_class results.first.class %>
36   - <div class="search-results-innerbox search-results-type-<%= partial %> <%= 'common-profile-list-block' if partial == 'profile' %>">
37   - <div class="search-results-innerbox2"><!-- the innerbox2 is a workarround for MSIE -->
  16 +
  17 + <div class="search-results-innerbox search-results-type-<%= partial %> <%= 'common-profile-list-block' if partial == 'profile' %>">
38 18 <ul>
39   - <% hit_pos = 0 %>
40 19 <% results.each do |hit| %>
41   - <% next if hit.respond_to?(:visible) && !hit.visible? %>
42   - <%= render :partial => partial_for_class(hit.class),
43   -
44   - :object => hit,
45   - :locals => { :pos => ( hit_pos += 1 ) } %>
  20 + <%= render :partial => partial_for_class(hit.class), :object => hit %>
46 21 <% end %>
47 22 </ul>
48   - <hr />
49   - </div><!-- end class="search-results-innerbox2" -->
50   - </div><!-- end class="search-results-innerbox" -->
51   - </div><!-- end class="search-results-<%= name %>" -->
52   - <% else %>
53   - <div class="search-results-<%= name %> search-results-empty search-results-box <%= pos2 %> <%= 'col%s_of3' % pos3.to_s %>">
54   - <% if @controller.action_name != 'assets' %>
55   - <% if @results.size != 1 %>
  23 + </div>
  24 + <% else %>
  25 + <% if @results.size > 1 %>
56 26 <h3><%= @names[name] %></h3>
57 27 <% end %>
  28 + <div class="search-results-innerbox search-results-type-empty">
  29 + <div> <%= _('None') %> </div>
  30 + </div>
58 31 <% end %>
59   - <div class="search-results-innerbox search-results-type-empty">
60   - <div> <%= _('None') %> </div>
61   - <hr />
62   - </div><!-- end class="search-results-innerbox" -->
63   - </div><!-- end class="search-results-<%= name %>" -->
  32 + </div>
64 33 <% end %>
65   -<% end %>
  34 +
  35 + <div style="clear:both"></div>
66 36  
67   -<br style="clear:both" />
68   -</div><!-- end id="search-results" -->
  37 + <%= add_zoom_to_images %>
  38 +</div>
69 39  
... ...
app/views/search/_enterprise_homepage.rhtml 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => 'text_article', :object => enterprise_homepage %>
... ...
app/views/search/_event.rhtml
1   -<li class="<%= icon_for_article(event) %>">
2   - <strong><%= link_to(event.title, event.url) %></strong>
3   - <div class="item_meta">
4   - <%= show_period(event.start_date, event.end_date) %>
  1 +<li class="search-event-item article-item">
  2 + <%= link_to(event.title, event.url, :class => "search-result-title") %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => event %>
5 5 </div>
  6 + <table class="noborder search-content-second-column">
  7 + <% if event.start_date %>
  8 + <tr class="searc-article-event-date">
  9 + <td class="search-field-label"><%= _('Start date') %></td>
  10 + <td class="article-item-date"><%= event.start_date %></td>
  11 + </tr>
  12 + <% end %>
  13 + <% if event.end_date %>
  14 + <tr class="searc-article-event-date">
  15 + <td class="search-field-label"><%= _('End date') %></td>
  16 + <td class="article-item-date"><%= event.end_date %></td>
  17 + </tr>
  18 + <% end %>
  19 +
  20 + <%= render :partial => 'article_common', :object => event %>
  21 + </table>
  22 + <%= render :partial => 'article_last_change', :object => event %>
  23 +
  24 + <div style="clear: both"></div>
6 25 </li>
... ...
app/views/search/_facets_menu.rhtml 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<% less_options_limit = 8 %>
  2 +
  3 +<div id="facets-menu">
  4 + <% @asset_class.map_facets_for(environment).each do |facet| %>
  5 +
  6 + <div id="facet-menu-<%= facet[:id].to_s %>" class="facet-menu">
  7 + <div class="facet-menu-label">
  8 + <%= @asset_class.facet_label(facet) %>
  9 + </div>
  10 +
  11 + <% results = @asset_class.map_facet_results(facet, params[:facet], @facets, @all_facets, :limit => less_options_limit) %>
  12 + <% facet_count = results.total_entries %>
  13 +
  14 + <% if facet_count > 0 %>
  15 + <div class="facet-menu-options facet-menu-more-options" style="display: none">
  16 + </div>
  17 +
  18 + <div class="facet-menu-options facet-menu-less-options">
  19 + <% results.each do |id, label, count| %>
  20 + <%= facet_link_html(facet, params, id, label, count) %><br />
  21 + <% end %>
  22 + </div> <br />
  23 +
  24 + <% if facet_count > less_options_limit %>
  25 + <%= link_to_function _("Options"),
  26 + "facet_options_toggle('#{facet[:id].to_s}', '#{url_for(params.merge(:action => 'facets_browse', :facet_id => facet[:id], :asset => @asset, :escape => false))}'); " +
  27 + "jQuery(this).toggleClass('facet-less-options')", :class => "facet-options-toggle" %>
  28 + <br />
  29 + <% end %>
  30 +
  31 + <% else %>
  32 + <span class="facet-any-result-found"><%= _("No filter available") %></span>
  33 + <% end %>
  34 + </div>
  35 + <% end %>
  36 +</div>
... ...
app/views/search/_facets_unselect_menu.rhtml 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<div class="facets-applied">
  2 + <% if params[:facet] and params[:facet].count > 0 %>
  3 + <span class="facets-applied-label"><%= _("Applied filters") %> </span>
  4 + <%= facet_selecteds_html_for(environment, asset_class(@asset), params) %>
  5 + <% end %>
  6 +</div>
... ...
app/views/search/_folder.rhtml 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +<li class="search-folder-item article-item">
  2 + <%= link_to folder.title, folder.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => folder %>
  5 + </div>
  6 + <table class="noborder search-content-second-column">
  7 + <tr class="search-folder-items">
  8 + <td class="search-field-label"><%= _("Last items") %></td>
  9 +
  10 + <% r = folder.children.last(3) %>
  11 + <td class="<%= "search-field-none" if r.empty? %>">
  12 + <% r.each do |a| %>
  13 + <%= link_to a.title, a.view_url, :class => 'search-folder-sample-item '+icon_for_article(a) %>
  14 + <% end %>
  15 + <%= _('None') if r.empty? %>
  16 + </td>
  17 + </tr>
  18 +
  19 + <%= render :partial => 'article_common', :object => folder %>
  20 + </table>
  21 + <%= render :partial => 'article_last_change', :object => folder %>
  22 +
  23 + <div style="clear:both"></div>
  24 +</li>
... ...
app/views/search/_forum.rhtml 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +<li class="search-forum-item article-item">
  2 + <%= link_to forum.title, forum.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => forum %>
  5 + </div>
  6 + <table class="noborder search-content-second-column">
  7 + <tr class="search-forum-items">
  8 + <td class="search-field-label"><%= _("Last topics") %></td>
  9 +
  10 + <% r = forum.children.find(:all, :order => :updated_at, :conditions => ['type != ?', 'RssFeed']).last(3) %>
  11 + <td class="<%= "search-field-none" if r.empty? %>">
  12 + <% r.each do |a| %>
  13 + <%= link_to a.title, a.view_url, :class => 'search-forum-sample-item '+icon_for_article(a) %>
  14 + <% end %>
  15 + <%= _('None') if r.empty? %>
  16 + </td>
  17 + </tr>
  18 +
  19 + <%= render :partial => 'article_common', :object => forum %>
  20 + </table>
  21 + <%= render :partial => 'article_last_change', :object => forum %>
  22 +
  23 + <div style="clear:both"></div>
  24 +</li>
... ...
app/views/search/_gallery.rhtml 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<li class="search-gallery article-item">
  2 + <%= link_to gallery.title, gallery.view_url, :class => 'search-result-title' %>
  3 + <div class="search-content-first-column">
  4 + <%= render :partial => 'image', :object => gallery %>
  5 + </div>
  6 + <table class="noborder search-content-second-column">
  7 + <%= render :partial => 'article_common', :object => gallery %>
  8 + </table>
  9 + <%= render :partial => 'article_last_change', :object => gallery %>
  10 +
  11 + <div style="clear: both"></div>
  12 +</li>
  13 +
... ...
app/views/search/_google_maps.rhtml
1   -<%= content_tag('script', '', :src => GoogleMaps.api_url(environment.default_hostname), :type => 'text/javascript') %>
2   -
3   -
  1 +<div style="clear: both;"/></div>
4 2 <div style='text-align: center;'>
5 3 <div id="map"></div>
6 4 </div>
7 5  
8   -<script type='text/javascript'>
9   -var points = {};
10   -
11   -function putMarker(lat, lng, title, summary) {
12   - var point_str = lat + ":" + lng;
13   -
14   - if (points[point_str]) {
15   - lng += (Math.random() - 0.5) * 0.02;
16   - lat += (Math.random() - 0.5) * 0.02;
17   - } else {
18   - points[point_str] = true;
19   - }
20   -
21   - var point = new GLatLng(lat, lng);
22   - var options = { 'title' : title, 'icon' : icon };
23   - var marker = new GMarker(point, options);
24   - map.addOverlay(marker);
25   - GEvent.addListener(marker, 'click', function() {
26   - map.openInfoWindowHtml(point, summary);
27   - });
28   - bounds.extend(point);
29   -}
30   -
31   -window.unload = function() {
32   - GUnload();
33   -};
34   -
35   -if (GBrowserIsCompatible()) {
36   - var map = new GMap2(document.getElementById("map"));
37   -
38   - new GKeyboardHandler(map);
39   - map.addControl(new GLargeMapControl());
40   - map.addControl(new GMapTypeControl());
41   -
42   - centerPoint = new GLatLng(-15.0, -50.1419);
43   - map.setCenter(centerPoint, <%= GoogleMaps.initial_zoom.to_json %>);
44   - var bounds = new GLatLngBounds();
45   -
46   - var baseIcon = new GIcon();
47   - baseIcon.iconSize=new GSize(32,32);
48   - baseIcon.shadowSize=new GSize(36,32);
49   - baseIcon.iconAnchor=new GPoint(16,32);
50   - baseIcon.infoWindowAnchor=new GPoint(16,0);
51   - <%
52   - icon = default_or_themed_icon("/images/icons-map/enterprise.png")
53   - icon_shadow = default_or_themed_icon("/images/icons-map/enterprise_shadow.png")
54   - %>
55   - var icon = new GIcon(baseIcon, "<%= icon %>", null, "<%= icon_shadow %>");
  6 +<%= content_tag('script', '', :src => "http://maps.google.com/maps/api/js?sensor=true", :type => 'text/javascript') %>
  7 +<%= javascript_include_tag('google_maps') %>
56 8  
57 9 <%
58   - @results.each do |name,results|
59   - results.each do |item|
60   - if item.lat && item.lng
61   - %>
62   - putMarker(<%= item.lat.to_json %>, <%= item.lng.to_json %>, <%= item.name.to_json %>, <%= display_item_map_info(item).to_json %>);
63   - <%
64   - end
65   - end
66   - end
  10 + icon = default_or_themed_icon("/images/icons-map/enterprise.png")
67 11 %>
68   -}
69 12  
70   - map.setZoom(map.getBoundsZoomLevel(bounds));
71   - map.setCenter(bounds.getCenter());
  13 +<script type='text/javascript'>
  14 + mapLoad(<%= GoogleMaps.initial_zoom.to_json %>);
  15 +
  16 + <% @results.each do |name,results| %>
  17 + <% results.each do |item| %>
  18 + <% if item.lat && item.lng %>
  19 + mapPutMarker(<%= item.lat.to_json %>, <%= item.lng.to_json %>, <%= item.name.to_json %>, '<%= icon %>',
  20 + '<%= url_for(:controller => :map_balloon, :action => name.to_s.singularize, :id => item.id) %>');
  21 + <% end %>
  22 + <% end %>
  23 + <% end %>
  24 +
  25 + mapCenter();
72 26 </script>
... ...
app/views/search/_image.rhtml 0 → 100644
... ... @@ -0,0 +1,52 @@
  1 +<div class="search-image-container">
  2 +
  3 + <% if image.is_a? UploadedFile and image.filename %>
  4 + <% extension = image.filename[(image.filename.rindex('.')+1)..-1].downcase %>
  5 + <% if ['jpg', 'jpeg', 'gif', 'png', 'tiff', 'svg'].include? extension %>
  6 + <%= link_to '', image.view_url, :class => "search-image-pic", :style => 'background-image: url(%s)'% image.public_filename(:thumb) %>
  7 + <% if image.width && image.height %>
  8 + <% javascript_tag do %>
  9 + image = jQuery('script').last().parent().find('.search-image-pic');
  10 + des_width = parseInt(image.css('width'));
  11 + des_height = parseInt(image.css('height'));
  12 +
  13 + width = <%= image.width %>;
  14 + height = <%= image.height %>;
  15 + scale_factor = width > height ? des_width/width : des_height/height;
  16 +
  17 + image.css({'width' : scale_factor*width +'px', 'height' : scale_factor*height+'px'});
  18 + <% end %>
  19 + <% end %>
  20 + <% elsif ['pdf'].include? extension %>
  21 + <%= link_to '', image.view_url, :class => 'search-image-pic icon-application-pdf' %>
  22 + <% elsif ['doc', 'docx', 'odt', 'rtf', 'txt', 'html', 'htm'].include? extension %>
  23 + <%= link_to '', image.view_url, :class => 'search-image-pic icon-application-vnd-oasis-opendocument-text' %>
  24 + <% elsif ['xls', 'xlsx', 'ods', 'csv', 'tsv', 'tab'].include? extension %>
  25 + <%= link_to '', image.view_url, :class => 'search-image-pic icon-application-vnd-oasis-opendocument-spreadsheet' %>
  26 + <% end %>
  27 + <% elsif image.is_a? Gallery %>
  28 + <div class="search-gallery-items">
  29 + <% r = image.children.find(:all, :order => :updated_at, :conditions => ['type = ?', 'UploadedFile']).last(3) %>
  30 + <% if r.length > 0 %>
  31 + <% r.each do |i| %>
  32 + <%= link_to '', i.view_url, :class => "search-image-pic", :style => 'background-image: url(%s)'% i.public_filename(:thumb) %>
  33 + <% end %>
  34 + <% else %>
  35 + <div class="search-no-image"><span><%= _('No image') %></span></div>
  36 + <% end %>
  37 + </div>
  38 + <% elsif image.is_a? Product %>
  39 + <% if image.image %>
  40 + <div class="zoomable-image">
  41 + <%= link_to '', product_path(image), :class => "search-image-pic",
  42 + :style => 'background-image: url(%s)'% image.default_image(:thumb) %>
  43 + <%= link_to content_tag(:span, _('Zoom in')), image.image.public_filename,
  44 + :class => 'zoomify-image' %>
  45 + </div>
  46 + <% else %>
  47 + <div class="search-no-image"><span><%= _('No image') %></span></div>
  48 + <% end %>
  49 + <% else %>
  50 + <div class="search-content-type-icon icon-content-<%=image.class.to_s.underscore.dasherize%>"></div>
  51 + <% end %>
  52 +</div>
... ...
app/views/search/_product.rhtml
1   -<%# FIXME add more information %>
2   -
3   -<%
4   -product_item_pos = 0 if ! product_item_pos
5   -product_item_pos += 1
6   -%>
7   -
8 1 <% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
9 2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%>
10 3  
11   -<li class="product-item <%= ( pos % 2 == 0 ) ? 'odd' : 'even' %>">
12   - <%= link_to_product product, :class => 'product-pic', :style => 'background-image:url(%s)' % product.default_image(:minor) %>
13   - <strong>
14   - <%= link_to_product product %>
15   - </strong>
16   - <ul>
17   - <li> <%= _('Price: %s') % (product.price ? product.price : _('Not informed')) %> </li>
18   - <% if product.enterprise && product.display_supplier_on_search? %>
19   - <li> <%= _('Supplier: %s') % link_to_homepage(product.enterprise.name, product.enterprise.identifier) %> </li>
20   - <% end %>
  4 +<li class="search-product-item">
21 5  
22   - <li> <%= _('Category:') + ' ' + link_to_product_category(product.product_category) %> </li>
  6 + <div class="search-product-item-first-column">
  7 + <%= render :partial => 'search/image', :object => product %>
23 8  
24   - <% extra_properties.each do |property| %>
25   - <li><%= property[:name] + ': ' + instance_eval(&property[:content]) %></li>
  9 + <% if product.available %>
  10 + <% if product.price && product.price > 0 %>
  11 + <% has_discount = product.discount && product.discount > 0 %>
  12 + <% if product.price %>
  13 + <span class="search-product-price-textid"><%=_("from") if has_discount %></span><%= price_span(product.price, :class => "search-product-price " + (has_discount ? 'with-discount' : '')) %>
  14 + <% if has_discount %>
  15 + <span class="search-product-price-textid"><%=_("by")%></span><%= price_span(product.price_with_discount, :class => "search-product-price") %>
  16 + <% end %>
  17 + <% if product.unit %>
  18 + <span class="search-product-unit">&nbsp;<%= _('/') %>&nbsp;<%= product.unit.name %></span>
  19 + <% end %>
  20 + <% end %>
  21 + <div class="search-product-inputs-info">
  22 + <% if p = product.percentage_from_solidarity_economy %>
  23 + <div class="search-product-percentage-from-solidarity-economy search-product-ecosol-percentage-icon-<%= p[0].to_s %>"
  24 + title="<%=_('Percentage of inputs from solidarity economy')%>">
  25 + <%= p[1] %>
  26 + </div>
  27 + <% end %>
  28 +
  29 + <% if product.price_described? %>
  30 + <% title = product.inputs.map{ |i|
  31 + '<div class="search-product-input-dots-to-price">' +
  32 + '<div class="search-product-input-name">' + i.product_category.name + '</div>' +
  33 + price_span(i.price_per_unit*i.amount_used, :class => 'search-product-input-price') +
  34 + '</div>' }.join('') %>
  35 + <% title += product.price_details.map{ |p|
  36 + '<div class="search-product-input-dots-to-price">' +
  37 + '<div class="search-product-input-name">' + p.production_cost.name + '</div>' +
  38 + price_span(p.price, :class => 'search-product-input-price') +
  39 + '</div>' }.join('') %>
  40 + <%= link_to_function _("Open Price"), '', :title => title, :class => "search-product-price-details" %>
  41 + <% end %>
  42 + </div>
  43 + <% end %>
  44 + <% else %>
  45 + <span class="product-not-available"><%= _('Not available') %></div>
26 46 <% end %>
27   - </ul>
  47 +
  48 + </div>
  49 + <div class="search-product-item-second-column">
  50 + <%= link_to_product product, :class => 'search-result-title' %>
  51 + <div class="search-product-supplier">
  52 + <span class="search-field-label"><%= _('Supplier') %> </span><%= link_to_homepage(product.enterprise.name, product.enterprise.identifier) %>
  53 + </div>
  54 + <div class="search-product-description">
  55 + <% if product.description %>
  56 + <% desc_stripped = strip_tags(product.description) %>
  57 + <span class="search-field-label"><%= _('Description') %> </span><%= excerpt(desc_stripped, desc_stripped.first(3), 300) %>
  58 + <% end %>
  59 + </div>
  60 + </div>
  61 + <div class="search-product-item-third-column">
  62 + <div class="search-product-region">
  63 + <% if product.enterprise.region %>
  64 + <span class="search-field-label"><%= _('City') %></span>
  65 + <br /><%= city_with_state(product.enterprise.region) %>
  66 + <% end %>
  67 + </div>
  68 + <div class="search-product-qualifiers">
  69 + <% if product.product_qualifiers.count > 0 %>
  70 + <span class="search-field-label"><%= _('Qualifiers') %></span>
  71 + <% product.product_qualifiers.each do |pq| %>
  72 + <% if pq.qualifier %>
  73 + <span class="search-product-qualifier"><%= pq.qualifier.name + (pq.certifier.nil? ? _(";") : '') %></span>
  74 + <% end %>
  75 + <% if pq.certifier %>
  76 + <span class="search-product-certifier">&nbsp;<%= _('cert. ') + pq.certifier.name + _(";") %></span>
  77 + <% end %>
  78 + <% end %>
  79 + <% end %>
  80 + </div>
  81 + </div>
28 82  
29 83 <%= extra_content.join('\n') %>
  84 + <% extra_properties.each do |property| %>
  85 + <div><%= property[:name] + ': ' + instance_eval(&property[:content]) %></div>
  86 + <% end %>
30 87  
  88 + <br /><br />
31 89 </li>
... ...
app/views/search/_product_categories_menu.rhtml
... ... @@ -1,42 +0,0 @@
1   -<% if @product_category %>
2   - <h3 class="current-cat-path">
3   - <%= @product_category.hierarchy.map {|cat| ((cat == @product_category) ? content_tag('span', cat.name) : link_to((cat.name), params.merge({:product_category => cat.id}))) }.join(' &rarr; ') %>
4   - </h3>
5   -<% end %>
6   -
7   -<% if product_categories_menu %>
8   -
9   -<div id="product-categories-menu">
10   - <ul>
11   - <% if product_categories_menu.empty? %>
12   - <% if @product_category %>
13   - <li class="cat-empty"> <%= _('There is no sub-categories for %s.') % @product_category.name %> </li>
14   - <% else %>
15   - <li class="cat-empty"> <%= _('There is no categories.') %> </li>
16   - <% end %>
17   - <% end %>
18   - <% product_categories_menu.each do |cat, hits, childs| %>
19   - <li class="cat-parent" >
20   - <%= link_to(
21   - cat.name + " " + content_tag('small', "(#{hits})"),
22   - params.merge({:product_category => cat.id})
23   - ) %>
24   - <% if !childs.blank? %>
25   - <div>
26   - <ul>
27   - <% childs.each do |child, child_hits| %>
28   - <li class="cat-child"> <%= link_to(
29   - child.name + " " + content_tag('small', "(#{child_hits})"),
30   - params.merge({:product_category => child.id})
31   - ) %> </li>
32   - <% end %>
33   - </ul>
34   - </div>
35   - <% end %>
36   - </li>
37   - <% end %>
38   - </ul>
39   -</div>
40   -
41   -<% end %>
42   -
app/views/search/_profile.rhtml
1   -<%= profile_image_link profile, :portrait %>
  1 +<li class="search-profile-item">
  2 + <% if @empty_query or @results.size > 1 or !profile.enterprise? %>
  3 + <%= profile_image_link profile, :portrait, 'div' %>
  4 + <% else %>
  5 + <div class="search-enterprise-item">
  6 + <div class="search-enterprise-item-column-left">
  7 + <%= profile_image_link profile, :portrait, 'div' %>
  8 + </div>
  9 + <div class="search-enterprise-item-column-right">
  10 + <%= link_to_homepage(profile.name, profile.identifier, :class => "search-result-title") %>
  11 + <div class="search-enterprise-description">
  12 + <% if profile.description %>
  13 + <% body_stripped = strip_tags(profile.description) %>
  14 + <% elsif profile.home_page and profile.home_page.body %>
  15 + <% body_stripped = strip_tags(profile.home_page.body) %>
  16 + <% end %>
  17 + <%= excerpt(body_stripped, body_stripped.first(3), 200) if body_stripped %>
  18 + </div>
  19 + <div class="search-enterprise-region">
  20 + <span class="search-enterprise-region-label"><%= _("City") %></span>
  21 + <% if profile.region %>
  22 + <span class="search-enterprise-region-name"><%= city_with_state(profile.region) %></span>
  23 + <% end %>
  24 + </div>
  25 + <% if !profile.description.blank? %>
  26 + <div><%= profile.description %></div><br />
  27 + <% end %>
  28 +
  29 + <div class="search-enterprise-categorization">
  30 + <% profile.top_level_categorization.each do |parent, children| %>
  31 + <% if parent.name != "Territórios" %>
  32 + <div class="search-enterprise-category-<%=parent.id%> search-enterprise-category">
  33 + <span class="search-enterprise-categorization-parent"><%= parent.name %></span>
  34 + <span class="search-enterprise-categorization-children">
  35 + <%= children.collect(&:name).join(', ') %>
  36 + </span>
  37 + </div>
  38 + <% end %>
  39 + <% end %>
  40 + </div>
  41 + </div>
  42 +
  43 + <hr class="clearfix" />
  44 + </div>
  45 + <% end %>
  46 +</li>
... ...
app/views/search/_results_header.rhtml 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +<div class="search-results-header <%= "search-no-results" if @results[@asset].nil? or @results[@asset].length == 0 %>">
  2 + <% if !@empty_query %>
  3 + <div class="search-results-header-information">
  4 + <%= label_total_found(@asset, @results[@asset].total_entries) %>
  5 + <% if params[:display] != 'map' %>
  6 + <span class="current-page"><%= _("Showing page %s of %s") % [@results[@asset].current_page, @results[@asset].total_pages] %></span>
  7 + <% end %>
  8 + </div>
  9 +
  10 + <div class="search-results-header-facets-order-by">
  11 + <%= facets_unselect_menu(@asset) %>
  12 + <%= order_by(@asset) if params[:display] != 'map' %>
  13 + </div>
  14 + <% else %>
  15 + <div id='search-filter-title'><%= @filter_title if @filter_title %></div>
  16 + <% end %>
  17 +
  18 + <div style="clear: both"></div>
  19 +</div>
... ...
app/views/search/_search_form.rhtml
1 1 <div class='search-form'>
2   -<% simple_search = false unless defined? simple_search %>
3 2  
4   -<% form_tag( { :controller => 'search', :action => 'index', :asset => nil, :category_path => ( @category ? @category.explode_path : [] ) },
5   - :method => 'get', :class => 'search_form' ) do %>
6   - <%= '<h3>%s</h3>' % form_title if defined? form_title %>
7   -
8   - <%= hidden_field_tag :display, params[:display] %>
9   -
10   - <%= hidden_field_tag :asset, params[:asset] %>
11   -
12   - <div class="search-field">
13   - <span class="formfield">
14   - <%= text_field_tag 'query', @query, :id => ( lightbox? ? 'popup-search-input' : '' ), :size => 50 %>
15   - <%= javascript_tag 'setTimeout("$(\"popup-search-input\").focus()", 10 )' if lightbox? %>
16   - </span>
17   - <%= submit_button(:search, _('Search'), :name => :search_whole_site_no) %>
18   - <% if @category %>
19   - <%= submit_button(:search, _('Search in whole site'), :name => :search_whole_site_yes) %>
  3 + <% form_tag( { :controller => 'search', :action => @asset ? @asset : 'index', :asset => nil, :category_path => ( @category ? @category.explode_path : [] ) },
  4 + :method => 'get', :class => 'search_form' ) do %>
  5 +
  6 + <%= hidden_field_tag :display, params[:display] %>
  7 +
  8 + <% params_uri = CGI::unescape(request.request_uri) %>
  9 + <% if params_uri.index('?') %>
  10 + <% params_uri[(params_uri.index('?')+1)..-1].to_s.split("&").each do |part| %>
  11 + <% if part.start_with? "facet" %>
  12 + <% name_value = part.split("=") %>
  13 + <%= hidden_field_tag name_value[0], name_value[1] %>
  14 + <% end %>
  15 + <% end %>
20 16 <% end %>
21   - </div>
22   -
23   - <div id='advanced-search-options' style="display: <%= simple_search ? 'none' : 'block' %>">
24   - <div class="search-options search-within">
25   - <h4><%= _('Search within:') %></h4>
26   - <br style='clear:left'/>
27   - <div>
28   - <span class="formfield">
29   - <%= select_city %>
30   - </span>
31   - <span class="formfield">
32   - <%= labelled_select(_('Distance:'), 'radius', :first, :last, nil, [15, 30, 50, 100, 150, 200, 300, 400, 500, 1000].map{|n|[n, n.to_s + 'km']}) %>
33   - </span>
34   - </div>
35   - </div><!-- fim class="search-options" -->
36   -
37   - <div class="search-options search-for">
38   - <h4><%= _('Search for:') %></h4>
39   - <ul>
40   - <% @search_in.map { |t,n| [t,getterm(n)] } .
41   - sort_by(&:last).each do |thing, name| %>
42   - <li>
43   - <%= labelled_check_box name, 'find_in[]', thing.to_s, @searching[thing.to_sym] %>
44   - </li>
45   - <% end %>
46   - </ul>
47   - <br style="clear:both" />
48   - </div><!-- fim class="search-options" -->
49   - </div><!-- end id="advanced-search-options" -->
  17 +
  18 + <div class="search-field">
  19 + <span class="formfield">
  20 + <%= text_field_tag 'query', @query, :id => 'search-input', :size => 50 %>
  21 + <%= javascript_tag "jQuery('#search-input').attr('title', \"#{hint}\").hint()" if defined?(hint) %>
  22 + </span>
  23 +
  24 + <%= submit_button(:search, _('Search')) %>
  25 + </div>
50 26  
51   - <% if simple_search %>
52   - <%= link_to_function(_('More options'), nil, :id => 'advanced_search_link') do |page|
53   - page['advanced_search_link'].hide
54   - page['advanced-search-options'].toggle
55   - end %>
  27 + <% end %>
  28 +
  29 + <% if @empty_query %>
  30 + <% hint = environment.search_hints[@asset] %>
  31 + <% if hint and !hint.blank? %>
  32 + <div class="search-hint"><%= hint %></div>
  33 + <% end %>
56 34 <% end %>
57 35  
58   - <% if lightbox?; button_bar do %>
59   - <%= lightbox_close_button _('Close') %>
60   - <% end; end %>
61   -
62   -<% end %>
63   -
64   -</div> <!-- id="search-form" -->
  36 + <div style="clear: both"></div>
  37 +</div>
... ...
app/views/search/_text_article.rhtml 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<li class="search-text-article-item article-item">
  2 + <%= link_to(text_article.title, text_article.url, :class => "search-result-title") %>
  3 +
  4 + <div class="search-content-first-column">
  5 + <%= render :partial => 'image', :object => text_article %>
  6 + </div>
  7 + <table class="noborder search-content-second-column">
  8 + <%= render :partial => 'article_common', :object => text_article %>
  9 + </table>
  10 + <%= render :partial => 'article_last_change', :object => text_article %>
  11 +
  12 + <div style="clear: both"></div>
  13 +</li>
... ...
app/views/search/_uploaded_file.rhtml 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +<li class="search-uploaded-file-item article-item">
  2 + <%= link_to uploaded_file.filename, uploaded_file.view_url, :class => 'search-result-title' %>
  3 +
  4 + <div class="search-content-first-column">
  5 + <%= render :partial => 'image', :object => uploaded_file %>
  6 + </div>
  7 +
  8 + <table class="noborder search-content-second-column">
  9 + <%= render :partial => 'article_author', :object => uploaded_file %>
  10 + <%= render :partial => 'article_description', :object => uploaded_file %>
  11 +
  12 + <% if uploaded_file.parent and uploaded_file.parent.published? %>
  13 + <tr class="search-uploaded-file-parent">
  14 + <td class="search-field-label"><%= uploaded_file.parent.gallery? ? _("Gallery") : _("Folder") %></td>
  15 + <td><%= link_to uploaded_file.parent.name, uploaded_file.parent.url %></td>
  16 + </tr>
  17 + <% end %>
  18 +
  19 + <%= render :partial => 'article_tags', :object => uploaded_file.tags %>
  20 + <%= render :partial => 'article_categories', :object => uploaded_file.categories %>
  21 + </table>
  22 + <%= render :partial => 'article_last_change', :object => uploaded_file %>
  23 +
  24 + <div style="clear:both"></div>
  25 +</li>
... ...
app/views/search/articles.rhtml
1   -<%= search_page_title( _('Articles'), { :query => @query,
2   - :category => @category ? @category.name : nil,
3   - :total_results => @total_results,
4   - :region => @region ? @region.name : nil,
5   - :distance => @radius } ) %>
  1 +<%= search_page_title( @titles[:articles], @category ) %>
6 2  
7   -<%= search_page_link_to_all( { :asset => params[:asset],
8   - :category => @category }) %>
  3 +<div id="search-column-left">
  4 + <% if !@empty_query %>
  5 + <%= facets_menu(:articles, @facets) %>
  6 + <% end %>
  7 +</div>
9 8  
10   -<%= render :partial => 'search_form', :locals => { :form_title => @query.blank? ? _('Search') : _("Refine your search"), :simple_search => true } %>
  9 +<div id="search-column-right">
  10 + <%= render :partial => 'search_form', :locals => { :hint => _('Type the title, author or content desired') } %>
  11 + <%= render :partial => 'results_header' %>
11 12  
12   -<%# FIXME ARMENGUE %>
13   -<%= display_results(false) %>
  13 + <%= display_results %>
  14 + <%= pagination_links @results[:articles] %>
  15 +</div>
14 16  
15   -<%= pagination_links @results.values.first %>
16   -
17   -<br style="clear:both" />
  17 +<div style="clear: both"></div>
... ...
app/views/search/category_index.rhtml
... ... @@ -1,23 +0,0 @@
1   -<div id="view-category">
2   -
3   - <div id="category-image"><%= image_tag(@category.image.public_filename(:thumb), :id => 'category-image') if @category.image %></div>
4   - <h1 id="category-name"><%= @category.name %></h1>
5   -
6   - <%= render :partial => 'display_results' %>
7   -
8   - <div id="category-childs">
9   - <h2> <%= _('Sub-categories') %> </h2>
10   - <% if @category.children.empty? %>
11   - <strong id="cat-no-child"><%= _('No sub-categories') %></strong>
12   - <% else %>
13   - <ul>
14   - <% @category.children.each do |c| %>
15   - <li> <%= link_to_category c, false %> </li>
16   - <% end %>
17   - </ul>
18   - <% end %>
19   - </div><!-- end id="child-categories" -->
20   -
21   -<br style="clear:both" />
22   -</div><!-- end id="view-category" -->
23   -
app/views/search/category_index.rhtml 0 → 120000
... ... @@ -0,0 +1 @@
  1 +index.rhtml
0 2 \ No newline at end of file
... ...
app/views/search/cities.rhtml
... ... @@ -1,7 +0,0 @@
1   -<% if !@cities.empty? %>
2   - <% @cities.each do |city| %>
3   - <option value=<%= city.id.to_s.inspect %>><%= city.name %></option>
4   - <% end %>
5   -<% else %>
6   - <option value=''><%= _('No City') %></option>
7   -<% end%>