Commit bd4954d3143c452fee2a074571ffc66cbc2b2eda

Authored by Rodrigo Souto
2 parents 25126f2b 28a75f78

Merge branch 'master' into next-origin

Conflicts:
	app/models/category.rb
	app/models/uploaded_file.rb
	test/functional/memberships_controller_test.rb
	test/functional/profile_controller_test.rb
	vendor/plugins/action_tracker_has_comments/init.rb
Showing 46 changed files with 389 additions and 75 deletions   Show diff stats
@@ -111,6 +111,7 @@ Diego Martinez <diegoamc90@gmail.com> @@ -111,6 +111,7 @@ Diego Martinez <diegoamc90@gmail.com>
111 Diego Martinez <diego@diego-K55A.(none)> 111 Diego Martinez <diego@diego-K55A.(none)>
112 Diego + Renan <renanteruoc@gmail.com> 112 Diego + Renan <renanteruoc@gmail.com>
113 Fernanda Lopes <nanda.listas+psl@gmail.com> 113 Fernanda Lopes <nanda.listas+psl@gmail.com>
  114 +Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
114 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)> 115 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
115 Grazieno Pellegrino <grazieno@gmail.com> 116 Grazieno Pellegrino <grazieno@gmail.com>
116 Isaac Canan <isaac@intelletto.com.br> 117 Isaac Canan <isaac@intelletto.com.br>
@@ -189,12 +190,14 @@ Renan Teruo + Diego Araujo &lt;renanteruoc@gmail.com&gt; @@ -189,12 +190,14 @@ Renan Teruo + Diego Araujo &lt;renanteruoc@gmail.com&gt;
189 Renan Teruo + Diego Araújo <renanteruoc@gmail.com> 190 Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
190 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com> 191 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
191 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com> 192 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
  193 +Rodrigo Souto <diguliu@gmail.com>
192 Rodrigo Souto <rodrigo@colivre.coop.br> 194 Rodrigo Souto <rodrigo@colivre.coop.br>
193 Ronny Kursawe <kursawe.ronny@googlemail.com> 195 Ronny Kursawe <kursawe.ronny@googlemail.com>
194 root <root@debian.sdr.serpro> 196 root <root@debian.sdr.serpro>
195 Samuel R. C. Vale <srcvale@holoscopio.com> 197 Samuel R. C. Vale <srcvale@holoscopio.com>
196 Valessio Brito <valessio@gmail.com> 198 Valessio Brito <valessio@gmail.com>
197 vfcosta <vfcosta@gmail.com> 199 vfcosta <vfcosta@gmail.com>
  200 +Vinicius Cubas Brand <viniciuscb@gmail.com>
198 Visita <visita@debian.(none)> 201 Visita <visita@debian.(none)>
199 Yann Lugrin <yann.lugrin@liquid-concept.ch> 202 Yann Lugrin <yann.lugrin@liquid-concept.ch>
200 203
app/controllers/application_controller.rb
@@ -174,8 +174,6 @@ class ApplicationController &lt; ActionController::Base @@ -174,8 +174,6 @@ class ApplicationController &lt; ActionController::Base
174 end 174 end
175 175
176 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={}) 176 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={})
177 - scope = scope.send(options[:filter]) if options[:filter]  
178 -  
179 @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) || 177 @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||
180 fallback_find_by_contents(asset, scope, query, paginate_options, options) 178 fallback_find_by_contents(asset, scope, query, paginate_options, options)
181 end 179 end
@@ -183,8 +181,9 @@ class ApplicationController &lt; ActionController::Base @@ -183,8 +181,9 @@ class ApplicationController &lt; ActionController::Base
183 private 181 private
184 182
185 def fallback_find_by_contents(asset, scope, query, paginate_options, options) 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 end 187 end
189 188
190 end 189 end
app/controllers/public/content_viewer_controller.rb
@@ -55,8 +55,9 @@ class ContentViewerController &lt; ApplicationController @@ -55,8 +55,9 @@ class ContentViewerController &lt; ApplicationController
55 55
56 @page = FilePresenter.for @page 56 @page = FilePresenter.for @page
57 57
58 - unless @page.mime_type == 'text/html' || params[:view] 58 + if @page.download? params[:view]
59 headers['Content-Type'] = @page.mime_type 59 headers['Content-Type'] = @page.mime_type
  60 + headers.merge! @page.download_headers
60 data = @page.data 61 data = @page.data
61 62
62 # TODO test the condition 63 # TODO test the condition
@@ -72,7 +73,7 @@ class ContentViewerController &lt; ApplicationController @@ -72,7 +73,7 @@ class ContentViewerController &lt; ApplicationController
72 73
73 #FIXME see a better way to do this. It's not need to pass this variable anymore 74 #FIXME see a better way to do this. It's not need to pass this variable anymore
74 @comment = Comment.new 75 @comment = Comment.new
75 - 76 +
76 if @page.has_posts? 77 if @page.has_posts?
77 posts = if params[:year] and params[:month] 78 posts = if params[:year] and params[:month]
78 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01") 79 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01")
app/helpers/application_helper.rb
@@ -730,8 +730,15 @@ module ApplicationHelper @@ -730,8 +730,15 @@ module ApplicationHelper
730 end 730 end
731 731
732 def rolename_for(profile, resource) 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 end 742 end
736 743
737 def role_color(role, env_id) 744 def role_color(role, env_id)
app/models/article.rb
@@ -194,7 +194,7 @@ class Article &lt; ActiveRecord::Base @@ -194,7 +194,7 @@ class Article &lt; ActiveRecord::Base
194 pending_categorizations.clear 194 pending_categorizations.clear
195 end 195 end
196 196
197 - acts_as_taggable 197 + acts_as_taggable
198 N_('Tag list') 198 N_('Tag list')
199 199
200 acts_as_filesystem 200 acts_as_filesystem
@@ -274,7 +274,7 @@ class Article &lt; ActiveRecord::Base @@ -274,7 +274,7 @@ class Article &lt; ActiveRecord::Base
274 end 274 end
275 275
276 # returns the data of the article. Must be overriden in each subclass to 276 # returns the data of the article. Must be overriden in each subclass to
277 - # provide the correct content for the article. 277 + # provide the correct content for the article.
278 def data 278 def data
279 body 279 body
280 end 280 end
@@ -370,6 +370,16 @@ class Article &lt; ActiveRecord::Base @@ -370,6 +370,16 @@ class Article &lt; ActiveRecord::Base
370 false 370 false
371 end 371 end
372 372
  373 + def download? view = nil
  374 + (self.uploaded_file? and not self.image?) or
  375 + (self.image? and view.blank?) or
  376 + (not self.uploaded_file? and self.mime_type != 'text/html')
  377 + end
  378 +
  379 + def download_headers
  380 + {}
  381 + end
  382 +
373 named_scope :native_translations, :conditions => { :translation_of_id => nil } 383 named_scope :native_translations, :conditions => { :translation_of_id => nil }
374 384
375 def translatable? 385 def translatable?
app/models/category.rb
@@ -7,13 +7,13 @@ class Category &lt; ActiveRecord::Base @@ -7,13 +7,13 @@ class Category &lt; ActiveRecord::Base
7 :slug => 1, 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 validates_presence_of :name, :environment_id 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 belongs_to :environment 13 belongs_to :environment
14 14
15 validates_inclusion_of :display_color, :in => 1..15, :allow_nil => true 15 validates_inclusion_of :display_color, :in => 1..15, :allow_nil => true
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 # Finds all top level categories for a given environment. 18 # Finds all top level categories for a given environment.
19 named_scope :top_level_for, lambda { |environment| 19 named_scope :top_level_for, lambda { |environment|
app/models/comment.rb
@@ -27,7 +27,7 @@ class Comment &lt; ActiveRecord::Base @@ -27,7 +27,7 @@ class Comment &lt; ActiveRecord::Base
27 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? }) 27 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? })
28 validates_each :name do |rec,attribute,value| 28 validates_each :name do |rec,attribute,value|
29 if rec.author_id && (!rec.name.blank? || !rec.email.blank?) 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 end 31 end
32 end 32 end
33 33
app/models/create_enterprise.rb
@@ -40,12 +40,12 @@ class CreateEnterprise &lt; Task @@ -40,12 +40,12 @@ class CreateEnterprise &lt; Task
40 40
41 if self.region && self.target 41 if self.region && self.target
42 unless self.region.validators.include?(self.target) || self.target_type == "Environment" 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 end 44 end
45 end 45 end
46 46
47 if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier) 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 end 49 end
50 end 50 end
51 51
app/models/domain.rb
@@ -10,14 +10,14 @@ class Domain &lt; ActiveRecord::Base @@ -10,14 +10,14 @@ class Domain &lt; ActiveRecord::Base
10 10
11 # <tt>name</tt> must be sequences of alphanumeric characters (a to z, 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. 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 # checks validations that could not be expressed using Rails' predefined 15 # checks validations that could not be expressed using Rails' predefined
16 # validations. In particular: 16 # validations. In particular:
17 # * <tt>name</tt> must not start with 'www.' 17 # * <tt>name</tt> must not start with 'www.'
18 def validate 18 def validate
19 if self.name =~ /^www\./ 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 end 21 end
22 end 22 end
23 23
app/models/environment.rb
@@ -170,7 +170,7 @@ class Environment &lt; ActiveRecord::Base @@ -170,7 +170,7 @@ class Environment &lt; ActiveRecord::Base
170 170
171 # One Environment can be reached by many domains 171 # One Environment can be reached by many domains
172 has_many :domains, :as => :owner 172 has_many :domains, :as => :owner
173 - has_many :profiles 173 + has_many :profiles, :dependent => :destroy
174 174
175 has_many :organizations 175 has_many :organizations
176 has_many :enterprises 176 has_many :enterprises
@@ -785,13 +785,6 @@ class Environment &lt; ActiveRecord::Base @@ -785,13 +785,6 @@ class Environment &lt; ActiveRecord::Base
785 self.save! 785 self.save!
786 end 786 end
787 787
788 - after_destroy :destroy_templates  
789 - def destroy_templates  
790 - [enterprise_template, inactive_enterprise_template, community_template, person_template].compact.each do |template|  
791 - template.destroy  
792 - end  
793 - end  
794 -  
795 after_create :create_default_licenses 788 after_create :create_default_licenses
796 def create_default_licenses 789 def create_default_licenses
797 License.create!(:name => 'CC (by)', :url => 'http://creativecommons.org/licenses/by/3.0/legalcode', :environment => self) 790 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,7 +25,7 @@ class Event &lt; Article
25 25
26 validates_each :start_date do |event,field,value| 26 validates_each :start_date do |event,field,value|
27 if event.end_date && event.start_date && event.start_date > event.end_date 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 end 29 end
30 end 30 end
31 31
app/models/image.rb
@@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base @@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base
17 :icon => '20x20!' }, 17 :icon => '20x20!' },
18 :max_size => 5.megabytes # remember to update validate message below 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 delay_attachment_fu_thumbnails 22 delay_attachment_fu_thumbnails
23 23
app/models/person.rb
@@ -240,7 +240,7 @@ class Person &lt; Profile @@ -240,7 +240,7 @@ class Person &lt; Profile
240 240
241 validates_each :email, :on => :update do |record,attr,value| 241 validates_each :email, :on => :update do |record,attr,value|
242 if User.find(:first, :conditions => ['email = ? and id != ? and environment_id = ?', value, record.user.id, record.environment.id]) 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 end 244 end
245 end 245 end
246 246
app/models/uploaded_file.rb
@@ -72,7 +72,7 @@ class UploadedFile &lt; Article @@ -72,7 +72,7 @@ class UploadedFile &lt; Article
72 :thumbnail_class => Thumbnail, 72 :thumbnail_class => Thumbnail,
73 :max_size => self.max_size 73 :max_size => self.max_size
74 74
75 - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of %{size}").sub('%{size}', self.max_size.to_humanreadable).fix_i18n 75 + validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of %{size}").sub('%{size}', self.max_size.to_humanreadable).fix_i18n
76 76
77 delay_attachment_fu_thumbnails 77 delay_attachment_fu_thumbnails
78 78
@@ -112,6 +112,12 @@ class UploadedFile &lt; Article @@ -112,6 +112,12 @@ class UploadedFile &lt; Article
112 self.name = self.filename 112 self.name = self.filename
113 end 113 end
114 114
  115 + def download_headers
  116 + {
  117 + 'Content-Disposition' => "attachment; filename=\"#{self.filename}\"",
  118 + }
  119 + end
  120 +
115 def data 121 def data
116 File.read(self.full_filename) 122 File.read(self.full_filename)
117 end 123 end
app/models/user.rb
@@ -114,7 +114,7 @@ class User &lt; ActiveRecord::Base @@ -114,7 +114,7 @@ class User &lt; ActiveRecord::Base
114 before_save :encrypt_password 114 before_save :encrypt_password
115 validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?}) 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 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil. 119 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil.
120 def self.authenticate(login, password, environment = nil) 120 def self.authenticate(login, password, environment = nil)
app/views/catalog/index.rhtml
@@ -92,7 +92,7 @@ @@ -92,7 +92,7 @@
92 <% end %> 92 <% end %>
93 <% product.price_details.each do |i| %> 93 <% product.price_details.each do |i| %>
94 <div class="search-product-input-dots-to-price"> 94 <div class="search-product-input-dots-to-price">
95 - <div class="search-product-input-name"><%= i.production_cost.name %></div> 95 + <div class="search-product-input-name"><%= i.name %></div>
96 <%= price_span i.price, :class => 'search-product-input-price' %> 96 <%= price_span i.price, :class => 'search-product-input-price' %>
97 </div> 97 </div>
98 <% end %> 98 <% end %>
app/views/profile/_person_profile.rhtml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 </tr> 4 </tr>
5 <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> 5 <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>
6 <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> 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 <%= display_field(_('Type:'), profile, :privacy_setting, true) %> 9 <%= display_field(_('Type:'), profile, :privacy_setting, true) %>
10 10
app/views/shared/_list_groups.html.erb
@@ -12,7 +12,9 @@ @@ -12,7 +12,9 @@
12 <%= _('Members: %s') % group.members_count.to_s %> <br/> 12 <%= _('Members: %s') % group.members_count.to_s %> <br/>
13 <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/> 13 <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/>
14 <% button_bar do %> 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 <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %> 18 <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %>
17 <% if (group.community? && user.has_permission?(:destroy_profile, group)) %> 19 <% if (group.community? && user.has_permission?(:destroy_profile, group)) %>
18 <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => group.identifier } %> 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 @@ @@ -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.45.0~rc20131202153818) squeeze-test; urgency=low
  2 +
  3 + * 0.45.0 RC2
  4 +
  5 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Mon, 02 Dec 2013 15:38:28 -0300
  6 +
  7 +noosfero (0.44.6) unstable; urgency=low
  8 +
  9 + * 0.44.5 Addendum
  10 +
  11 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Wed, 27 Nov 2013 16:45:02 -0300
  12 +
  13 +noosfero (0.44.5) unstable; urgency=low
  14 +
  15 + * Bugfix release
  16 +
  17 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Tue, 26 Nov 2013 17:39:39 -0300
  18 +
1 noosfero (0.44.4) unstable; urgency=low 19 noosfero (0.44.4) unstable; urgency=low
2 20
3 * Bugfix release 21 * Bugfix release
features/manage_products.feature
@@ -9,7 +9,7 @@ Feature: manage products @@ -9,7 +9,7 @@ Feature: manage products
9 And the following enterprises 9 And the following enterprises
10 | identifier | owner | name | enabled | 10 | identifier | owner | name | enabled |
11 | redemoinho | joaosilva | Rede Moinho | true | 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 Scenario: display "create new product" button 14 Scenario: display "create new product" button
15 Given I am logged in as "joaosilva" 15 Given I am logged in as "joaosilva"
gitignore.example
@@ -39,3 +39,6 @@ debian/*.debhelper @@ -39,3 +39,6 @@ debian/*.debhelper
39 debian/files 39 debian/files
40 debian/noosfero/ 40 debian/noosfero/
41 debian/noosfero-apache/ 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,7 +24,7 @@ module NeedsProfile
24 @profile ||= environment.profiles.find_by_identifier(params[:profile]) 24 @profile ||= environment.profiles.find_by_identifier(params[:profile])
25 if @profile 25 if @profile
26 profile_hostname = @profile.hostname 26 profile_hostname = @profile.hostname
27 - if profile_hostname && request.host == @environment.default_hostname 27 + if profile_hostname && profile_hostname != request.host
28 params.delete(:profile) 28 params.delete(:profile)
29 redirect_to(Noosfero.url_options.merge(params).merge(:host => profile_hostname)) 29 redirect_to(Noosfero.url_options.merge(params).merge(:host => profile_hostname))
30 end 30 end
lib/noosfero.rb
@@ -3,7 +3,7 @@ require &#39;fast_gettext&#39; @@ -3,7 +3,7 @@ require &#39;fast_gettext&#39;
3 3
4 module Noosfero 4 module Noosfero
5 PROJECT = 'noosfero' 5 PROJECT = 'noosfero'
6 - VERSION = '0.44.4' 6 + VERSION = '0.45.0~rc20131202153818'
7 7
8 def self.pattern_for_controllers_in_directory(dir) 8 def self.pattern_for_controllers_in_directory(dir)
9 disjunction = controllers_in_directory(dir).join('|') 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,8 +2,11 @@ require_dependency &#39;active_record&#39;
2 2
3 class ActiveRecord::Base 3 class ActiveRecord::Base
4 def self.pg_search_plugin_search(query) 4 def self.pg_search_plugin_search(query)
  5 + query.gsub!(/\|/,' ')
  6 + formatted_query = query.split.map{|w| w += ":*"}.join('|')
  7 +
5 if defined?(self::SEARCHABLE_FIELDS) 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 else 10 else
8 raise "No searchable fields defined for #{self.name}" 11 raise "No searchable fields defined for #{self.name}"
9 end 12 end
plugins/pg_search/lib/pg_search_plugin.rb
@@ -9,8 +9,9 @@ class PgSearchPlugin &lt; Noosfero::Plugin @@ -9,8 +9,9 @@ class PgSearchPlugin &lt; Noosfero::Plugin
9 end 9 end
10 10
11 def find_by_contents(asset, scope, query, paginate_options={}, options={}) 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 end 15 end
15 16
16 end 17 end
plugins/pg_search/test/unit/pg_search_plugin_test.rb
@@ -14,6 +14,13 @@ class PgSearchPluginTest &lt; ActiveSupport::TestCase @@ -14,6 +14,13 @@ class PgSearchPluginTest &lt; ActiveSupport::TestCase
14 assert_includes search(Profile, 'water'), profile 14 assert_includes search(Profile, 'water'), profile
15 end 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 # TODO This feature is available only on Postgresql 9.0 24 # TODO This feature is available only on Postgresql 9.0
18 # http://www.postgresql.org/docs/9.0/static/unaccent.html 25 # http://www.postgresql.org/docs/9.0/static/unaccent.html
19 # should 'ignore accents' do 26 # should 'ignore accents' do
plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb
@@ -94,10 +94,12 @@ class ShoppingCartPluginController &lt; PublicController @@ -94,10 +94,12 @@ class ShoppingCartPluginController &lt; PublicController
94 end 94 end
95 95
96 def buy 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 end 103 end
102 104
103 def send_request 105 def send_request
plugins/shopping_cart/controllers/shopping_cart_plugin_myprofile_controller.rb
@@ -63,7 +63,7 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController @@ -63,7 +63,7 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController
63 63
64 def treat_delivery_options(params) 64 def treat_delivery_options(params)
65 result = {} 65 result = {}
66 - return result if params.nil? || params[:delivery_options].nil? 66 + return result if params.nil? || params[:options].nil?
67 params[:options].size.times do |counter| 67 params[:options].size.times do |counter|
68 if params[:options][counter].present? && params[:prices][counter].present? 68 if params[:options][counter].present? && params[:prices][counter].present?
69 result[params[:options][counter]] = params[:prices][counter] 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,6 +187,12 @@ class ShoppingCartPluginControllerTest &lt; ActionController::TestCase
187 assert !cart?, "cart expected to be empty!" 187 assert !cart?, "cart expected to be empty!"
188 end 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 private 196 private
191 197
192 def json_response 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,6 +51,14 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase
51 assert settings.delivery_price == price 51 assert settings.delivery_price == price
52 end 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 should 'filter the reports correctly' do 62 should 'filter the reports correctly' do
55 another_enterprise = fast_create(Enterprise) 63 another_enterprise = fast_create(Enterprise)
56 po1 = ShoppingCartPlugin::PurchaseOrder.create!(:seller => enterprise, :status => ShoppingCartPlugin::PurchaseOrder::Status::OPENED) 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,7 +24,8 @@ class Article
24 {:slug => :text}, {:body => :text}, 24 {:slug => :text}, {:body => :text},
25 {:abstract => :text}, {:filename => :text}, 25 {:abstract => :text}, {:filename => :text},
26 # filtered fields 26 # filtered fields
27 - {:solr_plugin_public => :boolean}, {:environment_id => :integer}, 27 + {:solr_plugin_public => :boolean}, {:published => :boolean},
  28 + {:environment_id => :integer},
28 {:profile_id => :integer}, :language, 29 {:profile_id => :integer}, :language,
29 {:solr_plugin_category_filter => :integer}, 30 {:solr_plugin_category_filter => :integer},
30 # ordered/query-boosted fields 31 # ordered/query-boosted fields
plugins/solr/lib/ext/profile.rb
@@ -28,7 +28,7 @@ class Profile @@ -28,7 +28,7 @@ class Profile
28 {:solr_plugin_category_filter => :integer}, 28 {:solr_plugin_category_filter => :integer},
29 # ordered/query-boosted fields 29 # ordered/query-boosted fields
30 {:solr_plugin_name_sortable => :string}, {:user_id => :integer}, 30 {:solr_plugin_name_sortable => :string}, {:user_id => :integer},
31 - :enabled, :active, :validated, :public_profile, 31 + :enabled, :active, :validated, :public_profile, :visible,
32 {:lat => :float}, {:lng => :float}, 32 {:lat => :float}, {:lng => :float},
33 :updated_at, :created_at, 33 :updated_at, :created_at,
34 ], 34 ],
plugins/solr/lib/solr_plugin.rb
@@ -18,16 +18,71 @@ class SolrPlugin &lt; Noosfero::Plugin @@ -18,16 +18,71 @@ class SolrPlugin &lt; Noosfero::Plugin
18 true 18 true
19 end 19 end
20 20
  21 + def solr_search? empty_query, klass
  22 + not empty_query or klass == Product
  23 + end
  24 +
21 def find_by_contents(asset, scope, query, paginate_options={}, options={}) 25 def find_by_contents(asset, scope, query, paginate_options={}, options={})
22 klass = asset_class(asset) 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 solr_options = solr_options(class_asset(klass), category) 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 end 86 end
32 87
33 def method_missing method, *args, &block 88 def method_missing method, *args, &block
plugins/solr/test/unit/solr_plugin/plugin_test.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -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 @@ @@ -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 @@ @@ -0,0 +1 @@
  1 +../../../public/500.html
0 \ No newline at end of file 2 \ No newline at end of file
test/functional/catalog_controller_test.rb
@@ -235,4 +235,18 @@ class CatalogControllerTest &lt; ActionController::TestCase @@ -235,4 +235,18 @@ class CatalogControllerTest &lt; ActionController::TestCase
235 assert_equal [pc2, pc1, pc4, pc3], assigns(:categories) 235 assert_equal [pc2, pc1, pc4, pc3], assigns(:categories)
236 end 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 end 252 end
test/functional/content_viewer_controller_test.rb
@@ -64,7 +64,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -64,7 +64,20 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
64 assert_response :missing 64 assert_response :missing
65 end 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 # for example, RSS feeds 82 # for example, RSS feeds
70 profile = create_user('someone').person 83 profile = create_user('someone').person
@@ -1254,7 +1267,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1254,7 +1267,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1254 1267
1255 get 'view_page', :profile => profile.identifier, :page => article.path.split('/') 1268 get 'view_page', :profile => profile.identifier, :page => article.path.split('/')
1256 assert_tag :tag => 'a', :attributes => { :href => "/#{profile.identifier}/#{article.path}?comment_page=2", :rel => 'next' } 1269 assert_tag :tag => 'a', :attributes => { :href => "/#{profile.identifier}/#{article.path}?comment_page=2", :rel => 'next' }
1257 - end 1270 + end
1258 1271
1259 should 'not escape acceptable HTML in list of blog posts' do 1272 should 'not escape acceptable HTML in list of blog posts' do
1260 login_as('testinguser') 1273 login_as('testinguser')
@@ -1274,9 +1287,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1274,9 +1287,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1274 should 'display link to download of non-recognized file types on its page' do 1287 should 'display link to download of non-recognized file types on its page' do
1275 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'bin/unknown'), :profile => profile) 1288 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'bin/unknown'), :profile => profile)
1276 get :view_page, file.url.merge(:view=>:true) 1289 get :view_page, file.url.merge(:view=>:true)
1277 - assert_tag :tag => 'a',  
1278 - :content => file.filename,  
1279 - :attributes => { :href => file.public_filename } 1290 + assert_match /this is a sample text file/, @response.body
1280 end 1291 end
1281 1292
1282 end 1293 end
test/functional/memberships_controller_test.rb
@@ -6,7 +6,7 @@ require &#39;memberships_controller&#39; @@ -6,7 +6,7 @@ require &#39;memberships_controller&#39;
6 class MembershipsController; def rescue_action(e) raise e end; end 6 class MembershipsController; def rescue_action(e) raise e end; end
7 7
8 class MembershipsControllerTest < ActionController::TestCase 8 class MembershipsControllerTest < ActionController::TestCase
9 - 9 +
10 include ApplicationHelper 10 include ApplicationHelper
11 11
12 def setup 12 def setup
@@ -22,7 +22,7 @@ class MembershipsControllerTest &lt; ActionController::TestCase @@ -22,7 +22,7 @@ class MembershipsControllerTest &lt; ActionController::TestCase
22 def test_local_files_reference 22 def test_local_files_reference
23 assert_local_files_reference :get, :index, :profile => profile.identifier 23 assert_local_files_reference :get, :index, :profile => profile.identifier
24 end 24 end
25 - 25 +
26 def test_valid_xhtml 26 def test_valid_xhtml
27 assert_valid_xhtml 27 assert_valid_xhtml
28 end 28 end
@@ -258,4 +258,23 @@ class MembershipsControllerTest &lt; ActionController::TestCase @@ -258,4 +258,23 @@ class MembershipsControllerTest &lt; ActionController::TestCase
258 assert_tag :tag => 'a', :attributes => { :class => 'button icon-cancel with-text', :href => back_to } 258 assert_tag :tag => 'a', :attributes => { :class => 'button icon-cancel with-text', :href => back_to }
259 end 259 end
260 260
  261 + should 'only display control panel link to members with permission' do
  262 + c1 = fast_create(Community, :name => 'My own community')
  263 + c2 = fast_create(Community, :name => 'Not my community')
  264 +
  265 + owner = fast_create(Person)
  266 + c2.add_admin(owner)
  267 +
  268 + person = Person['testuser']
  269 + c1.add_admin(person)
  270 + c2.add_member(person)
  271 +
  272 + login_as('testuser')
  273 + get :index, :profile => 'testuser'
  274 +
  275 + assert_template 'index'
  276 + assert_no_tag :tag => 'a', :attributes => { :href => "/myprofile/#{c2.identifier}" }
  277 + assert_tag :tag => 'a', :attributes => { :href => "/myprofile/#{c1.identifier}" }
  278 + end
  279 +
261 end 280 end
test/functional/profile_controller_test.rb
@@ -1383,6 +1383,41 @@ class ProfileControllerTest &lt; ActionController::TestCase @@ -1383,6 +1383,41 @@ class ProfileControllerTest &lt; ActionController::TestCase
1383 assert_equal "Comment successfully added.", assigns(:message) 1383 assert_equal "Comment successfully added.", assigns(:message)
1384 end 1384 end
1385 1385
  1386 + should 'display comment in wall if user was removed' do
  1387 + UserStampSweeper.any_instance.stubs(:current_user).returns(profile)
  1388 + article = TinyMceArticle.create!(:profile => profile, :name => 'An article about free software')
  1389 + to_be_removed = create_user('removed_user').person
  1390 + comment = Comment.create!(:author => to_be_removed, :title => 'Test Comment', :body => 'My author does not exist =(', :source_id => article.id, :source_type => 'Article')
  1391 + to_be_removed.destroy
  1392 +
  1393 + login_as(profile.identifier)
  1394 + get :index, :profile => profile.identifier
  1395 +
  1396 + assert_tag :tag => 'span', :content => '(removed user)', :attributes => {:class => 'comment-user-status comment-user-status-wall icon-user-removed'}
  1397 + end
  1398 +
  1399 + should 'not display spam comments in wall' do
  1400 + UserStampSweeper.any_instance.stubs(:current_user).returns(profile)
  1401 + article = TinyMceArticle.create!(:profile => profile, :name => 'An article about spam\'s nutritional attributes')
  1402 + comment = Comment.create!(:author => profile, :title => 'Test Comment', :body => 'This article makes me hungry', :source_id => article.id, :source_type => 'Article')
  1403 + comment.spam!
  1404 + login_as(profile.identifier)
  1405 + get :index, :profile => profile.identifier
  1406 +
  1407 + assert !/This article makes me hungry/.match(@response.body), 'Spam comment was shown!'
  1408 + end
  1409 +
  1410 + should 'display comment in wall from non logged users' do
  1411 + UserStampSweeper.any_instance.stubs(:current_user).returns(profile)
  1412 + article = TinyMceArticle.create!(:profile => profile, :name => 'An article about free software')
  1413 + comment = Comment.create!(:name => 'outside user', :email => 'outside@localhost.localdomain', :title => 'Test Comment', :body => 'My author does not exist =(', :source_id => article.id, :source_type => 'Article')
  1414 +
  1415 + login_as(profile.identifier)
  1416 + get :index, :profile => profile.identifier
  1417 +
  1418 + assert_tag :tag => 'span', :content => '(unauthenticated user)', :attributes => {:class => 'comment-user-status comment-user-status-wall icon-user-unknown'}
  1419 + end
  1420 +
1386 should 'add locale on mailing' do 1421 should 'add locale on mailing' do
1387 community = fast_create(Community) 1422 community = fast_create(Community)
1388 create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community) 1423 create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
test/functional/profile_editor_controller_test.rb
@@ -624,6 +624,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase @@ -624,6 +624,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
624 624
625 profile.domains << Domain.new(:name => 'myowndomain.net') 625 profile.domains << Domain.new(:name => 'myowndomain.net')
626 profile.environment.domains << Domain.new(:name => 'myenv.net') 626 profile.environment.domains << Domain.new(:name => 'myenv.net')
  627 + ActionController::TestRequest.any_instance.stubs(:host).returns(profile.hostname)
627 628
628 get :edit, :profile => profile.identifier 629 get :edit, :profile => profile.identifier
629 assert_tag :tag => 'select', :attributes => { :name => 'profile_data[preferred_domain_id]' }, :descendant => { :tag => "option", :content => 'myowndomain.net' } 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,6 +640,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
639 640
640 profile.domains << Domain.new(:name => 'myowndomain.net') 641 profile.domains << Domain.new(:name => 'myowndomain.net')
641 profile.environment.domains << Domain.new(:name => 'myenv.net') 642 profile.environment.domains << Domain.new(:name => 'myenv.net')
  643 + ActionController::TestRequest.any_instance.stubs(:host).returns(profile.hostname)
642 644
643 get :edit, :profile => profile.identifier 645 get :edit, :profile => profile.identifier
644 assert_tag :tag => "select", :attributes => { :name => 'profile_data[preferred_domain_id]'}, :descendant => { :tag => 'option', :content => '&lt;Select domain&gt;', :attributes => { :value => '' } } 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,4 +980,13 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
978 assert_equal({}, person.reload.fields_privacy) 980 assert_equal({}, person.reload.fields_privacy)
979 end 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 end 992 end
test/unit/application_helper_test.rb
@@ -143,6 +143,18 @@ class ApplicationHelperTest &lt; ActiveSupport::TestCase @@ -143,6 +143,18 @@ class ApplicationHelperTest &lt; ActiveSupport::TestCase
143 assert_tag_in_string rolename_for(member2, community), :tag => 'span', :content => 'Profile Member' 143 assert_tag_in_string rolename_for(member2, community), :tag => 'span', :content => 'Profile Member'
144 end 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 should 'get theme from environment by default' do 158 should 'get theme from environment by default' do
147 @environment = mock 159 @environment = mock
148 @environment.stubs(:theme).returns('my-environment-theme') 160 @environment.stubs(:theme).returns('my-environment-theme')
test/unit/environment_test.rb
@@ -293,21 +293,6 @@ class EnvironmentTest &lt; ActiveSupport::TestCase @@ -293,21 +293,6 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
293 assert_equal blocks - env_blocks, Block.count 293 assert_equal blocks - env_blocks, Block.count
294 end 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 should 'have boxes and blocks upon creation' do 296 should 'have boxes and blocks upon creation' do
312 Environment.any_instance.stubs(:create_templates) # avoid creating templates, it's expensive 297 Environment.any_instance.stubs(:create_templates) # avoid creating templates, it's expensive
313 environment = Environment.create!(:name => 'a test environment') 298 environment = Environment.create!(:name => 'a test environment')
test/unit/profile_test.rb
@@ -1901,4 +1901,11 @@ class ProfileTest &lt; ActiveSupport::TestCase @@ -1901,4 +1901,11 @@ class ProfileTest &lt; ActiveSupport::TestCase
1901 assert !profile.may_display_location_to?(user) 1901 assert !profile.may_display_location_to?(user)
1902 end 1902 end
1903 1903
  1904 + should 'destroy profile if its environment is destroyed' do
  1905 + environment = fast_create(Environment)
  1906 + profile = fast_create(Profile, :environment_id => environment.id)
  1907 +
  1908 + environment.destroy
  1909 + assert_raise(ActiveRecord::RecordNotFound) {profile.reload}
  1910 + end
1904 end 1911 end
vendor/plugins/action_tracker_has_comments/init.rb
@@ -9,7 +9,18 @@ Rails.configuration.to_prepare do @@ -9,7 +9,18 @@ Rails.configuration.to_prepare do
9 9
10 def conditions_for_comments 10 def conditions_for_comments
11 type, id = (self.target_type == 'Article' ? ['Article', self.target_id] : [self.class.to_s, self.id]) 11 type, id = (self.target_type == 'Article' ? ['Article', self.target_id] : [self.class.to_s, self.id])
12 - "source_type = '#{type}' AND source_id = '#{id}' AND reply_of_id IS NULL" 12 + "source_type = '#{type}' AND source_id = '#{id}' AND spam IS NOT TRUE AND reply_of_id IS NULL"
  13 + end
  14 +
  15 + def comments_as_thread
  16 + result = {}
  17 + root = []
  18 + self.comments.each do |c|
  19 + c.replies = []
  20 + result[c.id] ||= c
  21 + c.reply_of_id.nil? ? root << c : result[c.reply_of_id].replies << c
  22 + end
  23 + root
13 end 24 end
14 25
15 end 26 end