Commit 96ad392a655a791e6df5359fe0e059aa7b53ba36
Exists in
master
and in
28 other branches
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 <root@debian.sdr.serpro> |
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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 } %> | ... | ... |
... | ... | @@ -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
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
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
plugins/pg_search/lib/ext/active_record.rb
... | ... | @@ -2,8 +2,11 @@ require_dependency 'active_record' |
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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 | ... | ... |
... | ... | @@ -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/functional/catalog_controller_test.rb
... | ... | @@ -235,4 +235,18 @@ class CatalogControllerTest < 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 < 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 < 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 'memberships_controller' |
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 < 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 < 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 < 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 < 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 < 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 => '<Select domain>', :attributes => { :value => '' } } |
... | ... | @@ -978,4 +980,13 @@ class ProfileEditorControllerTest < 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 < 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 < 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 < 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 | ... | ... |