Commit d6a6f2a408ce2bbe17c4fa302f37a62d07b5f1f5

Authored by Rodrigo Souto
2 parents 9f3246db 18ac7d64
Exists in staging

Merge branch 'private-notifications' into 'master'

profile-activities: generate and filter private notifications

Notifications of private profiles or provite contents were not being
generated. This patch generates these notifications and filter
activities being displayed to the user according to her/his permissions.

Making this work through pure sql is just too complex. It was needed to
filter results after pagination, for performance reasons, and perform
some workarounds to not mess with the paginated results.

See merge request !1009
app/controllers/public/profile_controller.rb
@@ -13,17 +13,13 @@ class ProfileController < PublicController @@ -13,17 +13,13 @@ class ProfileController < PublicController
13 13
14 protect 'send_mail_to_members', :profile, :only => [:send_mail] 14 protect 'send_mail_to_members', :profile, :only => [:send_mail]
15 15
16 - def index  
17 - @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) : []  
18 - if logged_in? && current_person.follows?(@profile)  
19 - @network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) if @network_activities.empty?  
20 - @activities = @profile.activities.paginate(:per_page => 15, :page => params[:page])  
21 - end  
22 -  
23 - # TODO Find a way to filter these through sql  
24 - @network_activities = filter_private_scraps(@network_activities)  
25 - @activities = filter_private_scraps(@activities) 16 + ACTIVITIES_PER_PAGE = 15
26 17
  18 + def index
  19 + @offsets = {:wall => 0, :network => 0}
  20 + page = (params[:page] || 1).to_i
  21 + @network_activities = loop_fetch_activities(@profile.tracked_notifications, :network, page) if !@profile.is_a?(Person) || follow_profile?
  22 + @activities = loop_fetch_activities(@profile.activities, :wall, page) if follow_profile?
27 @tags = profile.article_tags 23 @tags = profile.article_tags
28 allow_access_to_page 24 allow_access_to_page
29 end 25 end
@@ -255,7 +251,7 @@ class ProfileController < PublicController @@ -255,7 +251,7 @@ class ProfileController < PublicController
255 render :partial => 'profile_activities_list', :locals => {:activities => activities} 251 render :partial => 'profile_activities_list', :locals => {:activities => activities}
256 else 252 else
257 network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) 253 network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page])
258 - render :partial => 'profile_network_activities', :locals => {:network_activities => network_activities} 254 + render :partial => 'profile_network_activities', :locals => {:activities => network_activities}
259 end 255 end
260 end 256 end
261 257
@@ -267,14 +263,32 @@ class ProfileController < PublicController @@ -267,14 +263,32 @@ class ProfileController < PublicController
267 render :text => prepare_to_token_input_by_class(result).to_json 263 render :text => prepare_to_token_input_by_class(result).to_json
268 end 264 end
269 265
270 - def view_more_activities  
271 - @activities = @profile.activities.paginate(:per_page => 10, :page => params[:page])  
272 - render :partial => 'profile_activities_list', :locals => {:activities => @activities} 266 + def loop_fetch_activities(base_activities, kind, page)
  267 + activities = nil
  268 + while activities.nil? || (activities.empty? && page <= activities.total_pages)
  269 + activities = base_activities.offset(@offsets[kind.to_sym]).paginate(:per_page => ACTIVITIES_PER_PAGE, :page => page)
  270 + activities = filter_activities(activities, kind.to_sym)
  271 + page += 1
  272 + end
  273 + activities
273 end 274 end
274 275
275 - def view_more_network_activities  
276 - @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page])  
277 - render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} 276 + def view_more_activities
  277 + @activities = nil
  278 + @offsets = params[:offsets]
  279 + page = (params[:page] || 1).to_i
  280 + kind = params[:kind]
  281 +
  282 + if kind == 'wall'
  283 + base_activities = @profile.activities
  284 + partial = 'profile_activities_list'
  285 + else
  286 + base_activities = @profile.tracked_notifications
  287 + partial = 'profile_network_activities'
  288 + end
  289 +
  290 + @activities = loop_fetch_activities(base_activities, kind, page)
  291 + render :partial => partial, :locals => {:activities => @activities}
278 end 292 end
279 293
280 def more_comments 294 def more_comments
@@ -510,22 +524,22 @@ class ProfileController &lt; PublicController @@ -510,22 +524,22 @@ class ProfileController &lt; PublicController
510 followed.uniq 524 followed.uniq
511 end 525 end
512 526
513 - def filter_private_scraps(activities) 527 + def filter_activities(activities, kind)
  528 + @offsets ||= {:wall => 0, :network => 0}
  529 + return activities if environment.admins.include?(user)
514 activities = Array(activities) 530 activities = Array(activities)
515 - activities.delete_if do |item|  
516 - if item.kind_of?(ProfileActivity)  
517 - target = item.activity  
518 - owner = profile  
519 - else  
520 - target = item.target  
521 - owner = item.user  
522 - end  
523 - !environment.admins.include?(user) &&  
524 - owner != user &&  
525 - target.is_a?(Scrap) &&  
526 - target.marked_people.present? &&  
527 - !target.marked_people.include?(user)  
528 - end 531 + initial_count = activities.count
  532 + activities.delete_if do |activity|
  533 + activity = ActivityPresenter.for(activity)
  534 + next if activity.involved?(user)
  535 + activity.hidden_for?(user)
  536 + end
  537 + @offsets[kind] = @offsets[kind].to_i
  538 + @offsets[kind] += initial_count - activities.count
529 activities 539 activities
530 end 540 end
  541 +
  542 + def follow_profile?
  543 + logged_in? && current_person.follows?(@profile)
  544 + end
531 end 545 end
app/jobs/notify_activity_to_profiles_job.rb
@@ -11,7 +11,7 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id) @@ -11,7 +11,7 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id)
11 tracked_action = ActionTracker::Record.find(tracked_action_id) 11 tracked_action = ActionTracker::Record.find(tracked_action_id)
12 return unless tracked_action.user.present? 12 return unless tracked_action.user.present?
13 target = tracked_action.target 13 target = tracked_action.target
14 - if target.is_a?(Community) && ( NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb) || ! target.public_profile ) 14 + if target.is_a?(Community) && NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb)
15 ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id) 15 ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id)
16 return 16 return
17 end 17 end
app/models/article.rb
@@ -63,7 +63,7 @@ class Article &lt; ApplicationRecord @@ -63,7 +63,7 @@ class Article &lt; ApplicationRecord
63 _('Content') 63 _('Content')
64 end 64 end
65 65
66 - track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? } 66 + track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.notifiable? }
67 67
68 # xss_terminate plugin can't sanitize array fields 68 # xss_terminate plugin can't sanitize array fields
69 # sanitize_tag_list is used with SanitizeHelper 69 # sanitize_tag_list is used with SanitizeHelper
@@ -183,10 +183,6 @@ class Article &lt; ApplicationRecord @@ -183,10 +183,6 @@ class Article &lt; ApplicationRecord
183 end 183 end
184 end 184 end
185 185
186 - def is_trackable?  
187 - self.published? && self.notifiable? && self.advertise? && self.profile.public_profile  
188 - end  
189 -  
190 def external_link=(link) 186 def external_link=(link)
191 if !link.blank? && link !~ /^[a-z]+:\/\//i 187 if !link.blank? && link !~ /^[a-z]+:\/\//i
192 link = 'http://' + link 188 link = 'http://' + link
@@ -845,7 +841,7 @@ class Article &lt; ApplicationRecord @@ -845,7 +841,7 @@ class Article &lt; ApplicationRecord
845 end 841 end
846 842
847 def create_activity 843 def create_activity
848 - if is_trackable? && !image? 844 + if notifiable? && !image?
849 save_action_for_verb 'create_article', [:name, :url, :lead, :first_image], Proc.new{}, :author 845 save_action_for_verb 'create_article', [:name, :url, :lead, :first_image], Proc.new{}, :author
850 end 846 end
851 end 847 end
app/models/favorite_enterprise_person.rb
@@ -2,7 +2,7 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord @@ -2,7 +2,7 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord
2 2
3 attr_accessible :person, :enterprise 3 attr_accessible :person, :enterprise
4 4
5 - track_actions :favorite_enterprise, :after_create, keep_params: [:enterprise_name, :enterprise_url], if: proc{ |f| f.is_trackable? } 5 + track_actions :favorite_enterprise, :after_create, keep_params: [:enterprise_name, :enterprise_url], if: proc{ |f| f.notifiable? }
6 6
7 belongs_to :enterprise 7 belongs_to :enterprise
8 belongs_to :person 8 belongs_to :person
@@ -13,7 +13,7 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord @@ -13,7 +13,7 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord
13 13
14 protected 14 protected
15 15
16 - def is_trackable? 16 + def notifiable?
17 self.enterprise.public? 17 self.enterprise.public?
18 end 18 end
19 19
app/models/profile.rb
@@ -824,13 +824,14 @@ private :generate_url, :url_options @@ -824,13 +824,14 @@ private :generate_url, :url_options
824 824
825 # returns +true+ if the given +user+ can see profile information about this 825 # returns +true+ if the given +user+ can see profile information about this
826 # +profile+, and +false+ otherwise. 826 # +profile+, and +false+ otherwise.
827 - def display_info_to?(user) 827 + def display_info_to?(user = nil)
828 if self.public? 828 if self.public?
829 true 829 true
830 else 830 else
831 display_private_info_to?(user) 831 display_private_info_to?(user)
832 end 832 end
833 end 833 end
  834 + alias_method :display_to?, :display_info_to?
834 835
835 after_save :update_category_from_region 836 after_save :update_category_from_region
836 def update_category_from_region 837 def update_category_from_region
app/models/scrap.rb
@@ -67,6 +67,10 @@ class Scrap &lt; ApplicationRecord @@ -67,6 +67,10 @@ class Scrap &lt; ApplicationRecord
67 sender != receiver && (is_root? ? root.receiver.receives_scrap_notification? : receiver.receives_scrap_notification?) 67 sender != receiver && (is_root? ? root.receiver.receives_scrap_notification? : receiver.receives_scrap_notification?)
68 end 68 end
69 69
  70 + def display_to?(user = nil)
  71 + marked_people.blank? || marked_people.include?(user)
  72 + end
  73 +
70 protected 74 protected
71 75
72 def create_activity 76 def create_activity
app/models/uploaded_file.rb
@@ -190,8 +190,12 @@ class UploadedFile &lt; Article @@ -190,8 +190,12 @@ class UploadedFile &lt; Article
190 true 190 true
191 end 191 end
192 192
  193 + def image?
  194 + mime_type =~ /^image\//
  195 + end
  196 +
193 def notifiable? 197 def notifiable?
194 - true 198 + !image?
195 end 199 end
196 200
197 end 201 end
app/presenters/activity/generic.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class ActivityPresenter::Generic < ActivityPresenter
  2 + def self.accepts?(instance)
  3 + 1
  4 + end
  5 +end
app/presenters/activity_presenter.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +class ActivityPresenter < Presenter
  2 + def self.base_class
  3 + ActionTracker::Record
  4 + end
  5 +
  6 + def self.available?(instance)
  7 + instance.kind_of?(ActionTracker::Record) || instance.kind_of?(ProfileActivity)
  8 + end
  9 +
  10 + def self.target(instance)
  11 + if instance.kind_of?(ProfileActivity)
  12 + target(instance.activity)
  13 + elsif instance.kind_of?(ActionTracker::Record)
  14 + instance.target
  15 + else
  16 + instance
  17 + end
  18 + end
  19 +
  20 + def self.owner(instance)
  21 + instance.kind_of?(ProfileActivity) ? instance.profile : instance.user
  22 + end
  23 +
  24 + def target
  25 + self.class.target(encapsulated_instance)
  26 + end
  27 +
  28 + def owner
  29 + self.class.owner(encapsulated_instance)
  30 + end
  31 +
  32 + def hidden_for?(user)
  33 + target.respond_to?(:display_to?) && !target.display_to?(user)
  34 + end
  35 +
  36 + def involved?(user)
  37 + owner == user || target == user
  38 + end
  39 +end
  40 +
  41 +# Preload ActivityPresenter subclasses to allow `Presenter.for()` to work
  42 +Dir.glob(File.join('app', 'presenters', 'activity', '*.rb')) do |file|
  43 + load file
  44 +end
app/presenters/file/generic.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +# Made to encapsulate any UploadedFile
  2 +class FilePresenter::Generic < FilePresenter
  3 + # if returns low priority, because it is generic.
  4 + def self.accepts?(f)
  5 + 1 if f.is_a? UploadedFile
  6 + end
  7 +end
app/presenters/file/image.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +class FilePresenter::Image < FilePresenter
  2 + def self.accepts?(f)
  3 + return nil unless f.respond_to? :image?
  4 + f.image? ? 10 : nil
  5 + end
  6 +
  7 + def sized_icon(size)
  8 + public_filename size
  9 + end
  10 +
  11 + def icon_name
  12 + public_filename :icon
  13 + end
  14 +
  15 + def short_description
  16 + _('Image (%s)') % content_type.split('/')[1].upcase
  17 + end
  18 +
  19 + #Overwriting method from FilePresenter to allow download of images
  20 + def download?(view = nil)
  21 + view.blank? || view == 'false'
  22 + end
  23 +end
app/presenters/file_presenter.rb 0 → 100644
@@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
  1 +class FilePresenter < Presenter
  2 + def self.base_class
  3 + Article
  4 + end
  5 +
  6 + def self.available?(instance)
  7 + instance.kind_of?(UploadedFile) && !instance.kind_of?(Image)
  8 + end
  9 +
  10 + def download? view = nil
  11 + view.blank?
  12 + end
  13 +
  14 + def short_description
  15 + file_type = if content_type.present?
  16 + content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '')
  17 + else
  18 + _('Unknown')
  19 + end
  20 + _("File (%s)") % file_type
  21 + end
  22 +
  23 + # Define the css classes to style the page fragment with the file related
  24 + # content. If you want other classes to identify this area to your
  25 + # customized presenter, so do this:
  26 + # def css_class_list
  27 + # [super, 'myclass'].flatten
  28 + # end
  29 + def css_class_list
  30 + [ encapsulated_instance.css_class_list,
  31 + 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'),
  32 + 'content-type_' + self.content_type.split('/')[0],
  33 + 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-')
  34 + ].flatten
  35 + end
  36 +
  37 + # Enable file presenter to customize the css classes on view_page.rhtml
  38 + # You may not overwrite this method on your customized presenter.
  39 + def css_class_name
  40 + [css_class_list].flatten.compact.join(' ')
  41 + end
  42 +
  43 + # The generic icon class-name or the specific file path.
  44 + # You may replace this method on your custom FilePresenter.
  45 + # See the current used icons class-names in public/designs/icons/tango/style.css
  46 + def icon_name
  47 + if mime_type
  48 + [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ]
  49 + else
  50 + 'upload-file'
  51 + end
  52 + end
  53 +
  54 + # Automatic render `file_presenter/<custom>.html.erb` to display your
  55 + # custom presenter html content.
  56 + # You may not overwrite this method on your customized presenter.
  57 + # A variable with the same presenter name will be created to refer
  58 + # to the file object.
  59 + # Example:
  60 + # The `FilePresenter::Image` render `file_presenter/image.html.erb`
  61 + # inside the `file_presenter/image.html.erb` you can access the
  62 + # required `FilePresenter::Image` instance in the `image` variable.
  63 + def to_html(options = {})
  64 + file = self
  65 + proc do
  66 + render :partial => file.class.to_s.underscore,
  67 + :locals => { :options => options },
  68 + :object => file
  69 + end
  70 + end
  71 +end
  72 +
  73 +Dir.glob(File.join('app', 'presenters', 'file', '*.rb')) do |file|
  74 + load file
  75 +end
  76 +
  77 +# Preload FilePresenters from plugins to allow `FilePresenter.for()` to work
  78 +Dir.glob(File.join('plugins', '*', 'lib', 'presenters', '*.rb')) do |file|
  79 + load file
  80 +end
app/presenters/generic.rb
@@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
1 -# Made to encapsulate any UploadedFile  
2 -class FilePresenter::Generic < FilePresenter  
3 - # if returns low priority, because it is generic.  
4 - def self.accepts?(f)  
5 - 1 if f.is_a? UploadedFile  
6 - end  
7 -end  
app/presenters/image.rb
@@ -1,23 +0,0 @@ @@ -1,23 +0,0 @@
1 -class FilePresenter::Image < FilePresenter  
2 - def self.accepts?(f)  
3 - return nil unless f.respond_to? :image?  
4 - f.image? ? 10 : nil  
5 - end  
6 -  
7 - def sized_icon(size)  
8 - public_filename size  
9 - end  
10 -  
11 - def icon_name  
12 - public_filename :icon  
13 - end  
14 -  
15 - def short_description  
16 - _('Image (%s)') % content_type.split('/')[1].upcase  
17 - end  
18 -  
19 - #Overwriting method from FilePresenter to allow download of images  
20 - def download?(view = nil)  
21 - view.blank? || view == 'false'  
22 - end  
23 -end  
app/views/file_presenter/_image.html.erb
1 <% if image.gallery? && options[:gallery_view] %> 1 <% if image.gallery? && options[:gallery_view] %>
2 <% 2 <%
3 images = image.parent.images 3 images = image.parent.images
4 - current_index = images.index(image.encapsulated_file) 4 + current_index = images.index(image.encapsulated_instance)
5 total_of_images = images.count 5 total_of_images = images.count
6 link_to_previous = if current_index >= 1 6 link_to_previous = if current_index >= 1
7 link_to(_('&laquo; Previous').html_safe, images[current_index - 1].view_url, :class => 'previous') 7 link_to(_('&laquo; Previous').html_safe, images[current_index - 1].view_url, :class => 'previous')
app/views/profile/_default_activity.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> 2 <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
3 </div> 3 </div>
4 <div class='profile-activity-description'> 4 <div class='profile-activity-description'>
5 - <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p> 5 + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe(activity).html_safe %></p>
6 <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p> 6 <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
7 <div class='profile-wall-actions'> 7 <div class='profile-wall-actions'>
8 <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %> 8 <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %>
app/views/profile/_favorite_enterprise.html.erb
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 </div> 3 </div>
4 <div class='profile-activity-description'> 4 <div class='profile-activity-description'>
5 <p class='profile-activity-text'> 5 <p class='profile-activity-text'>
6 - <%= link_to activity.user.short_name(nil), activity.user.url %> <%= describe activity %> 6 + <%= link_to activity.user.short_name(nil), activity.user.url %> <%= describe(activity).html_safe %>
7 </p> 7 </p>
8 <p class='profile-activity-time'><%= time_ago_in_words activity.created_at %></p> 8 <p class='profile-activity-time'><%= time_ago_in_words activity.created_at %></p>
9 9
app/views/profile/_leave_scrap.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> 2 <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
3 </div> 3 </div>
4 <div class='profile-activity-description'> 4 <div class='profile-activity-description'>
5 - <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p> 5 + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe(activity).html_safe %></p>
6 <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p> 6 <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
7 <div class='profile-wall-actions'> 7 <div class='profile-wall-actions'>
8 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %> 8 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %>
app/views/profile/_profile_activities_list.html.erb
@@ -11,6 +11,6 @@ @@ -11,6 +11,6 @@
11 11
12 <% if activities.current_page < activities.total_pages %> 12 <% if activities.current_page < activities.total_pages %>
13 <div id='profile_activities_page_<%= activities.current_page %>'> 13 <div id='profile_activities_page_<%= activities.current_page %>'>
14 - <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1)}, :update => "profile_activities_page_#{activities.current_page}" %> 14 + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1), :offsets => @offsets, :kind => 'wall'}, :update => "profile_activities_page_#{activities.current_page}" %>
15 </div> 15 </div>
16 <% end %> 16 <% end %>
app/views/profile/_profile_network.html.erb
1 <h3><%= _("%s's network activity") % @profile.name %></h3> 1 <h3><%= _("%s's network activity") % @profile.name %></h3>
2 <ul id='network-activities' class='profile-activities'> 2 <ul id='network-activities' class='profile-activities'>
3 - <%= render :partial => 'profile_network_activities', :locals => {:network_activities => @network_activities} %> 3 + <%= render :partial => 'profile_network_activities', :locals => {:activities => @network_activities} %>
4 </ul> 4 </ul>
app/views/profile/_profile_network_activities.html.erb
1 -<% network_activities.each do |activity| %> 1 +<% activities.each do |activity| %>
2 <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'network' } if activity.visible? %> 2 <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'network' } if activity.visible? %>
3 <% end %> 3 <% end %>
4 -<% if network_activities.current_page < network_activities.total_pages %>  
5 - <div id='profile_network_activities_page_<%= network_activities.current_page %>'>  
6 - <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_network_activities', :page => (network_activities.current_page + 1)}, :update => "profile_network_activities_page_#{network_activities.current_page}" %> 4 +<% if activities.current_page < activities.total_pages %>
  5 + <div id='profile_network_activities_page_<%= activities.current_page %>'>
  6 + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1), :offsets => @offsets, :kind => 'network'}, :update => "profile_network_activities_page_#{activities.current_page}" %>
7 </div> 7 </div>
8 <% end %> 8 <% end %>
lib/file_presenter.rb
@@ -1,131 +0,0 @@ @@ -1,131 +0,0 @@
1 -# All file presenters must extends `FilePresenter` not only to ensure the  
2 -# same interface, but also to make `FilePresenter.for(file)` to work.  
3 -class FilePresenter  
4 -  
5 - # Will return a encapsulated `UploadedFile` or the same object if no  
6 - # one accepts it. That behave allow to give any model to this class,  
7 - # like a Article and have no trouble with that.  
8 - def self.for(f)  
9 - #FIXME This check after the || is redundant but increases the blog_page  
10 - # speed considerably.  
11 - return f if f.is_a?(FilePresenter ) || (!f.kind_of?(UploadedFile) && !f.kind_of?(Image))  
12 - klass = FilePresenter.subclasses.sort_by {|class_instance|  
13 - class_instance.accepts?(f) || 0  
14 - }.last  
15 - klass.accepts?(f) ? klass.new(f) : f  
16 - end  
17 -  
18 - def self.base_class  
19 - Article  
20 - end  
21 -  
22 - def initialize(f)  
23 - @file = f  
24 - end  
25 -  
26 - # Allows to use the original `UploadedFile` reference.  
27 - def encapsulated_file  
28 - @file  
29 - end  
30 -  
31 - def id  
32 - @file.id  
33 - end  
34 -  
35 - def reload  
36 - @file.reload  
37 - self  
38 - end  
39 -  
40 - def kind_of?(klass)  
41 - @file.kind_of?(klass)  
42 - end  
43 -  
44 - # This method must be overridden in subclasses.  
45 - #  
46 - # If the class accepts the file, return a number that represents the  
47 - # priority the class should be given to handle that file. Higher numbers  
48 - # mean higher priority.  
49 - #  
50 - # If the class does not accept the file, return false.  
51 - def self.accepts?(f)  
52 - nil  
53 - end  
54 -  
55 - def download? view = nil  
56 - view.blank?  
57 - end  
58 -  
59 - def short_description  
60 - file_type = if content_type.present?  
61 - content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '')  
62 - else  
63 - _('Unknown')  
64 - end  
65 - _("File (%s)") % file_type  
66 - end  
67 -  
68 - # Define the css classes to style the page fragment with the file related  
69 - # content. If you want other classes to identify this area to your  
70 - # customized presenter, so do this:  
71 - # def css_class_list  
72 - # [super, 'myclass'].flatten  
73 - # end  
74 - def css_class_list  
75 - [ @file.css_class_list,  
76 - 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'),  
77 - 'content-type_' + self.content_type.split('/')[0],  
78 - 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-')  
79 - ].flatten  
80 - end  
81 -  
82 - # Enable file presenter to customize the css classes on view_page.rhtml  
83 - # You may not overwrite this method on your customized presenter.  
84 - def css_class_name  
85 - [css_class_list].flatten.compact.join(' ')  
86 - end  
87 -  
88 - # The generic icon class-name or the specific file path.  
89 - # You may replace this method on your custom FilePresenter.  
90 - # See the current used icons class-names in public/designs/icons/tango/style.css  
91 - def icon_name  
92 - if mime_type  
93 - [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ]  
94 - else  
95 - 'upload-file'  
96 - end  
97 - end  
98 -  
99 - # Automatic render `file_presenter/<custom>.html.erb` to display your  
100 - # custom presenter html content.  
101 - # You may not overwrite this method on your customized presenter.  
102 - # A variable with the same presenter name will be created to refer  
103 - # to the file object.  
104 - # Example:  
105 - # The `FilePresenter::Image` render `file_presenter/image.html.erb`  
106 - # inside the `file_presenter/image.html.erb` you can access the  
107 - # required `FilePresenter::Image` instance in the `image` variable.  
108 - def to_html(options = {})  
109 - file = self  
110 - proc do  
111 - render :partial => file.class.to_s.underscore,  
112 - :locals => { :options => options },  
113 - :object => file  
114 - end  
115 - end  
116 -  
117 - # That makes the presenter to works like any other `UploadedFile` instance.  
118 - def method_missing(m, *args)  
119 - @file.send(m, *args)  
120 - end  
121 -end  
122 -  
123 -# Preload FilePresenters to allow `FilePresenter.for()` to work  
124 -Dir.glob(File.join('app', 'presenters', '*.rb')) do |file|  
125 - load file  
126 -end  
127 -  
128 -# Preload FilePresenters from plugins to allow `FilePresenter.for()` to work  
129 -Dir.glob(File.join('plugins', '*', 'lib', 'presenters', '*.rb')) do |file|  
130 - load file  
131 -end  
lib/presenter.rb 0 → 100644
@@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
  1 +class Presenter
  2 + # Define presenter base_class
  3 + def self.base_class
  4 + end
  5 +
  6 + # Define base type condition
  7 + def self.available?(instance)
  8 + false
  9 + end
  10 +
  11 + def self.for(instance)
  12 + return instance if instance.is_a?(Presenter) || !available?(instance)
  13 +
  14 + klass = subclasses.sort_by {|class_instance|
  15 + class_instance.accepts?(instance) || 0
  16 + }.last
  17 +
  18 + klass.accepts?(instance) ? klass.new(instance) : f
  19 + end
  20 +
  21 + def initialize(instance)
  22 + @instance = instance
  23 + end
  24 +
  25 + # Allows to use the original instance reference.
  26 + def encapsulated_instance
  27 + @instance
  28 + end
  29 +
  30 + def id
  31 + @instance.id
  32 + end
  33 +
  34 + def reload
  35 + @instance.reload
  36 + self
  37 + end
  38 +
  39 + def kind_of?(klass)
  40 + @instance.kind_of?(klass)
  41 + end
  42 +
  43 + # This method must be overridden in subclasses.
  44 + #
  45 + # If the class accepts the instance, return a number that represents the
  46 + # priority the class should be given to handle that instance. Higher numbers
  47 + # mean higher priority.
  48 + #
  49 + # If the class does not accept the instance, return false.
  50 + def self.accepts?(f)
  51 + nil
  52 + end
  53 +
  54 + # That makes the presenter to works like any other not encapsulated instance.
  55 + def method_missing(m, *args)
  56 + @instance.send(m, *args)
  57 + end
  58 +end
  59 +
  60 +# Preload Presenters to allow `Presenter.for()` to work
  61 +Dir.glob(File.join('app', 'presenters', '*.rb')) do |file|
  62 + load file
  63 +end
plugins/metadata/lib/metadata_plugin/controllers.rb
@@ -8,8 +8,8 @@ class MetadataPlugin::Controllers @@ -8,8 +8,8 @@ class MetadataPlugin::Controllers
8 lambda do 8 lambda do
9 if profile and @page and profile.home_page_id == @page.id 9 if profile and @page and profile.home_page_id == @page.id
10 @profile 10 @profile
11 - elsif @page.respond_to? :encapsulated_file  
12 - @page.encapsulated_file 11 + elsif @page.respond_to? :encapsulated_instance
  12 + @page.encapsulated_instance
13 else 13 else
14 @page 14 @page
15 end 15 end
plugins/products/models/products_plugin/product.rb
@@ -48,9 +48,9 @@ class ProductsPlugin::Product &lt; ApplicationRecord @@ -48,9 +48,9 @@ class ProductsPlugin::Product &lt; ApplicationRecord
48 extend ActsAsHavingSettings::ClassMethods 48 extend ActsAsHavingSettings::ClassMethods
49 acts_as_having_settings field: :data 49 acts_as_having_settings field: :data
50 50
51 - track_actions :create_product, :after_create, keep_params: [:name, :url ], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user  
52 - track_actions :update_product, :before_update, keep_params: [:name, :url], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user  
53 - track_actions :remove_product, :before_destroy, keep_params: [:name], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user 51 + track_actions :create_product, :after_create, keep_params: [:name, :url ], if: Proc.new { |a| a.notifiable? }, custom_user: :action_tracker_user
  52 + track_actions :update_product, :before_update, keep_params: [:name, :url], if: Proc.new { |a| a.notifiable? }, custom_user: :action_tracker_user
  53 + track_actions :remove_product, :before_destroy, keep_params: [:name], if: Proc.new { |a| a.notifiable? }, custom_user: :action_tracker_user
54 54
55 validates_uniqueness_of :name, scope: :profile_id, allow_nil: true, if: :validate_uniqueness_of_column_name? 55 validates_uniqueness_of :name, scope: :profile_id, allow_nil: true, if: :validate_uniqueness_of_column_name?
56 56
@@ -290,7 +290,7 @@ class ProductsPlugin::Product &lt; ApplicationRecord @@ -290,7 +290,7 @@ class ProductsPlugin::Product &lt; ApplicationRecord
290 true 290 true
291 end 291 end
292 292
293 - def is_trackable? 293 + def notifiable?
294 # shopping_cart create products without profile 294 # shopping_cart create products without profile
295 self.profile.present? 295 self.profile.present?
296 end 296 end
test/functional/profile_controller_test.rb
@@ -888,17 +888,17 @@ class ProfileControllerTest &lt; ActionController::TestCase @@ -888,17 +888,17 @@ class ProfileControllerTest &lt; ActionController::TestCase
888 login_as(profile.identifier) 888 login_as(profile.identifier)
889 ActionTracker::Record.delete_all 889 ActionTracker::Record.delete_all
890 get :index, :profile => p1.identifier 890 get :index, :profile => p1.identifier
891 - assert_equal [], assigns(:network_activities) 891 + assert assigns(:network_activities).blank?
892 assert_response :success 892 assert_response :success
893 assert_template 'index' 893 assert_template 'index'
894 894
895 get :index, :profile => p2.identifier 895 get :index, :profile => p2.identifier
896 - assert_equal [], assigns(:network_activities) 896 + assert assigns(:network_activities).blank?
897 assert_response :success 897 assert_response :success
898 assert_template 'index' 898 assert_template 'index'
899 899
900 get :index, :profile => p3.identifier 900 get :index, :profile => p3.identifier
901 - assert_equal [], assigns(:network_activities) 901 + assert assigns(:network_activities).blank?
902 assert_response :success 902 assert_response :success
903 assert_template 'index' 903 assert_template 'index'
904 end 904 end
@@ -1186,14 +1186,14 @@ class ProfileControllerTest &lt; ActionController::TestCase @@ -1186,14 +1186,14 @@ class ProfileControllerTest &lt; ActionController::TestCase
1186 40.times{ create(ActionTracker::Record, :user_id => profile.id, :user_type => 'Profile', :verb => 'create_article', :target_id => article.id, :target_type => 'Article', :params => {'name' => article.name, 'url' => article.url, 'lead' => article.lead, 'first_image' => article.first_image})} 1186 40.times{ create(ActionTracker::Record, :user_id => profile.id, :user_type => 'Profile', :verb => 'create_article', :target_id => article.id, :target_type => 'Article', :params => {'name' => article.name, 'url' => article.url, 'lead' => article.lead, 'first_image' => article.first_image})}
1187 assert_equal 40, profile.tracked_actions.count 1187 assert_equal 40, profile.tracked_actions.count
1188 assert_equal 40, profile.activities.size 1188 assert_equal 40, profile.activities.size
1189 - get :view_more_activities, :profile => profile.identifier, :page => 2 1189 + get :view_more_activities, :profile => profile.identifier, :page => 2, :kind => 'wall', :offsets => {:wall => 0, :network => 0}
1190 assert_response :success 1190 assert_response :success
1191 assert_template '_profile_activities_list' 1191 assert_template '_profile_activities_list'
1192 - assert_equal 10, assigns(:activities).size 1192 + assert_equal ProfileController::ACTIVITIES_PER_PAGE, assigns(:activities).size
1193 end 1193 end
1194 1194
1195 should "be logged in to access the view_more_activities action" do 1195 should "be logged in to access the view_more_activities action" do
1196 - get :view_more_activities, :profile => profile.identifier 1196 + get :view_more_activities, :profile => profile.identifier, :kind => 'wall', :offsets => {:wall => 0, :network => 0}
1197 assert_redirected_to :controller => 'account', :action => 'login' 1197 assert_redirected_to :controller => 'account', :action => 'login'
1198 end 1198 end
1199 1199
@@ -1201,14 +1201,14 @@ class ProfileControllerTest &lt; ActionController::TestCase @@ -1201,14 +1201,14 @@ class ProfileControllerTest &lt; ActionController::TestCase
1201 login_as(profile.identifier) 1201 login_as(profile.identifier)
1202 40.times{fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => fast_create(ActionTracker::Record, :user_id => profile.id)) } 1202 40.times{fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => fast_create(ActionTracker::Record, :user_id => profile.id)) }
1203 assert_equal 40, profile.tracked_notifications.count 1203 assert_equal 40, profile.tracked_notifications.count
1204 - get :view_more_network_activities, :profile => profile.identifier, :page => 2 1204 + get :view_more_activities, :profile => profile.identifier, :page => 2, :kind => 'network', :offsets => {:wall => 0, :network => 0}
1205 assert_response :success 1205 assert_response :success
1206 assert_template '_profile_network_activities' 1206 assert_template '_profile_network_activities'
1207 - assert_equal 10, assigns(:activities).size 1207 + assert_equal ProfileController::ACTIVITIES_PER_PAGE, assigns(:activities).size
1208 end 1208 end
1209 1209
1210 should "be logged in to access the view_more_network_activities action" do 1210 should "be logged in to access the view_more_network_activities action" do
1211 - get :view_more_network_activities, :profile => profile.identifier 1211 + get :view_more_activities, :profile => profile.identifier, :kind => 'network', :offsets => {:wall => 0, :network => 0}
1212 assert_redirected_to :controller => 'account', :action => 'login' 1212 assert_redirected_to :controller => 'account', :action => 'login'
1213 end 1213 end
1214 1214
@@ -2260,4 +2260,47 @@ class ProfileControllerTest &lt; ActionController::TestCase @@ -2260,4 +2260,47 @@ class ProfileControllerTest &lt; ActionController::TestCase
2260 assert assigns(:network_activities).include?(scrap_activity) 2260 assert assigns(:network_activities).include?(scrap_activity)
2261 end 2261 end
2262 2262
  2263 + should 'not filter any activity if the user is an environment admin' do
  2264 + admin = create_user('env-admin').person
  2265 + env = @profile.environment
  2266 + env.add_admin(admin)
  2267 + activities = mock
  2268 + activities.expects(:delete_if).never
  2269 + @controller.stubs(:user).returns(admin)
  2270 + @controller.stubs(:environment).returns(env)
  2271 + @controller.send(:filter_activities, activities, :wall)
  2272 + end
  2273 +
  2274 + should 'not call hidden_for? if the user is involved in the activity' do
  2275 + user = create_user('involved-user').person
  2276 + env = @profile.environment
  2277 + activity = mock
  2278 + activities = [activity]
  2279 + activity.stubs(:involved?).with(user).returns(true)
  2280 + activity.expects(:hidden_for?).never
  2281 + @controller.stubs(:user).returns(user)
  2282 + @controller.stubs(:environment).returns(env)
  2283 + result = @controller.send(:filter_activities, activities, :wall)
  2284 + assert_includes result, activity
  2285 + end
  2286 +
  2287 + should 'remove activities that should be hidden for the user' do
  2288 + user = create_user('sample-user').person
  2289 + env = @profile.environment
  2290 + a1 = mock
  2291 + a2 = mock
  2292 + a3 = mock
  2293 + activities = [a1, a2, a3]
  2294 + a1.stubs(:involved?).with(user).returns(false)
  2295 + a2.stubs(:involved?).with(user).returns(false)
  2296 + a3.stubs(:involved?).with(user).returns(false)
  2297 + a1.stubs(:hidden_for?).with(user).returns(false)
  2298 + a2.stubs(:hidden_for?).with(user).returns(true)
  2299 + a3.stubs(:hidden_for?).with(user).returns(false)
  2300 + @controller.stubs(:user).returns(user)
  2301 + @controller.stubs(:environment).returns(env)
  2302 + result = @controller.send(:filter_activities, activities, :wall)
  2303 + assert_equivalent [a1,a3], result
  2304 + end
  2305 +
2263 end 2306 end
test/unit/activity_presenter/generic.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +require_relative "../../test_helper"
  2 +
  3 +class ActivityPresenter::GenericTest < ActiveSupport::TestCase
  4 + should 'accept everything' do
  5 + activity = ActionTracker::Record.new
  6 +
  7 + activity.stubs(:target).returns(Profile.new)
  8 + assert ActivityPresenter::Generic.accepts?(activity)
  9 + activity.stubs(:target).returns(Article.new)
  10 + assert ActivityPresenter::Generic.accepts?(activity)
  11 + activity.stubs(:target).returns(Scrap.new)
  12 + assert ActivityPresenter::Generic.accepts?(activity)
  13 + activity.stubs(:target).returns(mock)
  14 + assert ActivityPresenter::Generic.accepts?(activity)
  15 + end
  16 +end
test/unit/activity_presenter_test.rb 0 → 100644
@@ -0,0 +1,86 @@ @@ -0,0 +1,86 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class ActivityPresenterTest < ActiveSupport::TestCase
  4 + should 'be available for ActionTracker::Record' do
  5 + assert ActivityPresenter.available?(ActionTracker::Record.new)
  6 + end
  7 +
  8 + should 'be available for ProfileActivity' do
  9 + assert ActivityPresenter.available?(ProfileActivity.new)
  10 + end
  11 +
  12 + should 'return correct target for ActionTracker::Record' do
  13 + target = mock
  14 + activity = ActionTracker::Record.new
  15 + activity.stubs(:target).returns(target)
  16 + assert_equal target, ActivityPresenter.target(activity)
  17 + end
  18 +
  19 + should 'return correct target for ProfileActivity' do
  20 + target = mock
  21 + notification = ProfileActivity.new
  22 + record = ActionTracker::Record.new
  23 + notification.stubs(:activity).returns(record)
  24 + record.stubs(:target).returns(target)
  25 +
  26 + assert_equal target, ActivityPresenter.target(notification)
  27 + end
  28 +
  29 + should 'return correct owner for ActionTracker::Record' do
  30 + owner = mock
  31 + activity = ActionTracker::Record.new
  32 + activity.stubs(:user).returns(owner)
  33 + assert_equal owner, ActivityPresenter.owner(activity)
  34 + end
  35 +
  36 + should 'return correct owner for ProfileActivity' do
  37 + owner = mock
  38 + notification = ProfileActivity.new
  39 + notification.stubs(:profile).returns(owner)
  40 +
  41 + assert_equal owner, ActivityPresenter.owner(notification)
  42 + end
  43 +
  44 + should 'not be hidden for user if target does not respond to display_to' do
  45 + user = fast_create(Person)
  46 + target = mock
  47 + presenter = ActivityPresenter.new(target)
  48 + refute presenter.hidden_for?(user)
  49 + end
  50 +
  51 + should 'be hidden for user based on target display_to' do
  52 + user = fast_create(Person)
  53 + target = mock
  54 + presenter = ActivityPresenter.new(target)
  55 +
  56 + target.stubs(:display_to?).with(user).returns(false)
  57 + assert presenter.hidden_for?(user)
  58 +
  59 + target.stubs(:display_to?).with(user).returns(true)
  60 + refute presenter.hidden_for?(user)
  61 + end
  62 +
  63 + should 'verify if user is involved as target with the activity' do
  64 + user = mock
  65 + presenter = ActivityPresenter.new(mock)
  66 + presenter.stubs(:target).returns(user)
  67 + presenter.stubs(:owner).returns(nil)
  68 + assert presenter.involved?(user)
  69 + end
  70 +
  71 + should 'verify if user is involved as owner with the activity' do
  72 + user = mock
  73 + presenter = ActivityPresenter.new(mock)
  74 + presenter.stubs(:target).returns(nil)
  75 + presenter.stubs(:owner).returns(user)
  76 + assert presenter.involved?(user)
  77 + end
  78 +
  79 + should 'refute if user is not involved' do
  80 + user = mock
  81 + presenter = ActivityPresenter.new(mock)
  82 + presenter.stubs(:target).returns(nil)
  83 + presenter.stubs(:owner).returns(nil)
  84 + refute presenter.involved?(user)
  85 + end
  86 +end
test/unit/approve_article_test.rb
@@ -319,13 +319,6 @@ class ApproveArticleTest &lt; ActiveSupport::TestCase @@ -319,13 +319,6 @@ class ApproveArticleTest &lt; ActiveSupport::TestCase
319 assert_equal approved_article, ActionTracker::Record.last.target 319 assert_equal approved_article, ActionTracker::Record.last.target
320 end 320 end
321 321
322 - should "have the same is_trackable method as original article" do  
323 - a = create(ApproveArticle, :article => article, :target => community, :requestor => profile)  
324 - a.finish  
325 -  
326 - assert_equal article.is_trackable?, article.class.last.is_trackable?  
327 - end  
328 -  
329 should 'not have target notification message if it is not a moderated oganization' do 322 should 'not have target notification message if it is not a moderated oganization' do
330 community.moderated_articles = false; community.save 323 community.moderated_articles = false; community.save
331 task = build(ApproveArticle, :article => article, :target => community, :requestor => profile) 324 task = build(ApproveArticle, :article => article, :target => community, :requestor => profile)
test/unit/article_test.rb
@@ -1044,34 +1044,6 @@ class ArticleTest &lt; ActiveSupport::TestCase @@ -1044,34 +1044,6 @@ class ArticleTest &lt; ActiveSupport::TestCase
1044 assert_equal profile, article.action_tracker_target 1044 assert_equal profile, article.action_tracker_target
1045 end 1045 end
1046 1046
1047 - should "have defined the is_trackable method defined" do  
1048 - assert Article.method_defined?(:is_trackable?)  
1049 - end  
1050 -  
1051 - should "the common trackable conditions return the correct value" do  
1052 - a = Article.new  
1053 - a.published = a.advertise = true  
1054 - assert_equal true, a.published?  
1055 - assert_equal false, a.notifiable?  
1056 - assert_equal true, a.advertise?  
1057 - assert_equal false, a.is_trackable?  
1058 -  
1059 - a.published=false  
1060 - assert_equal false, a.published?  
1061 - assert_equal false, a.is_trackable?  
1062 -  
1063 - a.published=true  
1064 - a.advertise=false  
1065 - assert_equal false, a.advertise?  
1066 - assert_equal false, a.is_trackable?  
1067 - end  
1068 -  
1069 - should "not be trackable if article is inside a private community" do  
1070 - private_community = fast_create(Community, :public_profile => false)  
1071 - a = fast_create(TextArticle, :profile_id => private_community.id)  
1072 - assert_equal false, a.is_trackable?  
1073 - end  
1074 -  
1075 should 'create the notification to organization and all organization members' do 1047 should 'create the notification to organization and all organization members' do
1076 Profile.destroy_all 1048 Profile.destroy_all
1077 ActionTracker::Record.destroy_all 1049 ActionTracker::Record.destroy_all
test/unit/notify_activity_to_profiles_job_test.rb
@@ -73,10 +73,11 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase @@ -73,10 +73,11 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase
73 assert not_marked.tracked_notifications.where(:target => scrap).blank? 73 assert not_marked.tracked_notifications.where(:target => scrap).blank?
74 end 74 end
75 75
76 - should 'not notify the communities members' do 76 + should 'notify the community members on private articles' do
77 person = fast_create(Person) 77 person = fast_create(Person)
78 community = fast_create(Community) 78 community = fast_create(Community)
79 - action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'create_article') 79 + article = fast_create(TextArticle, :published => false, :profile_id => community.id)
  80 + action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Article', :target_id => article.id, :verb => 'create_article')
80 refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb) 81 refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
81 m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person) 82 m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
82 fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id) 83 fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id)
@@ -116,7 +117,7 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase @@ -116,7 +117,7 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase
116 end 117 end
117 end 118 end
118 119
119 - should 'notify only the community if it is private' do 120 + should 'notify only the community and its members if it is private' do
120 person = fast_create(Person) 121 person = fast_create(Person)
121 private_community = fast_create(Community, :public_profile => false) 122 private_community = fast_create(Community, :public_profile => false)
122 action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => private_community.id, :verb => 'create_article') 123 action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => private_community.id, :verb => 'create_article')
@@ -131,14 +132,23 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase @@ -131,14 +132,23 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase
131 job.perform 132 job.perform
132 process_delayed_job_queue 133 process_delayed_job_queue
133 134
134 - assert_equal 1, ActionTrackerNotification.count  
135 - [person, p1, p2, m1, m2].each do |profile|  
136 - notification = ActionTrackerNotification.find_by profile_id: profile.id  
137 - assert notification.nil?  
138 - end 135 + assert_equal 4, ActionTrackerNotification.count
139 136
  137 + # Community notification
140 notification = ActionTrackerNotification.find_by profile_id: private_community.id 138 notification = ActionTrackerNotification.find_by profile_id: private_community.id
141 assert_equal action_tracker, notification.action_tracker 139 assert_equal action_tracker, notification.action_tracker
  140 +
  141 + # User notification
  142 + notification = ActionTrackerNotification.find_by profile_id: person.id
  143 + assert_equal action_tracker, notification.action_tracker
  144 +
  145 + # Community members notifications
  146 + assert ActionTrackerNotification.find_by profile_id: m1.id
  147 + assert ActionTrackerNotification.find_by profile_id: m2.id
  148 +
  149 + # No user friends notification
  150 + assert_nil ActionTrackerNotification.find_by profile_id: p1.id
  151 + assert_nil ActionTrackerNotification.find_by profile_id: p2.id
142 end 152 end
143 153
144 should 'not notify the community tracking join_community verb' do 154 should 'not notify the community tracking join_community verb' do
test/unit/scrap_test.rb
@@ -313,4 +313,20 @@ class ScrapTest &lt; ActiveSupport::TestCase @@ -313,4 +313,20 @@ class ScrapTest &lt; ActiveSupport::TestCase
313 assert_equal s, p2.activities.first.activity 313 assert_equal s, p2.activities.first.activity
314 end 314 end
315 315
  316 + should 'display to anyone if nobody marked' do
  317 + assert Scrap.new.display_to?(fast_create(Person))
  318 + end
  319 +
  320 + should 'display only to marked people' do
  321 + scrap = Scrap.new
  322 + u1 = mock
  323 + u2 = mock
  324 + u3 = mock
  325 + scrap.stubs(:marked_people).returns([u1, u3])
  326 +
  327 + assert scrap.display_to?(u1)
  328 + refute scrap.display_to?(u2)
  329 + assert scrap.display_to?(u3)
  330 + end
  331 +
316 end 332 end
test/unit/textile_article_test.rb
@@ -97,38 +97,6 @@ class TextArticleTest &lt; ActiveSupport::TestCase @@ -97,38 +97,6 @@ class TextArticleTest &lt; ActiveSupport::TestCase
97 assert_equal article, ActionTracker::Record.last.target 97 assert_equal article, ActionTracker::Record.last.target
98 end 98 end
99 99
100 - should 'not notify activity if the article is not advertise' do  
101 - ActionTracker::Record.delete_all  
102 - a = create TextArticle, name: 'bar', profile_id: profile.id, published: true, advertise: false  
103 - assert_equal true, a.published?  
104 - assert_equal true, a.notifiable?  
105 - assert_equal false, a.image?  
106 - assert_equal false, a.profile.is_a?(Community)  
107 - assert_equal 0, ActionTracker::Record.count  
108 - end  
109 -  
110 - should "have defined the is_trackable method defined" do  
111 - assert TextArticle.method_defined?(:is_trackable?)  
112 - end  
113 -  
114 - should "the common trackable conditions return the correct value" do  
115 - a = build(TextArticle, profile: profile)  
116 - a.published = a.advertise = true  
117 - assert_equal true, a.published?  
118 - assert_equal true, a.notifiable?  
119 - assert_equal true, a.advertise?  
120 - assert_equal true, a.is_trackable?  
121 -  
122 - a.published=false  
123 - assert_equal false, a.published?  
124 - assert_equal false, a.is_trackable?  
125 -  
126 - a.published=true  
127 - a.advertise=false  
128 - assert_equal false, a.advertise?  
129 - assert_equal false, a.is_trackable?  
130 - end  
131 -  
132 should 'generate proper HTML for links' do 100 should 'generate proper HTML for links' do
133 assert_tag_in_string build_article('"Noosfero":http://noosfero.org/').to_html, tag: 'a', attributes: { href: 'http://noosfero.org/' } 101 assert_tag_in_string build_article('"Noosfero":http://noosfero.org/').to_html, tag: 'a', attributes: { href: 'http://noosfero.org/' }
134 end 102 end
test/unit/tiny_mce_article_test.rb
@@ -162,38 +162,6 @@ class TinyMceArticleTest &lt; ActiveSupport::TestCase @@ -162,38 +162,6 @@ class TinyMceArticleTest &lt; ActiveSupport::TestCase
162 assert_equal article, ActionTracker::Record.last.target 162 assert_equal article, ActionTracker::Record.last.target
163 end 163 end
164 164
165 - should 'not notify activity if the article is not advertise' do  
166 - ActionTracker::Record.delete_all  
167 - a = create TextArticle, name: 'bar', profile_id: profile.id, published: true, advertise: false  
168 - assert_equal true, a.published?  
169 - assert_equal true, a.notifiable?  
170 - assert_equal false, a.image?  
171 - assert_equal false, a.profile.is_a?(Community)  
172 - assert_equal 0, ActionTracker::Record.count  
173 - end  
174 -  
175 - should "have defined the is_trackable method defined" do  
176 - assert TextArticle.method_defined?(:is_trackable?)  
177 - end  
178 -  
179 - should "the common trackable conditions return the correct value" do  
180 - a = build(TextArticle, :profile => profile)  
181 - a.published = a.advertise = true  
182 - assert_equal true, a.published?  
183 - assert_equal true, a.notifiable?  
184 - assert_equal true, a.advertise?  
185 - assert_equal true, a.is_trackable?  
186 -  
187 - a.published=false  
188 - assert_equal false, a.published?  
189 - assert_equal false, a.is_trackable?  
190 -  
191 - a.published=true  
192 - a.advertise=false  
193 - assert_equal false, a.advertise?  
194 - assert_equal false, a.is_trackable?  
195 - end  
196 -  
197 should 'not sanitize html5 audio tag on body' do 165 should 'not sanitize html5 audio tag on body' do
198 article = TextArticle.create!(:name => 'html5 audio', :body => "Audio: <audio controls='controls'><source src='http://example.ogg' type='audio/ogg' />Audio not playing?.</audio>", :profile => profile) 166 article = TextArticle.create!(:name => 'html5 audio', :body => "Audio: <audio controls='controls'><source src='http://example.ogg' type='audio/ogg' />Audio not playing?.</audio>", :profile => profile)
199 assert_tag_in_string article.body, :tag => 'audio', :attributes => {:controls => 'controls'} 167 assert_tag_in_string article.body, :tag => 'audio', :attributes => {:controls => 'controls'}