Commit 4d9daa44dbf61490640b32a8fb9f6f49f0f8b152

Authored by Rodrigo Souto
2 parents 1fe6b276 4b00dad4
Exists in staging and in 1 other branch production

Merge branch 'master' into staging

Showing 136 changed files with 1279 additions and 399 deletions   Show diff stats
.travis.yml
... ... @@ -37,7 +37,7 @@ before_install:
37 37 cache: bundler
38 38  
39 39 before_script:
40   - - mkdir -p tmp/{pids,cache} log
  40 + - mkdir -p tmp/{pids,cache} log cache
41 41 - script/noosfero-plugins disableall
42 42 #- bundle exec rake makemo &>/dev/null
43 43 # database
... ...
RELEASING.md
... ... @@ -3,37 +3,30 @@ Noosfero release tasks
3 3  
4 4 This file documents release-related activities.
5 5  
6   -Working with translations
7   --------------------------
8   -
9   -* Update translation files: `rake updatepo`. Then `git commit` them.
10   -* Send the PO files to the translators.
11   -* Get the PO files back from translators, put in `po/` under the correct language name (e.,g. `po/pt_BR/`) and `git commit`.
12   -* test translations: `rake makemo` and browse the application on the web.
13   -
14 6 Releasing noosfero
15 7 ------------------
16 8  
17   -Considering you are on a Debian GNU/Linux or Debian-based system
  9 +Considering you are on a Debian GNU/Linux or Debian-based system, the following
  10 +packages are required during the release process:
18 11  
19   - # apt-get install devscripts debhelper
  12 +```
  13 +# apt install git devscripts debhelper
  14 +```
20 15  
21 16 To prepare a release of noosfero, you must follow the steps below:
22 17  
23   -* Finish all requirements and bugs assigned to the to-be-released version
  18 +* Disable the automatic pushing of translation updates in weblate.
24 19 * Make sure all tests pass
25   -* Write release notes at the version's wiki topic
26 20 * Generate packages with `rake noosfero:release[(stable|test)]`. This task will:
27 21 * Update the version in lib/noosfero.rb and debian/changelog.
28 22 * Create the tarbal and the deb pkg under pkg/ directory.
29 23 * Create a git tag and push it.
30   - * Upload the pkg to the configured repository (if configured) on ~/.dput.cf.
  24 + * Upload the packages to the configured repository (if configured) on ~/.dput.cf.
31 25 * Test that the tarball and deb package are ok
32   -* Go to the version's wiki topic and edit it to reflect the new reality
33   -* Edit the topic WebPreferences and update DEBIAN_REPOSITORY_TOPICS setting
34   -* Attach the generated packages to that topic. Before attaching calculate the sha1 of the package (with sha1sum and paste the SHA1 hash as comment in the attachment form)
35   -* Download the attached and verify the MD5 hash
36 26 * Update an eventual demonstration version that you run.
37   -* Write an announcement e-mail to the relevant mailing lists pointing to the release notes, and maybe to the demonstration version.
  27 +* Write an announcement e-mail to the relevant mailing lists pointing to the
  28 + release notes, and maybe to the demonstration version.
  29 +* Re-enable the automatic pushing of trasnlatio updates in weblate.
38 30  
39   -If you had any problem during these steps, you can do `rake clobber_package` to completely delete the generated packages and start the process again.
  31 +If you had any problem during these steps, you can do `rake clobber_package` to
  32 +completely delete the generated packages and start the process again.
... ...
app/controllers/box_organizer_controller.rb
... ... @@ -83,12 +83,9 @@ class BoxOrganizerController < ApplicationController
83 83  
84 84 def save
85 85 @block = boxes_holder.blocks.find(params[:id])
86   - if @block.kind_of?(RawHTMLBlock) && !user.is_admin?(environment)
87   - render_access_denied
88   - else
89   - @block.update(params[:block])
90   - redirect_to :action => 'index'
91   - end
  86 + return render_access_denied unless @block.editable?(user)
  87 + @block.update(params[:block])
  88 + redirect_to :action => 'index'
92 89 end
93 90  
94 91 def boxes_editor?
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -32,7 +32,8 @@ class CmsController < MyProfileController
32 32 end
33 33  
34 34 protect_if :only => [:new, :upload_files] do |c, user, profile|
35   - parent = profile.articles.find_by_id(c.params[:parent_id])
  35 + parent_id = c.params[:article].present? ? c.params[:article][:parent_id] : c.params[:parent_id]
  36 + parent = profile.articles.find_by_id(parent_id)
36 37 user && user.can_post_content?(profile, parent)
37 38 end
38 39  
... ...
app/controllers/my_profile/profile_members_controller.rb
... ... @@ -2,8 +2,27 @@ class ProfileMembersController < MyProfileController
2 2 protect 'manage_memberships', :profile
3 3  
4 4 def index
5   - @members = profile.members_by_name
6   - @member_role = environment.roles.find_by_name('member')
  5 + @filters = params[:filters] || {:roles => []}
  6 + all_roles = Profile::Roles.organization_member_roles(environment.id) | Profile::Roles.organization_custom_roles(environment.id, profile.id)
  7 + @filters[:roles] = all_roles unless @filters[:roles].present?
  8 + @data = {}
  9 + field = 'name'
  10 + field = 'email' if @filters[:name] =~ /\@/
  11 +
  12 + @data[:members] = profile.members_by(field,@filters[:name]).by_role(@filters[:roles])
  13 + session[:members_filtered] = @data[:members].map{|m|m.id} if request.post?
  14 + @data[:roles] = all_roles
  15 +
  16 + end
  17 +
  18 + def send_mail
  19 + session[:members_filtered] = params[:members_filtered].select{|value| value!="0"}
  20 + if session[:members_filtered].present?
  21 + redirect_to :controller => :profile, :action => :send_mail
  22 + else
  23 + session[:notice] = _("Select at least one member.")
  24 + redirect_to :action => :index
  25 + end
7 26 end
8 27  
9 28 def update_roles
... ... @@ -156,4 +175,13 @@ class ProfileMembersController < MyProfileController
156 175 end
157 176 end
158 177  
  178 + def search_members
  179 + field = 'name'
  180 + field = 'email' if params[:filter_name] =~ /\@/
  181 +
  182 + result = profile.members_like field, params[:filter_name]
  183 + result = result.select{|member| member.can_view_field?(current_person, "email") } if field=="email"
  184 + render :json => result.map { |member| {:label => "#{member.name}#{member.can_view_field?(current_person, "email") ? " <#{member.email}>" : ""}", :value => member.name }}
  185 + end
  186 +
159 187 end
... ...
app/controllers/public/account_controller.rb
... ... @@ -198,7 +198,7 @@ class AccountController &lt; ApplicationController
198 198 if request.post?
199 199 begin
200 200 unless verify_recaptcha
201   - @change_password.errors.add(:base, _('Please type the words correctly'))
  201 + @change_password.errors.add(:base, _('Please type the captcha text correctly'))
202 202 return false
203 203 end
204 204  
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -68,11 +68,7 @@ class ContentViewerController &lt; ApplicationController
68 68 process_comments(params)
69 69  
70 70 if request.xhr? and params[:comment_order]
71   - if @comment_order == 'newest'
72   - @comments = @comments.reverse
73   - end
74   -
75   - return render :partial => 'comment/comment', :collection => @comments
  71 + return render :partial => 'comment/comments_with_pagination'
76 72 end
77 73  
78 74 if params[:slideshow]
... ... @@ -209,8 +205,6 @@ class ContentViewerController &lt; ApplicationController
209 205  
210 206 def rendered_file_download(view = nil)
211 207 if @page.download? view
212   - headers['Content-Type'] = @page.mime_type
213   - headers.merge! @page.download_headers
214 208 data = @page.data
215 209  
216 210 # TODO test the condition
... ... @@ -218,7 +212,12 @@ class ContentViewerController &lt; ApplicationController
218 212 raise "No data for file"
219 213 end
220 214  
221   - render :text => data, :layout => false
  215 + if @page.published && @page.uploaded_file?
  216 + redirect_to @page.public_filename
  217 + else
  218 + send_data data, @page.download_headers
  219 + end
  220 +
222 221 return true
223 222 end
224 223  
... ... @@ -244,8 +243,12 @@ class ContentViewerController &lt; ApplicationController
244 243  
245 244 def get_posts(year = nil, month = nil)
246 245 if year && month
247   - filter_date = DateTime.parse("#{year}-#{month}-01")
248   - return @page.posts.by_range(filter_date..filter_date.at_end_of_month)
  246 + begin
  247 + filter_date = DateTime.parse("#{year}-#{month}-01")
  248 + return @page.posts.by_range(filter_date..filter_date.at_end_of_month)
  249 + rescue ArgumentError
  250 + return @page.posts
  251 + end
249 252 else
250 253 return @page.posts
251 254 end
... ... @@ -276,8 +279,12 @@ class ContentViewerController &lt; ApplicationController
276 279 @comments = @page.comments.without_spam
277 280 @comments = @plugins.filter(:unavailable_comments, @comments)
278 281 @comments_count = @comments.count
279   - @comments = @comments.without_reply.paginate(:per_page => per_page, :page => params[:comment_page] )
280 282 @comment_order = params[:comment_order].nil? ? 'oldest' : params[:comment_order]
  283 + @comments = @comments.without_reply
  284 + if @comment_order == 'newest'
  285 + @comments = @comments.reverse
  286 + end
  287 + @comments = @comments.paginate(:per_page => per_page, :page => params[:comment_page] )
281 288 end
282 289  
283 290 private
... ...
app/controllers/public/profile_controller.rb
... ... @@ -155,6 +155,18 @@ class ProfileController &lt; PublicController
155 155 end
156 156 end
157 157  
  158 + def follow_article
  159 + article = profile.environment.articles.find params[:article_id]
  160 + article.person_followers << user
  161 + redirect_to article.url
  162 + end
  163 +
  164 + def unfollow_article
  165 + article = profile.environment.articles.find params[:article_id]
  166 + article.person_followers.delete(user)
  167 + redirect_to article.url
  168 + end
  169 +
158 170 def unblock
159 171 if current_user.person.is_admin?(profile.environment)
160 172 profile.unblock
... ... @@ -362,6 +374,7 @@ class ProfileController &lt; PublicController
362 374 def send_mail
363 375 @mailing = profile.mailings.build(params[:mailing])
364 376 @email_templates = profile.email_templates.find_all_by_template_type(:organization_members)
  377 + @mailing.data = session[:members_filtered] ? {:members_filtered => session[:members_filtered]} : {}
365 378 if request.post?
366 379 @mailing.locale = locale
367 380 @mailing.person = user
... ...
app/helpers/application_helper.rb
... ... @@ -150,14 +150,8 @@ module ApplicationHelper
150 150 link_to text, profile_path(:profile => profile) , options
151 151 end
152 152  
153   - def link_to_homepage(text, profile = nil, options = {})
154   - p = if profile
155   - Profile[profile]
156   - else
157   - user
158   - end
159   -
160   - link_to text, p.url, options
  153 + def link_to_homepage(text, profile, options = {})
  154 + link_to text, profile.url, options
161 155 end
162 156  
163 157 def link_if_permitted(link, permission = nil, target = nil)
... ... @@ -556,14 +550,25 @@ module ApplicationHelper
556 550 trigger_class = 'enterprise-trigger'
557 551 end
558 552 end
559   - extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
  553 +
  554 + extra_info_tag = ''
  555 + img_class = 'profile-image'
  556 +
  557 + if extra_info.is_a? Hash
  558 + extra_info_tag = content_tag( 'span', extra_info[:value], :class => 'extra_info '+extra_info[:class])
  559 + img_class +=' '+extra_info[:class]
  560 + else
  561 + extra_info_tag = content_tag( 'span', extra_info, :class => 'extra_info' )
  562 + end
  563 +
560 564 links = links_for_balloon(profile)
561 565 content_tag('div', content_tag(tag,
562   - (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") +
  566 + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ?
  567 + popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") +
563 568 link_to(
564   - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
  569 + content_tag( 'span', profile_image( profile, size ), :class => img_class ) +
565 570 content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
566   - extra_info + profile_sex_icon( profile ),
  571 + extra_info_tag + profile_sex_icon( profile ),
567 572 profile.url,
568 573 :class => 'profile_link url',
569 574 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
... ... @@ -711,7 +716,7 @@ module ApplicationHelper
711 716 class NoosferoFormBuilder < ActionView::Helpers::FormBuilder
712 717 extend ActionView::Helpers::TagHelper
713 718  
714   - def self.output_field(text, field_html, field_id = nil, options = {})
  719 + def self.output_field(text, field_html, field_id = nil)
715 720 # try to guess an id if none given
716 721 if field_id.nil?
717 722 field_html =~ /id=['"]([^'"]*)['"]/
... ... @@ -1040,10 +1045,11 @@ module ApplicationHelper
1040 1045 end
1041 1046  
1042 1047 def search_contents_menu
  1048 + host = environment.default_hostname
1043 1049 links = [
1044   - {s_('contents|More recent') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_recent'})}},
1045   - {s_('contents|More viewed') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_popular'})}},
1046   - {s_('contents|Most commented') => {:href => url_for({:controller => 'search', :action => 'contents', :filter => 'more_comments'})}}
  1050 + {s_('contents|More recent') => {href: url_for({host: host, controller: 'search', action: 'contents', filter: 'more_recent'})}},
  1051 + {s_('contents|More viewed') => {href: url_for({host: host, controller: 'search', action: 'contents', filter: 'more_popular'})}},
  1052 + {s_('contents|Most commented') => {href: url_for({host: host, controller: 'search', action: 'contents', filter: 'more_comments'})}}
1047 1053 ]
1048 1054 if logged_in?
1049 1055 links.push(_('New content') => modal_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_user.login, :cms => true})}))
... ... @@ -1055,10 +1061,11 @@ module ApplicationHelper
1055 1061 alias :browse_contents_menu :search_contents_menu
1056 1062  
1057 1063 def search_people_menu
  1064 + host = environment.default_hostname
1058 1065 links = [
1059   - {s_('people|More recent') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_recent'})}},
1060   - {s_('people|More active') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_active'})}},
1061   - {s_('people|More popular') => {:href => url_for({:controller => 'search', :action => 'people', :filter => 'more_popular'})}}
  1066 + {s_('people|More recent') => {href: url_for({host: host, controller: 'search', action: 'people', filter: 'more_recent'})}},
  1067 + {s_('people|More active') => {href: url_for({host: host, controller: 'search', action: 'people', filter: 'more_active'})}},
  1068 + {s_('people|More popular') => {href: url_for({host: host, controller: 'search', action: 'people', filter: 'more_popular'})}}
1062 1069 ]
1063 1070 if logged_in?
1064 1071 links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})})
... ... @@ -1071,10 +1078,11 @@ module ApplicationHelper
1071 1078 alias :browse_people_menu :search_people_menu
1072 1079  
1073 1080 def search_communities_menu
  1081 + host = environment.default_hostname
1074 1082 links = [
1075   - {s_('communities|More recent') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_recent'})}},
1076   - {s_('communities|More active') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_active'})}},
1077   - {s_('communities|More popular') => {:href => url_for({:controller => 'search', :action => 'communities', :filter => 'more_popular'})}}
  1083 + {s_('communities|More recent') => {href: url_for({host: host, controller: 'search', action: 'communities', filter: 'more_recent'})}},
  1084 + {s_('communities|More active') => {href: url_for({host: host, controller: 'search', action: 'communities', filter: 'more_active'})}},
  1085 + {s_('communities|More popular') => {href: url_for({host: host, controller: 'search', action: 'communities', filter: 'more_popular'})}}
1078 1086 ]
1079 1087 if logged_in?
1080 1088 links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})})
... ...
app/helpers/article_helper.rb
... ... @@ -169,4 +169,30 @@ module ArticleHelper
169 169 _('Edit')
170 170 end
171 171  
  172 + def follow_button_text(article)
  173 + if article.event?
  174 + _('Attend')
  175 + else
  176 + _('Follow')
  177 + end
  178 + end
  179 +
  180 + def unfollow_button_text(article)
  181 + if article.event?
  182 + _('Unattend')
  183 + else
  184 + _('Unfollow')
  185 + end
  186 + end
  187 +
  188 + def following_button(page, user)
  189 + if !user.blank? and user != page.author
  190 + if page.is_followed_by? user
  191 + button :cancel, unfollow_button_text(page), {:controller => 'profile', :action => 'unfollow_article', :article_id => page.id}
  192 + else
  193 + button :add, follow_button_text(page), {:controller => 'profile', :action => 'follow_article', :article_id => page.id}
  194 + end
  195 + end
  196 + end
  197 +
172 198 end
... ...
app/helpers/block_helper.rb
... ... @@ -14,6 +14,7 @@ module BlockHelper
14 14 </td>
15 15 <td>#{text_field_tag 'block[images][][address]', image[:address], :class => 'highlight-address', :size => 20}</td>
16 16 <td>#{text_field_tag 'block[images][][position]', image[:position], :class => 'highlight-position', :size => 1}</td>
  17 + <td>#{check_box_tag 'block[images][][new_window]', '1', image[:new_window], :class => 'highlight-new_window', :size => 1}</td>
17 18 </tr><tr class=\"image-title\" data-row-number='#{row_number}'>
18 19 <td colspan=\"3\"><label>#{
19 20 content_tag('span', _('Title')) +
... ...
app/helpers/boxes_helper.rb
... ... @@ -250,7 +250,7 @@ module BoxesHelper
250 250 end
251 251 end
252 252  
253   - if editable?(block)
  253 + if editable?(block, user)
254 254 buttons << modal_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id })
255 255 end
256 256  
... ... @@ -296,7 +296,7 @@ module BoxesHelper
296 296 return block.movable? || user.is_admin?
297 297 end
298 298  
299   - def editable?(block)
300   - return block.editable? || user.is_admin?
  299 + def editable?(block, user=nil)
  300 + return block.editable?(user) || user.is_admin?
301 301 end
302 302 end
... ...
app/helpers/forms_helper.rb
... ... @@ -7,9 +7,10 @@ module FormsHelper
7 7  
8 8 def labelled_check_box( human_name, name, value = "1", checked = false, options = {} )
9 9 options[:id] ||= 'checkbox-' + FormsHelper.next_id_number
10   - hidden_field_tag(name, '0') +
11   - check_box_tag( name, value, checked, options ) +
12   - content_tag( 'label', human_name, :for => options[:id] )
  10 + html = options[:add_hidden] == false ? "" : hidden_field_tag(name, '0')
  11 +
  12 + html += check_box_tag( name, value, checked, options ) +
  13 + content_tag( 'label', human_name, :for => options[:id] )
13 14 end
14 15  
15 16 def labelled_text_field( human_name, name, value=nil, options={} )
... ...
app/mailers/mailing.rb
... ... @@ -2,7 +2,10 @@ require_dependency &#39;mailing_job&#39;
2 2  
3 3 class Mailing < ActiveRecord::Base
4 4  
5   - attr_accessible :subject, :body
  5 + acts_as_having_settings :field => :data
  6 +
  7 + attr_accessible :subject, :body, :data
  8 +
6 9 validates_presence_of :source_id, :subject, :body
7 10 belongs_to :source, :foreign_key => :source_id, :polymorphic => true
8 11 belongs_to :person
... ...
app/mailers/organization_mailing.rb
... ... @@ -5,9 +5,17 @@ class OrganizationMailing &lt; Mailing
5 5 end
6 6  
7 7 def recipients(offset=0, limit=100)
8   - source.members.order(:id).offset(offset).limit(limit)
9   - .joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)")
  8 + result = source.members.order(:id).offset(offset).limit(limit)
  9 +
  10 + if data.present? and data.is_a?(Hash) and data[:members_filtered]
  11 + result = result.where('profiles.id IN (?)', data[:members_filtered])
  12 + end
  13 +
  14 + if result.blank?
  15 + result = result.joins("LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)")
10 16 .where("m.person_id" => nil)
  17 + end
  18 + result
11 19 end
12 20  
13 21 def each_recipient
... ...
app/models/article.rb
... ... @@ -8,8 +8,9 @@ class Article &lt; ActiveRecord::Base
8 8 :accept_comments, :feed, :published, :source, :source_name,
9 9 :highlighted, :notify_comments, :display_hits, :slug,
10 10 :external_feed_builder, :display_versions, :external_link,
11   - :author, :published_at, :person_followers, :show_to_followers,
12   - :image_builder, :display_preview, :archived
  11 + :image_builder, :show_to_followers,
  12 + :author, :display_preview, :published_at, :person_followers,
  13 + :archived
13 14  
14 15 acts_as_having_image
15 16  
... ... @@ -83,6 +84,10 @@ class Article &lt; ActiveRecord::Base
83 84  
84 85 has_many :comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc'
85 86  
  87 + has_many :article_followers, :dependent => :destroy
  88 + has_many :person_followers, :class_name => 'Person', :through => :article_followers, :source => :person
  89 + has_many :person_followers_emails, :class_name => 'User', :through => :person_followers, :source => :user, :select => :email
  90 +
86 91 has_many :article_categorizations, -> { where 'articles_categories.virtual = ?', false }
87 92 has_many :categories, :through => :article_categorizations
88 93  
... ... @@ -95,7 +100,6 @@ class Article &lt; ActiveRecord::Base
95 100 settings_items :author_name, :type => :string, :default => ""
96 101 settings_items :allow_members_to_edit, :type => :boolean, :default => false
97 102 settings_items :moderate_comments, :type => :boolean, :default => false
98   - settings_items :followers, :type => Array, :default => []
99 103 has_and_belongs_to_many :article_privacy_exceptions, :class_name => 'Person', :join_table => 'article_privacy_exceptions'
100 104  
101 105 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id'
... ... @@ -173,7 +177,6 @@ class Article &lt; ActiveRecord::Base
173 177 end
174 178 end
175 179  
176   -
177 180 def is_trackable?
178 181 self.published? && self.notifiable? && self.advertise? && self.profile.public_profile
179 182 end
... ... @@ -374,6 +377,10 @@ class Article &lt; ActiveRecord::Base
374 377 self.parent and self.parent.forum?
375 378 end
376 379  
  380 + def person_followers_email_list
  381 + person_followers_emails.map{|p|p.email}
  382 + end
  383 +
377 384 def info_from_last_update
378 385 last_comment = comments.last
379 386 if last_comment
... ... @@ -383,6 +390,10 @@ class Article &lt; ActiveRecord::Base
383 390 end
384 391 end
385 392  
  393 + def full_path
  394 + profile.hostname.blank? ? "/#{profile.identifier}/#{path}" : "/#{path}"
  395 + end
  396 +
386 397 def url
387 398 @url ||= self.profile.url.merge(:page => path.split('/'))
388 399 end
... ... @@ -408,13 +419,19 @@ class Article &lt; ActiveRecord::Base
408 419 end
409 420  
410 421 def download? view = nil
411   - (self.uploaded_file? and not self.image?) or
412   - (self.image? and view.blank?) or
413   - (not self.uploaded_file? and self.mime_type != 'text/html')
  422 + false
  423 + end
  424 +
  425 + def is_followed_by?(user)
  426 + self.person_followers.include? user
  427 + end
  428 +
  429 + def download_disposition
  430 + 'inline'
414 431 end
415 432  
416 433 def download_headers
417   - {}
  434 + { :filename => filename, :type => mime_type, :disposition => download_disposition}
418 435 end
419 436  
420 437 def alternate_languages
... ...
app/models/block.rb
... ... @@ -195,8 +195,8 @@ class Block &lt; ActiveRecord::Base
195 195 nil
196 196 end
197 197  
198   - # Is this block editable? (Default to <tt>false</tt>)
199   - def editable?
  198 + # Is this block editable? (Default to <tt>true</tt>)
  199 + def editable?(user=nil)
200 200 self.edit_modes == "all"
201 201 end
202 202  
... ...
app/models/comment.rb
... ... @@ -6,13 +6,14 @@ class Comment &lt; ActiveRecord::Base
6 6 :body => {:label => _('Content'), :weight => 2},
7 7 }
8 8  
9   - attr_accessible :body, :author, :name, :email, :title, :reply_of_id, :source
  9 + attr_accessible :body, :author, :name, :email, :title, :reply_of_id, :source, :follow_article
10 10  
11 11 validates_presence_of :body
12 12  
13 13 belongs_to :source, :counter_cache => true, :polymorphic => true
14 14 alias :article :source
15 15 alias :article= :source=
  16 + attr_accessor :follow_article
16 17  
17 18 belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id'
18 19 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
... ... @@ -102,10 +103,9 @@ class Comment &lt; ActiveRecord::Base
102 103  
103 104 after_create :new_follower
104 105 def new_follower
105   - if source.kind_of?(Article)
106   - article.followers += [author_email]
107   - article.followers -= article.profile.notification_emails
108   - article.followers.uniq!
  106 + if source.kind_of?(Article) and !author.nil? and @follow_article
  107 + article.person_followers += [author]
  108 + article.person_followers.uniq!
109 109 article.save
110 110 end
111 111 end
... ... @@ -147,7 +147,7 @@ class Comment &lt; ActiveRecord::Base
147 147 if !notification_emails.empty?
148 148 CommentNotifier.notification(self).deliver
149 149 end
150   - emails = article.followers - [author_email]
  150 + emails = article.person_followers_email_list - [author_email]
151 151 if !emails.empty?
152 152 CommentNotifier.mail_to_followers(self, emails).deliver
153 153 end
... ...
app/models/disabled_enterprise_message_block.rb
... ... @@ -19,7 +19,7 @@ class DisabledEnterpriseMessageBlock &lt; Block
19 19 end
20 20 end
21 21  
22   - def editable?
  22 + def editable?(user=nil)
23 23 false
24 24 end
25 25  
... ...
app/models/environment.rb
... ... @@ -56,6 +56,7 @@ class Environment &lt; ActiveRecord::Base
56 56 'manage_environment_trusted_sites' => N_('Manage environment trusted sites'),
57 57 'edit_appearance' => N_('Edit appearance'),
58 58 'manage_email_templates' => N_('Manage Email Templates'),
  59 + 'edit_raw_html_block' => N_('Edit Raw HTML block'),
59 60 }
60 61  
61 62 module Roles
... ...
app/models/highlights_block.rb
... ... @@ -15,6 +15,8 @@ class HighlightsBlock &lt; Block
15 15 if !Noosfero.root.nil? and !i[:address].start_with?(Noosfero.root + '/')
16 16 i[:address] = Noosfero.root + i[:address]
17 17 end
  18 + i[:new_window] = i[:new_window] == '1' ? true : false
  19 +
18 20 begin
19 21 file = UploadedFile.find(i[:image_id])
20 22 i[:image_src] = file.public_filename
... ...
app/models/person.rb
1 1 # A person is the profile of an user holding all relationships with the rest of the system
2 2 class Person < Profile
3 3  
4   - attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone,
5   - :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference,
6   - :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study,
7   - :custom_area_of_study, :professional_activity, :organization_website, :following_articles
  4 + attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles
8 5  
9 6 SEARCH_FILTERS = {
10 7 :order => %w[more_recent more_popular more_active],
... ... @@ -19,26 +16,29 @@ class Person &lt; Profile
19 16 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
20 17 acts_as_accessor
21 18  
22   - scope :members_of, -> resources {
  19 + scope :members_of, lambda { |resources, field = ''|
23 20 resources = Array(resources)
  21 + joins = [:role_assignments]
  22 + joins << :user if User.attribute_names.include? field
  23 +
24 24 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
25   - select('DISTINCT profiles.*').joins(:role_assignments).where([conditions])
  25 + distinct.select('profiles.*').joins(joins).where([conditions])
26 26 }
27 27  
28 28 scope :not_members_of, -> resources {
29 29 resources = Array(resources)
30 30 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
31   - select('DISTINCT profiles.*').where('"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "role_assignments" ON "role_assignments"."accessor_id" = "profiles"."id" AND "role_assignments"."accessor_type" = (Profile) WHERE "profiles"."type" IN (Person) AND (%s))' % conditions)
  31 + distinct.select('profiles.*').where('"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "role_assignments" ON "role_assignments"."accessor_id" = "profiles"."id" AND "role_assignments"."accessor_type" = (Profile) WHERE "profiles"."type" IN (Person) AND (%s))' % conditions)
32 32 }
33 33  
34 34 scope :by_role, -> roles {
35 35 roles = Array(roles)
36   - select('DISTINCT profiles.*').joins(:role_assignments).where('role_assignments.role_id IN (?)', roles)
  36 + distinct.select('profiles.*').joins(:role_assignments).where('role_assignments.role_id IN (?)', roles)
37 37 }
38 38  
39 39 scope :not_friends_of, -> resources {
40 40 resources = Array(resources)
41   - select('DISTINCT profiles.*').where('"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "friendships" ON "friendships"."person_id" = "profiles"."id" WHERE "friendships"."friend_id" IN (%s))' % resources.map(&:id))
  41 + distinct.select('profiles.*').where('"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "friendships" ON "friendships"."person_id" = "profiles"."id" WHERE "friendships"."friend_id" IN (%s))' % resources.map(&:id))
42 42 }
43 43  
44 44 scope :visible_for_person, lambda { |person|
... ... @@ -51,8 +51,7 @@ class Person &lt; Profile
51 51 ['( roles.key = ? AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR (
52 52 ( ( friendships.person_id = ? ) OR (profiles.public_profile = ?)) AND (profiles.visible = ?) )', 'environment_administrator', Profile.name, person.id, person.id, true, true]
53 53 ).uniq
54   - }
55   -
  54 + }
56 55  
57 56 def has_permission_with_admin?(permission, resource)
58 57 return true if resource.blank? || resource.admins.include?(self)
... ... @@ -90,7 +89,8 @@ class Person &lt; Profile
90 89 has_many :article_followers, :dependent => :destroy
91 90 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
92 91 has_many :comments, :foreign_key => :author_id
93   -
  92 + has_many :article_followers, :dependent => :destroy
  93 + has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
94 94 has_many :friendships, :dependent => :destroy
95 95 has_many :friends, :class_name => 'Person', :through => :friendships
96 96  
... ... @@ -123,10 +123,10 @@ class Person &lt; Profile
123 123 scope :more_popular, -> { order 'friends_count DESC' }
124 124  
125 125 scope :abusers, -> {
126   - joins(:abuse_complaints).where('tasks.status = 3').select('DISTINCT profiles.*')
  126 + joins(:abuse_complaints).where('tasks.status = 3').distinct.select('profiles.*')
127 127 }
128 128 scope :non_abusers, -> {
129   - select("DISTINCT profiles.*").
  129 + distinct.select("profiles.*").
130 130 joins("LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'").
131 131 where("tasks.status != 3 OR tasks.id is NULL")
132 132 }
... ... @@ -135,6 +135,11 @@ class Person &lt; Profile
135 135 scope :activated, -> { joins(:user).where('users.activation_code IS NULL AND users.activated_at IS NOT NULL') }
136 136 scope :deactivated, -> { joins(:user).where('NOT (users.activation_code IS NULL AND users.activated_at IS NOT NULL)') }
137 137  
  138 + scope :with_role, -> role_id {
  139 + distinct.joins(:role_assignments).
  140 + where("role_assignments.role_id = #{role_id}")
  141 + }
  142 +
138 143 after_destroy do |person|
139 144 Friendship.where(friend_id: person.id).each{ |friendship| friendship.destroy }
140 145 end
... ...
app/models/profile.rb
... ... @@ -78,6 +78,9 @@ class Profile &lt; ActiveRecord::Base
78 78 def self.organization_member_roles(env_id)
79 79 all_roles(env_id).select{ |r| r.key.match(/^profile_/) unless r.key.blank? || !r.profile_id.nil?}
80 80 end
  81 + def self.organization_custom_roles(env_id, profile_id)
  82 + all_roles(env_id).where('profile_id = ?', profile_id)
  83 + end
81 84 def self.all_roles(env_id)
82 85 Role.where(environment_id: env_id)
83 86 end
... ... @@ -119,7 +122,7 @@ class Profile &lt; ActiveRecord::Base
119 122 include Noosfero::Plugin::HotSpot
120 123  
121 124 scope :memberships_of, -> person {
122   - select('DISTINCT profiles.*').
  125 + distinct.select('profiles.*').
123 126 joins(:role_assignments).
124 127 where('role_assignments.accessor_type = ? AND role_assignments.accessor_id = ?', person.class.base_class.name, person.id)
125 128 }
... ... @@ -185,15 +188,23 @@ class Profile &lt; ActiveRecord::Base
185 188  
186 189 include TimeScopes
187 190  
188   - def members
  191 + def members(by_field = '')
189 192 scopes = plugins.dispatch_scopes(:organization_members, self)
190   - scopes << Person.members_of(self)
  193 + scopes << Person.members_of(self,by_field)
191 194 return scopes.first if scopes.size == 1
192 195 ScopeTool.union *scopes
193 196 end
194 197  
195   - def members_by_name
196   - members.order('profiles.name')
  198 + def members_by(field,value = nil)
  199 + if value and !value.blank?
  200 + members_like(field,value).order('profiles.name')
  201 + else
  202 + members.order('profiles.name')
  203 + end
  204 + end
  205 +
  206 + def members_like(field,value)
  207 + members(field).where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value
197 208 end
198 209  
199 210 class << self
... ... @@ -781,13 +792,13 @@ private :generate_url, :url_options
781 792 end
782 793  
783 794 # Adds a person as member of this Profile.
784   - def add_member(person)
  795 + def add_member(person, attributes={})
785 796 if self.has_members?
786 797 if self.closed? && members.count > 0
787 798 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
788 799 else
789   - self.affiliate(person, Profile::Roles.admin(environment.id)) if members.count == 0
790   - self.affiliate(person, Profile::Roles.member(environment.id))
  800 + self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0
  801 + self.affiliate(person, Profile::Roles.member(environment.id), attributes)
791 802 end
792 803 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel }
793 804 remove_from_suggestion_list person
... ... @@ -1164,6 +1175,10 @@ private :generate_url, :url_options
1164 1175 end
1165 1176 end
1166 1177  
  1178 + def can_view_field? current_person, field
  1179 + display_private_info_to?(current_person) || (public_fields.include?(field) && public?)
  1180 + end
  1181 +
1167 1182 validates_inclusion_of :redirection_after_login, :in => Environment.login_redirection_options.keys, :allow_nil => true
1168 1183 def preferred_login_redirection
1169 1184 redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login
... ...
app/models/raw_html_block.rb
... ... @@ -19,4 +19,9 @@ class RawHTMLBlock &lt; Block
19 19 def has_macro?
20 20 true
21 21 end
  22 +
  23 + def editable?(user)
  24 + user.has_permission?('edit_raw_html_block', environment)
  25 + end
  26 +
22 27 end
... ...
app/models/rss_feed.rb
... ... @@ -65,6 +65,10 @@ class RssFeed &lt; Article
65 65 'text/xml'
66 66 end
67 67  
  68 + def download?(view = nil)
  69 + true
  70 + end
  71 +
68 72 include Rails.application.routes.url_helpers
69 73 def fetch_articles
70 74 if parent && parent.has_posts?
... ...
app/models/uploaded_file.rb
... ... @@ -2,6 +2,9 @@
2 2 #
3 3 # Limitation: only file metadata are versioned. Only the latest version
4 4 # of the file itself is kept. (FIXME?)
  5 +
  6 +require 'sdbm'
  7 +
5 8 class UploadedFile < Article
6 9  
7 10 attr_accessible :uploaded_data, :title
... ... @@ -10,6 +13,19 @@ class UploadedFile &lt; Article
10 13 _('File')
11 14 end
12 15  
  16 + DBM_PRIVATE_FILE = 'cache/private_files'
  17 + after_save do |uploaded_file|
  18 + if uploaded_file.published_changed?
  19 + dbm = SDBM.open(DBM_PRIVATE_FILE)
  20 + if uploaded_file.published
  21 + dbm.delete(uploaded_file.public_filename)
  22 + else
  23 + dbm.store(uploaded_file.public_filename, uploaded_file.full_path)
  24 + end
  25 + dbm.close
  26 + end
  27 + end
  28 +
13 29 track_actions :upload_image, :after_create, :keep_params => ["view_url", "thumbnail_path", "parent.url", "parent.name"], :if => Proc.new { |a| a.published? && a.image? && !a.parent.nil? && a.parent.gallery? }, :custom_target => :parent
14 30  
15 31 def title
... ... @@ -106,10 +122,13 @@ class UploadedFile &lt; Article
106 122 self.name ||= self.filename
107 123 end
108 124  
109   - def download_headers
110   - {
111   - 'Content-Disposition' => "attachment; filename=\"#{self.filename}\"",
112   - }
  125 + def download_disposition
  126 + case content_type
  127 + when 'application/pdf'
  128 + 'inline'
  129 + else
  130 + 'attachment'
  131 + end
113 132 end
114 133  
115 134 def data
... ...
app/views/account/forgot_password.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <%= form_tag({:action => 'forgot_password'}, :method => 'post', :id => 'forgot-password-form') do %>
6 6 <%= labelled_form_field fields_label, text_field_tag(:value) %>
7 7  
8   - <h3><%= _('Please type the two words below') %></h3>
  8 + <h3><%= _('Please type the captcha text below') %></h3>
9 9 <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %>
10 10  
11 11 <div>
... ...
app/views/account/index.html.erb
... ... @@ -11,7 +11,7 @@
11 11 </p>
12 12  
13 13 <p>
14   -<%= link_to_homepage(_('My home page.')) %>
  14 +<%= link_to_homepage(_('My home page.'), user) %>
15 15 <%= _('See your homepage.') %>
16 16 </p>
17 17  
... ...
app/views/blocks/highlights.html.erb
... ... @@ -3,7 +3,7 @@
3 3 <div class='highlights-border'>
4 4 <div class='highlights-container'>
5 5 <% block.featured_images.each do |img| %>
6   - <a href="<%= img[:address] %>" title="<%= img[:title] %>" class="highlights-image-link">
  6 + <a href="<%= img[:address] %>" <%= 'target="_blank"' if img[:new_window] %> title="<%= img[:title] %>" class="highlights-image-link">
7 7 <%= image_tag [Noosfero.root, img[:image_src]].join, alt: img[:title] %>
8 8 <p class="highlights-label"><%= img[:title] %></p>
9 9 </a>
... ...
app/views/box_organizer/_highlights_block.html.erb
... ... @@ -3,7 +3,7 @@
3 3 <strong><%= _('Highlights') %></strong>
4 4  
5 5 <table class="noborder"><tbody id="highlights-data-table">
6   - <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th></tr>
  6 + <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th><th><%= _('New Window') %></th></tr>
7 7 <% @block.images.each_with_index do |image, index| %>
8 8 <%= highlights_block_config_image_fields @block, image, index %>
9 9 <% end %>
... ...
app/views/cms/_drag_and_drop_note.html.erb
1 1 <p>
2 2 <em>
3   - <%= _('Drag images to add them to the text.') %>
4   - <%= _('Click on file names to add links to the text.') %>
  3 + <%= _('Drag images to add them to the text or click on file names to add links to the text.') %>
5 4 </em>
6 5 </p>
... ...
app/views/cms/_text_editor_sidebar.html.erb
... ... @@ -17,8 +17,8 @@
17 17 :parent_id, profile, default_folder, {}, {},
18 18 "type='Folder' or type='Gallery'"
19 19 ) %>
  20 + <%= button(:newfolder, _('New folder'), '#', :id => 'new-folder-button') %>
20 21 </div>
21   - <%= button(:newfolder, _('New folder'), '#', :id => 'new-folder-button') %>
22 22 <p><%= file_field_tag('file', :multiple => true) %></p>
23 23 <% end %>
24 24 </div>
... ... @@ -31,7 +31,7 @@
31 31 <div id='published-media' class='text-editor-sidebar-box' data-url='<%= url_for({:controller => 'cms', :action => 'published_media_items', :profile => profile.identifier}) %>'>
32 32 <%= select_profile_folder(nil, :parent_id, profile, 'recent-media', {}, {},
33 33 "type='Folder' or type='Gallery'", {:root_label => _('Recent media')}) %>
34   - <%= labelled_form_field _('Search'), text_field_tag('q') %>
  34 + <%= labelled_form_field _('Search among your uploaded files'), text_field_tag('q', '', placeholder: _('Write words about the file you are looking for')) %>
35 35 <%= render :partial => 'drag_and_drop_note' %>
36 36 <div class='items'>
37 37 <%= render :partial => 'published_media_items' %>
... ...
app/views/comment/_comment_form.html.erb
... ... @@ -77,6 +77,10 @@ function check_captcha(button, confirm_action) {
77 77 <%= labelled_form_field(_('Title'), f.text_field(:title)) %>
78 78 <%= required labelled_form_field(_('Enter your comment'), f.text_area(:body, :rows => 5)) %>
79 79  
  80 + <% if logged_in? %>
  81 + <%= labelled_form_field check_box(:comment, :follow_article, {}, true, false) + _('Follow this article'), '' %>
  82 + <% end%>
  83 +
80 84 <%= hidden_field_tag(:confirm, 'false') %>
81 85 <%= hidden_field_tag(:view, params[:view])%>
82 86 <%= f.hidden_field(:reply_of_id) %>
... ...
app/views/comment/_comments_with_pagination.html.erb 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<% if @comments.present? %>
  2 + <%= render :partial => 'comment/comment', :collection => @comments %>
  3 + <%= pagination_links @comments, :param_name => 'comment_page', :params => { :comment_order => @comment_order } %>
  4 +<% end %>
... ...
app/views/content_viewer/_article_toolbar.html.erb
... ... @@ -57,6 +57,8 @@
57 57 <%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url], plugin_button[:html_options] %>
58 58 <% end %>
59 59  
  60 + <%= following_button @page, user %>
  61 +
60 62 <%= report_abuse(profile, :link, @page) %>
61 63  
62 64 </div>
... ...
app/views/content_viewer/_publishing_info.html.erb
... ... @@ -10,6 +10,24 @@
10 10 <%= (" - %s") % link_to_comments(@page)%>
11 11 </span>
12 12 <% end %>
  13 +
  14 +<span class="followers-count">
  15 +|
  16 +<% if @page.event? %>
  17 + <% if @page.person_followers.size > 0 %>
  18 + <%= _("%s will attend this event.") % [ pluralize(@page.person_followers.size, _("person"))]%>
  19 + <% else %>
  20 + <%= _("No one attending this event yet.") %>
  21 + <% end %>
  22 +<% else %>
  23 + <% if @page.person_followers.size > 0 %>
  24 + <%= _("%s following this article.") % [ pluralize(@page.person_followers.size, _("person"))]%>
  25 + <% else %>
  26 + <%= _("No one following this article yet.") %>
  27 + <% end %>
  28 +<% end %>
  29 +</span>
  30 +
13 31 </span>
14 32  
15 33 <% if @page.display_hits? || @page.license.present? %>
... ...
app/views/content_viewer/view_page.html.erb
... ... @@ -81,7 +81,7 @@
81 81 <ul class="article-comments-list">
82 82 <% if @comments.present? %>
83 83 <%= render :partial => 'comment/comment', :collection => @comments %>
84   - <%= pagination_links @comments, :param_name => 'comment_page' %>
  84 + <%= pagination_links @comments, :param_name => 'comment_page', :params => { :comment_order => @comment_order } %>
85 85 <% end %>
86 86 </ul>
87 87  
... ...
app/views/file_presenter/_generic.html.erb
... ... @@ -2,4 +2,4 @@
2 2 <%= generic.abstract %>
3 3 </div>
4 4  
5   -<%= button(:download, _('Download'), [Noosfero.root, generic.public_filename].join, class:'download-link', option:'primary', size:'lg') %>
  5 +<%= button(:download, _('Download'), generic.url, class:'download-link', option:'primary', size:'lg', :target => "_blank") %>
... ...
app/views/profile/_profile_members_list.html.erb
... ... @@ -9,7 +9,8 @@
9 9 </div>
10 10 <ul class="profile-list-<%= role %>" >
11 11 <% users.each do |u| %>
12   - <%= profile_image_link(u, :thumb) %>
  12 + <% extra_info = u.member_since_date(profile) == Date.today ? {:value =>_('New'), :class => 'new-profile'}:'' %>
  13 + <%= profile_image_link(u, :thumb, 'li', extra_info) %>
13 14 <% end %>
14 15 </ul>
15 16  
... ...
app/views/profile/members.html.erb
... ... @@ -19,7 +19,6 @@
19 19 :id => "members-tab",
20 20 :content => div_members
21 21 } %>
22   -
23 22 <% div_admins = content_tag :div, :class => "profile-admins" do
24 23 render :partial => 'profile_members_list',
25 24 :locals => {
... ...
app/views/profile/send_mail.html.erb
... ... @@ -10,6 +10,9 @@
10 10 </div>
11 11 <% end %>
12 12  
  13 + <% to = @mailing.data[:members_filtered].present? ? @mailing.recipients.map{|r| r.name}.join(', ') : _('All members')%>
  14 + <%= labelled_form_field(_('To:'), text_area(:data, 'members_filtered', :value => to, :rows => 4, :disabled => 'disabled', :class => 'send-mail-recipients')) %>
  15 +
13 16 <%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %>
14 17  
15 18 <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %>
... ...
app/views/profile_members/_index_buttons.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <%= button :person, _('Invite people to join'), :controller => 'invite', :action => 'invite_friends' %>
6 6 <% end %>
7 7 <% if profile.community? and user.has_permission?(:send_mail_to_members, profile) %>
8   - <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %>
  8 + <%= submit_button(:send, _('Send e-mail to members')) %>
9 9 <% end %>
10 10 <% @plugins.dispatch(:manage_members_extra_buttons).each do |plugin_button| %>
11 11 <%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url] %>
... ...
app/views/profile_members/_members_filter.erb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<%= form_tag '#', :method => 'post' do %>
  2 +
  3 + <%= field_set_tag _('Filter'), :class => 'filter_fields' do %>
  4 + <p>
  5 + <%= labelled_text_field(_('Name or Email')+': ', "filters[name]", @filters[:name], {:id => 'filter-name-autocomplete',:size => 30}) %>
  6 + </p>
  7 +
  8 + <p><%= _('Roles:') %> </p>
  9 + <% @data[:roles].each do |r| %>
  10 + <%= labelled_check_box(r.name, 'filters[roles][]', r.id, @filters[:roles].include?(r.id.to_s), :add_hidden => false) %><br/>
  11 + <% end %>
  12 + <p>
  13 + <%= submit_button(:search, _('Search')) %>
  14 + </p>
  15 + <% end %>
  16 +<% end %>
  17 +
  18 +<%= javascript_include_tag params[:controller] %>
0 19 \ No newline at end of file
... ...
app/views/profile_members/_members_list.html.erb
1   -<% collection = @collection == :profile_admins ? profile.admins : profile.members_by_name %>
  1 +<% members = @data ? @data[:members] : profile.members_by('name') %>
  2 +<% collection = @collection == :profile_admins ? profile.admins : members %>
2 3 <% title = @title ? @title : _('Current members') %>
3 4 <% remove_action = @remove_action ? @remove_action : {:action => 'unassociate'} %>
  5 +<%= javascript_include_tag params[:controller] %>
4 6  
5 7 <h3><%= title %></h3>
6 8  
7 9 <table>
  10 + <col width="1">
8 11 <tr>
  12 + <th><%= check_box_tag 'checkbox-all', 1, false, :onClick => "toggle(this)" %></th>
9 13 <th><%= _('Member') %></th>
10 14 <th><%= _('Actions') %></th>
11 15 </tr>
  16 +
12 17 <% collection.each do |m| %>
13 18 <tr title="<%= m.name %>">
  19 + <td><%= labelled_check_box('', 'members_filtered[]', m.id.to_s, false, :id => 'checkbox-'+m.identifier) %></td>
14 20 <td><%= link_to_profile m.short_name, m.identifier, :title => m.name %> </td>
15 21 <td>
16 22 <div class="members-buttons-cell">
... ... @@ -27,3 +33,8 @@
27 33 </tr>
28 34 <% end %>
29 35 </table>
  36 +<% if collection.empty? %>
  37 + <p>
  38 + <em><%= _('No members found to: %s') % profile.name %></em>
  39 + </p>
  40 +<% end %>
... ...
app/views/profile_members/index.html.erb
1 1 <h1><%= h profile.short_name(50) %></h1>
2 2  
3   -<%= render :partial => 'index_buttons' %>
  3 +<%= render :partial => 'members_filter' %>
4 4  
5   -<div id="members-list">
6   - <%= render :partial => 'members_list' %>
7   -</div>
  5 +<%= form_tag 'profile_members/send_mail', :method => 'post' do %>
  6 + <div id="members-list">
  7 + <%= render :partial => 'members_list' %>
  8 + </div>
8 9  
9   -<%= render :partial => 'index_buttons' %>
  10 + <%= render :partial => 'index_buttons' %>
  11 +
  12 +<% end %>
... ...
app/views/region_validators/region.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <ul>
6 6 <% for validator in @region.validators %>
7 7 <li>
8   - <%= link_to_homepage validator.name, validator.identifier %>
  8 + <%= link_to_homepage validator.name, validator %>
9 9 <%= link_to _('Remove validation rights'), { :action => 'remove', :id => @region.id, :validator_id => validator }, :method => 'post' %>
10 10 </li>
11 11 <% end %>
... ...
app/views/search/_full_enterprise.html.erb
... ... @@ -5,7 +5,7 @@
5 5 @order == 'more_recent' ? enterprise.send(@order + '_label') + show_date(enterprise.created_at) : enterprise.send(@order + '_label') %>
6 6 </div>
7 7 <div class="search-enterprise-item-column-right">
8   - <%= link_to_homepage(enterprise.name, enterprise.identifier, :class => "search-result-title") %>
  8 + <%= link_to_homepage enterprise.name, enterprise, class: "search-result-title" %>
9 9 <div class="search-enterprise-description">
10 10 <% if enterprise.description %>
11 11 <% body_stripped = strip_tags(enterprise.description) %>
... ...
app/views/search/_full_product.html.erb
... ... @@ -44,7 +44,7 @@
44 44 <div class="search-product-item-second-column">
45 45 <%= link_to_product product, :class => 'search-result-title' %>
46 46 <div class="search-product-supplier">
47   - <span class="search-field-label"><%= _('Supplier') %> </span><%= link_to_homepage(product.enterprise.name, product.enterprise.identifier) %>
  47 + <span class="search-field-label"><%= _('Supplier') %> </span><%= link_to_homepage product.enterprise.name, product.enterprise %>
48 48 </div>
49 49 <div class="search-product-description">
50 50 <% if product.description %>
... ...
app/views/search/_image.html.erb
... ... @@ -26,7 +26,7 @@
26 26 <% end %>
27 27 <% elsif image.is_a? Gallery %>
28 28 <div class="search-gallery-items">
29   - <% r = image.children.order(:updated_at).where('type = ?', 'UploadedFile').last(3) %>
  29 + <% r = image.children.latest.images.limit(3) %>
30 30 <% if r.length > 0 %>
31 31 <% r.each_index do |i| img = r[i] %>
32 32 <%= link_to '', img.view_url, :class => "search-image-pic pic-num#{i+1}",
... ... @@ -47,6 +47,8 @@
47 47 <% else %>
48 48 <div class="search-no-image"><span><%= _('No image') %></span></div>
49 49 <% end %>
  50 + <% elsif image.first_image.present? %>
  51 + <img src="<%= image.first_image %>" class="automatic-abstract-thumb search-image-pic">
50 52 <% else %>
51 53 <div class="search-content-type-icon icon-content-<%=image.class.to_s.underscore.dasherize%>"></div>
52 54 <% end %>
... ...
app/views/user_mailer/activation_code.text.erb
1 1 <%= _('Hi, %{recipient}!') % { :recipient => @recipient } %>
2 2  
3   -<%= word_wrap(_('Welcome to %{environment}! To activate your account, follow the link: %{activation_url}') % { :environment => @environment.name, :activation_url => @url + url_for(:controller => :account, :action => :activate, :activation_code => @activation_code, :redirection => @redirection, :join => @join) }) %>
  3 +<%= word_wrap(_('Welcome to %{environment}! To activate your account, follow the link: %{activation_url}') % { :environment => @environment.name, :activation_url => url_for(:controller => :account, :action => :activate, :activation_code => @activation_code, :redirection => @redirection, :join => @join) }) %>
4 4  
5 5 <%= _("Greetings,") %>
6 6  
7 7 --
8 8 <%= _('%s team.') % @environment.name %>
9   -<%= url_for @url %>
  9 +<%= @url %>
... ...
db/migrate/20150103134141_add_edit_raw_html_block_to_admin_role.rb 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +class AddEditRawHtmlBlockToAdminRole < ActiveRecord::Migration
  2 + def self.up
  3 + Environment.all.map(&:id).each do |id|
  4 + role = Environment::Roles.admin(id)
  5 + role.permissions << 'edit_raw_html_block'
  6 + role.save!
  7 + end
  8 + end
  9 +
  10 + def self.down
  11 + Environment.all.map(&:id).each do |id|
  12 + role = Environment::Roles.admin(id)
  13 + role.permissions -= ['edit_raw_html_block']
  14 + role.save!
  15 + end
  16 + end
  17 +end
... ...
db/migrate/20151105175041_create_article_followers.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class CreateArticleFollowers < ActiveRecord::Migration
  2 + def self.up
  3 + execute("CREATE TABLE article_followers AS (SELECT profiles.id AS person_id, t.id AS article_id, clock_timestamp() AS since FROM (SELECT articles.id, regexp_split_to_table(replace(replace(substring(articles.setting FROM ':followers:[^:]*'), ':followers:', ''), '- ', ''), '\n') AS follower FROM articles) t INNER JOIN users ON users.email = follower INNER JOIN profiles ON users.id = profiles.user_id WHERE follower != '');")
  4 + add_timestamps :article_followers
  5 + add_index :article_followers, :person_id
  6 + add_index :article_followers, :article_id
  7 + add_index :article_followers, [:person_id, :article_id], :unique => true
  8 + end
  9 +
  10 + def self.down
  11 + drop_table :article_followers
  12 + end
  13 +end
... ...
db/migrate/20151210230319_add_followers_count_to_article.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class AddFollowersCountToArticle < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :articles, :followers_count, :integer, :default => 0
  4 + execute "update articles set followers_count = (select count(*) from article_followers where article_followers.article_id = articles.id)"
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :articles, :followers_count
  9 + end
  10 +end
... ...
db/migrate/20160202142247_add_timestamps_to_role_assignments.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class AddTimestampsToRoleAssignments < ActiveRecord::Migration
  2 + def change
  3 + add_timestamps :role_assignments
  4 + end
  5 +end
... ...
db/migrate/20160224132937_add_data_to_mailing.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class AddDataToMailing < ActiveRecord::Migration
  2 + def change
  3 + add_column :mailings, :data, :text
  4 + end
  5 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended that you check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(version: 20151221105330) do
  14 +ActiveRecord::Schema.define(version: 20160224132937) do
15 15  
16 16 # These are extensions that must be enabled in order to support this database
17 17 enable_extension "plpgsql"
... ... @@ -88,9 +88,9 @@ ActiveRecord::Schema.define(version: 20151221105330) do
88 88 t.integer "profile_id"
89 89 end
90 90  
91   - create_table "article_followers", force: :cascade do |t|
92   - t.integer "person_id", null: false
93   - t.integer "article_id", null: false
  91 + create_table "article_followers", id: false, force: :cascade do |t|
  92 + t.integer "person_id"
  93 + t.integer "article_id"
94 94 t.datetime "since"
95 95 t.datetime "created_at"
96 96 t.datetime "updated_at"
... ... @@ -699,6 +699,7 @@ ActiveRecord::Schema.define(version: 20151221105330) do
699 699 t.string "locale"
700 700 t.datetime "created_at"
701 701 t.datetime "updated_at"
  702 + t.text "data"
702 703 end
703 704  
704 705 create_table "mark_comment_as_read_plugin", force: :cascade do |t|
... ... @@ -1071,12 +1072,14 @@ ActiveRecord::Schema.define(version: 20151221105330) do
1071 1072 end
1072 1073  
1073 1074 create_table "role_assignments", force: :cascade do |t|
1074   - t.integer "accessor_id", null: false
1075   - t.string "accessor_type"
1076   - t.integer "resource_id"
1077   - t.string "resource_type"
1078   - t.integer "role_id", null: false
1079   - t.boolean "is_global"
  1075 + t.integer "accessor_id", null: false
  1076 + t.string "accessor_type"
  1077 + t.integer "resource_id"
  1078 + t.string "resource_type"
  1079 + t.integer "role_id", null: false
  1080 + t.boolean "is_global"
  1081 + t.datetime "created_at"
  1082 + t.datetime "updated_at"
1080 1083 end
1081 1084  
1082 1085 create_table "roles", force: :cascade do |t|
... ...
debian/apache2/virtualhost.conf
... ... @@ -8,6 +8,19 @@ DocumentRoot &quot;/usr/share/noosfero/public&quot;
8 8  
9 9 RewriteEngine On
10 10  
  11 +# If your XMPP XMPP/BOSH isn't in localhost, change the config below to correct
  12 +# point to address
  13 +RewriteRule /http-bind http://localhost:5280/http-bind [P,QSA,L]
  14 +<Proxy http://localhost:5280/http-bind>
  15 + Order Allow,Deny
  16 + Allow from All
  17 +</Proxy>
  18 +
  19 +# Pass access to private files to backend
  20 +RewriteMap private_files "dbm=sdbm:/usr/share/noosfero/cache/private_files"
  21 +RewriteCond ${private_files:$1|NOT_FOUND} !NOT_FOUND
  22 +RewriteRule ^(/articles/.*) ${private_files:$1} [P,QSA,L]
  23 +
11 24 # Rewrite index to check for static index.html
12 25 RewriteRule ^/$ /index.html [QSA]
13 26  
... ...
debian/changelog
  1 +noosfero (1.4) jessie-test; urgency=medium
  2 +
  3 + * Noosfero 1.4
  4 +
  5 + -- Antonio Terceiro <terceiro@colivre.coop.br> Thu, 18 Feb 2016 16:20:23 -0200
  6 +
1 7 noosfero (1.4~rc3) jessie-test; urgency=medium
2 8  
3 9 * Noosfero 1.4 RC3
... ...
debian/dbinstall
... ... @@ -5,8 +5,6 @@ set -e
5 5 # dbconfig-common uses "pgsql", but we want "postgresql"
6 6 sed -i -e 's/adapter: pgsql/adapter: postgresql/' /etc/noosfero/database.yml
7 7  
8   -/etc/init.d/noosfero setup
9   -
10 8 cd /usr/share/noosfero && su noosfero -c "rake db:schema:load RAILS_ENV=production"
11 9 cd /usr/share/noosfero && su noosfero -c "rake db:migrate RAILS_ENV=production SCHEMA=/dev/null"
12 10 cd /usr/share/noosfero && su noosfero -c "rake db:data:minimal RAILS_ENV=production"
... ...
debian/dbupgrade
... ... @@ -2,7 +2,5 @@
2 2  
3 3 set -e
4 4  
5   -/etc/init.d/noosfero setup
6   -
7 5 cd /usr/share/noosfero && su noosfero -c "rake db:migrate RAILS_ENV=production SCHEMA=/dev/null"
8 6  
... ...
debian/noosfero.links
1 1 var/tmp/noosfero usr/share/noosfero/tmp
2 2 var/log/noosfero usr/share/noosfero/log
  3 +var/cache/noosfero usr/share/noosfero/cache
3 4 etc/noosfero/database.yml usr/share/noosfero/config/database.yml
4 5 etc/noosfero/unicorn.rb usr/share/noosfero/config/unicorn.rb
5 6 etc/noosfero/plugins usr/share/noosfero/config/plugins
... ...
debian/noosfero.postinst
... ... @@ -68,10 +68,17 @@ if [ ! -z &quot;$RET&quot; ]; then
68 68 export NOOSFERO_DOMAIN="$RET"
69 69 fi
70 70  
  71 +/etc/init.d/noosfero setup
  72 +
71 73 # dbconfig-common magic
72 74 . /usr/share/dbconfig-common/dpkg/postinst
73 75 dbc_go noosfero $@
74 76  
  77 +if [ ! -f /usr/share/noosfero/cache/private_files.pag ] && [ $1 = "configure" ] && [ -n $2 ]; then
  78 + echo "Creating private files dbm map..."
  79 + cd /usr/share/noosfero && su noosfero -c "rake cache:private_files RAILS_ENV=production"
  80 +fi
  81 +
75 82 # stop debconf to avoid the problem with infinite hanging, cfe
76 83 # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=295477
77 84 db_stop
... ...
debian/update-noosfero-apache
... ... @@ -17,13 +17,13 @@ if test -x /usr/share/noosfero/script/apacheconf; then
17 17 if ! test -e "$apache_site"; then
18 18 echo "Generating apache virtual host ..."
19 19 cd /usr/share/noosfero && su noosfero -c "RAILS_ENV=production ./script/apacheconf virtualhosts" > "$apache_site"
20   - else
21   - pattern="Include \/etc\/noosfero\/apache\/virtualhost.conf"
22   - include="Include \/usr\/share\/noosfero\/util\/chat\/apache\/xmpp.conf"
23   - if ! cat $apache_site | grep "^ *$include" > /dev/null ; then
24   - echo "Updating apache virtual host ..."
25   - sed -i "s/.*$pattern.*/ $include\n&/" $apache_site
26   - fi
  20 + fi
  21 +
  22 + # remove old way to include chat config
  23 + pattern="Include \/usr\/share\/noosfero\/util\/chat\/apache\/xmpp.conf"
  24 + if cat $apache_site | grep "^ *$pattern" > /dev/null ; then
  25 + echo "Removing obsolete chat configuration ..."
  26 + sed -i "/.*$pattern.*/d" $apache_site
27 27 fi
28 28  
29 29 echo 'Noosfero Apache configuration updated.'
... ...
etc/init.d/noosfero
... ... @@ -86,6 +86,13 @@ do_setup() {
86 86 chmod 750 /var/tmp/noosfero
87 87 fi
88 88  
  89 + # Noosfero cache directory
  90 + if [ ! -d /var/cache/noosfero ]; then
  91 + mkdir /var/cache/noosfero
  92 + chown $NOOSFERO_USER:root /var/cache/noosfero
  93 + chmod 755 /var/cache/noosfero
  94 + fi
  95 +
89 96 # symlink the directories into Noosfero directory
90 97 if [ ! -e $NOOSFERO_DIR/tmp ]; then
91 98 ln -s /var/tmp/noosfero $NOOSFERO_DIR/tmp
... ... @@ -96,6 +103,9 @@ do_setup() {
96 103 if [ ! -e $NOOSFERO_DIR/log ]; then
97 104 ln -s /var/log/noosfero $NOOSFERO_DIR/log
98 105 fi
  106 + if [ ! -e $NOOSFERO_DIR/cache ]; then
  107 + ln -s /var/cache/noosfero $NOOSFERO_DIR/cache
  108 + fi
99 109 }
100 110  
101 111 do_start() {
... ...
features/admin_categories.feature
... ... @@ -45,7 +45,7 @@ Feature: manage categories
45 45 And I should see "Steak"
46 46 When I follow "Hide"
47 47 Then I should not see "Vegetarian"
48   - And "Steak" should not be visible within "div"
  48 + And I should not see "Steak"
49 49  
50 50 @selenium
51 51 Scenario: the show link is available just for categories with category tree
... ...
features/categories_block.feature
... ... @@ -44,6 +44,7 @@ Feature: categories_block
44 44 And I follow "Edit" within ".block-outer .categories-block"
45 45 And I check "Product"
46 46 And I press "Save"
  47 + And I go to /
47 48 Then I should see "Food"
48 49 And I should see "Book"
49 50 And "Vegetarian" should not be visible within "span#category-name"
... ... @@ -62,6 +63,7 @@ Feature: categories_block
62 63 And I follow "Edit" within ".block-outer .categories-block"
63 64 And I check "Product"
64 65 And I press "Save"
  66 + And I go to /
65 67 Then I should see "Book"
66 68 And "Literature" should not be visible within "span#category-name"
67 69 When I follow "block_2_category_2"
... ...
features/comment.feature
... ... @@ -96,4 +96,4 @@ Feature: comment
96 96 Scenario: hide post a comment button when clicked
97 97 Given I am on /booking/article-to-comment
98 98 And I follow "Post a comment"
99   - Then "Post a comment" should not be visible within "#article"
  99 + Then "Post comment" should not be visible within "#article"
... ...
features/manage_categories.feature
... ... @@ -27,5 +27,7 @@ Feature: manage categories
27 27 Then I should not see "Beans"
28 28 And I should not see "Potatoes"
29 29 When I follow "Show"
  30 + And I wait 0.5 seconds for Services show animation to finish
  31 + And I follow "Show"
30 32 Then I should see "Beans"
31 33 And I should see "Potatoes"
... ...
features/manage_users.feature
... ... @@ -15,7 +15,7 @@ Background:
15 15 Scenario: deactive user
16 16 Given I follow "Deactivate user" within "tr[title='Joao Silva']"
17 17 When I confirm the browser dialog
18   - Then the "tr[title='Joao Silva'] td.actions a.icon-activate-user" button should be enabled
  18 + Then the field "tr[title='Joao Silva'] td.actions a.icon-activate-user" should be enabled
19 19  
20 20 @selenium
21 21 Scenario: activate user
... ... @@ -23,7 +23,7 @@ Background:
23 23 And I confirm the browser dialog
24 24 And I follow "Activate user" within "tr[title='Paulo Santos']"
25 25 When I confirm the browser dialog
26   - Then the "tr[title='Paulo Santos'] td.actions a.icon-deactivate-user" button should be enabled
  26 + Then the field "tr[title='Paulo Santos'] td.actions a.icon-deactivate-user" should be enabled
27 27  
28 28 @selenium
29 29 Scenario: remove user
... ... @@ -36,7 +36,7 @@ Background:
36 36 Scenario: admin user
37 37 Given I follow "Set admin role" within "tr[title='Joao Silva']"
38 38 When I confirm the browser dialog
39   - Then the "tr[title='Joao Silva'] td.actions a.icon-reset-admin-role" button should be enabled
  39 + Then the field "tr[title='Joao Silva'] td.actions a.icon-reset-admin-role" should be enabled
40 40  
41 41 @selenium
42 42 Scenario: unadmin user
... ... @@ -44,4 +44,4 @@ Background:
44 44 And I confirm the browser dialog
45 45 And I follow "Reset admin role" within "tr[title='Paulo Santos']"
46 46 When I confirm the browser dialog
47   - Then the "tr[title='Paulo Santos'] td.actions a.icon-set-admin-role" button should be enabled
  47 + Then the field "tr[title='Paulo Santos'] td.actions a.icon-set-admin-role" should be enabled
... ...
features/secret_community.feature
... ... @@ -17,6 +17,7 @@ Feature: Use a secret community
17 17 And I check "Secret"
18 18 And I press "Save"
19 19 And I follow "Logout"
  20 + And I go to /account/login
20 21  
21 22 @selenium
22 23 Scenario: Hide privacity options when secret is checked
... ...
features/send_email_to_organization_members.feature
... ... @@ -31,7 +31,8 @@ Feature: send emails to organization members
31 31 Scenario: Send e-mail to members
32 32 Given I am logged in as "joaosilva"
33 33 And I go to Sample Community's members management
34   - And I follow "Send e-mail to members"
  34 + And I check "checkbox-manoel"
  35 + And I press "Send e-mail to members"
35 36 And I fill in "Subject" with "Hello, member!"
36 37 And I fill in "Body" with "We have some news"
37 38 When I press "Send"
... ... @@ -40,7 +41,8 @@ Feature: send emails to organization members
40 41 Scenario: Not send e-mail to members if subject is blank
41 42 Given I am logged in as "joaosilva"
42 43 And I go to Sample Community's members management
43   - And I follow "Send e-mail to members"
  44 + And I check "checkbox-manoel"
  45 + And I press "Send e-mail to members"
44 46 And I fill in "Body" with "We have some news"
45 47 When I press "Send"
46 48 Then I should be on /profile/sample-community/send_mail
... ... @@ -48,7 +50,8 @@ Feature: send emails to organization members
48 50 Scenario: Not send e-mail to members if body is blank
49 51 Given I am logged in as "joaosilva"
50 52 And I go to Sample Community's members management
51   - And I follow "Send e-mail to members"
  53 + And I check "checkbox-manoel"
  54 + And I press "Send e-mail to members"
52 55 And I fill in "Subject" with "Hello, user!"
53 56 When I press "Send"
54 57 Then I should be on /profile/sample-community/send_mail
... ... @@ -56,7 +59,8 @@ Feature: send emails to organization members
56 59 Scenario: Cancel creation of mailing
57 60 Given I am logged in as "joaosilva"
58 61 And I go to Sample Community's members management
59   - And I follow "Send e-mail to members"
  62 + And I check "checkbox-manoel"
  63 + And I press "Send e-mail to members"
60 64 When I follow "Cancel e-mail"
61 65 Then I should be on Sample Community's members management
62 66  
... ...
features/step_definitions/custom_web_steps.rb
1 1 Then /^"([^"]*)" should not be visible within "([^"]*)"$/ do |text, selector|
2   - if page.has_content?(text)
3   - page.should have_no_css(selector, :text => text, :visible => false)
4   - end
  2 + page.should have_no_css selector, text: text, visible: false
5 3 end
6 4  
7 5 Then /^"([^"]*)" should be visible within "([^"]*)"$/ do |text, selector|
8   - if page.has_content?(text)
9   - page.should have_css(selector, :text => text, :visible => false)
10   - end
  6 + page.should have_css selector, text: text, visible: false
11 7 end
12 8  
13 9 Then /^I should see "([^"]*)" link$/ do |text|
... ... @@ -22,14 +18,14 @@ When /^I should see &quot;([^\&quot;]+)&quot; linking to &quot;([^\&quot;]+)&quot;$/ do |text, href|
22 18 page.should have_xpath("//a[@href='#{href}']")
23 19 end
24 20  
25   -Then /^the "([^"]*)" button should be disabled$/ do |selector|
26   - field = find(selector)
27   - field['disabled'].should be_truthy
28   -end
  21 +Then /^the field "([^"]*)" should be (enabled|disabled)$/ do |selector, status|
  22 + field = page.find(:css, selector)
29 23  
30   -Then /^the "([^"]*)" button should be enabled$/ do |selector|
31   - field = find(selector)
32   - field['disabled'].should_not be_truthy
  24 + if status == 'enabled'
  25 + field.disabled?.should_not be_truthy
  26 + else
  27 + field.disabled?.should be_truthy
  28 + end
33 29 end
34 30  
35 31 When /^I reload and wait for the page$/ do
... ...
features/step_definitions/web_steps.rb
... ... @@ -7,7 +7,7 @@
7 7  
8 8 require 'uri'
9 9 require 'cgi'
10   -require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
  10 +require_relative '../support/paths'
11 11  
12 12 module WithinHelpers
13 13 def with_scope(locator)
... ... @@ -39,37 +39,15 @@ end
39 39  
40 40 When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector|
41 41 with_scope(selector) do
42   - begin
43   - click_link(link, :match => :prefer_exact)
44   - rescue Selenium::WebDriver::Error::UnknownError => selenium_error
45   - if selenium_error.message.start_with? 'Element is not clickable at point'
46   - link = find_link(link)
47   - href = link[:href]
48   - onclick = link[:onClick]
49   -
50   - warn "#{selenium_error.message}\n\n"\
51   - "Trying to overcome this by:\n"
52   -
53   - onclick_return = true
54   -
55   - unless onclick.nil?
56   - warn "\t* Running onClick JS:\n"\
57   - "\t\t'#{onclick}'\n"
58   - onclick_return = page.execute_script onclick
59   - end
60   -
61   - if onclick_return
62   - warn "\t* Redirecting you to the link's href:\n"\
63   - "\t\t'#{href}'\n"
64   -
65   - visit href
66   - end
67   -
68   - warn "\nGood luck and be careful that this may produce hidden links to work on tests!\n"
69   - else
70   - raise selenium_error
71   - end
  42 + link = find :link_or_button, link, match: :prefer_exact
  43 + # If the link has child elements, then $(link).click() has no effect,
  44 + # so find the first child and click on it.
  45 + if Capybara.default_driver == :selenium
  46 + target = link.all('*').first || link
  47 + else
  48 + target = link
72 49 end
  50 + target.click
73 51 end
74 52 end
75 53  
... ...
features/support/debug.rb
... ... @@ -21,3 +21,8 @@ Before do |scenario|
21 21 puts "Can't find debugger or pry to debug"
22 22 end
23 23 end
  24 +
  25 +Then /^I open pry$/ do
  26 + require'pry';binding.pry
  27 +end
  28 +
... ...
features/support/env.rb
... ... @@ -65,5 +65,13 @@ Before do
65 65 fixtures_folder = Rails.root.join('test', 'fixtures')
66 66 fixtures = ['environments', 'roles']
67 67 ActiveRecord::Fixtures.create_fixtures(fixtures_folder, fixtures)
  68 +
  69 + # The same browser session is used across tests, so expire caching
  70 + # can create changes from scenario to another.
  71 + e=Environment.default
  72 + e.home_cache_in_minutes = 0
  73 + e.general_cache_in_minutes = 0
  74 + e.profile_cache_in_minutes = 0
  75 + e.save
68 76 end
69 77  
... ...
gitignore.example
... ... @@ -22,6 +22,7 @@ public/thumbnails
22 22 public/user_themes
23 23 public/designs/themes/default
24 24 public/designs/icons/default
  25 +cache
25 26  
26 27 public/assets
27 28 .sass-cache
... ...
lib/file_presenter.rb
... ... @@ -52,8 +52,8 @@ class FilePresenter
52 52 nil
53 53 end
54 54  
55   - def download?(view=nil)
56   - false
  55 + def download? view = nil
  56 + view.blank?
57 57 end
58 58  
59 59 def short_description
... ...
lib/noosfero/api/v1/articles.rb
... ... @@ -165,6 +165,37 @@ module Noosfero
165 165 end
166 166 end
167 167  
  168 + desc "Returns the total followers for the article" do
  169 + detail 'Get the followers of a specific article by id'
  170 + failure [[403, 'Forbidden']]
  171 + named 'ArticleFollowers'
  172 + end
  173 + get ':id/followers' do
  174 + article = find_article(environment.articles, params[:id])
  175 + total = article.person_followers.count
  176 + {:total_followers => total}
  177 + end
  178 +
  179 + desc "Add a follower for the article" do
  180 + detail 'Add the current user identified by private token, like a follower of a article'
  181 + params Noosfero::API::Entities::UserLogin.documentation
  182 + failure [[401, 'Unauthorized']]
  183 + named 'ArticleFollow'
  184 + end
  185 + post ':id/follow' do
  186 + authenticate!
  187 + article = find_article(environment.articles, params[:id])
  188 + if article.article_followers.exists?(:person_id => current_person.id)
  189 + {:success => false, :already_follow => true}
  190 + else
  191 + article_follower = ArticleFollower.new
  192 + article_follower.article = article
  193 + article_follower.person = current_person
  194 + article_follower.save!
  195 + {:success => true}
  196 + end
  197 + end
  198 +
168 199 desc 'Return the children of a article identified by id' do
169 200 detail 'Get all children articles of a specific article'
170 201 params Noosfero::API::Entities::Article.documentation
... ...
lib/noosfero/version.rb
1 1 module Noosfero
2 2 PROJECT = 'noosfero'
3   - VERSION = '1.4~rc3'
  3 + VERSION = '1.4'
4 4 end
5 5  
6 6 root = File.expand_path(File.dirname(__FILE__) + '/../..')
... ...
lib/noosfero/vote_ext.rb
... ... @@ -2,10 +2,10 @@ require_dependency &#39;models/vote&#39;
2 2  
3 3 class Vote
4 4  
5   - validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id], :if => :allow_duplicated_vote?
  5 + validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id], :unless => :allow_duplicate?
6 6  
7   - def allow_duplicated_vote?
8   - voter.present?
  7 + def allow_duplicate?
  8 + voter.blank?
9 9 end
10 10  
11 11 validate :verify_target_archived
... ...
lib/tasks/cache.rake 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +namespace :cache do
  2 + task :private_files => :environment do
  3 + require 'sdbm'
  4 +
  5 + hash = {}
  6 + UploadedFile.where(:published => false).find_each do |uploaded_file|
  7 + hash[uploaded_file.public_filename] = uploaded_file.full_path
  8 + end
  9 +
  10 + dbm = SDBM.open(UploadedFile::DBM_PRIVATE_FILE)
  11 + dbm.update(hash)
  12 + dbm.close
  13 + end
  14 +end
... ...
plugins/oauth_client/lib/ext/profile.rb
1 1 require_dependency 'profile'
2 2  
3   -Profile.descendants.each do |subclass|
4   - subclass.class_eval do
  3 +class Profile
5 4  
6   - has_many :oauth_auths, foreign_key: :profile_id, class_name: 'OauthClientPlugin::Auth', dependent: :destroy
7   - has_many :oauth_providers, through: :oauth_auths, source: :provider
  5 + has_many :oauth_auths, foreign_key: :profile_id, class_name: 'OauthClientPlugin::Auth', dependent: :destroy
  6 + has_many :oauth_providers, through: :oauth_auths, source: :provider
8 7  
9   - end
10 8 end
... ...
plugins/organization_ratings/features/rate_community.feature
... ... @@ -33,4 +33,4 @@ Feature: rate_community
33 33 Given I am on mycommunity's homepage
34 34 When I follow "Rate this Community"
35 35 Then I should see "Joao Silva" within ".star-profile-name"
36   - And I should see Joao Silva's profile image
37 36 \ No newline at end of file
  37 + And I should see Joao Silva's profile image
... ...
plugins/organization_ratings/features/vote_once_disable_cooldown.feature 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +Feature: vote_once_disable_cooldown
  2 + As a admin
  3 + I want to disable the cooldown time when vote once is enabled
  4 + Making it clearly that there is no cooldown when vote once is enabled
  5 +
  6 + Background:
  7 + Given plugin "OrganizationRatings" is enabled on environment
  8 + And I am logged in as admin
  9 + And I go to /admin/plugins
  10 + And I check "Organization Ratings"
  11 + And I press "Save changes"
  12 +
  13 + @selenium
  14 + Scenario: disable or enable the cooldown field when vote on is checked or unchecked
  15 + Given I follow "Administration"
  16 + And I follow "Plugins"
  17 + And I follow "Configuration"
  18 + And the field "#organization_ratings_config_cooldown" should be enabled
  19 + And I check "Vote once"
  20 + And the field "#organization_ratings_config_cooldown" should be disabled
  21 + And I uncheck "Vote once"
  22 + Then the field "#organization_ratings_config_cooldown" should be enabled
  23 +
... ...
plugins/organization_ratings/lib/organization_ratings_plugin.rb
... ... @@ -73,8 +73,8 @@ class OrganizationRatingsPlugin &lt; Noosfero::Plugin
73 73  
74 74 def js_files
75 75 %w(
76   - public/rate.js
77   - public/organization_rating_management.js
  76 + rate.js
  77 + organization_rating_management.js
78 78 )
79 79 end
80 80  
... ...
plugins/organization_ratings/public/organization_rating_management.js
... ... @@ -9,8 +9,8 @@
9 9  
10 10  
11 11 cacheDom: function() {
12   - this.$vote_once_checkbox = $("#environment_organization_ratings_vote_once");
13   - this.$hours_timer_input = $("#environment_organization_ratings_cooldown");
  12 + this.$vote_once_checkbox = $("#organization_ratings_config_vote_once");
  13 + this.$hours_timer_input = $("#organization_ratings_config_cooldown");
14 14 },
15 15  
16 16  
... ... @@ -21,10 +21,22 @@
21 21  
22 22 verifyHoursTimerDisable: function() {
23 23 if (this.$vote_once_checkbox.is(":checked")) {
24   - this.$hours_timer_input.attr("disabled", "disabled");
  24 + //this.$hours_timer_input.attr("disabled", "disabled");
  25 + this.disableVoteOnce();
25 26 } else {
26   - this.$hours_timer_input.removeAttr("disabled");
  27 + //this.$hours_timer_input.removeAttr("disabled");
  28 + this.enableVoteOnce();
27 29 }
  30 + },
  31 +
  32 +
  33 + enableVoteOnce: function() {
  34 + this.$hours_timer_input.removeAttr("disabled");
  35 + },
  36 +
  37 +
  38 + disableVoteOnce: function() {
  39 + this.$hours_timer_input.attr("disabled", "disabled");
28 40 }
29 41 }
30 42  
... ...
plugins/organization_ratings/public/style.css
... ... @@ -14,11 +14,11 @@
14 14 }
15 15  
16 16 .star-negative {
17   - background-image: url('public/images/star-negative.png');
  17 + background-image: url('images/star-negative.png');
18 18 }
19 19  
20 20 .star-positive {
21   - background-image: url('public/images/star-positive.png');
  21 + background-image: url('images/star-positive.png');
22 22 }
23 23  
24 24 .small-star-negative, .small-star-positive {
... ... @@ -31,11 +31,11 @@
31 31 }
32 32  
33 33 .small-star-negative {
34   - background-image: url('public/images/small-star-negative.png');
  34 + background-image: url('images/small-star-negative.png');
35 35 }
36 36  
37 37 .small-star-positive {
38   - background-image: url('public/images/small-star-positive.png');
  38 + background-image: url('images/small-star-positive.png');
39 39 }
40 40  
41 41 .medium-star-negative, .medium-star-positive {
... ... @@ -48,11 +48,11 @@
48 48 }
49 49  
50 50 .medium-star-positive {
51   - background-image: url('public/images/star-positive-medium.png');
  51 + background-image: url('images/star-positive-medium.png');
52 52 }
53 53  
54 54 .medium-star-negative {
55   - background-image: url('public/images/star-negative-medium.png');
  55 + background-image: url('images/star-negative-medium.png');
56 56 }
57 57  
58 58 .star-hide {
... ...
plugins/people_block/lib/ext/person.rb
... ... @@ -1,10 +0,0 @@
1   -require_dependency 'person'
2   -
3   -class Person
4   -
5   - scope :with_role, -> role_id {
6   - joins(:role_assignments).
7   - where("role_assignments.role_id = #{role_id}")
8   - }
9   -
10   -end
plugins/people_block/test/unit/members_block_test.rb
... ... @@ -280,6 +280,21 @@ class MembersBlockTest &lt; ActionView::TestCase
280 280 assert_includes block.roles, Profile::Roles.moderator(owner.environment.id)
281 281 end
282 282  
  283 + should 'count number of profiles by role' do
  284 + owner = fast_create(Community)
  285 + profile1 = fast_create(Person, {:public_profile => true})
  286 + profile2 = fast_create(Person, {:public_profile => true})
  287 +
  288 + owner.add_member profile2
  289 + owner.add_moderator profile1
  290 +
  291 + block = MembersBlock.new
  292 + block.visible_role = Profile::Roles.moderator(owner.environment.id).key
  293 + block.expects(:owner).returns(owner).at_least_once
  294 +
  295 + assert_equivalent [profile1], block.profile_list
  296 + end
  297 +
283 298 protected
284 299 include NoosferoTestHelper
285 300  
... ...
plugins/pg_search/test/unit/pg_search_plugin_test.rb
... ... @@ -25,7 +25,8 @@ class PgSearchPluginTest &lt; ActiveSupport::TestCase
25 25 profile1 = fast_create(Profile, :identifier => 'profile1', :name => 'debugger')
26 26 profile2 = fast_create(Profile, :identifier => 'profile2', :name => 'profile admin debugger')
27 27 profile3 = fast_create(Profile, :identifier => 'profile3', :name => 'admin debugger')
28   - assert_equal [profile2, profile3, profile1], search(Profile, 'profile admin deb')
  28 + profile4 = fast_create(Profile, :identifier => 'profile4', :name => 'simple user')
  29 + assert_equal [profile2, profile3, profile1, profile4], search(Profile, 'profile admin deb')
29 30 end
30 31  
31 32 should 'locate profile escaping special characters' do
... ...
plugins/site_tour/lib/ext/person.rb
1   -class Person
  1 +require_dependency 'person'
2 2  
  3 +class Person
3 4 settings_items :site_tour_plugin_actions, :type => Array, :default => []
4   -
5 5 end
... ...
plugins/stoa/test/functional/account_controller_test.rb
... ... @@ -15,6 +15,7 @@ class AccountControllerTest &lt; ActionController::TestCase
15 15 t.date "dtanas"
16 16 end
17 17 ActiveRecord::Base.establish_connection(:test)
  18 + StoaPlugin::UspUser.reset_column_information
18 19  
19 20 def setup
20 21 @controller = AccountController.new
... ...
plugins/sub_organizations/views/sub_organizations_plugin_profile/_full_related_organizations.html.erb
... ... @@ -10,7 +10,7 @@
10 10 <%= profile_image_link organization, :big, 'div' %>
11 11 </div>
12 12 <div class="related-organizations-item-column-right">
13   - <%= link_to_homepage(organization.name, organization.identifier, :class => "search-result-title") %>
  13 + <%= link_to_homepage(organization.name, organization, :class => "search-result-title") %>
14 14 <div class="related-organizations-description">
15 15 <% if organization.description %>
16 16 <% body_stripped = strip_tags(organization.description) %>
... ...
plugins/video/lib/video_plugin.rb
... ... @@ -33,10 +33,14 @@ class VideoPlugin &lt; Noosfero::Plugin
33 33 end
34 34  
35 35 def article_extra_toolbar_buttons(content)
36   - if content.kind_of?(VideoPlugin::VideoGallery)
37   - url = url_for(:action => 'new', :type=>'VideoPlugin::Video', :controller=>'cms', :parent_id => content.id)
38   - {:title => _('New Video'), :url => url, :icon => 'button with-text icon-new'}
39   - end
  36 + return [] if !content.kind_of?(VideoPlugin::VideoGallery)
  37 + {
  38 + :id=>"new-video-btn",
  39 + :class=>"button with-text icon-new",
  40 + :url=> {:action => 'new', :type=>'VideoPlugin::Video', :controller=>'cms', :parent_id => content.id},
  41 + :title=>_("New Video"),
  42 + :icon => :new
  43 + }
40 44 end
41 45  
42 46 end
... ...
plugins/video/views/content_viewer/video_plugin/_video_gallery.html.erb
... ... @@ -11,4 +11,4 @@
11 11 <em><%= _('(empty video gallery)') %></em>
12 12 <% else %>
13 13 <%= list_videos(:contents=>video_gallery.children) %>
14   -<% end %>
15 14 \ No newline at end of file
  15 +<% end %>
... ...
public/designs/themes/noosfero/style.css
... ... @@ -48,3 +48,9 @@
48 48 width: 80px;
49 49 }
50 50  
  51 +.action-profile-send_mail .send-mail-recipients {
  52 + color: #888888;
  53 + padding: 10px;
  54 + width: 475px;
  55 + line-height: 15px;
  56 +}
... ...
public/javascripts/comment_order.js
... ... @@ -10,10 +10,9 @@ function send_order(order, url) {
10 10 });
11 11 }
12 12  
13   -
14 13 jQuery(document).ready(function(){
15 14 jQuery("#comment_order").change(function(){
16   - var url = jQuery("#page_url").val();
  15 + var url = window.location.href;
17 16 send_order(this.value, url);
18 17 });
19   -});
20 18 \ No newline at end of file
  19 +});
... ...
public/javascripts/profile_members.js 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +(function($) {
  2 +
  3 + //Autocomplete to list members
  4 + $('#filter-name-autocomplete').autocomplete({
  5 + minLength:2,
  6 + source:function(request,response){
  7 + $.ajax({
  8 + url:document.location.pathname+'/search_members',
  9 + dataType:'json',
  10 + data:{
  11 + filter_name:request.term
  12 + },
  13 + success:response
  14 + });
  15 + }
  16 + });
  17 +})(jQuery);
  18 +
  19 +
  20 +function toggle(source) {
  21 + checkboxes = document.getElementsByName('members_filtered[]');
  22 + for(var i=0, n=checkboxes.length;i<n;i++) {
  23 + checkboxes[i].checked = source.checked;
  24 + }
  25 +}
... ...
public/stylesheets/profile-list.scss
... ... @@ -207,3 +207,23 @@
207 207 height: auto;
208 208 font-size: 12px;
209 209 }
  210 +.action-profile-members .profile_link{
  211 + position: relative;
  212 +}
  213 +.action-profile-members .profile_link span.new-profile:last-child{
  214 + position: absolute;
  215 + top: 3px;
  216 + right: 2px;
  217 + text-transform: uppercase;
  218 + color: #FFF;
  219 + font-size: 9px;
  220 + background: #66CC33;
  221 + padding: 2px;
  222 + display: block;
  223 + width: 35px;
  224 + font-weight: 700;
  225 +}
  226 +.action-profile-members .profile_link .fn{
  227 + font-style: normal;
  228 + color: #000;
  229 +}
... ...
script/apacheconf
... ... @@ -17,7 +17,6 @@ when &#39;virtualhosts&#39;
17 17 puts " #{server_directive} #{domain.name}"
18 18 server_directive = 'ServerAlias'
19 19 end
20   - puts " Include /usr/share/noosfero/util/chat/apache/xmpp.conf"
21 20 puts " Include /etc/noosfero/apache/virtualhost.conf"
22 21 puts "</VirtualHost>"
23 22 end
... ...
script/development
... ... @@ -23,7 +23,7 @@ start() {
23 23 trap stop INT TERM EXIT
24 24 whenever --write-crontab --set 'environment=development'
25 25 mkdir -p log
26   - touch log/development.log
  26 + touch log/development.log log/development_api.log
27 27 if [ -z "$RAILS_RELATIVE_URL_ROOT" ]; then
28 28 unicorn_rails --config-file lib/noosfero/unicorn.rb --daemonize $@
29 29 else
... ... @@ -32,7 +32,7 @@ start() {
32 32 --config-file lib/noosfero/unicorn.rb \
33 33 --daemonize
34 34 fi
35   - tail -n 0 -f log/development.log || true
  35 + tail -n 0 -f log/development.log log/development_api.log || true
36 36 }
37 37  
38 38 start $@
... ...
script/quick-start
... ... @@ -130,6 +130,7 @@ fi
130 130 # create needed directory
131 131 mkdir -p tmp/pids
132 132 mkdir -p tmp/cache
  133 +mkdir cache
133 134  
134 135 # use default gitignore rules
135 136 if [ ! -f .gitignore ]; then
... ...
test/fixtures/roles.yml
... ... @@ -103,3 +103,4 @@ environment_administrator:
103 103 - manage_environment_templates
104 104 - manage_environment_licenses
105 105 - manage_email_templates
  106 + - edit_raw_html_block
... ...
test/functional/cms_controller_test.rb
... ... @@ -1647,6 +1647,50 @@ class CmsControllerTest &lt; ActionController::TestCase
1647 1647 assert_tag :tag => 'input', :attributes => {:name => 'article[accept_comments]', :value => 1, :type => 'checkbox'}
1648 1648 end
1649 1649  
  1650 + should 'logged in user NOT be able to create topic on forum when topic creation is set to Me' do
  1651 + u = create_user('linux')
  1652 + login_as :linux
  1653 + profile.articles << f = Forum.new(:name => 'Forum for test',
  1654 + :topic_creation => 'self',
  1655 + :body => 'Forum Body')
  1656 +
  1657 + post :new, :profile => profile.identifier, :type => 'TinyMceArticle',
  1658 + :article => {:name => 'New Topic by linux', :body => 'Article Body',
  1659 + :parent_id => f.id}
  1660 +
  1661 + assert_template :access_denied
  1662 + assert_not_equal 'New Topic by linux', Article.last.name
  1663 + end
  1664 +
  1665 + should 'logged in user NOT be able to create topic on forum when topic creation is set to Friends/Members' do
  1666 + u = create_user('linux')
  1667 + login_as :linux
  1668 + profile.articles << f = Forum.new(:name => 'Forum for test',
  1669 + :topic_creation => 'related',
  1670 + :body => 'Forum Body')
  1671 +
  1672 + post :new, :profile => profile.identifier, :type => 'TinyMceArticle',
  1673 + :article => {:name => 'New Topic by linux', :body => 'Article Body',
  1674 + :parent_id => f.id}
  1675 +
  1676 + assert_template :access_denied
  1677 + assert_not_equal 'New Topic by linux', Article.last.name
  1678 + end
  1679 +
  1680 + should 'logged in user be able to create topic on forum when topic creation is set to Logged in users' do
  1681 + u = create_user('linux')
  1682 + login_as :linux
  1683 + profile.articles << f = Forum.new(:name => 'Forum for test',
  1684 + :topic_creation => 'users',
  1685 + :body => 'Forum Body')
  1686 +
  1687 + post :new, :profile => profile.identifier, :type => 'TinyMceArticle',
  1688 + :article => {:name => 'New Topic by linux', :body => 'Article Body',
  1689 + :parent_id => f.id}
  1690 +
  1691 + assert_equal 'New Topic by linux', Article.last.name
  1692 + end
  1693 +
1650 1694 should 'display accept comments option when editing forum post with a different label' do
1651 1695 profile.articles << f = Forum.new(:name => 'Forum for test')
1652 1696 profile.articles << a = TinyMceArticle.new(:name => 'Forum post for test', :parent => f)
... ...
test/functional/comment_controller_test.rb
... ... @@ -387,10 +387,26 @@ class CommentControllerTest &lt; ActionController::TestCase
387 387 Article.record_timestamps = true
388 388  
389 389 login_as @profile.identifier
390   - xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => { :title => 'crap!', :body => 'I think that this article is crap' }, :confirm => 'true'
  390 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:title => 'crap!', :body => 'I think that this article is crap' }, :confirm => 'true'
391 391 assert_not_equal yesterday, page.reload.updated_at
392 392 end
393 393  
  394 + should 'follow article when commenting' do
  395 + page = create(Article, :profile => profile, :name => 'myarticle', :body => 'the body of the text')
  396 + login_as @profile.identifier
  397 +
  398 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:title => 'crap!', :body => 'I think that this article is crap', :follow_article => true}, :confirm => 'true'
  399 + assert_includes page.person_followers, @profile
  400 + end
  401 +
  402 + should 'not follow article when commenting' do
  403 + page = create(Article, :profile => profile, :name => 'myarticle', :body => 'the body of the text')
  404 + login_as @profile.identifier
  405 +
  406 + xhr :post, :create, :profile => profile.identifier, :id => page.id, :comment => {:title => 'crap!', :body => 'I think that this article is crap', :follow_article => false }, :confirm => 'true'
  407 + assert_not_includes page.person_followers, @profile
  408 + end
  409 +
394 410 should 'be able to mark comments as spam' do
395 411 login_as profile.identifier
396 412 article = fast_create(Article, :profile_id => profile.id)
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -51,27 +51,26 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
51 51 assert_response :missing
52 52 end
53 53  
54   - should 'produce a download-link when article is a uploaded file' do
  54 + should 'produce a download-link when view page is true' do
55 55 profile = create_user('someone').person
56 56 html = UploadedFile.create! :uploaded_data => fixture_file_upload('/files/500.html', 'text/html'), :profile => profile
57 57 html.save!
58 58  
59   - get :view_page, :profile => 'someone', :page => [ '500.html' ]
  59 + get :view_page, :profile => 'someone', :page => [ '500.html' ], :view => true
60 60  
61 61 assert_response :success
62   - assert_match /#{html.public_filename}/, @response.body
  62 + assert_select "a[href=#{html.full_path}]"
63 63 end
64 64  
65   - should 'download file when article is image' do
  65 + should 'download file when view page is blank' do
66 66 profile = create_user('someone').person
67 67 image = UploadedFile.create! :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => profile
68 68 image.save!
69 69  
70 70 get :view_page, :profile => 'someone', :page => [ 'rails.png' ]
71 71  
72   - assert_response :success
73   - assert_not_nil assigns(:page).data
74   - assert_match /image\/png/, @response.headers['Content-Type']
  72 + assert_response :redirect
  73 + assert_redirected_to image.public_filename
75 74 end
76 75  
77 76 should 'display image on a page when article is image and has a view param' do
... ... @@ -342,6 +341,27 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
342 341 assert_tag :content => /list my comment/
343 342 end
344 343  
  344 + should 'order comments according to comments ordering option' do
  345 + article = fast_create(Article, :profile_id => profile.id)
  346 + for n in 1..24
  347 + article.comments.create!(:author => profile, :title => "some title #{n}", :body => "some body #{n}")
  348 + end
  349 +
  350 + get 'view_page', :profile => profile.identifier, :page => article.path.split('/')
  351 +
  352 + for i in 1..12
  353 + assert_tag :tag => 'div', :attributes => { :class => 'comment-details' }, :descendant => { :tag => 'h4', :content => "some title #{i}" }
  354 + assert_no_tag :tag => 'div', :attributes => { :class => 'comment-details' }, :descendant => { :tag => 'h4', :content => "some title #{i + 12}" }
  355 + end
  356 +
  357 + xhr :get, :view_page, :profile => profile.identifier, :page => article.path.split('/'), :comment_page => 1, :comment_order => 'newest'
  358 +
  359 + for i in 1..12
  360 + assert_no_tag :tag => 'div', :attributes => { :class => 'comment-details' }, :descendant => { :tag => 'h4', :content => "some title #{i}" }
  361 + assert_tag :tag => 'div', :attributes => { :class => 'comment-details' }, :descendant => { :tag => 'h4', :content => "some title #{i + 12}" }
  362 + end
  363 + end
  364 +
345 365 should 'redirect to new article path under an old path' do
346 366 p = create_user('test_user').person
347 367 a = p.articles.create(:name => 'old-name')
... ... @@ -1291,18 +1311,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1291 1311 assert_tag :tag => 'div', :attributes => { :id => 'article-actions' }, :descendant => { :tag => 'a', :attributes => { :title => 'This button is expired.', :class => 'button with-text icon-edit disabled' } }
1292 1312 end
1293 1313  
1294   - should 'remove email from article followers when unfollow' do
1295   - profile = create_user('testuser').person
1296   - follower_email = 'john@doe.br'
1297   - article = profile.articles.create(:name => 'test')
1298   - article.followers = [follower_email]
1299   - article.save
1300   -
1301   - assert_includes Article.find(article.id).followers, follower_email
1302   - post :view_page, :profile => profile.identifier, :page => [article.name], :unfollow => 'commit', :email => follower_email
1303   - assert_not_includes Article.find(article.id).followers, follower_email
1304   - end
1305   -
1306 1314 should 'not display comments marked as spam' do
1307 1315 article = fast_create(Article, :profile_id => profile.id)
1308 1316 ham = fast_create(Comment, :source_id => article.id, :source_type => 'Article', :title => 'some content')
... ... @@ -1377,7 +1385,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1377 1385 assert_equal 15, article.comments.count
1378 1386  
1379 1387 get 'view_page', :profile => profile.identifier, :page => article.path.split('/')
1380   - assert_tag :tag => 'a', :attributes => { :href => "/#{profile.identifier}/#{article.path}?comment_page=2", :rel => 'next' }
  1388 + assert_tag :tag => 'a', :attributes => { :href => "/#{profile.identifier}/#{article.path}?comment_order=oldest&amp;comment_page=2", :rel => 'next' }
1381 1389 end
1382 1390  
1383 1391 should 'not escape acceptable HTML in list of blog posts' do
... ...
test/functional/profile_controller_test.rb
... ... @@ -18,6 +18,19 @@ class ProfileControllerTest &lt; ActionController::TestCase
18 18 assert assigns(:friends)
19 19 end
20 20  
  21 + should 'remove person from article followers when unfollow' do
  22 + profile = create_user('testuser').person
  23 + follower = create_user('follower').person
  24 + article = profile.articles.create(:name => 'test')
  25 + article.person_followers = [follower]
  26 + article.save
  27 + login_as('follower')
  28 + article.reload
  29 + assert_includes Article.find(article.id).person_followers, follower
  30 + post :unfollow_article, :article_id => article.id
  31 + assert_not_includes Article.find(article.id).person_followers, follower
  32 + end
  33 +
21 34 should 'point to manage friends in user is seeing his own friends' do
22 35 login_as('testuser')
23 36 get :friends
... ... @@ -1338,6 +1351,24 @@ class ProfileControllerTest &lt; ActionController::TestCase
1338 1351 assert_equivalent [scrap,activity], assigns(:activities).map(&:activity)
1339 1352 end
1340 1353  
  1354 + should "follow an article" do
  1355 + article = TinyMceArticle.create!(:profile => profile, :name => 'An article about free software')
  1356 + login_as(@profile.identifier)
  1357 + post :follow_article, :profile => profile.identifier, :article_id => article.id
  1358 + assert_includes article.person_followers, @profile
  1359 + end
  1360 +
  1361 + should "unfollow an article" do
  1362 + article = TinyMceArticle.create!(:profile => profile, :name => 'An article about free software')
  1363 + article.person_followers << @profile
  1364 + article.save!
  1365 + assert_includes article.person_followers, @profile
  1366 +
  1367 + login_as(@profile.identifier)
  1368 + post :unfollow_article, :profile => profile.identifier, :article_id => article.id
  1369 + assert_not_includes article.person_followers, @profile
  1370 + end
  1371 +
1341 1372 should "be logged in to leave comment on an activity" do
1342 1373 article = TinyMceArticle.create!(:profile => profile, :name => 'An article about free software')
1343 1374 activity = ActionTracker::Record.last
... ... @@ -1434,11 +1465,41 @@ class ProfileControllerTest &lt; ActionController::TestCase
1434 1465 create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
1435 1466 login_as('profile_moderator_user')
1436 1467 @controller.stubs(:locale).returns('pt')
  1468 +
1437 1469 assert_difference 'Delayed::Job.count', 1 do
1438 1470 post :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
1439 1471 end
1440 1472 end
1441 1473  
  1474 + should 'send to members_filtered if available' do
  1475 + community = fast_create(Community)
  1476 + create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
  1477 + person = create_user('Any').person
  1478 + community.add_member(person)
  1479 + community.save!
  1480 + login_as('profile_moderator_user')
  1481 +
  1482 + post :send_mail, :profile => community.identifier, :mailing => {:subject => 'Hello', :body => 'We have some news'}
  1483 + assert_equivalent community.members, OrganizationMailing.last.recipients
  1484 +
  1485 + @request.session[:members_filtered] = [person.id]
  1486 + post :send_mail, :profile => community.identifier, :mailing => {:subject => 'RUN!!', :body => 'Run to the hills!!'}
  1487 + assert_equal [person], OrganizationMailing.last.recipients
  1488 + end
  1489 +
  1490 + should 'send email to all members if there is no valid member in members_filtered' do
  1491 + community = fast_create(Community)
  1492 + create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
  1493 + person = create_user('Any').person
  1494 + community.add_member(person)
  1495 + community.save!
  1496 + login_as('profile_moderator_user')
  1497 +
  1498 + @request.session[:members_filtered] = [Profile.last.id+1]
  1499 + post :send_mail, :profile => community.identifier, :mailing => {:subject => 'RUN!!', :body => 'Run to the hills!!'}
  1500 + assert_empty OrganizationMailing.last.recipients
  1501 + end
  1502 +
1442 1503 should 'save mailing' do
1443 1504 community = fast_create(Community)
1444 1505 create_user_with_permission('profile_moderator_user', 'send_mail_to_members', community)
... ...
test/functional/profile_design_controller_test.rb
... ... @@ -311,6 +311,12 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
311 311 assert_equal 999, @b1.article_id
312 312 end
313 313  
  314 + should 'not be able to save a non editable block' do
  315 + Block.any_instance.expects(:editable?).returns(false)
  316 + post :save, :profile => 'designtestuser', :id => @b1.id, :block => { }
  317 + assert_response :forbidden
  318 + end
  319 +
314 320 should 'be able to edit ProductsBlock' do
315 321 block = ProductsBlock.new
316 322  
... ...
test/functional/profile_members_controller_test.rb
... ... @@ -31,6 +31,31 @@ class ProfileMembersControllerTest &lt; ActionController::TestCase
31 31 assert_template 'index'
32 32 end
33 33  
  34 + should 'access index and filter members by name and roles' do
  35 +
  36 + ent = fast_create(Enterprise, :identifier => 'test_enterprise', :name => 'test enterprise')
  37 + roles = {
  38 + :admin => Profile::Roles.admin(Environment.default),
  39 + :member => Profile::Roles.member(Environment.default)
  40 + }
  41 +
  42 + member = create_user('test_member', :email => 'testmember@test.com.br').person
  43 + member.add_role(roles[:member], ent)
  44 +
  45 + admin = create_user('test_admin').person
  46 + admin.add_role roles[:admin], ent
  47 +
  48 + user = create_user_with_permission('test_user', 'manage_memberships', ent)
  49 + login_as :test_user
  50 +
  51 + post :index, :profile => 'test_enterprise' , :filters => {:name => 'testmember@test.com.br', :roles => [roles[:member].id]}
  52 +
  53 + assert_response :success
  54 + assert_template 'index'
  55 +
  56 + assert_includes assigns(:data)[:members], member
  57 + end
  58 +
34 59 should 'show form to change role' do
35 60 ent = fast_create(Enterprise, :identifier => 'test_enterprise', :name => 'test enterprise')
36 61 role = Profile::Roles.member(Environment.default)
... ... @@ -171,7 +196,7 @@ class ProfileMembersControllerTest &lt; ActionController::TestCase
171 196 login_as :test_user
172 197  
173 198 get :index, :profile => community.identifier
174   - assert_tag :tag => 'a', :attributes => {:href => /send_mail/}
  199 + assert_tag :tag => 'input', :attributes => {:value => 'Send e-mail to members'}
175 200 end
176 201  
177 202 should 'not display send email to members if doesn\'t have the permission' do
... ...
test/integration/enable_disable_features_test.rb
... ... @@ -13,17 +13,15 @@ class EnableDisableFeaturesTest &lt; ActionDispatch::IntegrationTest
13 13 assert_tag :tag => 'input', :attributes => { :name => 'environment[enabled_features][]', :value => 'feature2' }
14 14 assert_tag :tag => 'input', :attributes => { :name => 'environment[enabled_features][]', :value => 'feature3' }
15 15  
16   - post '/admin/features/update'
17   - assert_response :redirect
  16 + post_via_redirect '/admin/features/update'
  17 + assert_response :success
18 18  
19   - follow_redirect!
20 19 assert_response :success
21 20 assert_equal '/admin/features', path
22 21  
23   - post '/admin/features/update', :environments => { :enabled_features => [ 'feature1' ], :organization_approval_method => 'region' }
24   - assert_response :redirect
  22 + post_via_redirect '/admin/features/update', :environments => { :enabled_features => [ 'feature1' ], :organization_approval_method => 'region' }
  23 + assert_response :success
25 24  
26   - follow_redirect!
27 25 assert_equal '/admin/features', path
28 26  
29 27 end
... ...
test/integration/enterprise_registration_test.rb
... ... @@ -61,10 +61,9 @@ class EnterpriseRegistrationTest &lt; ActionDispatch::IntegrationTest
61 61 assert_response :success
62 62 assert_tag :form, :attributes => { :action => "/myprofile/myorg/enterprise_validation/approve/#{code}" }
63 63  
64   - post "/myprofile/myorg/enterprise_validation/approve/#{code}"
65   - assert_response :redirect
  64 + post_via_redirect "/myprofile/myorg/enterprise_validation/approve/#{code}"
  65 + assert_response :success
66 66  
67   - follow_redirect!
68 67 assert_equal "/myprofile/myorg/enterprise_validation/view_processed/#{code}", path
69 68 assert_tag :span, :attributes => { :class => 'validation_approved' }
70 69 end
... ...
test/integration/manage_documents_test.rb
... ... @@ -24,11 +24,10 @@ class ManageDocumentsTest &lt; ActionDispatch::IntegrationTest
24 24 assert_tag :tag => 'form', :attributes => { :action => '/myprofile/myuser/cms/new', :method => /post/i }
25 25  
26 26 assert_difference 'Article.count' do
27   - post '/myprofile/myuser/cms/new', :type => 'TinyMceArticle', :article => { :name => 'my article', :body => 'this is the body of ther article'}
  27 + post_via_redirect '/myprofile/myuser/cms/new', :type => 'TinyMceArticle', :article => { :name => 'my article', :body => 'this is the body of ther article'}
28 28 end
29 29  
30   - assert_response :redirect
31   - follow_redirect!
  30 + assert_response :success
32 31 a = Article.find_by_path('my-article')
33 32 assert_equal "/myuser/#{a.slug}", path
34 33 end
... ... @@ -55,14 +54,13 @@ class ManageDocumentsTest &lt; ActionDispatch::IntegrationTest
55 54 assert_tag :tag => 'form', :attributes => { :action => "/myprofile/myuser/cms/edit/#{article.id}", :method => /post/i }
56 55  
57 56 assert_no_difference 'Article.count' do
58   - post "/myprofile/myuser/cms/edit/#{article.id}", :article => { :name => 'my article', :body => 'this is the body of the article'}
  57 + post_via_redirect "/myprofile/myuser/cms/edit/#{article.id}", :article => { :name => 'my article', :body => 'this is the body of the article'}
59 58 end
60 59  
61 60 article.reload
62 61 assert_equal 'this is the body of the article', article.body
63 62  
64   - assert_response :redirect
65   - follow_redirect!
  63 + assert_response :success
66 64 a = Article.find_by_path('my-article')
67 65 assert_equal "/myuser/#{a.slug}", path
68 66 end
... ... @@ -84,10 +82,9 @@ class ManageDocumentsTest &lt; ActionDispatch::IntegrationTest
84 82 assert_response :success
85 83  
86 84 assert_tag tag: 'a', attributes: { href: "/myprofile/myuser/cms/destroy/#{article.id}", 'data-confirm' => /Are you sure/ }
87   - post "/myprofile/myuser/cms/destroy/#{article.id}"
  85 + post_via_redirect "/myprofile/myuser/cms/destroy/#{article.id}"
88 86  
89   - assert_response :redirect
90   - follow_redirect!
  87 + assert_response :success
91 88 assert_equal "/myuser", path
92 89  
93 90 # the article was actually deleted
... ...
test/integration/manage_friendships_test.rb
... ... @@ -24,11 +24,10 @@ class ManageFriendshipsTest &lt; ActionDispatch::IntegrationTest
24 24 get "/myprofile/#{@person.identifier}/friends/remove/#{@friend.id}"
25 25 assert_response :success
26 26  
27   - post "/myprofile/#{@person.identifier}/friends/remove/#{@friend.id}",
  27 + post_via_redirect "/myprofile/#{@person.identifier}/friends/remove/#{@friend.id}",
28 28 :confirmation => '1'
29   - assert_response :redirect
  29 + assert_response :success
30 30  
31   - follow_redirect!
32 31  
33 32 assert assigns(:friends).empty?
34 33 refute @person.is_a_friend?(@friend)
... ...
test/support/integration_test.rb
... ... @@ -14,9 +14,8 @@ class ActionDispatch::IntegrationTest
14 14 def login(username, password)
15 15 ActionDispatch::Integration::Session.any_instance.stubs(:https?).returns(true)
16 16  
17   - post '/account/login', :user => { :login => username, :password => password }
18   - assert_response :redirect
19   - follow_redirect!
  17 + post_via_redirect '/account/login', :user => { :login => username, :password => password }
  18 + assert_response :success
20 19 assert_not_equal '/account/login', path
21 20 end
22 21  
... ...
test/unit/access_control_test.rb 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +
  2 +require_relative "../test_helper"
  3 +
  4 +class AccessControlTest < ActiveSupport::TestCase
  5 +
  6 + include ActsAsAccessor
  7 +
  8 + should 'raise exception if parameter is not a profile' do
  9 + assert_raises(TypeError) { member_relation_of(nil) }
  10 + end
  11 +
  12 + should 'Verify relation among member and community' do
  13 + person = fast_create(Person)
  14 + community = fast_create(Community)
  15 + assert_difference 'person.member_relation_of(community).count', 2 do
  16 + community.add_member(person)
  17 + end
  18 + end
  19 +
  20 + should 'Member does not belong to community' do
  21 + person = fast_create(Person)
  22 + community = fast_create(Community)
  23 + assert_nil person.member_since_date(community)
  24 + end
  25 +
  26 + should 'Verify if enter date of member in community is available' do
  27 + person = fast_create(Person)
  28 + community = fast_create(Community)
  29 + community.add_member(person)
  30 +
  31 + assert_instance_of Date, person.member_since_date(community)
  32 + end
  33 +
  34 +end
... ...
test/unit/action_tracker_notification_test.rb
... ... @@ -89,7 +89,8 @@ class ActionTrackerNotificationTest &lt; ActiveSupport::TestCase
89 89 end
90 90  
91 91 should "have comments through article action_tracker" do
92   - person = create_user.person
  92 + user = User.current = create_user
  93 + person = user.person
93 94 article = create(TextileArticle, :profile_id => person.id)
94 95 process_delayed_job_queue
95 96 notification = ActionTrackerNotification.last
... ...
test/unit/api/articles_test.rb
... ... @@ -39,6 +39,41 @@ class ArticlesTest &lt; ActiveSupport::TestCase
39 39 assert_equal 403, last_response.status
40 40 end
41 41  
  42 + should 'follow a article identified by id' do
  43 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  44 + post "/api/v1/articles/#{article.id}/follow?#{params.to_query}"
  45 + json = JSON.parse(last_response.body)
  46 +
  47 + assert_not_equal 401, last_response.status
  48 + assert_equal true, json['success']
  49 + end
  50 +
  51 + should 'return the followers count of an article' do
  52 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  53 + article.person_followers << @person
  54 +
  55 + get "/api/v1/articles/#{article.id}?#{params.to_query}"
  56 + json = JSON.parse(last_response.body)
  57 +
  58 + assert_equal 200, last_response.status
  59 + assert_equal 1, json['article']['followers_count']
  60 + end
  61 +
  62 + should 'return the followers of a article identified by id' do
  63 + article = fast_create(Article, :profile_id => @person.id, :name => "Some thing")
  64 +
  65 + article_follower = ArticleFollower.new
  66 + article_follower.article = article
  67 + article_follower.person = @person
  68 + article_follower.save!
  69 +
  70 + get "/api/v1/articles/#{article.id}/followers?#{params.to_query}"
  71 + json = JSON.parse(last_response.body)
  72 +
  73 + assert_equal 200, last_response.status
  74 + assert_equal 1, json['total_followers']
  75 + end
  76 +
42 77 should 'list article children' do
43 78 article = create(Article, :profile_id => user.person.id, :name => "Parent")
44 79 child1 = create(Article, :parent_id => article.id, :profile_id => user.person.id, :name => "Some Child")
... ...
test/unit/application_helper_test.rb
... ... @@ -1043,6 +1043,31 @@ class ApplicationHelperTest &lt; ActionView::TestCase
1043 1043 assert_equal c.top_url, top_url
1044 1044 end
1045 1045  
  1046 + should "Extra info with hash" do
  1047 + @plugins = mock
  1048 + @plugins.stubs(:dispatch_first).returns(false)
  1049 + env = Environment.default
  1050 + stubs(:environment).returns(env)
  1051 + stubs(:profile).returns(profile)
  1052 + profile = fast_create(Person, :environment_id => env.id)
  1053 + info = {:value =>_('New'), :class => 'new-profile'}
  1054 + html = profile_image_link(profile, size=:portrait, tag='li', extra_info = info)
  1055 + assert_tag_in_string html, :tag => 'span', :attributes => { :class => 'profile-image new-profile' }
  1056 + assert_tag_in_string html, :tag => 'span', :attributes => { :class => 'extra_info new-profile' }, :content => 'New'
  1057 + end
  1058 +
  1059 + should "Extra info without hash" do
  1060 + @plugins = mock
  1061 + @plugins.stubs(:dispatch_first).returns(false)
  1062 + env = Environment.default
  1063 + stubs(:environment).returns(env)
  1064 + stubs(:profile).returns(profile)
  1065 + profile = fast_create(Person, :environment_id => env.id)
  1066 + info = 'new'
  1067 + html = profile_image_link(profile, size=:portrait, tag='li', extra_info = info)
  1068 + assert_tag_in_string html, :tag => 'span', :attributes => { :class => 'extra_info' }, :content => 'new'
  1069 + end
  1070 +
1046 1071 protected
1047 1072 include NoosferoTestHelper
1048 1073  
... ...
test/unit/article_block_test.rb
... ... @@ -140,6 +140,8 @@ class ArticleBlockTest &lt; ActiveSupport::TestCase
140 140 block.article = file
141 141 block.save!
142 142  
  143 + UploadedFile.any_instance.stubs(:url).returns('myhost.mydomain/path/to/file')
  144 +
143 145 assert_tag_in_string instance_eval(&block.content), :tag => 'a', :content => _('Download')
144 146 end
145 147  
... ...
test/unit/article_test.rb
... ... @@ -7,7 +7,8 @@ class ArticleTest &lt; ActiveSupport::TestCase
7 7  
8 8 def setup
9 9 ActiveSupport::TestCase::setup
10   - @profile = create_user('testing').person
  10 + user = User.current = create_user 'testing'
  11 + @profile = user.person
11 12 end
12 13 attr_reader :profile
13 14  
... ... @@ -21,6 +22,21 @@ class ArticleTest &lt; ActiveSupport::TestCase
21 22 refute a.errors[:profile_id.to_s].present?
22 23 end
23 24  
  25 + should 'keep unique users in list of followers' do
  26 + person1 = create_user('article_owner').person
  27 + person2 = create_user('article_follower').person
  28 +
  29 + article = fast_create(Article, :profile_id => person1.id)
  30 +
  31 + article.person_followers=[person2]
  32 + article.save
  33 + article.reload
  34 + article.person_followers=[person2]
  35 + article.save
  36 +
  37 + assert_equal 1, article.reload.person_followers.size
  38 + end
  39 +
24 40 should 'require value for name' do
25 41 a = Article.new
26 42 a.valid?
... ... @@ -468,14 +484,12 @@ class ArticleTest &lt; ActiveSupport::TestCase
468 484 end
469 485  
470 486 should 'say that logged off user cannot see private article' do
471   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
472 487 article = fast_create(Article, :name => 'test article', :profile_id => profile.id, :published => false)
473 488  
474 489 refute article.display_to?(nil)
475 490 end
476 491  
477 492 should 'say that not member of profile cannot see private article' do
478   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
479 493 article = fast_create(Article, :name => 'test article', :profile_id => profile.id, :published => false)
480 494 person = create_user('test_user').person
481 495  
... ... @@ -483,7 +497,6 @@ class ArticleTest &lt; ActiveSupport::TestCase
483 497 end
484 498  
485 499 should 'say that member user can not see private article' do
486   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
487 500 article = fast_create(Article, :name => 'test article', :profile_id => profile.id, :published => false, :show_to_followers => false)
488 501 person = create_user('test_user').person
489 502 profile.affiliate(person, Profile::Roles.member(profile.environment.id))
... ... @@ -492,25 +505,23 @@ class ArticleTest &lt; ActiveSupport::TestCase
492 505 end
493 506  
494 507 should 'say that profile admin can see private article' do
495   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
  508 + org = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
496 509 article = fast_create(Article, :name => 'test article', :profile_id => profile.id, :published => false)
497   - person = create_user('test_user').person
498   - profile.affiliate(person, Profile::Roles.admin(profile.environment.id))
  510 + org.affiliate(profile, Profile::Roles.admin(org.environment.id))
499 511  
500   - assert article.display_to?(person)
  512 + assert article.display_to?(profile)
501 513 end
502 514  
503 515 should 'say that profile moderator can see private article' do
504   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
505   - article = fast_create(Article, :name => 'test article', :profile_id => profile.id, :published => false)
506   - person = create_user('test_user').person
507   - profile.affiliate(person, Profile::Roles.moderator(profile.environment.id))
  516 + org = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
  517 + article = fast_create(Article, :name => 'test article', :profile_id => org.id, :published => false)
  518 + org.affiliate(profile, Profile::Roles.moderator(org.environment.id))
508 519  
509   - assert article.display_to?(person)
  520 + assert article.display_to?(profile)
510 521 end
511 522  
512 523 should 'show article to non member if article public but profile private' do
513   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile', :public_profile => false)
  524 + profile.update public_profile: false
514 525 article = fast_create(Article, :name => 'test article', :profile_id => profile.id, :published => true)
515 526 person1 = create_user('test_user1').person
516 527 profile.affiliate(person1, Profile::Roles.member(profile.environment.id))
... ... @@ -522,7 +533,6 @@ class ArticleTest &lt; ActiveSupport::TestCase
522 533 end
523 534  
524 535 should 'make new article private if created inside a private folder' do
525   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
526 536 folder = fast_create(Folder, :name => 'my_intranet', :profile_id => profile.id, :published => false)
527 537 article = fast_create(Article, :name => 'my private article', :profile_id => profile.id, :parent_id => folder.id)
528 538  
... ... @@ -530,7 +540,6 @@ class ArticleTest &lt; ActiveSupport::TestCase
530 540 end
531 541  
532 542 should 'save as private' do
533   - profile = fast_create(Profile, :name => 'test profile', :identifier => 'test_profile')
534 543 folder = fast_create(Folder, :name => 'my_intranet', :profile_id => profile.id, :published => false)
535 544 article = fast_create(Article, :name => 'my private article')
536 545 article.profile = profile
... ... @@ -1012,13 +1021,13 @@ class ArticleTest &lt; ActiveSupport::TestCase
1012 1021  
1013 1022 should 'not notify activity by default on create' do
1014 1023 ActionTracker::Record.delete_all
1015   - create Article, :name => 'test', :profile_id => fast_create(Profile).id, :published => true
  1024 + create Article, :name => 'test', :profile_id => profile.id, :published => true
1016 1025 assert_equal 0, ActionTracker::Record.count
1017 1026 end
1018 1027  
1019 1028 should 'not notify activity by default on update' do
1020 1029 ActionTracker::Record.delete_all
1021   - a = create Article, :name => 'bar', :profile_id => fast_create(Profile).id, :published => true
  1030 + a = create Article, :name => 'bar', :profile_id => profile.id, :published => true
1022 1031 a.name = 'foo'
1023 1032 a.save!
1024 1033 assert_equal 0, ActionTracker::Record.count
... ... @@ -1026,13 +1035,13 @@ class ArticleTest &lt; ActiveSupport::TestCase
1026 1035  
1027 1036 should 'not notify activity by default on destroy' do
1028 1037 ActionTracker::Record.delete_all
1029   - a = create Article, :name => 'bar', :profile_id => fast_create(Profile).id, :published => true
  1038 + a = create Article, :name => 'bar', :profile_id => profile.id, :published => true
1030 1039 a.destroy
1031 1040 assert_equal 0, ActionTracker::Record.count
1032 1041 end
1033 1042  
1034 1043 should 'create activity' do
1035   - a = create TextileArticle, :name => 'bar', :profile_id => fast_create(Profile).id, :published => true
  1044 + a = create TextileArticle, :name => 'bar', :profile_id => profile.id, :published => true
1036 1045 a.activity.destroy
1037 1046 assert_nil a.activity
1038 1047  
... ... @@ -1212,10 +1221,9 @@ class ArticleTest &lt; ActiveSupport::TestCase
1212 1221 end
1213 1222  
1214 1223 should 'get article galleries' do
1215   - p = fast_create(Profile)
1216   - a = fast_create(Article, :profile_id => p.id)
1217   - g = fast_create(Gallery, :profile_id => p.id)
1218   - assert_equal [g], p.articles.galleries
  1224 + a = fast_create(Article, :profile_id => profile.id)
  1225 + g = fast_create(Gallery, :profile_id => profile.id)
  1226 + assert_equal [g], profile.articles.galleries
1219 1227 end
1220 1228  
1221 1229 should 'has many translations' do
... ... @@ -1236,7 +1244,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1236 1244 end
1237 1245  
1238 1246 should 'validate inclusion of language' do
1239   - a = build(Article, :profile_id => fast_create(Profile).id)
  1247 + a = build(Article, :profile_id => profile.id)
1240 1248 a.language = '12'
1241 1249 a.valid?
1242 1250 assert a.errors[:language.to_s].present?
... ... @@ -1268,7 +1276,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1268 1276 end
1269 1277  
1270 1278 should 'list possible translations' do
1271   - native_article = fast_create(Article, :language => 'pt', :profile_id => fast_create(Profile).id )
  1279 + native_article = fast_create(Article, :language => 'pt', :profile_id => profile.id )
1272 1280 article_translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id)
1273 1281 possible_translations = native_article.possible_translations
1274 1282 refute possible_translations.include?('en')
... ... @@ -1278,7 +1286,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1278 1286 should 'verify if translation is already in use' do
1279 1287 native_article = fast_create(Article, :language => 'pt')
1280 1288 article_translation = fast_create(Article, :language => 'en', :translation_of_id => native_article.id)
1281   - a = build(Article, :profile => fast_create(Profile))
  1289 + a = build(Article, :profile => profile)
1282 1290 a.language = 'en'
1283 1291 a.translation_of = native_article
1284 1292 a.valid?
... ... @@ -1290,7 +1298,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1290 1298  
1291 1299 should 'verify if native translation is already in use' do
1292 1300 native_article = fast_create(Article, :language => 'pt')
1293   - a = build(Article, :profile => fast_create(Profile))
  1301 + a = build(Article, :profile => profile)
1294 1302 a.language = 'pt'
1295 1303 a.translation_of = native_article
1296 1304 a.valid?
... ... @@ -1302,7 +1310,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1302 1310  
1303 1311 should 'translation have a language' do
1304 1312 native_article = fast_create(Article, :language => 'pt')
1305   - a = build(Article, :profile_id => fast_create(Profile).id)
  1313 + a = build(Article, :profile_id => profile.id)
1306 1314 a.translation_of = native_article
1307 1315 a.valid?
1308 1316 assert a.errors[:language.to_s].present?
... ... @@ -1312,8 +1320,8 @@ class ArticleTest &lt; ActiveSupport::TestCase
1312 1320 end
1313 1321  
1314 1322 should 'native translation have a language' do
1315   - native_article = fast_create(Article, :profile_id => fast_create(Profile).id )
1316   - a = build(Article, :profile_id => fast_create(Profile).id)
  1323 + native_article = fast_create(Article, :profile_id => profile.id )
  1324 + a = build(Article, :profile_id => profile.id)
1317 1325 a.language = 'en'
1318 1326 a.translation_of = native_article
1319 1327 a.valid?
... ... @@ -1378,22 +1386,22 @@ class ArticleTest &lt; ActiveSupport::TestCase
1378 1386 end
1379 1387  
1380 1388 should 'get only non translated articles' do
1381   - p = fast_create(Profile)
1382   - native = fast_create(Article, :language => 'pt', :profile_id => p.id)
1383   - translation = fast_create(Article, :language => 'en', :translation_of_id => native.id, :profile_id => p.id)
1384   - assert_equal [native], p.articles.native_translations
  1389 + profile.articles.delete_all
  1390 + native = fast_create(Article, :language => 'pt', :profile_id => profile.id)
  1391 + translation = fast_create(Article, :language => 'en', :translation_of_id => native.id, :profile_id => profile.id)
  1392 + assert_equal [native], profile.articles.native_translations
1385 1393 end
1386 1394  
1387 1395 should 'not list own language as a possible translation if language has changed' do
1388   - a = build(Article, :language => 'pt', :profile_id => fast_create(Profile).id)
  1396 + a = build(Article, :language => 'pt', :profile_id => profile.id)
1389 1397 refute a.possible_translations.include?('pt')
1390   - a = fast_create(Article, :language => 'pt', :profile_id => fast_create(Profile).id )
  1398 + a = fast_create(Article, :language => 'pt', :profile_id => profile.id )
1391 1399 a.language = 'en'
1392 1400 refute a.possible_translations.include?('en')
1393 1401 end
1394 1402  
1395 1403 should 'list own language as a possible translation if language has not changed' do
1396   - a = fast_create(Article, :language => 'pt', :profile_id => fast_create(Profile).id)
  1404 + a = fast_create(Article, :language => 'pt', :profile_id => profile.id)
1397 1405 assert a.possible_translations.include?('pt')
1398 1406 end
1399 1407  
... ... @@ -1435,7 +1443,6 @@ class ArticleTest &lt; ActiveSupport::TestCase
1435 1443 should 'return only folders' do
1436 1444 not_folders = [RssFeed, TinyMceArticle, Event, TextileArticle]
1437 1445 folders = [Folder, Blog, Gallery, Forum]
1438   - profile = fast_create(Profile)
1439 1446  
1440 1447 not_folders.each do |klass|
1441 1448 item = fast_create(klass)
... ... @@ -1451,7 +1458,6 @@ class ArticleTest &lt; ActiveSupport::TestCase
1451 1458 should 'return no folders' do
1452 1459 not_folders = [RssFeed, TinyMceArticle, Event, TextileArticle]
1453 1460 folders = [Folder, Blog, Gallery, Forum]
1454   - profile = fast_create(Profile)
1455 1461  
1456 1462 not_folders.each do |klass|
1457 1463 item = fast_create(klass)
... ... @@ -1611,18 +1617,16 @@ class ArticleTest &lt; ActiveSupport::TestCase
1611 1617 end
1612 1618  
1613 1619 should 'delegate region info to profile' do
1614   - profile = fast_create(Profile)
1615   - Profile.any_instance.expects(:region)
1616   - Profile.any_instance.expects(:region_id)
  1620 + Person.any_instance.expects(:region)
  1621 + Person.any_instance.expects(:region_id)
1617 1622 article = fast_create(Article, :profile_id => profile.id)
1618 1623 article.region
1619 1624 article.region_id
1620 1625 end
1621 1626  
1622 1627 should 'delegate environment info to profile' do
1623   - profile = fast_create(Profile)
1624   - Profile.any_instance.expects(:environment)
1625   - Profile.any_instance.expects(:environment_id)
  1628 + Person.any_instance.expects(:environment)
  1629 + Person.any_instance.expects(:environment_id)
1626 1630 article = fast_create(Article, :profile_id => profile.id)
1627 1631 article.environment
1628 1632 article.environment_id
... ... @@ -1711,7 +1715,7 @@ class ArticleTest &lt; ActiveSupport::TestCase
1711 1715  
1712 1716 should 'has a empty list of followers by default' do
1713 1717 a = Article.new
1714   - assert_equal [], a.followers
  1718 + assert_equal [], a.person_followers
1715 1719 end
1716 1720  
1717 1721 should 'get first image from lead' do
... ... @@ -2229,6 +2233,14 @@ class ArticleTest &lt; ActiveSupport::TestCase
2229 2233 end
2230 2234 end
2231 2235  
  2236 + should 'be able to vote in an article without a user' do
  2237 + article = create(Article, :name => 'Test', :profile => profile, :last_changed_by => nil)
  2238 + assert_difference 'article.votes_for', 2 do
  2239 + Vote.create!(:voteable => article, :vote => 1)
  2240 + Vote.create!(:voteable => article, :vote => 1)
  2241 + end
  2242 + end
  2243 +
2232 2244 should 'have can_display_media_panel with default false' do
2233 2245 a = Article.new
2234 2246 assert !a.can_display_media_panel?
... ... @@ -2278,4 +2290,15 @@ class ArticleTest &lt; ActiveSupport::TestCase
2278 2290 assert_match 'Parent folder is archived', err.message
2279 2291 end
2280 2292  
  2293 + should 'return full_path' do
  2294 + p1 = fast_create(Profile)
  2295 + p2 = fast_create(Profile)
  2296 + p2.domains << Domain.create!(:name => 'p2.domain')
  2297 + a1 = fast_create(Article, :profile_id => p1.id)
  2298 + a2 = fast_create(Article, :profile_id => p2.id)
  2299 +
  2300 + assert_equal "/#{p1.identifier}/#{a1.path}", a1.full_path
  2301 + assert_equal "/#{a2.path}", a2.full_path
  2302 + end
  2303 +
2281 2304 end
... ...
test/unit/blog_helper_test.rb
... ... @@ -101,11 +101,9 @@ class BlogHelperTest &lt; ActionView::TestCase
101 101  
102 102 should 'display link to file if post is an uploaded_file' do
103 103 file = create(UploadedFile, :uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :profile => profile, :published => true, :parent => blog)
104   -
105 104 result = display_post(file)
106   - assert_tag_in_string result, :tag => 'a',
107   - :attributes => { :href => file.public_filename },
108   - :content => _('Download')
  105 +
  106 + assert_tag_in_string result, :tag => 'a', :content => _('Download')
109 107 end
110 108  
111 109 should 'display image if post is an image' do
... ...
test/unit/boxes_helper_test.rb
... ... @@ -187,6 +187,7 @@ class BoxesHelperTest &lt; ActionView::TestCase
187 187 block = Block.create!(:box => box)
188 188 block.stubs(:embedable?).returns(true)
189 189 stubs(:url_for).returns('')
  190 + @controller.stubs(:user).returns(box.owner)
190 191 assert_tag_in_string block_edit_buttons(block), :tag => 'a', :attributes => {:class => 'button icon-button icon-embed '}
191 192 end
192 193  
... ... @@ -195,6 +196,7 @@ class BoxesHelperTest &lt; ActionView::TestCase
195 196 block = Block.create!(:box => box)
196 197 block.stubs(:embedable?).returns(false)
197 198 stubs(:url_for).returns('')
  199 + @controller.stubs(:user).returns(box.owner)
198 200 assert_no_tag_in_string block_edit_buttons(block), :tag => 'a', :attributes => {:class => 'button icon-button icon-embed '}
199 201 end
200 202  
... ...
test/unit/comment_notifier_test.rb
... ... @@ -60,7 +60,7 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
60 60 should "deliver mail to followers" do
61 61 author = create_user('follower_author').person
62 62 follower = create_user('follower').person
63   - @article.followers += [follower.email]
  63 + @article.person_followers += [follower]
64 64 @article.save!
65 65 create_comment_and_notify(:source => @article, :author => author, :title => 'comment title', :body => 'comment body')
66 66 assert_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email
... ...
test/unit/comment_test.rb
... ... @@ -339,34 +339,27 @@ class CommentTest &lt; ActiveSupport::TestCase
339 339 assert c.rejected?
340 340 end
341 341  
342   - should 'subscribe user as follower of an article on new comment' do
  342 + should 'not subscribe user as follower of an article automatically on new comment' do
343 343 owner = create_user('owner_of_article').person
344 344 person = create_user('follower').person
345 345 article = fast_create(Article, :profile_id => owner.id)
346   - assert_not_includes article.followers, person.email
  346 + assert_not_includes article.person_followers, person
347 347 create(Comment, :source => article, :author => person, :title => 'new comment', :body => 'new comment')
348   - assert_includes article.reload.followers, person.email
  348 + assert_not_includes article.reload.person_followers, person
349 349 end
350 350  
351   - should 'subscribe guest user as follower of an article on new comment' do
  351 + should 'not subscribe guest user as follower of an article on new comment' do
352 352 article = fast_create(Article, :profile_id => create_user('article_owner').person.id)
353   - assert_not_includes article.followers, 'follower@example.com'
  353 + old_num_followers = article.reload.person_followers.size
354 354 create(Comment, :source => article, :name => 'follower', :email => 'follower@example.com', :title => 'new comment', :body => 'new comment')
355   - assert_includes article.reload.followers, 'follower@example.com'
356   - end
357   -
358   - should 'keep unique emails in list of followers' do
359   - article = fast_create(Article, :profile_id => create_user('article_owner').person.id)
360   - create(Comment, :source => article, :name => 'follower one', :email => 'follower@example.com', :title => 'new comment', :body => 'new comment')
361   - create(Comment, :source => article, :name => 'follower two', :email => 'follower@example.com', :title => 'another comment', :body => 'new comment')
362   - assert_equal 1, article.reload.followers.select{|v| v == 'follower@example.com'}.count
  355 + assert_equal old_num_followers, article.reload.person_followers.size
363 356 end
364 357  
365 358 should 'not subscribe owner as follower of an article on new comment' do
366 359 owner = create_user('owner_of_article').person
367 360 article = fast_create(Article, :profile_id => owner.id)
368 361 create(Comment, :source => article, :author => owner, :title => 'new comment', :body => 'new comment')
369   - assert_not_includes article.reload.followers, owner.email
  362 + assert_not_includes article.reload.person_followers, owner
370 363 end
371 364  
372 365 should 'not subscribe admins as follower of an article on new comment' do
... ... @@ -377,8 +370,13 @@ class CommentTest &lt; ActiveSupport::TestCase
377 370 article = fast_create(Article, :profile_id => owner.id)
378 371 create(Comment, :source => article, :author => follower, :title => 'new comment', :body => 'new comment')
379 372 create(Comment, :source => article, :author => admin, :title => 'new comment', :body => 'new comment')
380   - assert_not_includes article.reload.followers, admin.email
381   - assert_includes article.followers, follower.email
  373 +
  374 + article.person_followers += [follower]
  375 + article.save!
  376 + article.reload
  377 +
  378 + assert_not_includes article.reload.person_followers, admin
  379 + assert_includes article.reload.person_followers, follower
382 380 end
383 381  
384 382 should 'update article activity when add a comment' do
... ...
test/unit/highlights_block_test.rb
... ... @@ -54,7 +54,19 @@ class HighlightsBlockTest &lt; ActiveSupport::TestCase
54 54 should 'remove images with blank fields' do
55 55 h = HighlightsBlock.new(:images => [{:image_id => 1, :address => '/address', :position => 1, :title => 'address'}, {:image_id => '', :address => '', :position => '', :title => ''}])
56 56 h.save!
57   - assert_equal [{:image_id => 1, :address => '/address', :position => 1, :title => 'address', :image_src => nil}], h.images
  57 + assert_equal [{:image_id => 1, :address => '/address', :position => 1, :title => 'address', :new_window => false, :image_src => nil}], h.images
  58 + end
  59 +
  60 + should 'replace 1 and 0 by true and false in new_window attribute' do
  61 + image1 = {:image_id => 1, :address => '/address-1', :position => 1, :title => 'address-1', :new_window => '0'}
  62 + image2 = {:image_id => 2, :address => '/address-2', :position => 2, :title => 'address-2', :new_window => '1'}
  63 + h = HighlightsBlock.new(:images => [image1, image2])
  64 + h.save!
  65 + image1[:new_window] = false
  66 + image1[:image_src] = nil
  67 + image2[:new_window] = true
  68 + image2[:image_src] = nil
  69 + assert_equivalent [image1, image2], h.images
58 70 end
59 71  
60 72 should 'be able to update display setting' do
... ... @@ -84,7 +96,7 @@ class HighlightsBlockTest &lt; ActiveSupport::TestCase
84 96 block.save!
85 97 block.reload
86 98 assert_equal 2, block.images.count
87   - assert_equal [{:image_id => 1, :address => '/address', :position => 1, :title => 'address', :image_src => 'address'}], block.featured_images
  99 + assert_equal [{:image_id => 1, :address => '/address', :position => 1, :title => 'address', :new_window => false, :image_src => 'address'}], block.featured_images
88 100 end
89 101  
90 102 should 'list images in order' do
... ...
test/unit/noosfero_test.rb
... ... @@ -21,6 +21,7 @@ class NoosferoTest &lt; ActiveSupport::TestCase
21 21 should 'support setting default locale' do
22 22 Noosfero.default_locale = 'pt_BR'
23 23 assert_equal 'pt_BR', Noosfero.default_locale
  24 + Noosfero.default_locale = nil
24 25 end
25 26  
26 27 should 'identifier format' do
... ...
test/unit/organization_mailing_test.rb
... ... @@ -98,6 +98,11 @@ class OrganizationMailingTest &lt; ActiveSupport::TestCase
98 98 assert_equal [Person['user_one'], Person['user_two']], mailing.recipients
99 99 end
100 100  
  101 + should 'return recipients previously filtered' do
  102 + mailing = create(OrganizationMailing, :source => community, :subject => 'Hello', :body => 'We have some news', :person => person, :data => {:members_filtered => [Person['user_one'].id,Person['user_two'].id]})
  103 + assert_equivalent [Person['user_one'], Person['user_two']], mailing.recipients
  104 + end
  105 +
101 106 should 'return recipients according to limit' do
102 107 mailing = create(OrganizationMailing, :source => community, :subject => 'Hello', :body => 'We have some news', :person => person)
103 108 assert_equal [Person['user_one']], mailing.recipients(0, 1)
... ...
test/unit/person_notifier_test.rb
... ... @@ -49,7 +49,8 @@ class PersonNotifierTest &lt; ActiveSupport::TestCase
49 49 should 'display author name in delivered mail' do
50 50 @community.add_member(@member)
51 51 User.current = @admin.user
52   - Comment.create!(:author => @admin, :title => 'test comment', :body => 'body!', :source => @article)
  52 + comment = Comment.create!(:author => @admin, :title => 'test comment', :body => 'body!', :source => @article)
  53 + comment.save!
53 54 process_delayed_job_queue
54 55 notify
55 56 sent = ActionMailer::Base.deliveries.last
... ...
test/unit/person_test.rb
... ... @@ -1845,4 +1845,100 @@ class PersonTest &lt; ActiveSupport::TestCase
1845 1845 assert_equivalent [c1,c3], p1.comments
1846 1846 end
1847 1847  
  1848 + should 'get people of one community by moderator role' do
  1849 + community = fast_create(Community)
  1850 + p1 = fast_create(Person)
  1851 + p2 = fast_create(Person)
  1852 +
  1853 + community.add_member p1
  1854 + community.add_moderator p2
  1855 +
  1856 + assert_equivalent [p2], Person.with_role(Profile::Roles.moderator(community.environment.id).id)
  1857 + end
  1858 +
  1859 + should 'get people of one community by admin role' do
  1860 + community = fast_create(Community)
  1861 + p1 = fast_create(Person)
  1862 + p2 = fast_create(Person)
  1863 +
  1864 + community.add_admin p1
  1865 + community.add_member p2
  1866 +
  1867 + assert_equivalent [p1], Person.with_role(Profile::Roles.admin(community.environment.id).id)
  1868 + end
  1869 +
  1870 + should 'get people with admin role of any community' do
  1871 + c1 = fast_create(Community)
  1872 + p1 = fast_create(Person)
  1873 + p2 = fast_create(Person)
  1874 + c1.add_admin p1
  1875 + c1.add_member p2
  1876 +
  1877 + c2 = fast_create(Community)
  1878 + p3 = fast_create(Person)
  1879 + p4 = fast_create(Person)
  1880 +
  1881 + c2.add_admin p4
  1882 + c2.add_member p3
  1883 +
  1884 + assert_equivalent [p1, p4], Person.with_role(Profile::Roles.admin(c1.environment.id).id)
  1885 + end
  1886 +
  1887 + should 'get distinct people with moderator role of any community' do
  1888 + c1 = fast_create(Community)
  1889 + p1 = fast_create(Person)
  1890 + p2 = fast_create(Person)
  1891 + c1.add_member p1
  1892 + c1.add_moderator p2
  1893 +
  1894 + c2 = fast_create(Community)
  1895 + p3 = fast_create(Person)
  1896 + p4 = fast_create(Person)
  1897 +
  1898 + c2.add_member p4
  1899 + c2.add_moderator p3
  1900 + c2.add_moderator p2
  1901 +
  1902 + assert_equivalent [p2, p3], Person.with_role(Profile::Roles.moderator(c1.environment.id).id)
  1903 + end
  1904 +
  1905 + should 'count members of a community collected by moderator' do
  1906 + c1 = fast_create(Community)
  1907 + p1 = fast_create(Person)
  1908 + p2 = fast_create(Person)
  1909 + p3 = fast_create(Person)
  1910 + c1.add_member p1
  1911 + c1.add_moderator p2
  1912 + c1.add_member p3
  1913 +
  1914 + assert_equal 1, c1.members.with_role(Profile::Roles.moderator(c1.environment.id).id).count
  1915 + end
  1916 +
  1917 + should 'count people of any community collected by moderator' do
  1918 + c1 = fast_create(Community)
  1919 + p1 = fast_create(Person)
  1920 + p2 = fast_create(Person)
  1921 + c1.add_member p1
  1922 + c1.add_moderator p2
  1923 +
  1924 + c2 = fast_create(Community)
  1925 + p3 = fast_create(Person)
  1926 + p4 = fast_create(Person)
  1927 +
  1928 + c2.add_member p4
  1929 + c2.add_moderator p3
  1930 + c2.add_moderator p2
  1931 +
  1932 + assert_equal 2, Person.with_role(Profile::Roles.moderator(c1.environment.id).id).count
  1933 + end
  1934 +
  1935 + should 'check if a person is added like a member of a community today' do
  1936 + person = create_user('person').person
  1937 + community = fast_create(Community)
  1938 +
  1939 + community.add_member person
  1940 +
  1941 + assert !person.member_relation_of(community).empty?, "Person '#{person.identifier}' is not a member of Community '#{community.identifier}'"
  1942 + assert person.member_since_date(community) == Date.today,"Person '#{person.identifier}' is not added like a member of Community '#{community.identifier}' today"
  1943 + end
1848 1944 end
... ...
test/unit/profile_test.rb
... ... @@ -1816,6 +1816,21 @@ class ProfileTest &lt; ActiveSupport::TestCase
1816 1816 assert_equal [person], community.members
1817 1817 end
1818 1818  
  1819 + should 'return a list members by email of a community' do
  1820 + someone = create_user('Someone', email:'someone@test.com.br')
  1821 + someperson = create_user('Someperson',email:'someperson@test.com.br')
  1822 +
  1823 + community = fast_create(Community)
  1824 + community.add_member(someone.person)
  1825 + community.add_member(someperson.person)
  1826 +
  1827 + result = community.members_like 'email', '@test.com.br'
  1828 +
  1829 + assert_includes result, someone.person
  1830 + assert_includes result, someperson.person
  1831 +
  1832 + end
  1833 +
1819 1834 should 'count unique members of a community' do
1820 1835 person = fast_create(Person)
1821 1836 community = fast_create(Community)
... ...
test/unit/raw_html_block_test.rb
... ... @@ -22,4 +22,20 @@ class RawHTMLBlockTest &lt; ActiveSupport::TestCase
22 22 assert_match(/HTML$/, block.content)
23 23 end
24 24  
  25 + should 'not be editable for users without permission' do
  26 + environment = Environment.default
  27 + box = Box.new(:owner => environment)
  28 + block = RawHTMLBlock.new(:html => "HTML", :box => box)
  29 + user = create_user('testuser').person
  30 + assert !block.editable?(user)
  31 + end
  32 +
  33 + should 'be editable for users with permission' do
  34 + environment = Environment.default
  35 + box = Box.new(:owner => environment)
  36 + block = RawHTMLBlock.new(:html => "HTML", :box => box)
  37 + user = create_user_with_permission('testuser', 'edit_raw_html_block', environment)
  38 + assert block.editable?(user)
  39 + end
  40 +
25 41 end
... ...
test/unit/uploaded_file_test.rb
... ... @@ -357,4 +357,25 @@ class UploadedFileTest &lt; ActiveSupport::TestCase
357 357 assert_instance_of Fixnum, UploadedFile.max_size
358 358 end
359 359  
  360 + should 'add file to dbm if it becomes private' do
  361 + require 'sdbm'
  362 + public_file = create(UploadedFile, :uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :profile => profile, :published => true)
  363 + private_file = create(UploadedFile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => profile, :published => false)
  364 +
  365 + dbm = SDBM.open(UploadedFile::DBM_PRIVATE_FILE)
  366 + assert !dbm.has_key?(public_file.public_filename)
  367 + assert dbm.has_key?(private_file.public_filename)
  368 + dbm.close
  369 +
  370 + public_file.published = false
  371 + public_file.save!
  372 + private_file.published = true
  373 + private_file.save!
  374 +
  375 + dbm = SDBM.open(UploadedFile::DBM_PRIVATE_FILE)
  376 + assert dbm.has_key?(public_file.public_filename)
  377 + assert !dbm.has_key?(private_file.public_filename)
  378 + dbm.close
  379 + end
  380 +
360 381 end
... ...
util/chat/apache/xmpp.conf
... ... @@ -1,11 +0,0 @@
1   -# If your XMPP XMPP/BOSH isn't in localhost, change the config below to correct
2   -# point to address
3   -
4   - RewriteEngine On
5   - RewriteRule /http-bind http://localhost:5280/http-bind [P,QSA,L]
6   - <Proxy http://localhost:5280/http-bind>
7   - Order Allow,Deny
8   - Allow from All
9   - </Proxy>
10   -
11   -# vim: ft=apache
vendor/plugins/access_control/lib/acts_as_accessible.rb
... ... @@ -19,9 +19,9 @@ module ActsAsAccessible
19 19 nil
20 20 end
21 21  
22   - def affiliate(accessor, roles)
  22 + def affiliate(accessor, roles, attributes = {})
23 23 roles = Array(roles)
24   - roles.map {|role| accessor.add_role(role, self)}.any?
  24 + roles.map {|role| accessor.add_role(role, self, attributes)}.any?
25 25 end
26 26  
27 27 def disaffiliate(accessor, roles)
... ...
vendor/plugins/access_control/lib/acts_as_accessor.rb
... ... @@ -21,9 +21,9 @@ module ActsAsAccessor
21 21 (actual_roles - roles).each {|r| remove_role(r, resource)}
22 22 end
23 23  
24   - def add_role(role, resource)
25   - attributes = role_attributes(role, resource)
26   - if RoleAssignment.where(attributes).empty?
  24 + def add_role(role, resource, attributes = {})
  25 + attributes = role_attributes(role, resource).merge attributes
  26 + if RoleAssignment.find(:all, :conditions => attributes).empty?
27 27 ra = RoleAssignment.new(attributes)
28 28 role_assignments << ra
29 29 resource.role_assignments << ra
... ... @@ -44,6 +44,19 @@ module ActsAsAccessor
44 44 RoleAssignment.where(role_attributes nil, res)
45 45 end
46 46  
  47 + def member_relation_of(profile)
  48 + raise TypeError, "Expected instance of 'Profile' class, but '#{profile.class.name}' was founded" unless profile.is_a? Profile
  49 +
  50 + role_assignments.where(resource_id: profile.id)
  51 + end
  52 +
  53 + def member_since_date(profile)
  54 + result = member_relation_of(profile).to_a
  55 + unless result.empty?
  56 + result.last.created_at ? result.last.created_at.to_date : Date.yesterday
  57 + end
  58 + end
  59 +
47 60 protected
48 61 def role_attributes(role, resource)
49 62 attributes = {:accessor_id => self.id, :accessor_type => self.class.base_class.name}
... ...
vendor/plugins/access_control/lib/role_assignment.rb
1 1 class RoleAssignment < ActiveRecord::Base
2 2  
3   - attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type
  3 + attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type, :created_at
4 4  
5 5 belongs_to :role
6 6 belongs_to :accessor, :polymorphic => true
... ...