Commit d6a6f2a408ce2bbe17c4fa302f37a62d07b5f1f5
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
Showing
34 changed files
with
488 additions
and
336 deletions
Show diff stats
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 < PublicController | @@ -510,22 +524,22 @@ class ProfileController < 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 < Struct.new(:tracked_action_id) | @@ -11,7 +11,7 @@ class NotifyActivityToProfilesJob < 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 < ApplicationRecord | @@ -63,7 +63,7 @@ class Article < 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 < ApplicationRecord | @@ -183,10 +183,6 @@ class Article < 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 < ApplicationRecord | @@ -845,7 +841,7 @@ class Article < 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 < ApplicationRecord | @@ -2,7 +2,7 @@ class FavoriteEnterprisePerson < 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 < ApplicationRecord | @@ -13,7 +13,7 @@ class FavoriteEnterprisePerson < 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 < ApplicationRecord | @@ -67,6 +67,10 @@ class Scrap < 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 < Article | @@ -190,8 +190,12 @@ class UploadedFile < 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 |
@@ -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 |
@@ -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 |
@@ -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
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(_('« Previous').html_safe, images[current_index - 1].view_url, :class => 'previous') | 7 | link_to(_('« 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 |
@@ -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 < ApplicationRecord | @@ -48,9 +48,9 @@ class ProductsPlugin::Product < 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 < ApplicationRecord | @@ -290,7 +290,7 @@ class ProductsPlugin::Product < 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 < ActionController::TestCase | @@ -888,17 +888,17 @@ class ProfileControllerTest < 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 < ActionController::TestCase | @@ -1186,14 +1186,14 @@ class ProfileControllerTest < 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 < ActionController::TestCase | @@ -1201,14 +1201,14 @@ class ProfileControllerTest < 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 < ActionController::TestCase | @@ -2260,4 +2260,47 @@ class ProfileControllerTest < 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 |
@@ -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 |
@@ -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 < ActiveSupport::TestCase | @@ -319,13 +319,6 @@ class ApproveArticleTest < 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 < ActiveSupport::TestCase | @@ -1044,34 +1044,6 @@ class ArticleTest < 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 < ActiveSupport::TestCase | @@ -73,10 +73,11 @@ class NotifyActivityToProfilesJobTest < 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 < ActiveSupport::TestCase | @@ -116,7 +117,7 @@ class NotifyActivityToProfilesJobTest < 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 < ActiveSupport::TestCase | @@ -131,14 +132,23 @@ class NotifyActivityToProfilesJobTest < 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 < ActiveSupport::TestCase | @@ -313,4 +313,20 @@ class ScrapTest < 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 < ActiveSupport::TestCase | @@ -97,38 +97,6 @@ class TextArticleTest < 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 < ActiveSupport::TestCase | @@ -162,38 +162,6 @@ class TinyMceArticleTest < 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'} |