Commit 96ad392a655a791e6df5359fe0e059aa7b53ba36

Authored by Rodrigo Souto
2 parents c3308c85 5cafd9b6

Merge branch 'stable'

Conflicts:
	app/controllers/public/content_viewer_controller.rb
	features/manage_products.feature
Showing 45 changed files with 339 additions and 71 deletions   Show diff stats
AUTHORS
... ... @@ -111,6 +111,7 @@ Diego Martinez <diegoamc90@gmail.com>
111 111 Diego Martinez <diego@diego-K55A.(none)>
112 112 Diego + Renan <renanteruoc@gmail.com>
113 113 Fernanda Lopes <nanda.listas+psl@gmail.com>
  114 +Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
114 115 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
115 116 Grazieno Pellegrino <grazieno@gmail.com>
116 117 Isaac Canan <isaac@intelletto.com.br>
... ... @@ -195,6 +196,7 @@ root &lt;root@debian.sdr.serpro&gt;
195 196 Samuel R. C. Vale <srcvale@holoscopio.com>
196 197 Valessio Brito <valessio@gmail.com>
197 198 vfcosta <vfcosta@gmail.com>
  199 +Vinicius Cubas Brand <viniciuscb@gmail.com>
198 200 Visita <visita@debian.(none)>
199 201 Yann Lugrin <yann.lugrin@liquid-concept.ch>
200 202  
... ...
app/controllers/application_controller.rb
... ... @@ -174,8 +174,6 @@ class ApplicationController &lt; ActionController::Base
174 174 end
175 175  
176 176 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={})
177   - scope = scope.send(options[:filter]) if options[:filter]
178   -
179 177 @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||
180 178 fallback_find_by_contents(asset, scope, query, paginate_options, options)
181 179 end
... ... @@ -183,8 +181,9 @@ class ApplicationController &lt; ActionController::Base
183 181 private
184 182  
185 183 def fallback_find_by_contents(asset, scope, query, paginate_options, options)
186   - return {:results => scope.paginate(paginate_options)} if query.blank?
187   - {:results => scope.like_search(query).paginate(paginate_options)}
  184 + scope = scope.like_search(query) unless query.blank?
  185 + scope = scope.send(options[:filter]) unless options[:filter].blank?
  186 + {:results => scope.paginate(paginate_options)}
188 187 end
189 188  
190 189 end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -55,8 +55,9 @@ class ContentViewerController &lt; ApplicationController
55 55  
56 56 @page = FilePresenter.for @page
57 57  
58   - unless @page.mime_type == 'text/html' || params[:view]
  58 + if @page.download? params[:view]
59 59 headers['Content-Type'] = @page.mime_type
  60 + headers.merge! @page.download_headers
60 61 data = @page.data
61 62  
62 63 # TODO test the condition
... ... @@ -72,7 +73,7 @@ class ContentViewerController &lt; ApplicationController
72 73  
73 74 #FIXME see a better way to do this. It's not need to pass this variable anymore
74 75 @comment = Comment.new
75   -
  76 +
76 77 if @page.has_posts?
77 78 posts = if params[:year] and params[:month]
78 79 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01")
... ...
app/helpers/application_helper.rb
... ... @@ -730,8 +730,15 @@ module ApplicationHelper
730 730 end
731 731  
732 732 def rolename_for(profile, resource)
733   - role = profile.role_assignments.find_by_resource_id(resource.id).role
734   - content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}")
  733 + roles = profile.role_assignments.
  734 + where(:resource_id => resource.id).
  735 + sort_by{ |role_assignment| role_assignment.role_id }.
  736 + map(&:role)
  737 + names = []
  738 + roles.each do |role|
  739 + names << content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}")
  740 + end
  741 + names.join(', ')
735 742 end
736 743  
737 744 def role_color(role, env_id)
... ...
app/models/article.rb
... ... @@ -192,7 +192,7 @@ class Article &lt; ActiveRecord::Base
192 192 pending_categorizations.clear
193 193 end
194 194  
195   - acts_as_taggable
  195 + acts_as_taggable
196 196 N_('Tag list')
197 197  
198 198 acts_as_filesystem
... ... @@ -272,7 +272,7 @@ class Article &lt; ActiveRecord::Base
272 272 end
273 273  
274 274 # returns the data of the article. Must be overriden in each subclass to
275   - # provide the correct content for the article.
  275 + # provide the correct content for the article.
276 276 def data
277 277 body
278 278 end
... ... @@ -368,6 +368,16 @@ class Article &lt; ActiveRecord::Base
368 368 false
369 369 end
370 370  
  371 + def download? view = nil
  372 + (self.uploaded_file? and not self.image?) or
  373 + (self.image? and view.blank?) or
  374 + (not self.uploaded_file? and self.mime_type != 'text/html')
  375 + end
  376 +
  377 + def download_headers
  378 + {}
  379 + end
  380 +
371 381 named_scope :native_translations, :conditions => { :translation_of_id => nil }
372 382  
373 383 def translatable?
... ...
app/models/category.rb
... ... @@ -7,13 +7,13 @@ class Category &lt; ActiveRecord::Base
7 7 :slug => 1,
8 8 }
9 9  
10   - validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('%{fn} cannot be like that.').fix_i18n
  10 + validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('{fn} cannot be like that.').fix_i18n
11 11 validates_presence_of :name, :environment_id
12   - validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n
  12 + validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('{fn} is already being used by another category.').fix_i18n
13 13 belongs_to :environment
14 14  
15 15 validates_inclusion_of :display_color, :in => [ 1, 2, 3, 4, nil ]
16   - validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.').fix_i18n
  16 + validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('{fn} was already assigned to another category.').fix_i18n
17 17  
18 18 # Finds all top level categories for a given environment.
19 19 named_scope :top_level_for, lambda { |environment|
... ...
app/models/comment.rb
... ... @@ -27,7 +27,7 @@ class Comment &lt; ActiveRecord::Base
27 27 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? })
28 28 validates_each :name do |rec,attribute,value|
29 29 if rec.author_id && (!rec.name.blank? || !rec.email.blank?)
30   - rec.errors.add(:name, _('%{fn} can only be informed for unauthenticated authors').fix_i18n)
  30 + rec.errors.add(:name, _('{fn} can only be informed for unauthenticated authors').fix_i18n)
31 31 end
32 32 end
33 33  
... ...
app/models/create_enterprise.rb
... ... @@ -40,12 +40,12 @@ class CreateEnterprise &lt; Task
40 40  
41 41 if self.region && self.target
42 42 unless self.region.validators.include?(self.target) || self.target_type == "Environment"
43   - self.errors.add(:target, _('%{fn} is not a validator for the chosen region').fix_i18n)
  43 + self.errors.add(:target, _('{fn} is not a validator for the chosen region').fix_i18n)
44 44 end
45 45 end
46 46  
47 47 if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier)
48   - self.errors.add(:identifier, _('%{fn} is already being as identifier by another enterprise, organization or person.').fix_i18n)
  48 + self.errors.add(:identifier, _('{fn} is already being as identifier by another enterprise, organization or person.').fix_i18n)
49 49 end
50 50 end
51 51  
... ...
app/models/domain.rb
... ... @@ -10,14 +10,14 @@ class Domain &lt; ActiveRecord::Base
10 10  
11 11 # <tt>name</tt> must be sequences of alphanumeric characters (a to z,
12 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
  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:
17 17 # * <tt>name</tt> must not start with 'www.'
18 18 def validate
19 19 if self.name =~ /^www\./
20   - self.errors.add(:name, _('%{fn} must not start with www.').fix_i18n)
  20 + self.errors.add(:name, _('{fn} must not start with www.').fix_i18n)
21 21 end
22 22 end
23 23  
... ...
app/models/environment.rb
... ... @@ -169,7 +169,7 @@ class Environment &lt; ActiveRecord::Base
169 169  
170 170 # One Environment can be reached by many domains
171 171 has_many :domains, :as => :owner
172   - has_many :profiles
  172 + has_many :profiles, :dependent => :destroy
173 173  
174 174 has_many :organizations
175 175 has_many :enterprises
... ... @@ -784,13 +784,6 @@ class Environment &lt; ActiveRecord::Base
784 784 self.save!
785 785 end
786 786  
787   - after_destroy :destroy_templates
788   - def destroy_templates
789   - [enterprise_template, inactive_enterprise_template, community_template, person_template].compact.each do |template|
790   - template.destroy
791   - end
792   - end
793   -
794 787 after_create :create_default_licenses
795 788 def create_default_licenses
796 789 License.create!(:name => 'CC (by)', :url => 'http://creativecommons.org/licenses/by/3.0/legalcode', :environment => self)
... ...
app/models/event.rb
... ... @@ -25,7 +25,7 @@ class Event &lt; Article
25 25  
26 26 validates_each :start_date do |event,field,value|
27 27 if event.end_date && event.start_date && event.start_date > event.end_date
28   - event.errors.add(:start_date, _('%{fn} cannot come before end date.').fix_i18n)
  28 + event.errors.add(:start_date, _('{fn} cannot come before end date.').fix_i18n)
29 29 end
30 30 end
31 31  
... ...
app/models/image.rb
... ... @@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base
17 17 :icon => '20x20!' },
18 18 :max_size => 5.megabytes # remember to update validate message below
19 19  
20   - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
  20 + validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
21 21  
22 22 delay_attachment_fu_thumbnails
23 23  
... ...
app/models/person.rb
... ... @@ -240,7 +240,7 @@ class Person &lt; Profile
240 240  
241 241 validates_each :email, :on => :update do |record,attr,value|
242 242 if User.find(:first, :conditions => ['email = ? and id != ? and environment_id = ?', value, record.user.id, record.environment.id])
243   - record.errors.add(attr, _('%{fn} is already used by other user').fix_i18n)
  243 + record.errors.add(attr, _('{fn} is already used by other user').fix_i18n)
244 244 end
245 245 end
246 246  
... ...
app/models/uploaded_file.rb
... ... @@ -54,7 +54,7 @@ class UploadedFile &lt; Article
54 54 :thumbnail_class => Thumbnail,
55 55 :max_size => 5.megabytes # remember to update validate message below
56 56  
57   - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
  57 + validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
58 58  
59 59 delay_attachment_fu_thumbnails
60 60  
... ... @@ -94,6 +94,12 @@ class UploadedFile &lt; Article
94 94 self.name = self.filename
95 95 end
96 96  
  97 + def download_headers
  98 + {
  99 + 'Content-Disposition' => "attachment; filename=\"#{self.filename}\"",
  100 + }
  101 + end
  102 +
97 103 def data
98 104 File.read(self.full_filename)
99 105 end
... ...
app/models/user.rb
... ... @@ -114,7 +114,7 @@ class User &lt; ActiveRecord::Base
114 114 before_save :encrypt_password
115 115 validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?})
116 116  
117   - validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('%{fn} must be checked in order to signup.').fix_i18n
  117 + validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n
118 118  
119 119 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil.
120 120 def self.authenticate(login, password, environment = nil)
... ...
app/views/profile/_person_profile.rhtml
... ... @@ -4,7 +4,7 @@
4 4 </tr>
5 5 <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>
6 6 <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%>
7   - <%= display_field(_('Location:'), profile, :location, true) %>
  7 + <%= display_field _('Location:'), profile, :location %>
8 8  
9 9 <%= display_field(_('Type:'), profile, :privacy_setting, true) %>
10 10  
... ...
app/views/shared/_list_groups.html.erb
... ... @@ -12,7 +12,9 @@
12 12 <%= _('Members: %s') % group.members_count.to_s %> <br/>
13 13 <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/>
14 14 <% button_bar do %>
15   - <%= button 'menu-ctrl-panel', _('Control panel of this group'), group.admin_url %>
  15 + <% if user.has_permission?(:edit_profile, group) %>
  16 + <%= button 'menu-ctrl-panel', _('Control panel of this group'), group.admin_url %>
  17 + <% end %>
16 18 <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %>
17 19 <% if (group.community? && user.has_permission?(:destroy_profile, group)) %>
18 20 <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => group.identifier } %>
... ...
db/migrate/20131121162641_remove_orphan_profiles.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class RemoveOrphanProfiles < ActiveRecord::Migration
  2 + def self.up
  3 + profiles = Profile.joins('LEFT JOIN environments ON profiles.environment_id=environments.id').where('environments.id IS NULL')
  4 + profiles.map(&:destroy)
  5 + end
  6 +
  7 + def self.down
  8 + say 'This migration can not be reverted.'
  9 + end
  10 +end
... ...
debian/changelog
  1 +noosfero (0.44.5) unstable; urgency=low
  2 +
  3 + * Bugfix release
  4 +
  5 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Tue, 26 Nov 2013 17:39:39 -0300
  6 +
1 7 noosfero (0.44.4) unstable; urgency=low
2 8  
3 9 * Bugfix release
... ...
features/manage_products.feature
... ... @@ -9,7 +9,7 @@ Feature: manage products
9 9 And the following enterprises
10 10 | identifier | owner | name | enabled |
11 11 | redemoinho | joaosilva | Rede Moinho | true |
12   - And feature "products_for_enterprises" is enabled on environment
  12 + And feature "products_for_enterprises" is enabled on environment
13 13  
14 14 Scenario: display "create new product" button
15 15 Given I am logged in as "joaosilva"
... ...
gitignore.example
... ... @@ -39,3 +39,6 @@ debian/*.debhelper
39 39 debian/files
40 40 debian/noosfero/
41 41 debian/noosfero-apache/
  42 +features/plugins/*
  43 +plugins/solr/config/solr.yml
  44 +/solr
... ...
lib/needs_profile.rb
... ... @@ -24,7 +24,7 @@ module NeedsProfile
24 24 @profile ||= environment.profiles.find_by_identifier(params[:profile])
25 25 if @profile
26 26 profile_hostname = @profile.hostname
27   - if profile_hostname && request.host == @environment.default_hostname
  27 + if profile_hostname && profile_hostname != request.host
28 28 params.delete(:profile)
29 29 redirect_to(Noosfero.url_options.merge(params).merge(:host => profile_hostname))
30 30 end
... ...
lib/noosfero.rb
... ... @@ -3,7 +3,7 @@ require &#39;fast_gettext&#39;
3 3  
4 4 module Noosfero
5 5 PROJECT = 'noosfero'
6   - VERSION = '0.44.4'
  6 + VERSION = '0.44.5'
7 7  
8 8 def self.pattern_for_controllers_in_directory(dir)
9 9 disjunction = controllers_in_directory(dir).join('|')
... ...
plugins/pg_search/lib/ext/active_record.rb
... ... @@ -2,8 +2,11 @@ require_dependency &#39;active_record&#39;
2 2  
3 3 class ActiveRecord::Base
4 4 def self.pg_search_plugin_search(query)
  5 + query.gsub!(/\|/,' ')
  6 + formatted_query = query.split.map{|w| w += ":*"}.join('|')
  7 +
5 8 if defined?(self::SEARCHABLE_FIELDS)
6   - where("to_tsvector('simple', #{pg_search_plugin_fields}) @@ to_tsquery('#{query}:*')")
  9 + where("to_tsvector('simple', #{pg_search_plugin_fields}) @@ to_tsquery('#{formatted_query}')")
7 10 else
8 11 raise "No searchable fields defined for #{self.name}"
9 12 end
... ...
plugins/pg_search/lib/pg_search_plugin.rb
... ... @@ -9,8 +9,9 @@ class PgSearchPlugin &lt; Noosfero::Plugin
9 9 end
10 10  
11 11 def find_by_contents(asset, scope, query, paginate_options={}, options={})
12   - return if query.blank?
13   - {:results => scope.pg_search_plugin_search(query).paginate(paginate_options)}
  12 + scope = scope.pg_search_plugin_search(query) unless query.blank?
  13 + scope = scope.send(options[:filter]) unless options[:filter]
  14 + {:results => scope.paginate(paginate_options)}
14 15 end
15 16  
16 17 end
... ...
plugins/pg_search/test/unit/pg_search_plugin_test.rb
... ... @@ -14,6 +14,13 @@ class PgSearchPluginTest &lt; ActiveSupport::TestCase
14 14 assert_includes search(Profile, 'water'), profile
15 15 end
16 16  
  17 + should 'locate one or more profiles' do
  18 + profile1 = fast_create(Profile, :identifier => 'administrator')
  19 + profile2 = fast_create(Profile, :identifier => 'debugger')
  20 + assert_includes search(Profile, 'admin deb'), profile1
  21 + assert_includes search(Profile, 'admin deb'), profile2
  22 + end
  23 +
17 24 # TODO This feature is available only on Postgresql 9.0
18 25 # http://www.postgresql.org/docs/9.0/static/unaccent.html
19 26 # should 'ignore accents' do
... ...
plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb
... ... @@ -94,10 +94,12 @@ class ShoppingCartPluginController &lt; PublicController
94 94 end
95 95  
96 96 def buy
97   - @cart = cart
98   - @enterprise = environment.enterprises.find(cart[:enterprise_id])
99   - @settings = Noosfero::Plugin::Settings.new(@enterprise, ShoppingCartPlugin)
100   - render :layout => false
  97 + if validate_cart_presence
  98 + @cart = cart
  99 + @enterprise = environment.enterprises.find(cart[:enterprise_id])
  100 + @settings = Noosfero::Plugin::Settings.new(@enterprise, ShoppingCartPlugin)
  101 + render :layout => false
  102 + end
101 103 end
102 104  
103 105 def send_request
... ...
plugins/shopping_cart/controllers/shopping_cart_plugin_myprofile_controller.rb
... ... @@ -63,7 +63,7 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController
63 63  
64 64 def treat_delivery_options(params)
65 65 result = {}
66   - return result if params.nil? || params[:delivery_options].nil?
  66 + return result if params.nil? || params[:options].nil?
67 67 params[:options].size.times do |counter|
68 68 if params[:options][counter].present? && params[:prices][counter].present?
69 69 result[params[:options][counter]] = params[:prices][counter]
... ...
plugins/shopping_cart/test/functional/shopping_cart_plugin_controller_test.rb
... ... @@ -187,6 +187,12 @@ class ShoppingCartPluginControllerTest &lt; ActionController::TestCase
187 187 assert !cart?, "cart expected to be empty!"
188 188 end
189 189  
  190 + should 'not allow buy without any cart' do
  191 + get :buy
  192 + assert !json_response[:ok]
  193 + assert_equal 2, json_response['error']['code']
  194 + end
  195 +
190 196 private
191 197  
192 198 def json_response
... ...
plugins/shopping_cart/test/functional/shopping_cart_plugin_myprofile_controller_test.rb
... ... @@ -51,6 +51,14 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase
51 51 assert settings.delivery_price == price
52 52 end
53 53  
  54 + should 'be able to choose delivery_options' do
  55 + delivery_options = {:options => ['car', 'bike'], :prices => ['20', '5']}
  56 + post :edit, :profile => enterprise.identifier, :settings => {:delivery_options => delivery_options}
  57 +
  58 + assert_equal '20', settings.delivery_options['car']
  59 + assert_equal '5', settings.delivery_options['bike']
  60 + end
  61 +
54 62 should 'filter the reports correctly' do
55 63 another_enterprise = fast_create(Enterprise)
56 64 po1 = ShoppingCartPlugin::PurchaseOrder.create!(:seller => enterprise, :status => ShoppingCartPlugin::PurchaseOrder::Status::OPENED)
... ...
plugins/solr/lib/ext/article.rb
... ... @@ -24,7 +24,8 @@ class Article
24 24 {:slug => :text}, {:body => :text},
25 25 {:abstract => :text}, {:filename => :text},
26 26 # filtered fields
27   - {:solr_plugin_public => :boolean}, {:environment_id => :integer},
  27 + {:solr_plugin_public => :boolean}, {:published => :boolean},
  28 + {:environment_id => :integer},
28 29 {:profile_id => :integer}, :language,
29 30 {:solr_plugin_category_filter => :integer},
30 31 # ordered/query-boosted fields
... ...
plugins/solr/lib/ext/profile.rb
... ... @@ -28,7 +28,7 @@ class Profile
28 28 {:solr_plugin_category_filter => :integer},
29 29 # ordered/query-boosted fields
30 30 {:solr_plugin_name_sortable => :string}, {:user_id => :integer},
31   - :enabled, :active, :validated, :public_profile,
  31 + :enabled, :active, :validated, :public_profile, :visible,
32 32 {:lat => :float}, {:lng => :float},
33 33 :updated_at, :created_at,
34 34 ],
... ...
plugins/solr/lib/solr_plugin.rb
... ... @@ -18,16 +18,71 @@ class SolrPlugin &lt; Noosfero::Plugin
18 18 true
19 19 end
20 20  
  21 + def solr_search? empty_query, klass
  22 + not empty_query or klass == Product
  23 + end
  24 +
21 25 def find_by_contents(asset, scope, query, paginate_options={}, options={})
22 26 klass = asset_class(asset)
23   - category = options.delete(:category)
24   - filter = options.delete(:filter)
  27 + category = options[:category]
  28 + empty_query = empty_query? query, category
25 29  
26   - return if empty_query?(query, category) && klass != Product
  30 + unless solr_search? empty_query, klass
  31 + return options[:filter] ? {:results => scope.send(options[:filter]).paginate(paginate_options)} : nil
  32 + end
27 33  
28 34 solr_options = solr_options(class_asset(klass), category)
29   - solr_options.merge!(products_options(user)) if klass == Product && empty_query?(query, category)
30   - klass.find_by_contents(query, paginate_options, solr_options.merge(options))
  35 + solr_options[:filter_queries] ||= []
  36 + solr_options[:filter_queries] += scopes_to_solr_filters scope, klass, options
  37 + solr_options.merge! products_options(user) if klass == Product and empty_query
  38 + solr_options.merge! options.except(:category, :filter)
  39 +
  40 + scope.find_by_contents query, paginate_options, solr_options
  41 + end
  42 +
  43 + protected
  44 +
  45 + def scopes_to_solr_filters scope, klass = nil, options = {}
  46 + filter_queries = []
  47 + klass ||= scope.base_class
  48 + solr_fields = klass.configuration[:solr_fields].keys
  49 + scopes_applied = scope.scopes_applied.dup rescue [] #rescue association and class direct filtering
  50 +
  51 + scope.current_scoped_methods[:create].each do |attr, value|
  52 + next unless solr_fields.include? attr.to_sym
  53 +
  54 + # if the filter is present here, then prefer it
  55 + scopes_applied.reject!{ |name| name == attr.to_sym }
  56 +
  57 + filter_queries << "#{attr}:#{value}"
  58 + end
  59 +
  60 + scopes_applied.each do |name|
  61 + next if name.to_s == options[:filter].to_s
  62 +
  63 + has_value = name === Hash
  64 + if has_value
  65 + name, args = name.keys.first, name.values.first
  66 + value = args.first
  67 + end
  68 +
  69 + related_field = nil
  70 + related_field = name if solr_fields.include? name
  71 + related_field = "solr_plugin_#{name}" if solr_fields.include? :"solr_plugin_#{name}"
  72 +
  73 + if has_value
  74 + if related_field
  75 + filter_queries << "#{related_field}:#{value}"
  76 + else
  77 + filter_queries << klass.send("solr_filter_#{name}", *args)
  78 + end
  79 + else
  80 + raise "Undeclared solr field for scope #{name}" if related_field.nil?
  81 + filter_queries << "#{related_field}:true"
  82 + end
  83 + end
  84 +
  85 + filter_queries
31 86 end
32 87  
33 88 def method_missing method, *args, &block
... ...
plugins/solr/test/unit/solr_plugin/plugin_test.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class SolrPlugin::PluginTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @plugin = SolrPlugin.new
  7 + end
  8 + attr_reader :plugin
  9 +
  10 + should 'convert scopes to solr filters' do
  11 + person = create_user('test').person
  12 + result = plugin.send :scopes_to_solr_filters, person.files.public.published
  13 + assert_equal result, ["profile_id:#{person.id}", "published:'true'", "solr_plugin_public:true"]
  14 + end
  15 +
  16 +end
... ...
plugins/solr/vendor/plugins/named_scope_with_applied_names/init.rb 0 → 100644
... ... @@ -0,0 +1,57 @@
  1 +require_dependency 'active_record/named_scope'
  2 +
  3 +if Rails::VERSION::STRING < "2.3.99"
  4 +
  5 + module ::ActiveRecord
  6 +
  7 + module NamedScope
  8 +
  9 + module ClassMethods
  10 +
  11 + def named_scope_with_applied_names name, options = {}, &block
  12 + named_scope_without_applied_names name, options, &block
  13 +
  14 + name = name.to_sym
  15 + scopes[name] = lambda do |parent_scope, *args|
  16 + scope = Scope.new(parent_scope, case options
  17 + when Hash
  18 + options
  19 + when Proc
  20 + if self.model_name != parent_scope.model_name
  21 + options.bind(parent_scope).call(*args)
  22 + else
  23 + options.call(*args)
  24 + end
  25 + end, &block)
  26 + scope.scope_name = name
  27 + scope
  28 + end
  29 + end
  30 + alias_method_chain :named_scope, :applied_names
  31 + end
  32 +
  33 + class Scope
  34 + attr_accessor :scope_name, :scopes_applied
  35 +
  36 + def initialize_with_applied_names proxy_scope, options, &block
  37 + initialize_without_applied_names proxy_scope, options, &block
  38 + self.scopes_applied ||= []
  39 + self.scopes_applied += proxy_scope.send :scopes_applied if Scope === proxy_scope
  40 +
  41 + # unrelated bugfix: use if instead of unless
  42 + if (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
  43 + @current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
  44 + end
  45 + end
  46 + alias_method_chain :initialize, :applied_names
  47 +
  48 + def scope_name= name
  49 + @scope_name = name
  50 + self.scopes_applied << @scope_name
  51 + end
  52 +
  53 + end
  54 +
  55 + end
  56 + end
  57 +end
... ...
test/fixtures/files/500.html 0 → 120000
... ... @@ -0,0 +1 @@
  1 +../../../public/500.html
0 2 \ No newline at end of file
... ...
test/functional/catalog_controller_test.rb
... ... @@ -235,4 +235,18 @@ class CatalogControllerTest &lt; ActionController::TestCase
235 235 assert_equal [pc2, pc1, pc4, pc3], assigns(:categories)
236 236 end
237 237  
  238 + should 'use price_detail name instead of production_cost name straight' do
  239 + p1 = fast_create(Product, :product_category_id => @product_category.id, :enterprise_id => @enterprise.id)
  240 + p2 = fast_create(Product, :product_category_id => @product_category.id, :enterprise_id => @enterprise.id)
  241 + Product.any_instance.stubs(:price_described?).returns(true)
  242 + production_cost = fast_create(ProductionCost)
  243 + pd1 = PriceDetail.create!(:product => p1, :production_cost => production_cost)
  244 + pd2 = PriceDetail.create!(:product => p2)
  245 +
  246 + get :index, :profile => @enterprise.identifier
  247 +
  248 + assert_tag :tag => 'div', :attributes => {:class => 'search-product-input-name'}, :content => production_cost.name
  249 + assert_tag :tag => 'div', :attributes => {:class => 'search-product-input-name'}, :content => 'Other costs'
  250 + end
  251 +
238 252 end
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -64,7 +64,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
64 64 assert_response :missing
65 65 end
66 66  
67   - should 'produce a download-like when article is not text/html' do
  67 + should 'produce a download-link when article is a uploaded file' do
  68 + profile = create_user('someone').person
  69 + html = UploadedFile.create! :uploaded_data => fixture_file_upload('/files/500.html', 'text/html'), :profile => profile
  70 + html.save!
  71 +
  72 + get :view_page, :profile => 'someone', :page => [ '500.html' ]
  73 +
  74 + assert_response :success
  75 + assert_match /^text\/html/, @response.headers['Content-Type']
  76 + assert @response.headers['Content-Disposition'].present?
  77 + assert_match /attachment/, @response.headers['Content-Disposition']
  78 + end
  79 +
  80 + should 'produce a download-link when article is not text/html' do
68 81  
69 82 # for example, RSS feeds
70 83 profile = create_user('someone').person
... ... @@ -1254,7 +1267,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1254 1267  
1255 1268 get 'view_page', :profile => profile.identifier, :page => article.path.split('/')
1256 1269 assert_tag :tag => 'a', :attributes => { :href => "/#{profile.identifier}/#{article.path}?comment_page=2", :rel => 'next' }
1257   - end
  1270 + end
1258 1271  
1259 1272 should 'not escape acceptable HTML in list of blog posts' do
1260 1273 login_as('testinguser')
... ...
test/functional/memberships_controller_test.rb
... ... @@ -6,7 +6,7 @@ require &#39;memberships_controller&#39;
6 6 class MembershipsController; def rescue_action(e) raise e end; end
7 7  
8 8 class MembershipsControllerTest < ActionController::TestCase
9   -
  9 +
10 10 include ApplicationHelper
11 11  
12 12 def setup
... ... @@ -22,7 +22,7 @@ class MembershipsControllerTest &lt; ActionController::TestCase
22 22 def test_local_files_reference
23 23 assert_local_files_reference :get, :index, :profile => profile.identifier
24 24 end
25   -
  25 +
26 26 def test_valid_xhtml
27 27 assert_valid_xhtml
28 28 end
... ... @@ -245,4 +245,23 @@ class MembershipsControllerTest &lt; ActionController::TestCase
245 245 assert_tag :tag => 'input', :attributes => {:id => 'community_plugin2', :type => 'hidden', :value => 'Plugin 2'}
246 246 end
247 247  
  248 + should 'only display control panel link to members with permission' do
  249 + c1 = fast_create(Community, :name => 'My own community')
  250 + c2 = fast_create(Community, :name => 'Not my community')
  251 +
  252 + owner = fast_create(Person)
  253 + c2.add_admin(owner)
  254 +
  255 + person = Person['testuser']
  256 + c1.add_admin(person)
  257 + c2.add_member(person)
  258 +
  259 + login_as('testuser')
  260 + get :index, :profile => 'testuser'
  261 +
  262 + assert_template 'index'
  263 + assert_no_tag :tag => 'a', :attributes => { :href => "/myprofile/#{c2.identifier}" }
  264 + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{c1.identifier}" }
  265 + end
  266 +
248 267 end
... ...
test/functional/profile_controller_test.rb
... ... @@ -1344,6 +1344,17 @@ class ProfileControllerTest &lt; ActionController::TestCase
1344 1344 assert_tag :tag => 'span', :content => '(removed user)', :attributes => {:class => 'comment-user-status comment-user-status-wall icon-user-removed'}
1345 1345 end
1346 1346  
  1347 + should 'not display spam comments in wall' do
  1348 + UserStampSweeper.any_instance.stubs(:current_user).returns(profile)
  1349 + article = TinyMceArticle.create!(:profile => profile, :name => 'An article about spam\'s nutritional attributes')
  1350 + comment = Comment.create!(:author => profile, :title => 'Test Comment', :body => 'This article makes me hungry', :source_id => article.id, :source_type => 'Article')
  1351 + comment.spam!
  1352 + login_as(profile.identifier)
  1353 + get :index, :profile => profile.identifier
  1354 +
  1355 + assert !/This article makes me hungry/.match(@response.body), 'Spam comment was shown!'
  1356 + end
  1357 +
1347 1358 should 'display comment in wall from non logged users' do
1348 1359 UserStampSweeper.any_instance.stubs(:current_user).returns(profile)
1349 1360 article = TinyMceArticle.create!(:profile => profile, :name => 'An article about free software')
... ...
test/functional/profile_editor_controller_test.rb
... ... @@ -624,6 +624,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
624 624  
625 625 profile.domains << Domain.new(:name => 'myowndomain.net')
626 626 profile.environment.domains << Domain.new(:name => 'myenv.net')
  627 + ActionController::TestRequest.any_instance.stubs(:host).returns(profile.hostname)
627 628  
628 629 get :edit, :profile => profile.identifier
629 630 assert_tag :tag => 'select', :attributes => { :name => 'profile_data[preferred_domain_id]' }, :descendant => { :tag => "option", :content => 'myowndomain.net' }
... ... @@ -639,6 +640,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
639 640  
640 641 profile.domains << Domain.new(:name => 'myowndomain.net')
641 642 profile.environment.domains << Domain.new(:name => 'myenv.net')
  643 + ActionController::TestRequest.any_instance.stubs(:host).returns(profile.hostname)
642 644  
643 645 get :edit, :profile => profile.identifier
644 646 assert_tag :tag => "select", :attributes => { :name => 'profile_data[preferred_domain_id]'}, :descendant => { :tag => 'option', :content => '&lt;Select domain&gt;', :attributes => { :value => '' } }
... ... @@ -978,4 +980,13 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
978 980 assert_equal({}, person.reload.fields_privacy)
979 981 end
980 982  
  983 + should 'not redirect if the profile_hostname is the same as environment hostname' do
  984 + Person.any_instance.stubs(:hostname).returns('hostname.org')
  985 + Environment.any_instance.stubs(:default_hostname).returns('hostname.org')
  986 + ActionController::TestRequest.any_instance.stubs(:host).returns('hostname.org')
  987 + get :index, :profile => profile.identifier
  988 + assert_response :success
  989 + end
  990 +
  991 +
981 992 end
... ...
test/unit/application_helper_test.rb
... ... @@ -143,6 +143,18 @@ class ApplicationHelperTest &lt; ActiveSupport::TestCase
143 143 assert_tag_in_string rolename_for(member2, community), :tag => 'span', :content => 'Profile Member'
144 144 end
145 145  
  146 + should 'rolenames for a member admin' do
  147 + member1 = create_user('usertest1').person
  148 + member2 = create_user('usertest2').person
  149 + community = fast_create(Community, :name => 'new community', :identifier => 'new-community', :environment_id => Environment.default.id)
  150 + community.add_member(member1)
  151 + # member2 is both a admin and a member
  152 + community.add_member(member2)
  153 + community.add_admin(member2)
  154 + assert_tag_in_string rolename_for(member2, community), :tag => 'span', :content => 'Profile Member'
  155 + assert_tag_in_string rolename_for(member2, community), :tag => 'span', :content => 'Profile Administrator'
  156 + end
  157 +
146 158 should 'get theme from environment by default' do
147 159 @environment = mock
148 160 @environment.stubs(:theme).returns('my-environment-theme')
... ...
test/unit/environment_test.rb
... ... @@ -293,21 +293,6 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
293 293 assert_equal blocks - env_blocks, Block.count
294 294 end
295 295  
296   - should 'destroy templates' do
297   - env = fast_create(Environment)
298   - templates = [mock, mock, mock, mock]
299   - templates.each do |item|
300   - item.expects(:destroy)
301   - end
302   -
303   - env.stubs(:person_template).returns(templates[0])
304   - env.stubs(:community_template).returns(templates[1])
305   - env.stubs(:enterprise_template).returns(templates[2])
306   - env.stubs(:inactive_enterprise_template).returns(templates[3])
307   -
308   - env.destroy
309   - end
310   -
311 296 should 'have boxes and blocks upon creation' do
312 297 Environment.any_instance.stubs(:create_templates) # avoid creating templates, it's expensive
313 298 environment = Environment.create!(:name => 'a test environment')
... ...
test/unit/profile_test.rb
... ... @@ -1888,4 +1888,11 @@ class ProfileTest &lt; ActiveSupport::TestCase
1888 1888 assert !profile.may_display_location_to?(user)
1889 1889 end
1890 1890  
  1891 + should 'destroy profile if its environment is destroyed' do
  1892 + environment = fast_create(Environment)
  1893 + profile = fast_create(Profile, :environment_id => environment.id)
  1894 +
  1895 + environment.destroy
  1896 + assert_raise(ActiveRecord::RecordNotFound) {profile.reload}
  1897 + end
1891 1898 end
... ...
vendor/plugins/action_tracker_has_comments/init.rb
... ... @@ -7,7 +7,7 @@ Rails.configuration.to_prepare do
7 7  
8 8 def conditions_for_comments
9 9 type, id = (self.target_type == 'Article' ? ['Article', self.target_id] : [self.class.to_s, self.id])
10   - "source_type = '#{type}' AND source_id = '#{id}'"
  10 + "source_type = '#{type}' AND source_id = '#{id}' AND spam IS NOT TRUE"
11 11 end
12 12  
13 13 def comments_as_thread
... ...