diff --git a/app/controllers/public/profile_controller.rb b/app/controllers/public/profile_controller.rb index f354c5f..6ab96f1 100644 --- a/app/controllers/public/profile_controller.rb +++ b/app/controllers/public/profile_controller.rb @@ -13,17 +13,13 @@ class ProfileController < PublicController protect 'send_mail_to_members', :profile, :only => [:send_mail] - def index - @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) : [] - if logged_in? && current_person.follows?(@profile) - @network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) if @network_activities.empty? - @activities = @profile.activities.paginate(:per_page => 15, :page => params[:page]) - end - - # TODO Find a way to filter these through sql - @network_activities = filter_private_scraps(@network_activities) - @activities = filter_private_scraps(@activities) + ACTIVITIES_PER_PAGE = 15 + def index + @offsets = {:wall => 0, :network => 0} + page = (params[:page] || 1).to_i + @network_activities = loop_fetch_activities(@profile.tracked_notifications, :network, page) if !@profile.is_a?(Person) || follow_profile? + @activities = loop_fetch_activities(@profile.activities, :wall, page) if follow_profile? @tags = profile.article_tags allow_access_to_page end @@ -255,7 +251,7 @@ class ProfileController < PublicController render :partial => 'profile_activities_list', :locals => {:activities => activities} else network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) - render :partial => 'profile_network_activities', :locals => {:network_activities => network_activities} + render :partial => 'profile_network_activities', :locals => {:activities => network_activities} end end @@ -267,14 +263,32 @@ class ProfileController < PublicController render :text => prepare_to_token_input_by_class(result).to_json end - def view_more_activities - @activities = @profile.activities.paginate(:per_page => 10, :page => params[:page]) - render :partial => 'profile_activities_list', :locals => {:activities => @activities} + def loop_fetch_activities(base_activities, kind, page) + activities = nil + while activities.nil? || (activities.empty? && page <= activities.total_pages) + activities = base_activities.offset(@offsets[kind.to_sym]).paginate(:per_page => ACTIVITIES_PER_PAGE, :page => page) + activities = filter_activities(activities, kind.to_sym) + page += 1 + end + activities end - def view_more_network_activities - @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page]) - render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} + def view_more_activities + @activities = nil + @offsets = params[:offsets] + page = (params[:page] || 1).to_i + kind = params[:kind] + + if kind == 'wall' + base_activities = @profile.activities + partial = 'profile_activities_list' + else + base_activities = @profile.tracked_notifications + partial = 'profile_network_activities' + end + + @activities = loop_fetch_activities(base_activities, kind, page) + render :partial => partial, :locals => {:activities => @activities} end def more_comments @@ -510,22 +524,22 @@ class ProfileController < PublicController followed.uniq end - def filter_private_scraps(activities) + def filter_activities(activities, kind) + @offsets ||= {:wall => 0, :network => 0} + return activities if environment.admins.include?(user) activities = Array(activities) - activities.delete_if do |item| - if item.kind_of?(ProfileActivity) - target = item.activity - owner = profile - else - target = item.target - owner = item.user - end - !environment.admins.include?(user) && - owner != user && - target.is_a?(Scrap) && - target.marked_people.present? && - !target.marked_people.include?(user) - end + initial_count = activities.count + activities.delete_if do |activity| + activity = ActivityPresenter.for(activity) + next if activity.involved?(user) + activity.hidden_for?(user) + end + @offsets[kind] = @offsets[kind].to_i + @offsets[kind] += initial_count - activities.count activities end + + def follow_profile? + logged_in? && current_person.follows?(@profile) + end end diff --git a/app/jobs/notify_activity_to_profiles_job.rb b/app/jobs/notify_activity_to_profiles_job.rb index 98a94ee..856f8c5 100644 --- a/app/jobs/notify_activity_to_profiles_job.rb +++ b/app/jobs/notify_activity_to_profiles_job.rb @@ -11,7 +11,7 @@ class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id) tracked_action = ActionTracker::Record.find(tracked_action_id) return unless tracked_action.user.present? target = tracked_action.target - if target.is_a?(Community) && ( NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb) || ! target.public_profile ) + if target.is_a?(Community) && NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb) ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id) return end diff --git a/app/models/article.rb b/app/models/article.rb index 5fc55c2..51e34d9 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -63,7 +63,7 @@ class Article < ApplicationRecord _('Content') end - track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? } + track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.notifiable? } # xss_terminate plugin can't sanitize array fields # sanitize_tag_list is used with SanitizeHelper @@ -183,10 +183,6 @@ class Article < ApplicationRecord end end - def is_trackable? - self.published? && self.notifiable? && self.advertise? && self.profile.public_profile - end - def external_link=(link) if !link.blank? && link !~ /^[a-z]+:\/\//i link = 'http://' + link @@ -845,7 +841,7 @@ class Article < ApplicationRecord end def create_activity - if is_trackable? && !image? + if notifiable? && !image? save_action_for_verb 'create_article', [:name, :url, :lead, :first_image], Proc.new{}, :author end end diff --git a/app/models/favorite_enterprise_person.rb b/app/models/favorite_enterprise_person.rb index 7312ff0..063d58b 100644 --- a/app/models/favorite_enterprise_person.rb +++ b/app/models/favorite_enterprise_person.rb @@ -2,7 +2,7 @@ class FavoriteEnterprisePerson < ApplicationRecord attr_accessible :person, :enterprise - track_actions :favorite_enterprise, :after_create, keep_params: [:enterprise_name, :enterprise_url], if: proc{ |f| f.is_trackable? } + track_actions :favorite_enterprise, :after_create, keep_params: [:enterprise_name, :enterprise_url], if: proc{ |f| f.notifiable? } belongs_to :enterprise belongs_to :person @@ -13,7 +13,7 @@ class FavoriteEnterprisePerson < ApplicationRecord protected - def is_trackable? + def notifiable? self.enterprise.public? end diff --git a/app/models/profile.rb b/app/models/profile.rb index 9ce3e8b..144d515 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -824,13 +824,14 @@ private :generate_url, :url_options # returns +true+ if the given +user+ can see profile information about this # +profile+, and +false+ otherwise. - def display_info_to?(user) + def display_info_to?(user = nil) if self.public? true else display_private_info_to?(user) end end + alias_method :display_to?, :display_info_to? after_save :update_category_from_region def update_category_from_region diff --git a/app/models/scrap.rb b/app/models/scrap.rb index 8a1fb8a..fb1868f 100644 --- a/app/models/scrap.rb +++ b/app/models/scrap.rb @@ -67,6 +67,10 @@ class Scrap < ApplicationRecord sender != receiver && (is_root? ? root.receiver.receives_scrap_notification? : receiver.receives_scrap_notification?) end + def display_to?(user = nil) + marked_people.blank? || marked_people.include?(user) + end + protected def create_activity diff --git a/app/models/uploaded_file.rb b/app/models/uploaded_file.rb index 18e09d2..6471d01 100644 --- a/app/models/uploaded_file.rb +++ b/app/models/uploaded_file.rb @@ -190,8 +190,12 @@ class UploadedFile < Article true end + def image? + mime_type =~ /^image\// + end + def notifiable? - true + !image? end end diff --git a/app/presenters/activity/generic.rb b/app/presenters/activity/generic.rb new file mode 100644 index 0000000..457ba1a --- /dev/null +++ b/app/presenters/activity/generic.rb @@ -0,0 +1,5 @@ +class ActivityPresenter::Generic < ActivityPresenter + def self.accepts?(instance) + 1 + end +end diff --git a/app/presenters/activity_presenter.rb b/app/presenters/activity_presenter.rb new file mode 100644 index 0000000..13be64d --- /dev/null +++ b/app/presenters/activity_presenter.rb @@ -0,0 +1,44 @@ +class ActivityPresenter < Presenter + def self.base_class + ActionTracker::Record + end + + def self.available?(instance) + instance.kind_of?(ActionTracker::Record) || instance.kind_of?(ProfileActivity) + end + + def self.target(instance) + if instance.kind_of?(ProfileActivity) + target(instance.activity) + elsif instance.kind_of?(ActionTracker::Record) + instance.target + else + instance + end + end + + def self.owner(instance) + instance.kind_of?(ProfileActivity) ? instance.profile : instance.user + end + + def target + self.class.target(encapsulated_instance) + end + + def owner + self.class.owner(encapsulated_instance) + end + + def hidden_for?(user) + target.respond_to?(:display_to?) && !target.display_to?(user) + end + + def involved?(user) + owner == user || target == user + end +end + +# Preload ActivityPresenter subclasses to allow `Presenter.for()` to work +Dir.glob(File.join('app', 'presenters', 'activity', '*.rb')) do |file| + load file +end diff --git a/app/presenters/file/generic.rb b/app/presenters/file/generic.rb new file mode 100644 index 0000000..bfb0e94 --- /dev/null +++ b/app/presenters/file/generic.rb @@ -0,0 +1,7 @@ +# Made to encapsulate any UploadedFile +class FilePresenter::Generic < FilePresenter + # if returns low priority, because it is generic. + def self.accepts?(f) + 1 if f.is_a? UploadedFile + end +end diff --git a/app/presenters/file/image.rb b/app/presenters/file/image.rb new file mode 100644 index 0000000..6394d86 --- /dev/null +++ b/app/presenters/file/image.rb @@ -0,0 +1,23 @@ +class FilePresenter::Image < FilePresenter + def self.accepts?(f) + return nil unless f.respond_to? :image? + f.image? ? 10 : nil + end + + def sized_icon(size) + public_filename size + end + + def icon_name + public_filename :icon + end + + def short_description + _('Image (%s)') % content_type.split('/')[1].upcase + end + + #Overwriting method from FilePresenter to allow download of images + def download?(view = nil) + view.blank? || view == 'false' + end +end diff --git a/app/presenters/file_presenter.rb b/app/presenters/file_presenter.rb new file mode 100644 index 0000000..4ca6bdc --- /dev/null +++ b/app/presenters/file_presenter.rb @@ -0,0 +1,80 @@ +class FilePresenter < Presenter + def self.base_class + Article + end + + def self.available?(instance) + instance.kind_of?(UploadedFile) && !instance.kind_of?(Image) + end + + def download? view = nil + view.blank? + end + + def short_description + file_type = if content_type.present? + content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '') + else + _('Unknown') + end + _("File (%s)") % file_type + end + + # Define the css classes to style the page fragment with the file related + # content. If you want other classes to identify this area to your + # customized presenter, so do this: + # def css_class_list + # [super, 'myclass'].flatten + # end + def css_class_list + [ encapsulated_instance.css_class_list, + 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'), + 'content-type_' + self.content_type.split('/')[0], + 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-') + ].flatten + end + + # Enable file presenter to customize the css classes on view_page.rhtml + # You may not overwrite this method on your customized presenter. + def css_class_name + [css_class_list].flatten.compact.join(' ') + end + + # The generic icon class-name or the specific file path. + # You may replace this method on your custom FilePresenter. + # See the current used icons class-names in public/designs/icons/tango/style.css + def icon_name + if mime_type + [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ] + else + 'upload-file' + end + end + + # Automatic render `file_presenter/.html.erb` to display your + # custom presenter html content. + # You may not overwrite this method on your customized presenter. + # A variable with the same presenter name will be created to refer + # to the file object. + # Example: + # The `FilePresenter::Image` render `file_presenter/image.html.erb` + # inside the `file_presenter/image.html.erb` you can access the + # required `FilePresenter::Image` instance in the `image` variable. + def to_html(options = {}) + file = self + proc do + render :partial => file.class.to_s.underscore, + :locals => { :options => options }, + :object => file + end + end +end + +Dir.glob(File.join('app', 'presenters', 'file', '*.rb')) do |file| + load file +end + +# Preload FilePresenters from plugins to allow `FilePresenter.for()` to work +Dir.glob(File.join('plugins', '*', 'lib', 'presenters', '*.rb')) do |file| + load file +end diff --git a/app/presenters/generic.rb b/app/presenters/generic.rb deleted file mode 100644 index bfb0e94..0000000 --- a/app/presenters/generic.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Made to encapsulate any UploadedFile -class FilePresenter::Generic < FilePresenter - # if returns low priority, because it is generic. - def self.accepts?(f) - 1 if f.is_a? UploadedFile - end -end diff --git a/app/presenters/image.rb b/app/presenters/image.rb deleted file mode 100644 index 6394d86..0000000 --- a/app/presenters/image.rb +++ /dev/null @@ -1,23 +0,0 @@ -class FilePresenter::Image < FilePresenter - def self.accepts?(f) - return nil unless f.respond_to? :image? - f.image? ? 10 : nil - end - - def sized_icon(size) - public_filename size - end - - def icon_name - public_filename :icon - end - - def short_description - _('Image (%s)') % content_type.split('/')[1].upcase - end - - #Overwriting method from FilePresenter to allow download of images - def download?(view = nil) - view.blank? || view == 'false' - end -end diff --git a/app/views/file_presenter/_image.html.erb b/app/views/file_presenter/_image.html.erb index 2f07700..10ae57e 100644 --- a/app/views/file_presenter/_image.html.erb +++ b/app/views/file_presenter/_image.html.erb @@ -1,7 +1,7 @@ <% if image.gallery? && options[:gallery_view] %> <% images = image.parent.images - current_index = images.index(image.encapsulated_file) + current_index = images.index(image.encapsulated_instance) total_of_images = images.count link_to_previous = if current_index >= 1 link_to(_('« Previous').html_safe, images[current_index - 1].view_url, :class => 'previous') diff --git a/app/views/profile/_default_activity.html.erb b/app/views/profile/_default_activity.html.erb index 51ee36c..14a4aa6 100644 --- a/app/views/profile/_default_activity.html.erb +++ b/app/views/profile/_default_activity.html.erb @@ -2,7 +2,7 @@ <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
-

<%= link_to activity.user.name, activity.user.url %> <%= describe activity %>

+

<%= link_to activity.user.name, activity.user.url %> <%= describe(activity).html_safe %>

<%= time_ago_in_words(activity.created_at) %>

<%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %> diff --git a/app/views/profile/_favorite_enterprise.html.erb b/app/views/profile/_favorite_enterprise.html.erb index 185aa3a..c927420 100644 --- a/app/views/profile/_favorite_enterprise.html.erb +++ b/app/views/profile/_favorite_enterprise.html.erb @@ -3,7 +3,7 @@

- <%= link_to activity.user.short_name(nil), activity.user.url %> <%= describe activity %> + <%= link_to activity.user.short_name(nil), activity.user.url %> <%= describe(activity).html_safe %>

<%= time_ago_in_words activity.created_at %>

diff --git a/app/views/profile/_leave_scrap.html.erb b/app/views/profile/_leave_scrap.html.erb index 121dd77..248ec96 100644 --- a/app/views/profile/_leave_scrap.html.erb +++ b/app/views/profile/_leave_scrap.html.erb @@ -2,7 +2,7 @@ <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
-

<%= link_to activity.user.name, activity.user.url %> <%= describe activity %>

+

<%= link_to activity.user.name, activity.user.url %> <%= describe(activity).html_safe %>

<%= time_ago_in_words(activity.created_at) %>

<%= 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 %> diff --git a/app/views/profile/_profile_activities_list.html.erb b/app/views/profile/_profile_activities_list.html.erb index 79e0f58..e62478e 100644 --- a/app/views/profile/_profile_activities_list.html.erb +++ b/app/views/profile/_profile_activities_list.html.erb @@ -11,6 +11,6 @@ <% if activities.current_page < activities.total_pages %>
- <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1)}, :update => "profile_activities_page_#{activities.current_page}" %> + <%= 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}" %>
<% end %> diff --git a/app/views/profile/_profile_network.html.erb b/app/views/profile/_profile_network.html.erb index 99de5dd..4a01e4c 100644 --- a/app/views/profile/_profile_network.html.erb +++ b/app/views/profile/_profile_network.html.erb @@ -1,4 +1,4 @@

<%= _("%s's network activity") % @profile.name %>

    - <%= render :partial => 'profile_network_activities', :locals => {:network_activities => @network_activities} %> + <%= render :partial => 'profile_network_activities', :locals => {:activities => @network_activities} %>
diff --git a/app/views/profile/_profile_network_activities.html.erb b/app/views/profile/_profile_network_activities.html.erb index 8399fb2..d4e35a4 100644 --- a/app/views/profile/_profile_network_activities.html.erb +++ b/app/views/profile/_profile_network_activities.html.erb @@ -1,8 +1,8 @@ -<% network_activities.each do |activity| %> +<% activities.each do |activity| %> <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'network' } if activity.visible? %> <% end %> -<% if network_activities.current_page < network_activities.total_pages %> -
- <%= 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}" %> +<% if activities.current_page < activities.total_pages %> +
+ <%= 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}" %>
<% end %> diff --git a/lib/file_presenter.rb b/lib/file_presenter.rb deleted file mode 100644 index 3f925d4..0000000 --- a/lib/file_presenter.rb +++ /dev/null @@ -1,131 +0,0 @@ -# All file presenters must extends `FilePresenter` not only to ensure the -# same interface, but also to make `FilePresenter.for(file)` to work. -class FilePresenter - - # Will return a encapsulated `UploadedFile` or the same object if no - # one accepts it. That behave allow to give any model to this class, - # like a Article and have no trouble with that. - def self.for(f) - #FIXME This check after the || is redundant but increases the blog_page - # speed considerably. - return f if f.is_a?(FilePresenter ) || (!f.kind_of?(UploadedFile) && !f.kind_of?(Image)) - klass = FilePresenter.subclasses.sort_by {|class_instance| - class_instance.accepts?(f) || 0 - }.last - klass.accepts?(f) ? klass.new(f) : f - end - - def self.base_class - Article - end - - def initialize(f) - @file = f - end - - # Allows to use the original `UploadedFile` reference. - def encapsulated_file - @file - end - - def id - @file.id - end - - def reload - @file.reload - self - end - - def kind_of?(klass) - @file.kind_of?(klass) - end - - # This method must be overridden in subclasses. - # - # If the class accepts the file, return a number that represents the - # priority the class should be given to handle that file. Higher numbers - # mean higher priority. - # - # If the class does not accept the file, return false. - def self.accepts?(f) - nil - end - - def download? view = nil - view.blank? - end - - def short_description - file_type = if content_type.present? - content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '') - else - _('Unknown') - end - _("File (%s)") % file_type - end - - # Define the css classes to style the page fragment with the file related - # content. If you want other classes to identify this area to your - # customized presenter, so do this: - # def css_class_list - # [super, 'myclass'].flatten - # end - def css_class_list - [ @file.css_class_list, - 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'), - 'content-type_' + self.content_type.split('/')[0], - 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-') - ].flatten - end - - # Enable file presenter to customize the css classes on view_page.rhtml - # You may not overwrite this method on your customized presenter. - def css_class_name - [css_class_list].flatten.compact.join(' ') - end - - # The generic icon class-name or the specific file path. - # You may replace this method on your custom FilePresenter. - # See the current used icons class-names in public/designs/icons/tango/style.css - def icon_name - if mime_type - [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ] - else - 'upload-file' - end - end - - # Automatic render `file_presenter/.html.erb` to display your - # custom presenter html content. - # You may not overwrite this method on your customized presenter. - # A variable with the same presenter name will be created to refer - # to the file object. - # Example: - # The `FilePresenter::Image` render `file_presenter/image.html.erb` - # inside the `file_presenter/image.html.erb` you can access the - # required `FilePresenter::Image` instance in the `image` variable. - def to_html(options = {}) - file = self - proc do - render :partial => file.class.to_s.underscore, - :locals => { :options => options }, - :object => file - end - end - - # That makes the presenter to works like any other `UploadedFile` instance. - def method_missing(m, *args) - @file.send(m, *args) - end -end - -# Preload FilePresenters to allow `FilePresenter.for()` to work -Dir.glob(File.join('app', 'presenters', '*.rb')) do |file| - load file -end - -# Preload FilePresenters from plugins to allow `FilePresenter.for()` to work -Dir.glob(File.join('plugins', '*', 'lib', 'presenters', '*.rb')) do |file| - load file -end diff --git a/lib/presenter.rb b/lib/presenter.rb new file mode 100644 index 0000000..e415c06 --- /dev/null +++ b/lib/presenter.rb @@ -0,0 +1,63 @@ +class Presenter + # Define presenter base_class + def self.base_class + end + + # Define base type condition + def self.available?(instance) + false + end + + def self.for(instance) + return instance if instance.is_a?(Presenter) || !available?(instance) + + klass = subclasses.sort_by {|class_instance| + class_instance.accepts?(instance) || 0 + }.last + + klass.accepts?(instance) ? klass.new(instance) : f + end + + def initialize(instance) + @instance = instance + end + + # Allows to use the original instance reference. + def encapsulated_instance + @instance + end + + def id + @instance.id + end + + def reload + @instance.reload + self + end + + def kind_of?(klass) + @instance.kind_of?(klass) + end + + # This method must be overridden in subclasses. + # + # If the class accepts the instance, return a number that represents the + # priority the class should be given to handle that instance. Higher numbers + # mean higher priority. + # + # If the class does not accept the instance, return false. + def self.accepts?(f) + nil + end + + # That makes the presenter to works like any other not encapsulated instance. + def method_missing(m, *args) + @instance.send(m, *args) + end +end + +# Preload Presenters to allow `Presenter.for()` to work +Dir.glob(File.join('app', 'presenters', '*.rb')) do |file| + load file +end diff --git a/plugins/metadata/lib/metadata_plugin/controllers.rb b/plugins/metadata/lib/metadata_plugin/controllers.rb index 63db241..1a3294e 100644 --- a/plugins/metadata/lib/metadata_plugin/controllers.rb +++ b/plugins/metadata/lib/metadata_plugin/controllers.rb @@ -8,8 +8,8 @@ class MetadataPlugin::Controllers lambda do if profile and @page and profile.home_page_id == @page.id @profile - elsif @page.respond_to? :encapsulated_file - @page.encapsulated_file + elsif @page.respond_to? :encapsulated_instance + @page.encapsulated_instance else @page end diff --git a/plugins/products/models/products_plugin/product.rb b/plugins/products/models/products_plugin/product.rb index 0f5113e..192ef6f 100644 --- a/plugins/products/models/products_plugin/product.rb +++ b/plugins/products/models/products_plugin/product.rb @@ -48,9 +48,9 @@ class ProductsPlugin::Product < ApplicationRecord extend ActsAsHavingSettings::ClassMethods acts_as_having_settings field: :data - track_actions :create_product, :after_create, keep_params: [:name, :url ], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user - track_actions :update_product, :before_update, keep_params: [:name, :url], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user - track_actions :remove_product, :before_destroy, keep_params: [:name], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user + track_actions :create_product, :after_create, keep_params: [:name, :url ], if: Proc.new { |a| a.notifiable? }, custom_user: :action_tracker_user + track_actions :update_product, :before_update, keep_params: [:name, :url], if: Proc.new { |a| a.notifiable? }, custom_user: :action_tracker_user + track_actions :remove_product, :before_destroy, keep_params: [:name], if: Proc.new { |a| a.notifiable? }, custom_user: :action_tracker_user validates_uniqueness_of :name, scope: :profile_id, allow_nil: true, if: :validate_uniqueness_of_column_name? @@ -290,7 +290,7 @@ class ProductsPlugin::Product < ApplicationRecord true end - def is_trackable? + def notifiable? # shopping_cart create products without profile self.profile.present? end diff --git a/test/functional/profile_controller_test.rb b/test/functional/profile_controller_test.rb index 369e888..5009536 100644 --- a/test/functional/profile_controller_test.rb +++ b/test/functional/profile_controller_test.rb @@ -888,17 +888,17 @@ class ProfileControllerTest < ActionController::TestCase login_as(profile.identifier) ActionTracker::Record.delete_all get :index, :profile => p1.identifier - assert_equal [], assigns(:network_activities) + assert assigns(:network_activities).blank? assert_response :success assert_template 'index' get :index, :profile => p2.identifier - assert_equal [], assigns(:network_activities) + assert assigns(:network_activities).blank? assert_response :success assert_template 'index' get :index, :profile => p3.identifier - assert_equal [], assigns(:network_activities) + assert assigns(:network_activities).blank? assert_response :success assert_template 'index' end @@ -1186,14 +1186,14 @@ class ProfileControllerTest < ActionController::TestCase 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})} assert_equal 40, profile.tracked_actions.count assert_equal 40, profile.activities.size - get :view_more_activities, :profile => profile.identifier, :page => 2 + get :view_more_activities, :profile => profile.identifier, :page => 2, :kind => 'wall', :offsets => {:wall => 0, :network => 0} assert_response :success assert_template '_profile_activities_list' - assert_equal 10, assigns(:activities).size + assert_equal ProfileController::ACTIVITIES_PER_PAGE, assigns(:activities).size end should "be logged in to access the view_more_activities action" do - get :view_more_activities, :profile => profile.identifier + get :view_more_activities, :profile => profile.identifier, :kind => 'wall', :offsets => {:wall => 0, :network => 0} assert_redirected_to :controller => 'account', :action => 'login' end @@ -1201,14 +1201,14 @@ class ProfileControllerTest < ActionController::TestCase login_as(profile.identifier) 40.times{fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => fast_create(ActionTracker::Record, :user_id => profile.id)) } assert_equal 40, profile.tracked_notifications.count - get :view_more_network_activities, :profile => profile.identifier, :page => 2 + get :view_more_activities, :profile => profile.identifier, :page => 2, :kind => 'network', :offsets => {:wall => 0, :network => 0} assert_response :success assert_template '_profile_network_activities' - assert_equal 10, assigns(:activities).size + assert_equal ProfileController::ACTIVITIES_PER_PAGE, assigns(:activities).size end should "be logged in to access the view_more_network_activities action" do - get :view_more_network_activities, :profile => profile.identifier + get :view_more_activities, :profile => profile.identifier, :kind => 'network', :offsets => {:wall => 0, :network => 0} assert_redirected_to :controller => 'account', :action => 'login' end @@ -2260,4 +2260,47 @@ class ProfileControllerTest < ActionController::TestCase assert assigns(:network_activities).include?(scrap_activity) end + should 'not filter any activity if the user is an environment admin' do + admin = create_user('env-admin').person + env = @profile.environment + env.add_admin(admin) + activities = mock + activities.expects(:delete_if).never + @controller.stubs(:user).returns(admin) + @controller.stubs(:environment).returns(env) + @controller.send(:filter_activities, activities, :wall) + end + + should 'not call hidden_for? if the user is involved in the activity' do + user = create_user('involved-user').person + env = @profile.environment + activity = mock + activities = [activity] + activity.stubs(:involved?).with(user).returns(true) + activity.expects(:hidden_for?).never + @controller.stubs(:user).returns(user) + @controller.stubs(:environment).returns(env) + result = @controller.send(:filter_activities, activities, :wall) + assert_includes result, activity + end + + should 'remove activities that should be hidden for the user' do + user = create_user('sample-user').person + env = @profile.environment + a1 = mock + a2 = mock + a3 = mock + activities = [a1, a2, a3] + a1.stubs(:involved?).with(user).returns(false) + a2.stubs(:involved?).with(user).returns(false) + a3.stubs(:involved?).with(user).returns(false) + a1.stubs(:hidden_for?).with(user).returns(false) + a2.stubs(:hidden_for?).with(user).returns(true) + a3.stubs(:hidden_for?).with(user).returns(false) + @controller.stubs(:user).returns(user) + @controller.stubs(:environment).returns(env) + result = @controller.send(:filter_activities, activities, :wall) + assert_equivalent [a1,a3], result + end + end diff --git a/test/unit/activity_presenter/generic.rb b/test/unit/activity_presenter/generic.rb new file mode 100644 index 0000000..b4f744c --- /dev/null +++ b/test/unit/activity_presenter/generic.rb @@ -0,0 +1,16 @@ +require_relative "../../test_helper" + +class ActivityPresenter::GenericTest < ActiveSupport::TestCase + should 'accept everything' do + activity = ActionTracker::Record.new + + activity.stubs(:target).returns(Profile.new) + assert ActivityPresenter::Generic.accepts?(activity) + activity.stubs(:target).returns(Article.new) + assert ActivityPresenter::Generic.accepts?(activity) + activity.stubs(:target).returns(Scrap.new) + assert ActivityPresenter::Generic.accepts?(activity) + activity.stubs(:target).returns(mock) + assert ActivityPresenter::Generic.accepts?(activity) + end +end diff --git a/test/unit/activity_presenter_test.rb b/test/unit/activity_presenter_test.rb new file mode 100644 index 0000000..06a064d --- /dev/null +++ b/test/unit/activity_presenter_test.rb @@ -0,0 +1,86 @@ +require_relative "../test_helper" + +class ActivityPresenterTest < ActiveSupport::TestCase + should 'be available for ActionTracker::Record' do + assert ActivityPresenter.available?(ActionTracker::Record.new) + end + + should 'be available for ProfileActivity' do + assert ActivityPresenter.available?(ProfileActivity.new) + end + + should 'return correct target for ActionTracker::Record' do + target = mock + activity = ActionTracker::Record.new + activity.stubs(:target).returns(target) + assert_equal target, ActivityPresenter.target(activity) + end + + should 'return correct target for ProfileActivity' do + target = mock + notification = ProfileActivity.new + record = ActionTracker::Record.new + notification.stubs(:activity).returns(record) + record.stubs(:target).returns(target) + + assert_equal target, ActivityPresenter.target(notification) + end + + should 'return correct owner for ActionTracker::Record' do + owner = mock + activity = ActionTracker::Record.new + activity.stubs(:user).returns(owner) + assert_equal owner, ActivityPresenter.owner(activity) + end + + should 'return correct owner for ProfileActivity' do + owner = mock + notification = ProfileActivity.new + notification.stubs(:profile).returns(owner) + + assert_equal owner, ActivityPresenter.owner(notification) + end + + should 'not be hidden for user if target does not respond to display_to' do + user = fast_create(Person) + target = mock + presenter = ActivityPresenter.new(target) + refute presenter.hidden_for?(user) + end + + should 'be hidden for user based on target display_to' do + user = fast_create(Person) + target = mock + presenter = ActivityPresenter.new(target) + + target.stubs(:display_to?).with(user).returns(false) + assert presenter.hidden_for?(user) + + target.stubs(:display_to?).with(user).returns(true) + refute presenter.hidden_for?(user) + end + + should 'verify if user is involved as target with the activity' do + user = mock + presenter = ActivityPresenter.new(mock) + presenter.stubs(:target).returns(user) + presenter.stubs(:owner).returns(nil) + assert presenter.involved?(user) + end + + should 'verify if user is involved as owner with the activity' do + user = mock + presenter = ActivityPresenter.new(mock) + presenter.stubs(:target).returns(nil) + presenter.stubs(:owner).returns(user) + assert presenter.involved?(user) + end + + should 'refute if user is not involved' do + user = mock + presenter = ActivityPresenter.new(mock) + presenter.stubs(:target).returns(nil) + presenter.stubs(:owner).returns(nil) + refute presenter.involved?(user) + end +end diff --git a/test/unit/approve_article_test.rb b/test/unit/approve_article_test.rb index 5565c0a..c8e74ad 100644 --- a/test/unit/approve_article_test.rb +++ b/test/unit/approve_article_test.rb @@ -319,13 +319,6 @@ class ApproveArticleTest < ActiveSupport::TestCase assert_equal approved_article, ActionTracker::Record.last.target end - should "have the same is_trackable method as original article" do - a = create(ApproveArticle, :article => article, :target => community, :requestor => profile) - a.finish - - assert_equal article.is_trackable?, article.class.last.is_trackable? - end - should 'not have target notification message if it is not a moderated oganization' do community.moderated_articles = false; community.save task = build(ApproveArticle, :article => article, :target => community, :requestor => profile) diff --git a/test/unit/article_test.rb b/test/unit/article_test.rb index ac4d45e..5b34f6f 100644 --- a/test/unit/article_test.rb +++ b/test/unit/article_test.rb @@ -1044,34 +1044,6 @@ class ArticleTest < ActiveSupport::TestCase assert_equal profile, article.action_tracker_target end - should "have defined the is_trackable method defined" do - assert Article.method_defined?(:is_trackable?) - end - - should "the common trackable conditions return the correct value" do - a = Article.new - a.published = a.advertise = true - assert_equal true, a.published? - assert_equal false, a.notifiable? - assert_equal true, a.advertise? - assert_equal false, a.is_trackable? - - a.published=false - assert_equal false, a.published? - assert_equal false, a.is_trackable? - - a.published=true - a.advertise=false - assert_equal false, a.advertise? - assert_equal false, a.is_trackable? - end - - should "not be trackable if article is inside a private community" do - private_community = fast_create(Community, :public_profile => false) - a = fast_create(TextArticle, :profile_id => private_community.id) - assert_equal false, a.is_trackable? - end - should 'create the notification to organization and all organization members' do Profile.destroy_all ActionTracker::Record.destroy_all diff --git a/test/unit/notify_activity_to_profiles_job_test.rb b/test/unit/notify_activity_to_profiles_job_test.rb index 3d7920f..f77acb6 100644 --- a/test/unit/notify_activity_to_profiles_job_test.rb +++ b/test/unit/notify_activity_to_profiles_job_test.rb @@ -73,10 +73,11 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase assert not_marked.tracked_notifications.where(:target => scrap).blank? end - should 'not notify the communities members' do + should 'notify the community members on private articles' do person = fast_create(Person) community = fast_create(Community) - action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'create_article') + article = fast_create(TextArticle, :published => false, :profile_id => community.id) + action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Article', :target_id => article.id, :verb => 'create_article') refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb) m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person) fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id) @@ -116,7 +117,7 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase end end - should 'notify only the community if it is private' do + should 'notify only the community and its members if it is private' do person = fast_create(Person) private_community = fast_create(Community, :public_profile => false) 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 job.perform process_delayed_job_queue - assert_equal 1, ActionTrackerNotification.count - [person, p1, p2, m1, m2].each do |profile| - notification = ActionTrackerNotification.find_by profile_id: profile.id - assert notification.nil? - end + assert_equal 4, ActionTrackerNotification.count + # Community notification notification = ActionTrackerNotification.find_by profile_id: private_community.id assert_equal action_tracker, notification.action_tracker + + # User notification + notification = ActionTrackerNotification.find_by profile_id: person.id + assert_equal action_tracker, notification.action_tracker + + # Community members notifications + assert ActionTrackerNotification.find_by profile_id: m1.id + assert ActionTrackerNotification.find_by profile_id: m2.id + + # No user friends notification + assert_nil ActionTrackerNotification.find_by profile_id: p1.id + assert_nil ActionTrackerNotification.find_by profile_id: p2.id end should 'not notify the community tracking join_community verb' do diff --git a/test/unit/scrap_test.rb b/test/unit/scrap_test.rb index 49e5c20..bb16546 100644 --- a/test/unit/scrap_test.rb +++ b/test/unit/scrap_test.rb @@ -313,4 +313,20 @@ class ScrapTest < ActiveSupport::TestCase assert_equal s, p2.activities.first.activity end + should 'display to anyone if nobody marked' do + assert Scrap.new.display_to?(fast_create(Person)) + end + + should 'display only to marked people' do + scrap = Scrap.new + u1 = mock + u2 = mock + u3 = mock + scrap.stubs(:marked_people).returns([u1, u3]) + + assert scrap.display_to?(u1) + refute scrap.display_to?(u2) + assert scrap.display_to?(u3) + end + end diff --git a/test/unit/textile_article_test.rb b/test/unit/textile_article_test.rb index e9442a3..edf09dc 100644 --- a/test/unit/textile_article_test.rb +++ b/test/unit/textile_article_test.rb @@ -97,38 +97,6 @@ class TextArticleTest < ActiveSupport::TestCase assert_equal article, ActionTracker::Record.last.target end - should 'not notify activity if the article is not advertise' do - ActionTracker::Record.delete_all - a = create TextArticle, name: 'bar', profile_id: profile.id, published: true, advertise: false - assert_equal true, a.published? - assert_equal true, a.notifiable? - assert_equal false, a.image? - assert_equal false, a.profile.is_a?(Community) - assert_equal 0, ActionTracker::Record.count - end - - should "have defined the is_trackable method defined" do - assert TextArticle.method_defined?(:is_trackable?) - end - - should "the common trackable conditions return the correct value" do - a = build(TextArticle, profile: profile) - a.published = a.advertise = true - assert_equal true, a.published? - assert_equal true, a.notifiable? - assert_equal true, a.advertise? - assert_equal true, a.is_trackable? - - a.published=false - assert_equal false, a.published? - assert_equal false, a.is_trackable? - - a.published=true - a.advertise=false - assert_equal false, a.advertise? - assert_equal false, a.is_trackable? - end - should 'generate proper HTML for links' do assert_tag_in_string build_article('"Noosfero":http://noosfero.org/').to_html, tag: 'a', attributes: { href: 'http://noosfero.org/' } end diff --git a/test/unit/tiny_mce_article_test.rb b/test/unit/tiny_mce_article_test.rb index 024fa59..e647db4 100644 --- a/test/unit/tiny_mce_article_test.rb +++ b/test/unit/tiny_mce_article_test.rb @@ -162,38 +162,6 @@ class TinyMceArticleTest < ActiveSupport::TestCase assert_equal article, ActionTracker::Record.last.target end - should 'not notify activity if the article is not advertise' do - ActionTracker::Record.delete_all - a = create TextArticle, name: 'bar', profile_id: profile.id, published: true, advertise: false - assert_equal true, a.published? - assert_equal true, a.notifiable? - assert_equal false, a.image? - assert_equal false, a.profile.is_a?(Community) - assert_equal 0, ActionTracker::Record.count - end - - should "have defined the is_trackable method defined" do - assert TextArticle.method_defined?(:is_trackable?) - end - - should "the common trackable conditions return the correct value" do - a = build(TextArticle, :profile => profile) - a.published = a.advertise = true - assert_equal true, a.published? - assert_equal true, a.notifiable? - assert_equal true, a.advertise? - assert_equal true, a.is_trackable? - - a.published=false - assert_equal false, a.published? - assert_equal false, a.is_trackable? - - a.published=true - a.advertise=false - assert_equal false, a.advertise? - assert_equal false, a.is_trackable? - end - should 'not sanitize html5 audio tag on body' do article = TextArticle.create!(:name => 'html5 audio', :body => "Audio: ", :profile => profile) assert_tag_in_string article.body, :tag => 'audio', :attributes => {:controls => 'controls'} -- libgit2 0.21.2