diff --git a/app/controllers/application.rb b/app/controllers/application.rb index 2d2fa42..239055e 100644 --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@ -93,8 +93,16 @@ class ApplicationController < ActionController::Base verify :method => :post, :only => actions, :redirect_to => redirect end + helper_method :current_person, :current_person + protected + def user + current_user.person if logged_in? + end + + alias :current_person :user + # TODO: move this logic somewhere else (Domain class?) def detect_stuff_by_domain @domain = Domain.find_by_name(request.host) @@ -126,10 +134,6 @@ class ApplicationController < ActionController::Base render :template => 'shared/access_denied.rhtml', :status => 403 end - def user - current_user.person if logged_in? - end - def load_category unless params[:category_path].blank? path = params[:category_path].join('/') diff --git a/app/controllers/public/profile_controller.rb b/app/controllers/public/profile_controller.rb index 5914680..e823acb 100644 --- a/app/controllers/public/profile_controller.rb +++ b/app/controllers/public/profile_controller.rb @@ -3,11 +3,21 @@ class ProfileController < PublicController needs_profile before_filter :check_access_to_profile, :except => [:join, :index] before_filter :store_before_join, :only => [:join] - before_filter :login_required, :only => [:join, :leave, :unblock] + before_filter :login_required, :only => [:join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities] + helper TagsHelper def index + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) + @network_activities = [] + @wall_items = [] + if !@profile.is_a?(Person) + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) + elsif logged_in? && current_person.follows?(@profile) + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) + @wall_items = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if @profile.is_a?(Person) + end @tags = profile.article_tags unless profile.display_info_to?(user) profile.visible? ? private_profile : invisible_profile @@ -123,6 +133,53 @@ class ProfileController < PublicController end end + def leave_scrap + sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id]) + receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id]) + @scrap = Scrap.new(params[:scrap]) + @scrap.sender= sender + @scrap.receiver= receiver + @tab_action = params[:tab_action] + @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.") + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if params[:not_load_scraps].nil? + render :partial => 'leave_scrap' + end + + def view_more_scraps + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) + render :partial => 'profile_scraps', :locals => {:scraps => @scraps} + end + + def view_more_activities + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) + render :partial => 'profile_activities', :locals => {:activities => @activities} + end + + def view_more_network_activities + @activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) + render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} + end + + def remove_scrap + begin + scrap = current_user.person.scraps(params[:scrap_id]) + scrap.destroy + render :text => _('Scrap successfully removed.') + rescue + render :text => _('You could not remove this scrap') + end + end + + def remove_activity + begin + activity = current_user.person.tracked_actions.find(params[:activity_id]) + ActionTrackerNotification.find(:first, :conditions => {:profile_id => current_user.person, :action_tracker_id => activity}).destroy + render :text => _('Activity successfully removed.') + rescue + render :text => _('You could not remove this activity') + end + end + protected def check_access_to_profile diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8eb4822..95f3913 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -414,6 +414,7 @@ module ApplicationHelper # # If the profile has no image set yet, then a default image is used. def profile_image(profile, size=:portrait, opt={}) + return '' if profile.nil? opt[:alt] ||= profile.name() opt[:title] ||= '' opt[:class] ||= '' @@ -515,14 +516,14 @@ module ApplicationHelper if profile.kind_of?(Person) [ {_('Home Page') => url_for(profile.url)}, - {_('Profile') => url_for(profile.public_profile_url)}, + {_('Wall') => url_for(profile.public_profile_url)}, {_('Friends') => url_for(:controller => :profile, :action => :friends, :profile => profile.identifier)}, {_('Communities') => url_for(:controller => :profile, :action => :communities, :profile => profile.identifier)} ] elsif profile.kind_of?(Community) [ {_('Home Page') => url_for(profile.url)}, - {_('Profile') => url_for(profile.public_profile_url)}, + {_('Wall') => url_for(profile.public_profile_url)}, {_('Members') => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}, {_('Agenda') => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)} ] @@ -1027,8 +1028,12 @@ module ApplicationHelper link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button')) end + def jquery_theme + theme_option(:jquery_theme) || 'smoothness_mod' + end + def jquery_ui_theme_stylesheet_path - 'jquery.ui/sunny-mod/jquery-ui-1.8.2.custom' + 'jquery.ui/' + jquery_theme + '/jquery-ui-1.8.2.custom' end def ui_error(message) @@ -1131,4 +1136,20 @@ module ApplicationHelper link_to('' + _('Logout') + '', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) end + def limited_text_area(object_name, method, limit, text_area_id, options = {}) + content_tag(:div, [ + text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)), + content_tag(:p, content_tag(:span, limit) + _(' characters left'), :id => text_area_id + '_left'), + content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none') + ], :class => 'limited-text-area') + end + + def pluralize_without_count(count, singular, plural = nil) + count == 1 ? singular : (plural || singular.pluralize) + end + + def unique_with_count(list, connector = 'for') + list.sort.inject(Hash.new(0)){|h,i| h[i] += 1; h }.collect{ |x, n| [n, connector, x].join(" ") }.sort + end + end diff --git a/app/models/action_tracker_notification.rb b/app/models/action_tracker_notification.rb new file mode 100644 index 0000000..5a6e916 --- /dev/null +++ b/app/models/action_tracker_notification.rb @@ -0,0 +1,11 @@ +class ActionTrackerNotification < ActiveRecord::Base + + belongs_to :profile + belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id' + + validates_presence_of :profile_id, :action_tracker_id + validates_uniqueness_of :action_tracker_id, :scope => :profile_id + +end + +ActionTracker::Record.has_many :action_tracker_notifications, :class_name => 'ActionTrackerNotification', :foreign_key => 'action_tracker_id', :dependent => :destroy diff --git a/app/models/add_friend.rb b/app/models/add_friend.rb index b0e7164..c5718cf 100644 --- a/app/models/add_friend.rb +++ b/app/models/add_friend.rb @@ -15,8 +15,8 @@ class AddFriend < Task alias :friend= :target= def perform - requestor.add_friend(target, group_for_person) target.add_friend(requestor, group_for_friend) + requestor.add_friend(target, group_for_person) end def description diff --git a/app/models/article.rb b/app/models/article.rb index 3f76782..551470b 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -2,6 +2,11 @@ require 'hpricot' class Article < ActiveRecord::Base + track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.published? && !a.profile.is_a?(Community) && !a.image? } + track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.published? && (a.body_changed? || a.name_changed?) } + track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => :published? + track_actions :publish_article_in_community, :after_create, :keep_params => ["name", "url", "profile.url", "profile.name"], :if => Proc.new { |a| a.published? && a.profile.is_a?(Community) && !a.image? } + # xss_terminate plugin can't sanitize array fields before_save :sanitize_tag_list diff --git a/app/models/comment.rb b/app/models/comment.rb index 82c0e53..b8a92ca 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,7 @@ class Comment < ActiveRecord::Base - + + track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"] + validates_presence_of :title, :body belongs_to :article, :counter_cache => true belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' diff --git a/app/models/community.rb b/app/models/community.rb index 4285efb..f18e2b7 100644 --- a/app/models/community.rb +++ b/app/models/community.rb @@ -67,4 +67,12 @@ class Community < Organization def blocks_to_expire_cache [MembersBlock] end + + def each_member(offset=0) + while member = self.members.first(:order => :id, :offset => offset) + yield member + offset = offset + 1 + end + end + end diff --git a/app/models/friendship.rb b/app/models/friendship.rb index 6afc8a7..57b2e21 100644 --- a/app/models/friendship.rb +++ b/app/models/friendship.rb @@ -1,5 +1,6 @@ class Friendship < ActiveRecord::Base + track_actions :new_friendship, :after_create, :keep_params => ["friend.name", "friend.url", "friend.profile_custom_icon"], :unless => Proc.new { |f| f.friend.is_a_friend?(f.person) } + belongs_to :person, :foreign_key => :person_id belongs_to :friend, :class_name => 'Person', :foreign_key => 'friend_id' - end diff --git a/app/models/person.rb b/app/models/person.rb index b252c8b..d6edf46 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -1,6 +1,7 @@ # A person is the profile of an user holding all relationships with the rest of the system class Person < Profile + acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} acts_as_accessor named_scope :members_of, lambda { |resource| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.resource_type = ? AND role_assignments.resource_id = ?', resource.class.base_class.name, resource.id ] } } @@ -16,6 +17,9 @@ class Person < Profile has_many :mailings + has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC" + has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id + named_scope :more_popular, :select => "#{Profile.qualified_column_names}, count(friend_id) as total", :group => Profile.qualified_column_names, @@ -31,6 +35,23 @@ class Person < Profile self.user.destroy if self.user end + def scraps(scrap=nil) + scrap = scrap.is_a?(Scrap) ? scrap.id : scrap + scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) + end + + def can_control_scrap?(scrap) + begin + !self.scraps(scrap).nil? + rescue + false + end + end + + def can_control_activity?(activity) + self.tracked_notifications.exists?(activity) + end + # Sets the identifier for this person. Raises an exception when called on a # existing person (since peoples' identifiers cannot be changed) def identifier=(value) @@ -305,4 +326,34 @@ class Person < Profile }[amount] || _("%s friends") % amount end + def self.notify_activity(tracked_action) + profile = tracked_action.dispatcher.profile if tracked_action.dispatcher.is_a?(Article) + profile = tracked_action.dispatcher.resource if tracked_action.dispatcher.is_a?(RoleAssignment) + profile = tracked_action.dispatcher.article.profile if tracked_action.dispatcher.is_a?(Comment) + profile_id = profile.nil? ? nil : profile.id + Delayed::Job.enqueue NotifyActivityToProfilesJob.new(tracked_action.id, profile_id) + ActionTrackerNotification.create(:action_tracker => tracked_action, :profile => tracked_action.user) + end + + def is_member_of?(profile) + profile.members.include?(self) + end + + def follows?(profile) + profile.followed_by?(self) + end + + def each_friend(offset=0) + while friend = self.friends.first(:order => :id, :offset => offset) + yield friend + offset = offset + 1 + end + end + + protected + + def followed_by?(profile) + self == profile || self.is_a_friend?(profile) + end + end diff --git a/app/models/profile.rb b/app/models/profile.rb index 3855b0a..5fdb43a 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -87,6 +87,11 @@ class Profile < ActiveRecord::Base :order => "total DESC, total_comments DESC", :conditions => ["articles.created_at BETWEEN ? AND ?", 7.days.ago, DateTime.now] + acts_as_trackable :dependent => :destroy + + has_many :action_tracker_notifications, :foreign_key => 'profile_id' + has_many :tracked_notifications, :through => :action_tracker_notifications, :source => :action_tracker, :order => 'updated_at DESC' + # FIXME ugly workaround def self.human_attribute_name(attrib) _(self.superclass.human_attribute_name(attrib)) @@ -743,8 +748,16 @@ private :generate_url, :url_options }[amount] || _("%s members") % amount end + def profile_custom_icon + self.image.public_filename(:icon) unless self.image.blank? + end + protected + def followed_by?(person) + person.is_member_of?(self) + end + def display_private_info_to?(user) if user.nil? false diff --git a/app/models/scrap.rb b/app/models/scrap.rb new file mode 100644 index 0000000..bc95667 --- /dev/null +++ b/app/models/scrap.rb @@ -0,0 +1,28 @@ +class Scrap < ActiveRecord::Base + validates_presence_of :content + validates_presence_of :sender_id, :receiver_id + + belongs_to :receiver, :class_name => 'Person', :foreign_key => 'receiver_id' + belongs_to :sender, :class_name => 'Person', :foreign_key => 'sender_id' + has_many :replies, :class_name => 'Scrap', :foreign_key => 'scrap_id', :dependent => :destroy + belongs_to :root, :class_name => 'Scrap', :foreign_key => 'scrap_id' + + named_scope :all_scraps, lambda {|profile| {:conditions => ["receiver_id = ? OR sender_id = ?", profile, profile], :limit => 30}} + + named_scope :not_replies, :conditions => {:scrap_id => nil} + + track_actions :leave_scrap, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.receiver != s.sender} + track_actions :leave_scrap_to_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.receiver == s.sender} + + after_create do |scrap| + scrap.root.update_attribute('updated_at', DateTime.now) unless scrap.root.nil? + end + + before_validation :strip_all_html_tags + + def strip_all_html_tags + sanitizer = HTML::WhiteListSanitizer.new + self.content = sanitizer.sanitize(self.content, :tags => []) + end + +end diff --git a/app/models/uploaded_file.rb b/app/models/uploaded_file.rb index e1ed33a..2863ac2 100644 --- a/app/models/uploaded_file.rb +++ b/app/models/uploaded_file.rb @@ -4,6 +4,8 @@ # of the file itself is kept. (FIXME?) class UploadedFile < Article + track_actions :upload_image, :after_create, :keep_params => ["view_url", "thumbnail_path", "parent.url", "parent.name"], :if => Proc.new { |a| a.published? && a.image? && !a.parent.nil? && a.parent.display_as_gallery? } + include ShortFilename settings_items :title, :type => 'string' @@ -14,6 +16,10 @@ class UploadedFile < Article validates_size_of :title, :maximum => 60, :if => (lambda { |file| !file.title.blank? }) + def thumbnail_path + self.image? ? self.full_filename(:thumb).gsub(File.join(RAILS_ROOT, 'public'), '') : nil + end + def display_title title.blank? ? name : title end diff --git a/app/views/profile/_common.rhtml b/app/views/profile/_common.rhtml new file mode 100644 index 0000000..37b0f04 --- /dev/null +++ b/app/views/profile/_common.rhtml @@ -0,0 +1,53 @@ + <% unless @action %> + <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours.from_now) do %> + + + <%= _('Content') %> + + + + <% profile.blogs.each do |blog| %> + + <%= blog.name + ':' %> + + <%= link_to(n_('One post', '%{num} posts', blog.posts.published.count) % { :num => blog.posts.published.count }, blog.url) %> + + + <% end %> + <% profile.image_galleries.each do |gallery| %> + + <%= gallery.name + ':' %> + + <%= link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) %> + + + <% end %> + + + <%= _('Events:') %> + + <%= link_to profile.events.published.count, :controller => 'events', :action => 'events' %> + + + + + <%= _('Tags:') %> + + + <%= tag_cloud @tags, :id, { :action => 'tags' }, :max_size => 18, :min_size => 10%> + + + + <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> + + <%= _('Interests') %> + + <% profile.interests.each do |item| %> + + + <%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %> + + <% end %> + <% end %> + <% end %> + <% end %> diff --git a/app/views/profile/_leave_scrap.rhtml b/app/views/profile/_leave_scrap.rhtml new file mode 100644 index 0000000..67dc049 --- /dev/null +++ b/app/views/profile/_leave_scrap.rhtml @@ -0,0 +1,4 @@ +<%= @message %> +<% unless @scraps.nil? %> + <%= render :partial => 'profile_scraps', :locals => {:scraps => @scraps} %> +<% end %> diff --git a/app/views/profile/_organization.rhtml b/app/views/profile/_organization.rhtml index 9f562a1..e7773b7 100644 --- a/app/views/profile/_organization.rhtml +++ b/app/views/profile/_organization.rhtml @@ -1,35 +1,56 @@ - - <%= _('Basic information')%> - - - - <%= _('Members') %> - - <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %> - - - -<%= display_field(_('Type:'), profile, :privacy_setting, true) %> - -<%= display_field(_('Location:'), profile, :location, true) %> - <%= _('Created at:') %> - <%= show_date(profile.created_at) %> - + +
+ + -<% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %> - - - - <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %> - - -<% end %> + <%= render :partial => 'profile_network' %> - - <%= _('Administrators:') %> - - <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %> +
+ + + + + + + + + + + <%= display_field(_('Type:'), profile, :privacy_setting, true) %> + + <%= display_field(_('Location:'), profile, :location, true) %> + + + + + + + <% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %> + + + + + <% end %> + + + + + + + <%= render :partial => 'common' %> +
<%= _('Basic information')%>
<%= _('Members') %> + <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %> +
<%= _('Created at:') %><%= show_date(profile.created_at) %>
+ <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %> +
<%= _('Administrators:') %> + <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %> +
+
+
diff --git a/app/views/profile/_person.rhtml b/app/views/profile/_person.rhtml index 41b72d5..ddc8084 100644 --- a/app/views/profile/_person.rhtml +++ b/app/views/profile/_person.rhtml @@ -1,71 +1,104 @@ - <%= _('Basic information')%> - -<%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> -<%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> -<%= display_field(_('Location:'), profile, :location, true) %> - -<%= display_field(_('Type:'), profile, :privacy_setting, true) %> - - - <%= _('Created at:') %> - <%= show_date(profile.created_at) %> - - -<% if profile == user || profile.friends.include?(user) %> - - <%= _('Contact')%> - - <%= display_field(_('Address:'), profile, :address) %> - <%= display_field(_('ZIP code:'), profile, :zip_code) %> - <%= display_field(_('Contact phone:'), profile, :contact_phone) %> - <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %> -<% end %> + +
+ + -<% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %> - <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %> - - <%= _('Work')%> - - <% end %> - <%= display_field(_('Organization:'), profile, :organization) %> - <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%> + <% if logged_in? && current_person.follows?(@profile) %> + <%= render :partial => 'profile_wall' %> + <%= render :partial => 'profile_network' %> + <% end %> + <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %> + <%= render :partial => 'profile_activity' %> + <% end %> - <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> - - <%= __('Enterprises') %> - - <% profile.enterprises.each do |item| %> - - - <%= button 'menu-enterprise', item.name, item.url %> - - <% end %> - <% end %> +
+ + + + + <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> + <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> + <%= display_field(_('Location:'), profile, :location, true) %> + + <%= display_field(_('Type:'), profile, :privacy_setting, true) %> + + + + + + + <% if profile == user || profile.friends.include?(user) %> + + + + <%= display_field(_('Address:'), profile, :address) %> + <%= display_field(_('ZIP code:'), profile, :zip_code) %> + <%= display_field(_('Contact phone:'), profile, :contact_phone) %> + <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %> + <% end %> + + <% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %> + <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %> + + + + <% end %> + <%= display_field(_('Organization:'), profile, :organization) %> + <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%> + + + <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> + + + + <% profile.enterprises.each do |item| %> + + + + + <% end %> + <% end %> + + + + + + + + + + + + + + <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> + + + + <% profile.interests.each do |item| %> + + + + + <% end %> + <% end %> - - - - - - - - - - - + <%= render :partial => 'common' %> - <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> - - - - <% profile.interests.each do |item| %> - - - - - <% end %> - <% end %> - -<% end %> +
<%= _('Basic information')%>
<%= _('Created at:') %><%= show_date(profile.created_at) %>
<%= _('Contact')%>
<%= _('Work')%>
<%= __('Enterprises') %>
<%= button 'menu-enterprise', item.name, item.url %>
<%= _('Network')%>
<%= __('Friends') + ':' %><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %>
<%= __('Communities') + ':' %><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %>
<%= _('Interests') %>
<%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %>
<%= _('Network')%>
<%= __('Friends') + ':' %><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %>
<%= __('Communities') + ':' %><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %>
<%= _('Interests') %>
<%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %>
+ <% end %> +
+
+ + diff --git a/app/views/profile/_profile_activities.rhtml b/app/views/profile/_profile_activities.rhtml new file mode 100644 index 0000000..2c62997 --- /dev/null +++ b/app/views/profile/_profile_activities.rhtml @@ -0,0 +1,18 @@ +<% activities.each do |activity| %> +
  • +
    + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> +
    +
    +

    <%= time_ago_in_words(activity.created_at) + ' ' + _('ago') %>

    +

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

    + <%= button_to_remote(:delete, content_tag(:span, _('Remove')), :url =>{:action => 'remove_activity', :activity_id => activity.id}, :update => "profile-activity-item-#{activity.id}") if logged_in? && current_person == @profile %> +
    +
    +
  • +<% end %> +<% 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}" %> +
    +<% end %> diff --git a/app/views/profile/_profile_activity.rhtml b/app/views/profile/_profile_activity.rhtml new file mode 100644 index 0000000..772e39b --- /dev/null +++ b/app/views/profile/_profile_activity.rhtml @@ -0,0 +1,6 @@ +
    +

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

    + +
    diff --git a/app/views/profile/_profile_network.rhtml b/app/views/profile/_profile_network.rhtml new file mode 100644 index 0000000..7022934 --- /dev/null +++ b/app/views/profile/_profile_network.rhtml @@ -0,0 +1,6 @@ +
    +

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

    + +
    diff --git a/app/views/profile/_profile_network_activities.rhtml b/app/views/profile/_profile_network_activities.rhtml new file mode 100644 index 0000000..5177863 --- /dev/null +++ b/app/views/profile/_profile_network_activities.rhtml @@ -0,0 +1,33 @@ + <% network_activities.each do |activity| %> +
  • +
    + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %> + <% if logged_in? && current_person.follows?(activity.user) && current_person != activity.user %> +

    <%= link_to_function _('Scrap'), "hide_and_show(['#profile-network-message-response-#{activity.id}'],['#profile-network-message-#{activity.id}', '#profile-network-form-#{activity.id}']);$('content_#{activity.id}').value='';return false", :class => "profile-send-message icon-scrap" %>

    + <% end %> +
    +
    +

    <%= time_ago_in_words(activity.created_at) + ' ' + _('ago') %>

    +

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

    +
    + +
    +
  • + <% 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}" %> +
    +<% end %> diff --git a/app/views/profile/_profile_scrap.rhtml b/app/views/profile/_profile_scrap.rhtml new file mode 100644 index 0000000..9d5775e --- /dev/null +++ b/app/views/profile/_profile_scrap.rhtml @@ -0,0 +1,50 @@ +
  • +
    + <%= link_to(profile_image(scrap.sender, :minor), scrap.sender.url) %> + <% if logged_in? && current_person.follows?(scrap.sender) && current_person != scrap.sender %> +

    <%= link_to_function _('Scrap'), "hide_and_show(['#profile-wall-message-response-#{scrap.id}'],['#profile-wall-message-#{scrap.id}', '#profile-wall-form-#{scrap.id}']);$('content_#{scrap.id}').value='';return false", :class => "profile-send-message icon-scrap" %>

    + <% end %> +
    +
    +

    <%= link_to scrap.sender.name, scrap.sender.url %>

    +

    <%= time_ago_in_words(scrap.created_at) + ' ' + _('ago') %>

    +

    <%= auto_link_urls scrap.content %>

    + <%= button_to_remote(:delete, content_tag(:span, _('Remove')), :url =>{:action => 'remove_scrap', :scrap_id => scrap.id}, :update => "profile-wall-item-#{scrap.id}") if logged_in? && user.can_control_scrap?(scrap) %> + <% if logged_in? && current_person.follows?(scrap.sender) && scrap.root.nil? %> +

    <%= link_to_function _('Reply'), "hide_and_show(['#profile-wall-reply-response-#{scrap.id}'],['#profile-wall-reply-#{scrap.id}', '#profile-wall-reply-form-#{scrap.id}']);$('reply_content_#{scrap.id}').value='';$('scrap_id_#{scrap.id}').value='#{scrap.id}';return false", :class => "profile-send-reply icon-reply" %>

    + <% end %> + +
    + + +
    +
  • diff --git a/app/views/profile/_profile_scraps.rhtml b/app/views/profile/_profile_scraps.rhtml new file mode 100644 index 0000000..9e07a21 --- /dev/null +++ b/app/views/profile/_profile_scraps.rhtml @@ -0,0 +1,8 @@ +<% scraps.map do |scrap| %> + <%= render :partial => 'profile_scrap', :locals => {:scrap => scrap} %> +<% end %> +<% if scraps.current_page < scraps.total_pages %> +
    + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_scraps', :page => (scraps.current_page + 1)}, :update => "profile_scraps_page_#{scraps.current_page}" %> +
    +<% end %> diff --git a/app/views/profile/_profile_wall.rhtml b/app/views/profile/_profile_wall.rhtml new file mode 100644 index 0000000..8956aea --- /dev/null +++ b/app/views/profile/_profile_wall.rhtml @@ -0,0 +1,14 @@ +
    +

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

    +
    + <%= flash[:error] %> + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %> + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %> + <%= submit_button :scrap, _('Leave a scrap') %> + <% end %> +
    +
    + +
    diff --git a/app/views/profile/index.rhtml b/app/views/profile/index.rhtml index 240459f..504a5db 100644 --- a/app/views/profile/index.rhtml +++ b/app/views/profile/index.rhtml @@ -14,58 +14,4 @@ <%= render :partial => partial_for_class(profile.class) %> - - <% unless @action %> - <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours.from_now) do %> - - - - - <% profile.blogs.each do |blog| %> - - - - - <% end %> - <% profile.image_galleries.each do |gallery| %> - - - - - <% end %> - - - - - - - - - - - <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> - - - - <% profile.interests.each do |item| %> - - - - - <% end %> - <% end %> - <% end %> - <% end %>
    - <%= _('Content') %> -
    <%= blog.name + ':' %> - <%= link_to(n_('One post', '%{num} posts', blog.posts.published.count) % { :num => blog.posts.published.count }, blog.url) %> -
    <%= gallery.name + ':' %> - <%= link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) %> -
    <%= _('Events:') %> - <%= link_to profile.events.published.count, :controller => 'events', :action => 'events' %> -
    - <%= _('Tags:') %> - - <%= tag_cloud @tags, :id, { :action => 'tags' }, :max_size => 18, :min_size => 10%> -
    <%= _('Interests') %>
    <%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %>
    diff --git a/config/initializers/action_tracker.rb b/config/initializers/action_tracker.rb new file mode 100644 index 0000000..b5b95cd --- /dev/null +++ b/config/initializers/action_tracker.rb @@ -0,0 +1,51 @@ +require 'noosfero/i18n' + +# ActionTracker plugin stuff + +ActionTrackerConfig.verbs = { + :create_article => { + :description => _('published %s %s: %s') % ['{{ta.get_name.size}}', '{{_(pluralize_without_count(ta.get_name.size, "article"))}}', '{{ta.collect_group_with_index(:name){ |n,i| link_to(truncate(n), ta.get_url[i])}.to_sentence(:connector => _("and"))}}'], + :type => :groupable + }, + :update_article => { + :description => _('updated %s %s: %s') % ['{{ta.get_name.uniq.size}}', '{{_(pluralize_without_count(ta.get_name.uniq.size, "article"))}}', '{{ta.collect_group_with_index(:name){ |n,i| link_to(truncate(n), ta.get_url[i])}.uniq.to_sentence(:connector => _("and"))}}'], + :type => :groupable + }, + :remove_article => { + :description => _('removed %s %s: %s') % ['{{ta.get_name.size}}', '{{_(pluralize_without_count(ta.get_name.size, "article"))}}', '{{ta.get_name.collect{ |n| truncate(n) }.to_sentence(:connector => _("and"))}}'], + :type => :groupable + }, + :publish_article_in_community => { + :description => _('published %s %s in communities: %s') % ['{{ta.get_name.size}}', '{{_(pluralize_without_count(ta.get_name.size, "article"))}}', '{{ta.collect_group_with_index(:name){ |n, i| link_to(truncate(n), ta.get_url[i]) + " (" + _("in") + " " + link_to(ta.get_profile_name[i], ta.get_profile_url[i]) + ")"}.to_sentence(:connector => _("and"))}}'], + :type => :groupable + }, + :new_friendship => { + :description => _('has made %s %s:
    %s') % ['{{ta.get_friend_name.size}}', '{{_(pluralize_without_count(ta.get_friend_name.size, "new friend"))}}', '{{ta.collect_group_with_index(:friend_name){ |n,i| link_to(content_tag(:img, nil, :src => (ta.get_friend_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png"))), ta.get_friend_url[i], :title => n)}.join}}'], + :type => :groupable + }, + :join_community => { + :description => _('has joined %s %s:
    %s') % ['{{ta.get_resource_name.size}}', '{{_(pluralize_without_count(ta.get_resource_name.size, "community"))}}', '{{ta.collect_group_with_index(:resource_name){ |n,i| link_to(content_tag(:img, nil, :src => (ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png"))), ta.get_resource_url[i], :title => n)}.join}}'], + :type => :groupable + }, + :leave_community => { + :description => _('has left %s %s:
    %s') % ['{{ta.get_resource_name.size}}', '{{_(pluralize_without_count(ta.get_resource_name.size, "community"))}}', '{{ta.collect_group_with_index(:resource_name){ |n,i| link_to(content_tag(:img, nil, :src => (ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png"))), ta.get_resource_url[i], :title => n)}.join}}'], + :type => :groupable + }, + :upload_image => { + :description => _('uploaded %s %s:
    %s
    %s') % ['{{ta.get_view_url.size}}', '{{_(pluralize_without_count(ta.get_view_url.size, "image"))}}', '{{ta.collect_group_with_index(:thumbnail_path){ |t,i| content_tag(:span, link_to(content_tag(:img, nil, :src => t), ta.get_view_url[i]))}.last(3).join}}', '{{unique_with_count(ta.collect_group_with_index(:parent_name){ |n,i| link_to(n, ta.get_parent_url[i])}, _("in the gallery")).join("
    ")}}'], + :type => :groupable + }, + :leave_comment => { + :description => _('has left the following comment "%s" on the article %s:
    "%s" (%s)') % ["{{truncate(ta.get_title)}}", "{{link_to(truncate(ta.get_article_title), ta.get_article_url)}}", "{{truncate(ta.get_body, 50)}}", "{{link_to(_('read'), ta.get_url)}}"] + }, + :leave_scrap => { + :description => _('sent a message to %s:
    "%s"') % ["{{link_to(ta.get_receiver_name, ta.get_receiver_url)}}", "{{auto_link_urls(ta.get_content)}}"] + }, + :leave_scrap_to_self => { + :description => _('wrote:
    "%s"') % "{{auto_link_urls(ta.get_content)}}" + } +} + +ActionTrackerConfig.current_user_method = :current_person + +ActionTrackerConfig.timeout = 24.hours diff --git a/db/migrate/20100910205341_create_scraps.rb b/db/migrate/20100910205341_create_scraps.rb new file mode 100644 index 0000000..e4b599d --- /dev/null +++ b/db/migrate/20100910205341_create_scraps.rb @@ -0,0 +1,14 @@ +class CreateScraps < ActiveRecord::Migration + def self.up + create_table :scraps do |t| + t.text :content + t.integer :sender_id, :receiver_id + t.integer :scrap_id + t.timestamps + end + end + + def self.down + drop_table :scraps + end +end diff --git a/db/migrate/20100910205408_create_action_tracker.rb b/db/migrate/20100910205408_create_action_tracker.rb new file mode 100644 index 0000000..e383f6d --- /dev/null +++ b/db/migrate/20100910205408_create_action_tracker.rb @@ -0,0 +1,21 @@ +class CreateActionTracker < ActiveRecord::Migration + def self.up + create_table :action_tracker do |t| + t.belongs_to :user, :polymorphic => true + t.belongs_to :dispatcher, :polymorphic => true + t.text :params + t.string :verb + t.timestamps + end + + change_table :action_tracker do |t| + t.index [:user_id, :user_type] + t.index [:dispatcher_id, :dispatcher_type] + t.index :verb + end + end + + def self.down + drop_table :action_tracker + end +end diff --git a/db/migrate/20100910205427_create_action_tracker_notifications.rb b/db/migrate/20100910205427_create_action_tracker_notifications.rb new file mode 100644 index 0000000..f05df8a --- /dev/null +++ b/db/migrate/20100910205427_create_action_tracker_notifications.rb @@ -0,0 +1,12 @@ +class CreateActionTrackerNotifications < ActiveRecord::Migration + def self.up + create_table :action_tracker_notifications do |t| + t.references :action_tracker + t.references :profile + end + end + + def self.down + drop_table :action_tracker_notifications + end +end diff --git a/lib/notify_activity_job.rb b/lib/notify_activity_job.rb new file mode 100644 index 0000000..9ce4d54 --- /dev/null +++ b/lib/notify_activity_job.rb @@ -0,0 +1,7 @@ +class NotifyActivityJob < Struct.new(:tracked_action_id, :profile_id) + def perform + tracked_action = ActionTracker::Record.find(tracked_action_id) + profile = Profile.find(profile_id) + ActionTrackerNotification.create(:action_tracker => tracked_action, :profile => profile) + end +end diff --git a/lib/notify_activity_to_profiles_job.rb b/lib/notify_activity_to_profiles_job.rb new file mode 100644 index 0000000..9ce20e0 --- /dev/null +++ b/lib/notify_activity_to_profiles_job.rb @@ -0,0 +1,16 @@ +class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id, :target_profile_id) + def perform + profile = Profile.find(target_profile_id) unless target_profile_id.nil? + tracked_action = ActionTracker::Record.find(tracked_action_id) + tracked_action.user.each_friend do |friend| + Delayed::Job.enqueue NotifyActivityJob.new(tracked_action_id, friend.id) + end + if profile.is_a?(Community) + profile.each_member do |member| + next if member == tracked_action.user + Delayed::Job.enqueue NotifyActivityJob.new(tracked_action_id, member.id) + end + ActionTrackerNotification.create(:action_tracker => tracked_action, :profile => profile) + end + end +end diff --git a/public/designs/icons/tango/style.css b/public/designs/icons/tango/style.css index 5c4a0f6..470b71d 100644 --- a/public/designs/icons/tango/style.css +++ b/public/designs/icons/tango/style.css @@ -71,3 +71,5 @@ .icon-media-next { background-image: url(Tango/16x16/actions/media-skip-forward.png) } .icon-lock { background-image: url(Tango/16x16/actions/lock.png) } .icon-chat { background-image: url(Tango/16x16/apps/internet-group-chat.png) } +.icon-scrap { background-image: url(Tango/16x16/actions/format-justify-left.png) } +.icon-reply { background-image: url(Tango/16x16/actions/edit-redo.png) } diff --git a/public/designs/themes/noosfero/theme.yml b/public/designs/themes/noosfero/theme.yml index b2ceccc..68ae402 100644 --- a/public/designs/themes/noosfero/theme.yml +++ b/public/designs/themes/noosfero/theme.yml @@ -1,3 +1,4 @@ theme: "Noosfero default theme" layout: "application-ng" +jquery_theme: "smoothness_mod" icon_theme: [default, pidgin] diff --git a/public/images/scrap-bg-gray.png b/public/images/scrap-bg-gray.png new file mode 100644 index 0000000..b10351f Binary files /dev/null and b/public/images/scrap-bg-gray.png differ diff --git a/public/images/scrap-bg.png b/public/images/scrap-bg.png new file mode 100644 index 0000000..c2637ab Binary files /dev/null and b/public/images/scrap-bg.png differ diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 9e48a46..2533d70 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -545,3 +545,29 @@ jQuery(function($) { $('body').addClass('webkit'); } }); + +function hide_and_show(hide_elements, show_elements) { + for(i=0; i < hide_elements.length; i++){ + jQuery(hide_elements[i]).hide(); + } + for(i=0; i < show_elements.length; i++){ + jQuery(show_elements[i]).show(); + } +} + +function limited_text_area(textid, limit) { + var text = jQuery('#' + textid).val(); + jQuery('#' + textid).css('height', jQuery('#' + textid).attr('scrollHeight') + 'px'); + var textlength = text.length; + jQuery('#' + textid + '_left span').html(limit - textlength); + if (textlength > limit) { + jQuery('#' + textid + '_left').hide(); + jQuery('#' + textid + '_limit').show(); + jQuery('#' + textid).val(text.substr(0,limit)); + return false; + } else { + jQuery('#' + textid + '_left').show(); + jQuery('#' + textid + '_limit').hide(); + return true; + } +} diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 9ed4fc8..e69bb5d 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -265,7 +265,7 @@ table.profile th { } table.profile td { border: none; - padding-left: 0px; + padding: 0px; } table.profile tr:hover td { background: none; @@ -4617,3 +4617,261 @@ h1#agenda-title { text-align: center; display: block; } + +#profile-activity ul, #profile-network ul, #profile-wall ul { + padding-left: 0; + clear: both; +} + +#profile-activity li, #profile-network li, #profile-wall li { + display: block; + padding: 0; + margin-bottom: 8px; +} + +#profile-activity .profile-activity-image, #profile-network .profile-network-image, #profile-wall .profile-wall-image { + float: left; + width: 100px; + height: 80px; + margin: 0; + padding: 0; + border: 1px solid transparent; + text-align: center; +} + +#profile-activity .profile-activity-description, #profile-network .profile-network-description, #profile-wall .profile-wall-description { + float: left; + min-height: 80px; + margin: 0; + padding: 0; + border: 1px solid #ccc; + width: 355px; + overflow: hidden; + background-color: #fff; + position: relative; +} + +#profile-activity .profile-activity-description .icon-delete span, +#profile-network .profile-network-description .icon-delete span, +#profile-wall .profile-wall-description .icon-delete span { + display: none; +} + +#profile-activity .profile-activity-description .icon-delete, +#profile-network .profile-network-description .icon-delete, +#profile-wall .profile-wall-description .icon-delete { + position: absolute; + top: 4px; + right: 4px; + background-position: center center; +} + +#profile-activity .profile-activity-text, #profile-network .profile-network-text, #profile-wall .profile-wall-text { + font-size: 13px; + margin: 5px; + padding-top: 15px; +} + +#profile-wall .profile-wall-text { + padding-top: 0; +} + +#profile-activity .profile-activity-time, #profile-network .profile-network-time, #profile-wall .profile-wall-time { + font-size: 11px; + margin: 5px; + color: #333; +} + +#profile-activity hr, #profile-network hr, #profile-wall hr { + clear: both; + border: 0; +} + +#profile-activity .profile-activity-send-message, #profile-network .profile-network-send-message, #profile-wall .profile-wall-send-message { + text-decoration: none; + font-size: 11px; + color: #000; + margin: 10px 0 0 0; +} + +#profile-activity .profile-activity-send-message a, #profile-network .profile-network-send-message a, #profile-wall .profile-wall-send-message a { + text-decoration: none; +} + +#profile-activity .profile-activity-text img, #profile-network .profile-network-text img, #profile-wall .profile-wall-text img { + padding: 1px; + border: 1px solid #ccc; + margin: 4px 3px 0 0; +} + +.profile-wall-image, +.leave_scrap_to_self .profile-network-image, +.leave_scrap .profile-network-image, +.leave_scrap_to_self .profile-activity-image, +.leave_scrap .profile-activity-image { + background: transparent url(/images/scrap-bg.png) 104% 10px no-repeat; + position: relative; + left: 1px; + z-index: 2; +} + +#leave_scrap_content { + width: 450px; + border: 1px solid #ccc; + padding: 2px; +} + +#profile-network .upload_image .profile-network-text span, +#profile-activity .upload_image .profile-activity-text span { + width: 109px; + height: 109px; + overflow: hidden; + display: inline-block; + padding: 0; + margin: 4px 3px 0 0; + text-align: center; +} + +#profile-network .upload_image .profile-network-text img, +#profile-activity .upload_image .profile-activity-text img { + padding: 0; + border: 0; + margin: 0; +} + +#leave_scrap_response { + font-size: 12px; + text-align: right; + font-weight: bold; + color: #333; + margin: 4px; +} + +.profile-network-message-response, .profile-wall-message-response { + color: #204a87; + padding: 4px; + color: #333; + text-align: right; + font-size: 12px; +} + +.profile-wall-sender, +.profile-wall-time, +.profile-wall-description, +.profile-activity-sender, +.profile-activity-time, +.profile-activity-description, +.profile-network-sender, +.profile-network-time, +.profile-network-description { + padding-left: 5px; +} + +.profile-network-sender, +.profile-wall-sender { + margin: 2px 0; +} + +#profile-activity .profile-activity-time, +#profile-network .profile-network-time, +#profile-wall .profile-wall-time { + margin: 0; +} + +#profile-network textarea, +#profile-wall textarea { + margin: 4px 0; + width: 453px; + border: 1px solid #ccc; + padding: 2px; + overflow: hidden; +} + +.profile-network-message, +.profile-wall-message { + margin: 0; +} + +.profile-send-message { + color: #555; + border: 1px solid #ccc; + background-color: #eee; + padding: 4px 4px 4px 20px; + background-repeat: no-repeat; + background-position: 4px center; +} + +.ui-widget-content .profile-send-message:hover, +.profile-send-message:hover { + color: #eee; + background-color: #555; +} + +.limited-text-area p { + margin: 0; + font-size: 11px; + color: #333; + text-align: right; +} + +#leave_scrap input.button { + float: right; + margin-bottom: 10px; +} + +#leave_scrap_content_limit, +#leave_scrap_content_left { + float: left; +} + +#leave_scrap { + float: left; + padding: 4px; + border: 1px dotted #aaa; + margin-bottom: 10px; +} + +#leave_scrap textarea { + width: 440px; + overflow: hidden; +} + +.profile-send-reply { + background-color: #eee; + border: 1px solid #aaa; + padding: 2px; + padding-left: 20px; + background-repeat: no-repeat; + background-position: 2px center; + color: #aaa; + text-decoration: none; + margin-left: 8px; +} + +#content .profile-send-reply:hover { + text-decoration: none; +} + +#profile-wall .profile-wall-description .profile-wall-image { + background-image: url(/images/scrap-bg-gray.png); +} + +#profile-wall .profile-wall-description .profile-wall-description { + width: 242px; + background-color: #eee; +} + +#profile-network .profile-wall-description textarea, +#profile-wall .profile-wall-description textarea { + width: 338px; + margin-left: 2px; +} + +#profile-network .profile-wall-description .limited-text-area p, +#profile-wall .profile-wall-description .limited-text-area p { + margin-right: 10px; +} + +.limited-text-area div.fieldWithErrors { + background: transparent; +} diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-anim_basic_16x16.gif b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-anim_basic_16x16.gif new file mode 100644 index 0000000..085ccae Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-anim_basic_16x16.gif differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000..5b5dab2 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_75_ffffff_40x100.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000..ac8b229 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_75_ffffff_40x100.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_55_fbf9ee_1x400.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000..ad3d634 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_65_ffffff_1x400.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000..42ccba2 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_dadada_1x400.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000..5a46b47 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_dadada_1x400.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_e6e6e6_1x400.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 0000000..86c2baa Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_e6e6e6_1x400.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_95_fef1ec_1x400.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000..4443fdc Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_95_fef1ec_1x400.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000..7c9fa6c Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_222222_256x240.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000..b273ff1 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_222222_256x240.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_2e83ff_256x240.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000..09d1cdc Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_2e83ff_256x240.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_454545_256x240.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000..59bd45b Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_454545_256x240.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_888888_256x240.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_888888_256x240.png new file mode 100644 index 0000000..6d02426 Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_888888_256x240.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_cd0a0a_256x240.png b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000..2ab019b Binary files /dev/null and b/public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_cd0a0a_256x240.png differ diff --git a/public/stylesheets/jquery.ui/smoothness_mod/jquery-ui-1.8.2.custom.css b/public/stylesheets/jquery.ui/smoothness_mod/jquery-ui-1.8.2.custom.css new file mode 100644 index 0000000..3fb1f3e --- /dev/null +++ b/public/stylesheets/jquery.ui/smoothness_mod/jquery-ui-1.8.2.custom.css @@ -0,0 +1,489 @@ +/* +* jQuery UI CSS Framework +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* +* jQuery UI CSS Framework +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +*/ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { border-bottom: 1px solid #aaaaaa; color: #222222; font-weight: bold; } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all { -moz-border-radius-topleft: 4px; -webkit-border-radius-topleft: 4px; border-radius-topleft: 4px; -moz-border-radius-topright: 4px; -webkit-border-radius-topright: 4px; border-radius-topright: 4px;} + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* Resizable +----------------------------------*/ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Selectable +----------------------------------*/ +.ui-selectable-helper { border:1px dotted black } +/* Accordion +----------------------------------*/ +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } +/* IE7-/Win - Fix extra vertical space in lists */ +.ui-accordion a { zoom: 1; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete +----------------------------------*/ +.ui-autocomplete { position: absolute; cursor: default; } +.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* Menu +----------------------------------*/ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} +/* Button +----------------------------------*/ + +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button { padding: .4em 1em; } + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ + + + + + +/* Dialog +----------------------------------*/ +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* Slider +----------------------------------*/ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs +----------------------------------*/ +.ui-tabs { position: relative; padding: 0; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; border: 1px solid #aaa; border-top: 0; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* Datepicker +----------------------------------*/ +.ui-datepicker { width: 17em; padding: .2em .2em 0; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* Progressbar +----------------------------------*/ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } diff --git a/test/action_tracker_test_helper.rb b/test/action_tracker_test_helper.rb new file mode 100644 index 0000000..bc6fea3 --- /dev/null +++ b/test/action_tracker_test_helper.rb @@ -0,0 +1,16 @@ +class UserStampSweeper < ActionController::Caching::Sweeper + private + def current_user + Person.first + end +end + +module ActionTracker + class Record + def back_in_time(time = 25.hours) + self.updated_at = Time.now.ago(time) + self.send :update_without_callbacks + self + end + end +end diff --git a/test/factories.rb b/test/factories.rb index 5bb7dce..61ecb4c 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -1,7 +1,7 @@ module Noosfero::Factory def fast_create(name, attrs = {}, options = {}) - data = defaults_for(name).merge(attrs) + data = defaults_for(name.to_s.gsub('::','')).merge(attrs) klass = name.to_s.camelize.constantize if klass.superclass != ActiveRecord::Base data[:type] = klass.to_s @@ -360,4 +360,44 @@ module Noosfero::Factory alias :defaults_for_certifier :defaults_for_qualifier + ############################################### + # Scrap + ############################################### + + def defaults_for_scrap(params = {}) + { :content => 'soment content ', :sender_id => 1, :receiver_id => 1, :created_at => DateTime.now }.merge(params) + end + + ############################################### + # ActionTrackerNotification + ############################################### + + def defaults_for_action_tracker_notification(params = {}) + { :action_tracker_id => 1, :profile_id => 1 }.merge(params) + end + + ############################################### + # ActionTracker + ############################################### + + def defaults_for_action_tracker_record(params = {}) + { :created_at => DateTime.now, :verb => :leave_comment, :user_type => 'Profile', :user_id => 1 }.merge(params) + end + + ############################################### + # Friendship + ############################################### + + def defaults_for_friendship(params = {}) + { :created_at => DateTime.now, :person_id => 1, :friend_id => 2 }.merge(params) + end + + ############################################### + # RoleAssignment + ############################################### + + def defaults_for_role_assignment(params = {}) + { :role_id => 1, :accessor_id => 1, :accessor_type => 'Profile', :resource_id => 2, :resource_type => 'Profile' }.merge(params) + end + end diff --git a/test/functional/profile_controller_test.rb b/test/functional/profile_controller_test.rb index 23e0a45..d4d402d 100644 --- a/test/functional/profile_controller_test.rb +++ b/test/functional/profile_controller_test.rb @@ -709,4 +709,496 @@ class ProfileControllerTest < Test::Unit::TestCase assert_match(/Last post/, @response.body) # Latest post must come in the feed end + should "be logged in to leave a scrap" do + count = Scrap.count + post :leave_scrap, :profile => profile.identifier, :scrap => {:content => 'something'} + assert_equal count, Scrap.count + assert_redirected_to :controller => 'account', :action => 'login' + end + + should "leave a scrap in the own profile" do + login_as(profile.identifier) + count = Scrap.count + assert profile.scraps_received.empty? + post :leave_scrap, :profile => profile.identifier, :scrap => {:content => 'something'} + assert_equal count + 1, Scrap.count + assert_response :success + assert_equal "Message successfully sent.", assigns(:message) + profile.reload + assert !profile.scraps_received.empty? + end + + should "leave a scrap on another profile" do + login_as(profile.identifier) + count = Scrap.count + another_person = fast_create(Person) + assert another_person.scraps_received.empty? + post :leave_scrap, :profile => another_person.identifier, :scrap => {:content => 'something'} + assert_equal count + 1, Scrap.count + assert_response :success + assert_equal "Message successfully sent.", assigns(:message) + another_person.reload + assert !another_person.scraps_received.empty? + end + + should "the owner of scrap could remove it" do + login_as(profile.identifier) + scrap = fast_create(Scrap, :sender_id => profile.id) + count = Scrap + assert_difference Scrap, :count, -1 do + post :remove_scrap, :profile => profile.identifier, :scrap_id => scrap.id + end + end + + should "the receiver scrap remove it" do + login_as(profile.identifier) + scrap = fast_create(Scrap, :receiver_id => profile.id) + count = Scrap + assert_difference Scrap, :count, -1 do + post :remove_scrap, :profile => profile.identifier, :scrap_id => scrap.id + end + end + + should "not remove others scraps" do + login_as(profile.identifier) + person = fast_create(Person) + scrap = fast_create(Scrap, :sender_id => person.id, :receiver_id => person.id) + count = Scrap + assert_difference Scrap, :count, 0 do + post :remove_scrap, :profile => profile.identifier, :scrap_id => scrap.id + end + end + + should "be logged in to remove a scrap" do + count = Scrap.count + post :remove_scrap, :profile => profile.identifier, :scrap => {:content => 'something'} + assert_equal count, Scrap.count + assert_redirected_to :controller => 'account', :action => 'login' + end + + should "not remove an scrap of another user" do + login_as(profile.identifier) + person = fast_create(Person) + scrap = fast_create(Scrap, :receiver_id => person.id) + count = Scrap.count + post :remove_scrap, :profile => person.identifier, :scrap_id => scrap.id + assert_equal count, Scrap.count + end + + should "the sender be the logged user by default" do + login_as(profile.identifier) + count = Scrap.count + another_person = fast_create(Person) + post :leave_scrap, :profile => another_person.identifier, :scrap => {:content => 'something'} + last = Scrap.last + assert_equal profile, last.sender + end + + should "the receiver be the current profile by default" do + login_as(profile.identifier) + count = Scrap.count + another_person = fast_create(Person) + post :leave_scrap, :profile => another_person.identifier, :scrap => {:content => 'something'} + last = Scrap.last + assert_equal another_person, last.receiver + end + + should "report to user the scrap errors on creation" do + login_as(profile.identifier) + count = Scrap.count + post :leave_scrap, :profile => profile.identifier, :scrap => {:content => ''} + assert_response :success + assert_equal "You can't leave an empty message.", assigns(:message) + end + + should 'see all activities of the current profile' do + p1= Person.first + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + p3= fast_create(Person) + assert !p1.is_a_friend?(p3) + ActionTracker::Record.destroy_all + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1)) + a1 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3)) + a2 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p3) + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1)) + a3 = ActionTracker::Record.last + login_as(profile.identifier) + get :index, :profile => p1.identifier + assert_not_nil assigns(:activities) + assert_equal [a1], assigns(:activities) + end + + should 'see the activities_items paginated' do + p1= Person.first + ActionTracker::Record.destroy_all + 40.times{Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))} + login_as(p1.identifier) + get :index, :profile => p1.identifier + assert_equal 30, assigns(:activities).count + end + + should 'see not see the friends activities in the current profile activity' do + p1= Person.first + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + p3= fast_create(Person) + p1.add_friend(p3) + assert p1.is_a_friend?(p3) + ActionTracker::Record.destroy_all + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1)) + a1 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3)) + a2 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p3) + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1)) + a3 = ActionTracker::Record.last + login_as(profile.identifier) + get :index, :profile => p1.identifier + assert_not_nil assigns(:activities) + assert_equal [a1], assigns(:activities) + end + + should 'see all the activities in the current profile network' do + p1= Person.first + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + p3= fast_create(Person) + p3.add_friend(p1) + assert p3.is_a_friend?(p1) + ActionTracker::Record.destroy_all + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1)) + a1 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3)) + a2 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p3) + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1)) + a3 = ActionTracker::Record.last + + + @controller.stubs(:logged_in?).returns(true) + user = mock() + user.stubs(:person).returns(p3) + user.stubs(:login).returns('some') + @controller.stubs(:current_user).returns(user) + Person.any_instance.stubs(:follows?).returns(true) + + process_delayed_job_queue + get :index, :profile => p1.identifier + assert_not_nil assigns(:network_activities) + assert_equal [], [a1,a3] - assigns(:network_activities) + assert_equal assigns(:network_activities) - [a1, a3], [] + end + + should 'the network activity be visible only to profile followers' do + p1= Person.first + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + p3= fast_create(Person) + p3.add_friend(p1) + assert p3.is_a_friend?(p1) + ActionTracker::Record.destroy_all + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1)) + a1 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3)) + a2 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p3) + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1)) + a3 = ActionTracker::Record.last + + @controller.stubs(:logged_in?).returns(true) + user = mock() + user.stubs(:person).returns(p2) + user.stubs(:login).returns('some') + @controller.stubs(:current_user).returns(user) + get :index, :profile => p1.identifier + assert_equal [], assigns(:network_activities) + + user = mock() + user.stubs(:person).returns(p3) + user.stubs(:login).returns('some') + @controller.stubs(:current_user).returns(user) + Person.any_instance.stubs(:follows?).returns(true) + process_delayed_job_queue + get :index, :profile => p3.identifier + assert_equal [], [a1,a3] - assigns(:network_activities) + assert_equal assigns(:network_activities) - [a1, a3], [] + end + + should 'the network activity be paginated' do + p1= Person.first + 40.times{Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))} + + @controller.stubs(:logged_in?).returns(true) + user = mock() + user.stubs(:person).returns(p1) + user.stubs(:login).returns('some') + @controller.stubs(:current_user).returns(user) + get :index, :profile => p1.identifier + assert_equal 30, assigns(:network_activities).count + end + + should 'the network activity be visible only to logged users' do + p1= ActionTracker::Record.current_user_from_model + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + p3= fast_create(Person) + p3.add_friend(p1) + assert p3.is_a_friend?(p1) + ActionTracker::Record.destroy_all + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1)) + a1 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3)) + a2 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p3) + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1)) + a3 = ActionTracker::Record.last + + login_as(profile.identifier) + get :index, :profile => p1.identifier + assert_equal [], assigns(:network_activities) + assert_response :success + assert_template 'index' + + get :index, :profile => p2.identifier + assert_equal [], assigns(:network_activities) + assert_response :success + assert_template 'index' + + get :index, :profile => p3.identifier + assert_equal [], assigns(:network_activities) + assert_response :success + assert_template 'index' + end + + should 'the network activity be visible to uses not logged in on communities and enteprises' do + p1= Person.first + community = fast_create(Community) + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + community.add_member(p1) + community.add_member(p2) + ActionTracker::Record.destroy_all + Article.create! :name => 'a', :profile_id => community.id + Article.create! :name => 'b', :profile_id => community.id + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Article.create! :name => 'c', :profile_id => community.id + process_delayed_job_queue + + get :index, :profile => community.identifier + assert_not_equal [], assigns(:network_items) + assert_response :success + assert_template 'index' + end + + should 'the network activity be paginated on communities' do + community = fast_create(Community) + at = fast_create(ActionTracker::Record, :user_id => profile.id) + 40.times{ fast_create(ActionTrackerNotification, :profile_id => community.id, :action_tracker_id => at.id) } + get :index, :profile => community.identifier + assert_equal 30, assigns(:network_activities).count + end + + should 'the self activity not crashes with user not logged in' do + p1= Person.first + p2= fast_create(Person) + assert !p1.is_a_friend?(p2) + p3= fast_create(Person) + p3.add_friend(p1) + assert p3.is_a_friend?(p1) + ActionTracker::Record.destroy_all + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1)) + a1 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p2) + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3)) + a2 = ActionTracker::Record.last + UserStampSweeper.any_instance.stubs(:current_user).returns(p3) + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1)) + a3 = ActionTracker::Record.last + + get :index, :profile => p1.identifier + assert_response :success + assert_template 'index' + end + + should 'have wall_itens defined' do + p1= ActionTracker::Record.current_user_from_model + get :index, :profile => p1.identifier + assert_equal [], assigns(:wall_items) + end + + should 'the wall_itens be the received scraps' do + p1 = ActionTracker::Record.current_user_from_model + p2 = fast_create(Person) + p3 = fast_create(Person) + s1 = fast_create(Scrap, :sender_id => p1.id, :receiver_id => p2.id) + s2 = fast_create(Scrap, :sender_id => p2.id, :receiver_id => p1.id) + s3 = fast_create(Scrap, :sender_id => p3.id, :receiver_id => p1.id) + + @controller.stubs(:logged_in?).returns(true) + user = mock() + user.stubs(:person).returns(p1) + user.stubs(:login).returns('some') + @controller.stubs(:current_user).returns(user) + Person.any_instance.stubs(:follows?).returns(true) + get :index, :profile => p1.identifier + assert_equal [s2,s3], assigns(:wall_items) + end + + should 'the wall_itens be paginated' do + p1 = Person.first + 40.times{fast_create(Scrap, :sender_id => p1.id)} + + @controller.stubs(:logged_in?).returns(true) + user = mock() + user.stubs(:person).returns(p1) + user.stubs(:login).returns('some') + @controller.stubs(:current_user).returns(user) + Person.any_instance.stubs(:follows?).returns(true) + assert_equal 40, p1.scraps_received.not_replies.count + get :index, :profile => p1.identifier + assert_equal 30, assigns(:wall_items).count + end + + should "the owner of activity could remove it" do + login_as(profile.identifier) + at = fast_create(ActionTracker::Record, :user_id => profile.id) + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) + assert_difference ActionTrackerNotification, :count, -1 do + post :remove_activity, :profile => profile.identifier, :activity_id => at.id + end + end + + should "not remove others activities" do + login_as(profile.identifier) + person = fast_create(Person) + at = fast_create(ActionTracker::Record, :user_id => profile.id) + atn = fast_create(ActionTrackerNotification, :profile_id => person.id, :action_tracker_id => at.id) + count = ActionTrackerNotification + assert_difference ActionTrackerNotification, :count, 0 do + post :remove_activity, :profile => profile.identifier, :activity_id => at.id + end + end + + should "be logged in to remove the activity" do + at = fast_create(ActionTracker::Record, :user_id => profile.id) + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) + count = ActionTrackerNotification.count + post :remove_activity, :profile => profile.identifier, :activity_id => at.id + assert_equal count, ActionTrackerNotification.count + assert_redirected_to :controller => 'account', :action => 'login' + end + + should "not remove an activity of another user" do + login_as(profile.identifier) + person = fast_create(Person) + at = fast_create(ActionTracker::Record, :user_id => profile.id) + atn = fast_create(ActionTrackerNotification, :profile_id => person.id, :action_tracker_id => at.id) + count = ActionTrackerNotification.count + post :remove_activity, :profile => person.identifier, :activity_id => at.id + assert_equal count, ActionTrackerNotification.count + end + + should "not show the scrap button on network activity if the user don't follow the user" do + login_as(profile.identifier) + person = fast_create(Person) + at = fast_create(ActionTracker::Record, :user_id => person.id) + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) + get :index, :profile => profile.identifier + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-network-send-message'} + + person.add_friend(profile) + get :index, :profile => profile.identifier + assert_tag :tag => 'p', :attributes => {:class => 'profile-network-send-message'} + end + + should "not show the scrap button on network activity if the user is himself" do + login_as(profile.identifier) + at = fast_create(ActionTracker::Record, :user_id => profile.id) + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) + get :index, :profile => profile.identifier + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-network-send-message'} + end + + should "not show the scrap button on wall activity if the user don't follow the user" do + login_as(profile.identifier) + person = fast_create(Person) + scrap = fast_create(Scrap, :sender_id => person.id, :receiver_id => profile.id) + get :index, :profile => profile.identifier + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-wall-send-message'} + + person.add_friend(profile) + get :index, :profile => profile.identifier + assert_tag :tag => 'p', :attributes => {:class => 'profile-wall-send-message'} + end + + should "not show the scrap button on wall activity if the user is himself" do + login_as(profile.identifier) + scrap = fast_create(Scrap, :sender_id => profile.id, :receiver_id => profile.id) + get :index, :profile => profile.identifier + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-wall-send-message'} + end + + should "not show the activities to offline users if the profile is private" do + at = fast_create(ActionTracker::Record, :user_id => profile.id) + profile.public_profile=false + profile.save + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) + get :index, :profile => profile.identifier + assert_equal [at], profile.tracked_actions + assert_no_tag :tag => 'li', :attributes => {:id => "profile-activity-item-#{atn.id}"} + end + + should "view more scraps paginate the scraps" do + login_as(profile.identifier) + 40.times{fast_create(Scrap, :receiver_id => profile.id)} + get :view_more_scraps, :profile => profile.identifier, :page => 2 + assert_response :success + assert_template '_profile_scraps' + assert_equal 10, assigns(:scraps).count + end + + should "be logged in to access the view_more_scraps action" do + get :view_more_scraps, :profile => profile.identifier + assert_redirected_to :controller => 'account', :action => 'login' + end + + should "view more activities paginated" do + login_as(profile.identifier) + 40.times{ fast_create(ActionTracker::Record, :user_id => profile.id)} + assert_equal 40, profile.tracked_actions.count + get :view_more_activities, :profile => profile.identifier, :page => 2 + assert_response :success + assert_template '_profile_activities' + assert_equal 10, assigns(:activities).count + end + + should "be logged in to access the view_more_activities action" do + get :view_more_activities, :profile => profile.identifier + assert_redirected_to :controller => 'account', :action => 'login' + end + + should "view more network activities paginated" do + login_as(profile.identifier) + at = fast_create(ActionTracker::Record, :user_id => profile.id) + 40.times{fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) } + assert_equal 40, profile.tracked_notifications.count + get :view_more_network_activities, :profile => profile.identifier, :page => 2 + assert_response :success + assert_template '_profile_network_activities' + assert_equal 10, assigns(:activities).count + end + + should "be logged in to access the view_more_network_activities action" do + get :view_more_network_activities, :profile => profile.identifier + assert_redirected_to :controller => 'account', :action => 'login' + end + end diff --git a/test/test_helper.rb b/test/test_helper.rb index 50dad41..a77dd13 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -8,6 +8,7 @@ require 'hpricot' require 'noosfero/test' require File.dirname(__FILE__) + '/factories' require File.dirname(__FILE__) + '/noosfero_doc_test' +require File.dirname(__FILE__) + '/action_tracker_test_helper' FileUtils.rm_rf(File.join(RAILS_ROOT, 'index', 'test')) diff --git a/test/unit/action_tracker_notification_test.rb b/test/unit/action_tracker_notification_test.rb new file mode 100644 index 0000000..c0128ef --- /dev/null +++ b/test/unit/action_tracker_notification_test.rb @@ -0,0 +1,51 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ActionTrackerNotificationTest < ActiveSupport::TestCase + + should "have the profile" do + a = ActionTrackerNotification.new + a.valid? + assert a.errors.invalid?(:profile_id) + + a.profile_id= 1 + a.valid? + assert !a.errors.invalid?(:profile_id) + end + + should "have the action tracker" do + a = ActionTrackerNotification.new + a.valid? + assert a.errors.invalid?(:action_tracker_id) + + a.action_tracker_id= 1 + a.valid? + assert !a.errors.invalid?(:action_tracker_id) + end + + should "be associated to Person" do + person = fast_create(Person) + a = ActionTrackerNotification.new + assert_nothing_raised do + a.profile = person + end + end + + should "be associated to ActionTracker" do + action_tracker = ActionTracker::Record.new + a = ActionTrackerNotification.new + assert_nothing_raised do + a.action_tracker= action_tracker + end + end + + should "destroy the notifications if the activity is destroyed" do + action_tracker = fast_create(ActionTracker::Record) + count = ActionTrackerNotification.count + fast_create(ActionTrackerNotification, :action_tracker_id => action_tracker.id) + fast_create(ActionTrackerNotification, :action_tracker_id => action_tracker.id) + fast_create(ActionTrackerNotification, :action_tracker_id => action_tracker.id) + action_tracker.destroy + assert_equal count, ActionTrackerNotification.count + end + +end diff --git a/test/unit/application_helper_test.rb b/test/unit/application_helper_test.rb index be71020..e974459 100644 --- a/test/unit/application_helper_test.rb +++ b/test/unit/application_helper_test.rb @@ -484,7 +484,7 @@ class ApplicationHelperTest < Test::Unit::TestCase person.stubs(:url).returns('url for person') person.stubs(:public_profile_url).returns('url for person') links = links_for_balloon(person) - assert_equal ['Home Page', 'Profile', 'Friends', 'Communities'], links.map{|i| i.keys.first} + assert_equal ['Home Page', 'Wall', 'Friends', 'Communities'], links.map{|i| i.keys.first} end should 'return ordered list of links to balloon to Community' do @@ -495,7 +495,7 @@ class ApplicationHelperTest < Test::Unit::TestCase community.stubs(:url).returns('url for community') community.stubs(:public_profile_url).returns('url for community') links = links_for_balloon(community) - assert_equal ['Home Page', 'Profile', 'Members', 'Agenda'], links.map{|i| i.keys.first} + assert_equal ['Home Page', 'Wall', 'Members', 'Agenda'], links.map{|i| i.keys.first} end should 'return ordered list of links to balloon to Enterprise' do @@ -577,6 +577,16 @@ class ApplicationHelperTest < Test::Unit::TestCase assert_not_nil mime end + should 'pluralize without count' do + assert_equal "tests", pluralize_without_count(2, "test") + assert_equal "test", pluralize_without_count(1, "test") + assert_equal "testes", pluralize_without_count(2, "test", "testes") + end + + should 'unique with count' do + assert_equal ["1 for b", "2 for c", "3 for a"], unique_with_count(%w(a b c a c a)) + end + protected def url_for(args = {}) diff --git a/test/unit/article_test.rb b/test/unit/article_test.rb index 924816a..0f9822d 100644 --- a/test/unit/article_test.rb +++ b/test/unit/article_test.rb @@ -918,4 +918,80 @@ class ArticleTest < Test::Unit::TestCase assert_equal '', a.lead end + should 'track action when a published article is created outside a community' do + article = Article.create! :name => 'Tracked Article', :profile_id => profile.id + assert article.published? + assert_kind_of Person, article.profile + ta = ActionTracker::Record.last + assert_equal 'Tracked Article', ta.get_name.last + assert_equal article.url, ta.get_url.last + assert_kind_of Person, ta.user + ta.back_in_time(26.hours) + article = Article.create! :name => 'Another Tracked Article', :profile_id => profile.id + ta = ActionTracker::Record.last + assert_equal ['Another Tracked Article'], ta.get_name + assert_equal [article.url], ta.get_url + end + + should 'track action when a published article is created in a community' do + community = fast_create(Community) + p1 = ActionTracker::Record.current_user_from_model + p2 = fast_create(Person) + p3 = fast_create(Person) + community.add_member(p1) + community.add_member(p2) + assert p1.is_member_of?(community) + assert p2.is_member_of?(community) + assert !p3.is_member_of?(community) + Article.destroy_all + ActionTracker::Record.destroy_all + article = Article.create! :name => 'Tracked Article', :profile_id => community.id + assert article.published? + assert_kind_of Community, article.profile + ta = ActionTracker::Record.last + assert_equal 'Tracked Article', ta.get_name.last + assert_equal article.url, ta.get_url.last + assert_kind_of Person, ta.user + process_delayed_job_queue + assert_equal 3, ActionTrackerNotification.count + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [p1,p2,community].include?(profile) + end + end + + should 'track action when a published article is updated' do + a = Article.create! :name => 'a', :profile_id => profile.id + a.update_attributes! :name => 'b' + ta = ActionTracker::Record.last + assert_equal ['b'], ta.get_name + assert_equal [a.reload.url], ta.get_url + a.update_attributes! :name => 'c' + ta = ActionTracker::Record.last + assert_equal ['b','c'], ta.get_name + assert_equal [a.url,a.reload.url], ta.get_url + a.update_attributes! :body => 'test' + ta = ActionTracker::Record.last + assert_equal ['b','c','c'], ta.get_name + assert_equal [a.url,a.reload.url,a.reload.url], ta.get_url + a.update_attributes! :hits => 50 + ta = ActionTracker::Record.last + assert_equal ['b','c','c'], ta.get_name + assert_equal [a.url,a.reload.url,a.reload.url], ta.get_url + end + + should 'track action when a published article is removed' do + a = Article.create! :name => 'a', :profile_id => profile.id + a.destroy + ta = ActionTracker::Record.last + assert_equal ['a'], ta.get_name + a = Article.create! :name => 'b', :profile_id => profile.id + a.destroy + ta = ActionTracker::Record.last + assert_equal ['a','b'], ta.get_name + a = Article.create! :name => 'c', :profile_id => profile.id, :published => false + a.destroy + ta = ActionTracker::Record.last + assert_equal ['a','b'], ta.get_name + end + end diff --git a/test/unit/comment_test.rb b/test/unit/comment_test.rb index 97f4558..5e90879 100644 --- a/test/unit/comment_test.rb +++ b/test/unit/comment_test.rb @@ -214,4 +214,16 @@ class CommentTest < Test::Unit::TestCase assert File.exists?(File.join(Rails.root, 'public', image)), "#{image} does not exist." end + should 'track action when comment is created' do + owner = create_user('testuser').person + article = owner.articles.create!(:name => 'test', :body => '...') + comment = article.comments.create!(:article => article, :name => 'foo', :title => 'bar', :body => 'my comment', :email => 'cracker@test.org') + ta = ActionTracker::Record.last + assert_equal 'bar', ta.get_title + assert_equal 'my comment', ta.get_body + assert_equal 'test', ta.get_article_title + assert_equal article.url, ta.get_article_url + assert_equal comment.url, ta.get_url + end + end diff --git a/test/unit/community_test.rb b/test/unit/community_test.rb index a6de20f..18382af 100644 --- a/test/unit/community_test.rb +++ b/test/unit/community_test.rb @@ -215,4 +215,63 @@ class CommunityTest < Test::Unit::TestCase assert_no_match /[<>]/, community.description end + should "the followed_by method be protected and true to the community members by default" do + c = fast_create(Community) + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + + assert !p1.is_member_of?(c) + c.add_member(p1) + assert p1.is_member_of?(c) + + assert !p3.is_member_of?(c) + c.add_member(p3) + assert p3.is_member_of?(c) + + assert_equal true, c.send(:followed_by?,p1) + assert_equal true, c.send(:followed_by?,p3) + assert_equal false, c.send(:followed_by?,p2) + end + + should "be created an tracked action when the user is join to the community" do + p1 = Person.first + community = fast_create(Community) + p2 = fast_create(Person) + p3 = fast_create(Person) + + RoleAssignment.delete_all + ActionTrackerNotification.delete_all + RoleAssignment.any_instance.stubs(:role_id).returns(3) + assert_difference(ActionTrackerNotification, :count, 3) do + community.add_member(p1) + community.add_member(p3) + assert p1.is_member_of?(community) + assert !p2.is_member_of?(community) + assert p3.is_member_of?(community) + process_delayed_job_queue + end + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [community,p1,p3].include?(profile) + end + end + + should "be created an tracked action to the community when an community's article is commented" do + ActionTrackerNotification.delete_all + p1 = Person.first + community = fast_create(Community) + p2 = fast_create(Person) + p3 = fast_create(Person) + community.add_member(p3) + article = fast_create(Article, :profile_id => community.id) + ActionTracker::Record.destroy_all + assert_difference(ActionTrackerNotification, :count, 3) do + Comment.create!(:article_id => article.id, :title => 'some', :body => 'some', :author_id => p2.id) + process_delayed_job_queue + end + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [community,p1,p3].include?(profile) + end + end + end diff --git a/test/unit/enterprise_test.rb b/test/unit/enterprise_test.rb index 3c150d5..a7b580b 100644 --- a/test/unit/enterprise_test.rb +++ b/test/unit/enterprise_test.rb @@ -398,4 +398,24 @@ class EnterpriseTest < Test::Unit::TestCase assert_equal product.inputs, enterprise.inputs end + should "the followed_by? be true only to members" do + e = fast_create(Enterprise) + e.stubs(:closed?).returns(false) + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + + assert !p1.is_member_of?(e) + e.add_member(p1) + assert p1.is_member_of?(e) + + assert !p3.is_member_of?(e) + e.add_member(p3) + assert p3.is_member_of?(e) + + assert_equal true, e.send(:followed_by?,p1) + assert_equal true, e.send(:followed_by?,p3) + assert_equal false, e.send(:followed_by?,p2) + end + end diff --git a/test/unit/friendship_test.rb b/test/unit/friendship_test.rb index 5365cac..5917523 100644 --- a/test/unit/friendship_test.rb +++ b/test/unit/friendship_test.rb @@ -22,4 +22,24 @@ class FriendshipTest < Test::Unit::TestCase end + should 'create tracked action' do + f = Friendship.create! :person => create_user('a').person, :friend => create_user('b').person + ta = ActionTracker::Record.last + person = Person.first + assert_equal person.name, ta.user.name + assert_equal 'b', ta.get_friend_name[0] + f = Friendship.create! :person => create_user('a').person, :friend => create_user('c').person + ta = ActionTracker::Record.last + assert_equal person.name, ta.user.name + assert_equal 'c', ta.get_friend_name[1] + end + + should 'create tracked action only if they are not friends yet' do + a, b = create_user('a').person, create_user('b').person + f = Friendship.create! :person => a, :friend => b + assert_equal ['b'], ActionTracker::Record.last.get_friend_name + f = Friendship.create! :person => b, :friend => a + assert_equal ['b'], ActionTracker::Record.last.get_friend_name + end + end diff --git a/test/unit/notify_activity_job_test.rb b/test/unit/notify_activity_job_test.rb new file mode 100644 index 0000000..77bb649 --- /dev/null +++ b/test/unit/notify_activity_job_test.rb @@ -0,0 +1,18 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class NotifyActivityJobTest < ActiveSupport::TestCase + + should 'create the ActionTrackerNotification' do + action_tracker = fast_create(ActionTracker::Record) + profile = fast_create(Profile) + count = ActionTrackerNotification.count + job = NotifyActivityJob.new(action_tracker.id, profile.id) + job.perform + + assert_equal count + 1, ActionTrackerNotification.count + last = ActionTrackerNotification.last + assert_equal action_tracker, last.action_tracker + assert_equal profile, last.profile + end + +end diff --git a/test/unit/notify_activity_to_profiles_job_test.rb b/test/unit/notify_activity_to_profiles_job_test.rb new file mode 100644 index 0000000..9eb5225 --- /dev/null +++ b/test/unit/notify_activity_to_profiles_job_test.rb @@ -0,0 +1,21 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase + + should 'create the ActionTrackerNotification' do + person = fast_create(Person) + community = fast_create(Community) + action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id) + p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person) + fast_create(Friendship, :person_id => person.id, :friend_id => p1.id) + fast_create(Friendship, :person_id => person.id, :friend_id => p2.id) + fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id) + fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id) + ActionTrackerNotification.delete_all + job = NotifyActivityToProfilesJob.new(action_tracker.id, community.id) + job.perform + process_delayed_job_queue + assert_equal 5, ActionTrackerNotification.count + end + +end diff --git a/test/unit/organization_test.rb b/test/unit/organization_test.rb index c5ef7aa..d3c6b75 100644 --- a/test/unit/organization_test.rb +++ b/test/unit/organization_test.rb @@ -268,4 +268,23 @@ class OrganizationTest < Test::Unit::TestCase assert_no_match /[<>]/, organization.management_information end + should "the followed_by? be true only to members" do + o = fast_create(Organization) + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + + assert !p1.is_member_of?(o) + o.add_member(p1) + assert p1.is_member_of?(o) + + assert !p3.is_member_of?(o) + o.add_member(p3) + assert p3.is_member_of?(o) + + assert_equal true, o.send(:followed_by?,p1) + assert_equal true, o.send(:followed_by?,p3) + assert_equal false, o.send(:followed_by?,p2) + end + end diff --git a/test/unit/person_test.rb b/test/unit/person_test.rb index cf1625d..ae3fe44 100644 --- a/test/unit/person_test.rb +++ b/test/unit/person_test.rb @@ -642,4 +642,337 @@ class PersonTest < Test::Unit::TestCase end end + should "see get all sent scraps" do + p1 = fast_create(Person) + assert_equal [], p1.scraps_sent + fast_create(Scrap, :sender_id => p1.id) + fast_create(Scrap, :sender_id => p1.id) + assert_equal 2, p1.scraps_sent.count + p2 = fast_create(Person) + fast_create(Scrap, :sender_id => p2.id) + assert_equal 2, p1.scraps_sent.count + fast_create(Scrap, :sender_id => p1.id) + assert_equal 3, p1.scraps_sent.count + fast_create(Scrap, :receiver_id => p1.id) + assert_equal 3, p1.scraps_sent.count + end + + should "see get all received scraps" do + p1 = fast_create(Person) + assert_equal [], p1.scraps_received + fast_create(Scrap, :receiver_id => p1.id) + fast_create(Scrap, :receiver_id => p1.id) + assert_equal 2, p1.scraps_received.count + p2 = fast_create(Person) + fast_create(Scrap, :receiver_id => p2.id) + assert_equal 2, p1.scraps_received.count + fast_create(Scrap, :receiver_id => p1.id) + assert_equal 3, p1.scraps_received.count + fast_create(Scrap, :sender_id => p1.id) + assert_equal 3, p1.scraps_received.count + end + + should "see get all received scraps that are not replies" do + p1 = fast_create(Person) + s1 = fast_create(Scrap, :receiver_id => p1.id) + s2 = fast_create(Scrap, :receiver_id => p1.id) + s3 = fast_create(Scrap, :receiver_id => p1.id, :scrap_id => s1.id) + assert_equal 3, p1.scraps_received.count + assert_equal [s1,s2], p1.scraps_received.not_replies + p2 = fast_create(Person) + s4 = fast_create(Scrap, :receiver_id => p2.id) + s5 = fast_create(Scrap, :receiver_id => p2.id, :scrap_id => s4.id) + assert_equal 2, p2.scraps_received.count + assert_equal [s4], p2.scraps_received.not_replies + end + + should "the followed_by method be protected and true to the person friends and herself by default" do + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + p4 = fast_create(Person) + + p1.add_friend(p2) + assert p1.is_a_friend?(p2) + p1.add_friend(p4) + assert p1.is_a_friend?(p4) + + assert_equal true, p1.send(:followed_by?,p1) + assert_equal true, p1.send(:followed_by?,p2) + assert_equal true, p1.send(:followed_by?,p4) + assert_equal false, p1.send(:followed_by?,p3) + end + + should "the person follows her friends and herself by default" do + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + p4 = fast_create(Person) + + p2.add_friend(p1) + assert p2.is_a_friend?(p1) + p4.add_friend(p1) + assert p4.is_a_friend?(p1) + + assert_equal true, p1.follows?(p1) + assert_equal true, p1.follows?(p2) + assert_equal true, p1.follows?(p4) + assert_equal false, p1.follows?(p3) + end + + should "a person member of a community follows the community" do + c = fast_create(Community) + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + + assert !p1.is_member_of?(c) + c.add_member(p1) + assert p1.is_member_of?(c) + + assert !p3.is_member_of?(c) + c.add_member(p3) + assert p3.is_member_of?(c) + + assert_equal true, p1.follows?(c) + assert_equal true, p3.follows?(c) + assert_equal false, p2.follows?(c) + end + + should "the person member of a enterprise follows the enterprise" do + e = fast_create(Enterprise) + e.stubs(:closed?).returns(false) + p1 = fast_create(Person) + p2 = fast_create(Person) + p3 = fast_create(Person) + + assert !p1.is_member_of?(e) + e.add_member(p1) + assert p1.is_member_of?(e) + + assert !p3.is_member_of?(e) + e.add_member(p3) + assert p3.is_member_of?(e) + + assert_equal true, p1.follows?(e) + assert_equal true, p3.follows?(e) + assert_equal false, p2.follows?(e) + end + + should "the person see all of your scraps" do + person = fast_create(Person) + s1 = fast_create(Scrap, :sender_id => person.id) + assert_equal [s1], person.scraps + s2 = fast_create(Scrap, :sender_id => person.id) + assert_equal [s1,s2], person.scraps + s3 = fast_create(Scrap, :receiver_id => person.id) + assert_equal [s1,s2,s3], person.scraps + end + + should "the person browse for a scrap with a Scrap object" do + person = fast_create(Person) + s1 = fast_create(Scrap, :sender_id => person.id) + s2 = fast_create(Scrap, :sender_id => person.id) + s3 = fast_create(Scrap, :receiver_id => person.id) + assert_equal s2, person.scraps(s2) + end + + should "the person browse for a scrap with an inter and string id" do + person = fast_create(Person) + s1 = fast_create(Scrap, :sender_id => person.id) + s2 = fast_create(Scrap, :sender_id => person.id) + s3 = fast_create(Scrap, :receiver_id => person.id) + assert_equal s2, person.scraps(s2.id) + assert_equal s2, person.scraps(s2.id.to_s) + end + + should "the tracked action be notified to person friends and herself" do + p1 = Person.first + p2 = fast_create(Person) + p3 = fast_create(Person) + p4 = fast_create(Person) + + p1.add_friend(p2) + assert p1.is_a_friend?(p2) + assert !p1.is_a_friend?(p3) + p1.add_friend(p4) + assert p1.is_a_friend?(p4) + + action_tracker = fast_create(ActionTracker::Record) + ActionTrackerNotification.delete_all + count = ActionTrackerNotification.count + Delayed::Job.destroy_all + Person.notify_activity(action_tracker) + process_delayed_job_queue + assert_equal count + 3, ActionTrackerNotification.count + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + [p1,p2,p4].include?(profile) + end + end + + should "the tracked action be notified to friends with delayed job" do + p1 = Person.first + p2 = fast_create(Person) + p3 = fast_create(Person) + p4 = fast_create(Person) + + p1.add_friend(p2) + assert p1.is_a_friend?(p2) + assert !p1.is_a_friend?(p3) + p1.add_friend(p4) + assert p1.is_a_friend?(p4) + + action_tracker = fast_create(ActionTracker::Record) + + assert_difference(Delayed::Job, :count, 1) do + Person.notify_activity(action_tracker) + end + end + + should "the tracked action notify friends with one delayed job process followed by others jobs created after run the firt job" do + p1 = Person.first + p2 = fast_create(Person) + p3 = fast_create(Person) + p4 = fast_create(Person) + + p1.add_friend(p2) + assert p1.is_a_friend?(p2) + assert !p1.is_a_friend?(p3) + p1.add_friend(p4) + assert p1.is_a_friend?(p4) + + action_tracker = fast_create(ActionTracker::Record) + + Delayed::Job.delete_all + assert_difference(Delayed::Job, :count, 1) do + Person.notify_activity(action_tracker) + end + + assert_difference(ActionTrackerNotification, :count, 2) do + process_delayed_job_queue + end + end + + should "the community tracked action be notified to the author and to community members" do + p1 = Person.first + community = fast_create(Community) + p2 = fast_create(Person) + p3 = fast_create(Person) + + community.add_member(p1) + assert p1.is_member_of?(community) + community.add_member(p3) + assert p3.is_member_of?(community) + assert !p2.is_member_of?(community) + process_delayed_job_queue + + action_tracker = fast_create(ActionTracker::Record) + article = mock() + action_tracker.stubs(:dispatcher).returns(article) + article.stubs(:is_a?).with(Article).returns(true) + article.stubs(:is_a?).with(RoleAssignment).returns(false) + article.stubs(:is_a?).with(Comment).returns(false) + article.stubs(:profile).returns(community) + ActionTrackerNotification.delete_all + assert_difference(ActionTrackerNotification, :count, 3) do + Person.notify_activity(action_tracker) + process_delayed_job_queue + end + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [community,p1,p3].include?(profile) + end + end + + should "the community tracked action be notified to members with delayed job" do + p1 = Person.first + community = fast_create(Community) + p2 = fast_create(Person) + p3 = fast_create(Person) + p4 = fast_create(Person) + + community.add_member(p1) + assert p1.is_member_of?(community) + community.add_member(p3) + assert p3.is_member_of?(community) + community.add_member(p4) + assert p4.is_member_of?(community) + assert !p2.is_member_of?(community) + + action_tracker = fast_create(ActionTracker::Record) + article = mock() + action_tracker.stubs(:dispatcher).returns(article) + article.stubs(:is_a?).with(Article).returns(true) + article.stubs(:is_a?).with(RoleAssignment).returns(false) + article.stubs(:is_a?).with(Comment).returns(false) + article.stubs(:profile).returns(community) + ActionTrackerNotification.delete_all + + assert_difference(Delayed::Job, :count, 2) do + Person.notify_activity(action_tracker) + end + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [community,p1,p3,p4].include?(profile) + end + end + + should "remove activities if the person is destroyed" do + ActionTracker::Record.destroy_all + ActionTrackerNotification.destroy_all + person = fast_create(Person) + a1 = fast_create(ActionTracker::Record, :user_id => person.id ) + a2 = fast_create(ActionTracker::Record, :user_id => person.id ) + a3 = fast_create(ActionTracker::Record) + assert_equal 3, ActionTracker::Record.count + fast_create(ActionTrackerNotification, :action_tracker_id => a1.id) + fast_create(ActionTrackerNotification, :action_tracker_id => a1.id) + fast_create(ActionTrackerNotification, :action_tracker_id => a3.id) + fast_create(ActionTrackerNotification, :action_tracker_id => a2.id) + assert_equal 4, ActionTrackerNotification.count + person.destroy + assert_equal 1, ActionTracker::Record.count + assert_equal 1, ActionTrackerNotification.count + end + + should "control scrap if is sender or receiver" do + p1, p2 = fast_create(Person), fast_create(Person) + s = fast_create(Scrap, :sender_id => p1.id, :receiver_id => p2.id) + assert p1.can_control_scrap?(s) + assert p2.can_control_scrap?(s) + end + + should "not control scrap if is not sender or receiver" do + p1, p2 = fast_create(Person), fast_create(Person) + s = fast_create(Scrap, :sender_id => p1.id, :receiver_id => p1.id) + assert p1.can_control_scrap?(s) + assert !p2.can_control_scrap?(s) + end + + should "control activity or not" do + p1, p2 = fast_create(Person), fast_create(Person) + a = fast_create(ActionTracker::Record, :user_id => p2.id) + n = fast_create(ActionTrackerNotification, :profile_id => p2.id, :action_tracker_id => a.id) + assert !p1.reload.can_control_activity?(a) + assert p2.reload.can_control_activity?(a) + end + + should 'track only one action when a person joins a community' do + ActionTracker::Record.delete_all + p = create_user('test_user').person + c = fast_create(Community, :name => "Foo") + c.add_member(p) + assert_equal ["Foo"], ActionTracker::Record.last.get_resource_name + c.reload.add_moderator(p.reload) + assert_equal ["Foo"], ActionTracker::Record.last.get_resource_name + end + + should 'track only one action when a person leaves a community' do + p = create_user('test_user').person + c = fast_create(Community, :name => "Foo") + c.add_member(p) + c.add_moderator(p) + ActionTracker::Record.delete_all + c.remove_member(p) + assert_equal ["Foo"], ActionTracker::Record.last.get_resource_name + end + end diff --git a/test/unit/profile_test.rb b/test/unit/profile_test.rb index 285d1d7..80f2f8b 100644 --- a/test/unit/profile_test.rb +++ b/test/unit/profile_test.rb @@ -1798,6 +1798,13 @@ class ProfileTest < Test::Unit::TestCase assert_equal [f1], p.image_galleries end + should 'get custom profile icon' do + profile = build(Profile, :image_builder => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')}) + assert_kind_of String, profile.profile_custom_icon + profile = build(Profile, :image_builder => {:uploaded_data => nil}) + assert_nil profile.profile_custom_icon + end + private def assert_invalid_identifier(id) diff --git a/test/unit/rss_feed_test.rb b/test/unit/rss_feed_test.rb index f571ecd..fbe1d5a 100644 --- a/test/unit/rss_feed_test.rb +++ b/test/unit/rss_feed_test.rb @@ -123,9 +123,7 @@ class RssFeedTest < Test::Unit::TestCase feed.profile = profile feed.save! - profile.environment.expects(:default_hostname).returns('mysite.net').at_least_once - - assert_match "http://mysite.net/testuser", feed.data + assert_match "http://colivre.net/testuser", feed.data end should 'provide link to each article' do diff --git a/test/unit/scrap_test.rb b/test/unit/scrap_test.rb new file mode 100644 index 0000000..4fca7ed --- /dev/null +++ b/test/unit/scrap_test.rb @@ -0,0 +1,197 @@ +require File.join(File.dirname(__FILE__), '..', 'test_helper') + +class ScrapTest < ActiveSupport::TestCase + should "have the content" do + s = Scrap.new + s.valid? + assert s.errors.invalid?(:content) + + s.content = '' + s.valid? + assert s.errors.invalid?(:content) + + s.content = 'some content' + s.valid? + assert !s.errors.invalid?(:content) + end + + should "have the sender" do + s = Scrap.new + s.valid? + assert s.errors.invalid?(:sender_id) + + s.sender_id = 1 + s.valid? + assert !s.errors.invalid?(:sender_id) + end + + should "have the receiver" do + s = Scrap.new + s.valid? + assert s.errors.invalid?(:receiver_id) + + s.receiver_id = 1 + s.valid? + assert !s.errors.invalid?(:receiver_id) + end + + should "be associated to Person as sender" do + person = fast_create(Person) + s = Scrap.new + assert_nothing_raised do + s.sender = person + end + end + + should "be associated to Person as receiver" do + person = fast_create(Person) + s = Scrap.new + assert_nothing_raised do + s.receiver = person + end + end + + should "collect all scraps sent and received of a person" do + person = fast_create(Person) + s1 = fast_create(Scrap, :sender_id => person.id) + assert_equal [s1], Scrap.all_scraps(person) + s2 = fast_create(Scrap, :sender_id => person.id) + assert_equal [s1,s2], Scrap.all_scraps(person) + s3 = fast_create(Scrap, :receiver_id => person.id) + assert_equal [s1,s2,s3], Scrap.all_scraps(person) + end + + should "create the leave_scrap action tracker verb on scrap creation of one user to another" do + p1 = ActionTracker::Record.current_user_from_model + p2 = fast_create(Person) + s = Scrap.new + s.sender= p1 + s.receiver= p2 + s.content = 'some content' + s.save! + ta = ActionTracker::Record.last + assert_equal s.content, ta.params['content'] + assert_equal s.sender.name, ta.params['sender_name'] + assert_equal s.receiver.name, ta.params['receiver_name'] + assert_equal s.receiver.url, ta.params['receiver_url'] + assert_equal 'leave_scrap', ta.verb + assert_equal p1, ta.user + end + + should "notify leave_scrap action tracker verb to friends and itself" do + p1 = ActionTracker::Record.current_user_from_model + p2 = fast_create(Person) + p1.add_friend(p2) + ActionTrackerNotification.destroy_all + Delayed::Job.destroy_all + s = Scrap.new + s.sender= p1 + s.receiver= p2 + s.content = 'some content' + s.save! + process_delayed_job_queue + assert_equal 2, ActionTrackerNotification.count + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [p1,p2].include?(profile) + end + end + + should "create the leave_scrap_to_self action tracker verb on scrap creation of one user to itself" do + p1 = Person.first + s = Scrap.new + s.sender= p1 + s.receiver= p1 + s.content = 'some content' + s.save! + ta = ActionTracker::Record.last + assert_equal s.content, ta.params['content'] + assert_equal s.sender.name, ta.params['sender_name'] + assert_equal 'leave_scrap_to_self', ta.verb + assert_equal p1, ta.user + end + + should "notify leave_scrap_to_self action tracker verb to friends and itself" do + p1 = Person.first + p2 = fast_create(Person) + p1.add_friend(p2) + ActionTrackerNotification.destroy_all + Delayed::Job.destroy_all + s = Scrap.new + s.sender= p1 + s.receiver= p1 + s.content = 'some content' + s.save! + process_delayed_job_queue + assert_equal 2, ActionTrackerNotification.count + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile| + assert [p1,p2].include?(profile) + end + end + + should "get replies of a scrap" do + s = fast_create(Scrap) + s1 = fast_create(Scrap, :scrap_id => s.id) + s2 = fast_create(Scrap) + s3 = fast_create(Scrap, :scrap_id => s.id) + assert_equal [s1,s3], s.replies + end + + should "get only replies scrap" do + s0 = fast_create(Scrap) + s1 = fast_create(Scrap, :scrap_id => s0.id) + s2 = fast_create(Scrap) + s3 = fast_create(Scrap, :scrap_id => s0.id) + assert_equal [s0,s2], Scrap.not_replies + end + + should "remove the replies is the root is removed" do + Scrap.delete_all + s = fast_create(Scrap) + s1 = fast_create(Scrap, :scrap_id => s.id) + s2 = fast_create(Scrap, :scrap_id => s.id) + assert_equal [s1,s2], s.replies + assert_equal 3, Scrap.count + s.destroy + assert_equal 0, Scrap.count + end + + should "update the scrap on reply creation" do + Scrap.delete_all + s = fast_create(Scrap, :updated_at => DateTime.parse('2010-01-01')) + assert_equal DateTime.parse('2010-01-01'), s.updated_at.strftime('%Y-%m-%d') + DateTime.stubs(:now).returns(DateTime.parse('2010-09-07')) + s1 = Scrap.create(defaults_for_scrap(:scrap_id => s.id)) + s.reload + assert_not_equal DateTime.parse('2010-01-01'), s.updated_at.strftime('%Y-%m-%d') + end + + should "have the root defined" do + s = fast_create(Scrap) + s1 = fast_create(Scrap, :scrap_id => s.id) + s2 = fast_create(Scrap, :scrap_id => s.id) + assert_equal s, s1.root + assert_equal s, s2.root + end + + should 'strip all html tags' do + s, r = fast_create(Person), fast_create(Person) + s = Scrap.new :sender => s, :receiver => r, :content => "

    Test Rails

    " + assert_equal "Test Rails", s.strip_all_html_tags + end + + should 'strip html before save' do + s, r = fast_create(Person), fast_create(Person) + s = Scrap.new :sender => s, :receiver => r, :content => "

    Test Rails

    " + s.save! + assert_equal "Test Rails", s.reload.content + end + + should 'strip html before validate' do + s, r = fast_create(Person), fast_create(Person) + s = Scrap.new :sender => s, :receiver => r, :content => "

    " + assert !s.valid? + s.content = "

    Test

    " + assert s.valid? + end + +end diff --git a/test/unit/uploaded_file_test.rb b/test/unit/uploaded_file_test.rb index c7c9c3d..c5e2fac 100644 --- a/test/unit/uploaded_file_test.rb +++ b/test/unit/uploaded_file_test.rb @@ -228,4 +228,55 @@ class UploadedFileTest < Test::Unit::TestCase assert File.exists?(image) end end + + should 'return a thumbnail for images' do + f = UploadedFile.new + f.expects(:image?).returns(true) + f.expects(:full_filename).with(:thumb).returns(File.join(RAILS_ROOT, 'public', 'images', '0000', '0005', 'x.png')) + assert_equal '/images/0000/0005/x.png', f.thumbnail_path + f = UploadedFile.new + f.stubs(:full_filename).with(:thumb).returns(File.join(RAILS_ROOT, 'public', 'images', '0000', '0005', 'x.png')) + f.expects(:image?).returns(false) + assert_nil f.thumbnail_path + end + + should 'track action when a published image is uploaded in a gallery' do + p = fast_create(Folder, :profile_id => @profile.id) + p.view_as = 'image_gallery'; p.save! + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => p, :profile => @profile) + ta = ActionTracker::Record.last(:conditions => { :verb => "upload_image" }) + assert_kind_of String, ta.get_thumbnail_path[0] + assert_equal [f.reload.view_url], ta.get_view_url + assert_equal [p.reload.url], ta.get_parent_url + assert_equal [p.name], ta.get_parent_name + end + + should 'not track action when is not image' do + ActionTracker::Record.delete_all + p = fast_create(Folder, :profile_id => @profile.id) + p.view_as = 'image_gallery'; p.save! + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :parent => p, :profile => @profile) + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" }) + end + + should 'not track action when has no parent' do + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => nil, :profile => @profile) + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" }) + end + + should 'not track action when is not published' do + ActionTracker::Record.delete_all + p = fast_create(Folder, :profile_id => @profile.id) + p.view_as = 'image_gallery'; p.save! + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => p, :profile => @profile, :published => false) + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" }) + end + + should 'not track action when parent is not gallery' do + ActionTracker::Record.delete_all + p = fast_create(Folder, :profile_id => @profile.id) + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => p, :profile => @profile) + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" }) + end + end diff --git a/vendor/plugins/access_control/lib/role_assignment.rb b/vendor/plugins/access_control/lib/role_assignment.rb index 828ea8c..3a2f5cf 100644 --- a/vendor/plugins/access_control/lib/role_assignment.rb +++ b/vendor/plugins/access_control/lib/role_assignment.rb @@ -4,7 +4,11 @@ class RoleAssignment < ActiveRecord::Base belongs_to :resource, :polymorphic => true validates_presence_of :role, :accessor - + + track_actions :join_community, :after_create, :keep_params => ["resource.name", "resource.url", "resource.profile_custom_icon"], :if => Proc.new { |x| x.resource.is_a?(Community) && x.accessor.role_assignments.count(:conditions => { :resource_id => x.resource.id, :resource_type => 'Profile' }) == 1 } + + track_actions :leave_community, :before_destroy, :keep_params => ["resource.name", "resource.url", "resource.profile_custom_icon"], :if => Proc.new { |x| x.resource.is_a?(Community) && x.accessor.role_assignments.count(:conditions => { :resource_id => x.resource.id, :resource_type => 'Profile' }) == 1 } + def has_permission?(perm, res) return false unless role.has_permission?(perm.to_s) && (resource || is_global) return true if is_global diff --git a/vendor/plugins/action_tracker/MIT-LICENSE b/vendor/plugins/action_tracker/MIT-LICENSE new file mode 100644 index 0000000..ec4ddd4 --- /dev/null +++ b/vendor/plugins/action_tracker/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010 [name of plugin creator] + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/action_tracker/README b/vendor/plugins/action_tracker/README new file mode 100644 index 0000000..b41694a --- /dev/null +++ b/vendor/plugins/action_tracker/README @@ -0,0 +1,14 @@ +============= +ActionTracker +============= +Installation +============= +$ ./script/generate action_tracker + +The migration will be created. + +$ rake db:migrate + +The table will be created. + +Copyright (c) 2010 Caio SBA , released under the MIT license diff --git a/vendor/plugins/action_tracker/Rakefile b/vendor/plugins/action_tracker/Rakefile new file mode 100644 index 0000000..e668ab6 --- /dev/null +++ b/vendor/plugins/action_tracker/Rakefile @@ -0,0 +1,23 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the action_tracker plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +desc 'Generate documentation for the action_tracker plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'ActionTracker' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/vendor/plugins/action_tracker/generators/action_tracker/action_tracker_generator.rb b/vendor/plugins/action_tracker/generators/action_tracker/action_tracker_generator.rb new file mode 100644 index 0000000..1d51167 --- /dev/null +++ b/vendor/plugins/action_tracker/generators/action_tracker/action_tracker_generator.rb @@ -0,0 +1,7 @@ +class ActionTrackerGenerator < Rails::Generator::Base + def manifest + record do |m| + m.migration_template 'migration.rb', File.join('db', 'migrate'), :migration_file_name => 'create_action_tracker' + end + end +end diff --git a/vendor/plugins/action_tracker/generators/action_tracker/templates/migration.rb b/vendor/plugins/action_tracker/generators/action_tracker/templates/migration.rb new file mode 100644 index 0000000..e383f6d --- /dev/null +++ b/vendor/plugins/action_tracker/generators/action_tracker/templates/migration.rb @@ -0,0 +1,21 @@ +class CreateActionTracker < ActiveRecord::Migration + def self.up + create_table :action_tracker do |t| + t.belongs_to :user, :polymorphic => true + t.belongs_to :dispatcher, :polymorphic => true + t.text :params + t.string :verb + t.timestamps + end + + change_table :action_tracker do |t| + t.index [:user_id, :user_type] + t.index [:dispatcher_id, :dispatcher_type] + t.index :verb + end + end + + def self.down + drop_table :action_tracker + end +end diff --git a/vendor/plugins/action_tracker/init.rb b/vendor/plugins/action_tracker/init.rb new file mode 100644 index 0000000..d875352 --- /dev/null +++ b/vendor/plugins/action_tracker/init.rb @@ -0,0 +1,10 @@ +require "user_stamp" + +UserStamp.creator_attribute = :user +UserStamp.updater_attribute = :user + +class ActionController::Base + extend UserStamp::ClassMethods +end + +require "action_tracker" diff --git a/vendor/plugins/action_tracker/install.rb b/vendor/plugins/action_tracker/install.rb new file mode 100644 index 0000000..f7732d3 --- /dev/null +++ b/vendor/plugins/action_tracker/install.rb @@ -0,0 +1 @@ +# Install hook code here diff --git a/vendor/plugins/action_tracker/lib/action_tracker.rb b/vendor/plugins/action_tracker/lib/action_tracker.rb new file mode 100644 index 0000000..cc6da3d --- /dev/null +++ b/vendor/plugins/action_tracker/lib/action_tracker.rb @@ -0,0 +1,137 @@ +require File.join(File.dirname(__FILE__), 'action_tracker_model.rb') + +module ActionTracker + + module ControllerMethods + + def self.included(base) + base.send :user_stamp, ActionTracker::Record + base.send :extend, ClassMethods + end + + module ClassMethods + + def track_actions_after(verb, options = {}, &block) + track_actions_by_time(verb, :after, options, &block) + end + + def track_actions_before(verb, options = {}, &block) + track_actions_by_time(verb, :before, options, &block) + end + + def track_actions(verb, options = {}, &block) + track_actions_by_time(verb, ActionTrackerConfig.default_filter_time, options, &block) + end + + def track_actions_by_time(verb, time, options = {}, &block) + keep_params = options.delete(:keep_params) || options.delete('keep_params') || :all + send("#{time}_filter", options) do |x| + x.save_action_for_verb(verb.to_s, keep_params) + block.call(x) unless block.nil? + end + send :include, InstanceMethods + end + end + + module InstanceMethods + def save_action_for_verb(verb, keep_params = :all) + if keep_params.is_a? Array + stored_params = params.reject { |key, value| !keep_params.include?(key.to_sym) and !keep_params.include?(key.to_s) } + elsif keep_params.to_s == 'none' + stored_params = {} + elsif keep_params.to_s == 'all' + stored_params = params + end + user = send ActionTrackerConfig.current_user_method + tracked_action = case ActionTrackerConfig.verb_type(verb) + when :groupable + Record.add_or_create :verb => verb, :user => user, :params => stored_params + when :updatable + Record.update_or_create :verb => verb, :user => user, :params => stored_params + when :single + Record.new :verb => verb, :user => user, :params => stored_params + end + user.tracked_actions << tracked_action + end + end + + end + + module ModelMethods + + def self.included(base) + base.send :extend, ClassMethods + end + + module ClassMethods + def track_actions(verb, callback, options = {}, &block) + keep_params = options.delete(:keep_params) || options.delete('keep_params') || :all + post_proc = options.delete(:post_processing) || options.delete('post_processing') || Proc.new{} + send(callback, Proc.new { |tracked| tracked.save_action_for_verb(verb.to_s, keep_params, post_proc) }, options) + send :include, InstanceMethods + end + + def acts_as_trackable(options = {}) + has_many :tracked_actions, { :class_name => "ActionTracker::Record", :order => "updated_at DESC", :foreign_key => :user_id }.merge(options) + send :include, InstanceMethods + end + end + + module InstanceMethods + def time_spent_doing(verb, conditions = {}) + time = 0 + tracked_actions.all(:conditions => conditions.merge({ :verb => verb.to_s })).each do |t| + time += t.updated_at - t.created_at + end + time.to_f + end + + def save_action_for_verb(verb, keep_params = :all, post_proc = Proc.new{}) + user = ActionTracker::Record.current_user_from_model + return nil if user.nil? + if keep_params.is_a? Array + stored_params = {} + keep_params.each do |param| + result = self + param.to_s.split('.').each { |m| result = result.send(m) } + stored_params[param.to_s.gsub(/\./, '_')] = result + end + elsif keep_params.to_s == 'none' + stored_params = {} + elsif keep_params.to_s == 'all' + stored_params = self.attributes + end + tracked_action = case ActionTrackerConfig.verb_type(verb) + when :groupable + Record.add_or_create :verb => verb, :params => stored_params + when :updatable + Record.update_or_create :verb => verb, :params => stored_params + when :single + Record.new :verb => verb, :params => stored_params + end + tracked_action.dispatcher = self + user.tracked_actions << tracked_action + post_proc.call tracked_action.reload + end + + end + + end + + module ViewHelper + def describe(ta) + returning "" do |result| + if ta.is_a?(ActionTracker::Record) + result << ta.description.gsub(/\{\{(.*?)\}\}/) { eval $1 } + else + result << "" + end + end + end + end + +end + +ActionController::Base.send :include, ActionTracker::ControllerMethods +ActiveRecord::Base.send :include, ActionTracker::ModelMethods +ActionView::Base.send :include, ActionTracker::ViewHelper diff --git a/vendor/plugins/action_tracker/lib/action_tracker_config.rb b/vendor/plugins/action_tracker/lib/action_tracker_config.rb new file mode 100644 index 0000000..82aa930 --- /dev/null +++ b/vendor/plugins/action_tracker/lib/action_tracker_config.rb @@ -0,0 +1,60 @@ +class ActionTrackerConfig + + def self.config + @action_tracker_config ||= {} + end + + def self.config=(h) + @action_tracker_config = h + end + + def self.verbs + config[:verbs] || {} + end + + def self.verbs=(h) + config[:verbs] = h + end + + def self.verb_names + verbs.keys.map(&:to_s) + end + + def self.current_user_method + config[:current_user_method] || :current_user + end + + def self.current_user_method=(method_name) + UserStamp.current_user_method = config[:current_user_method] = method_name + end + + def self.default_filter_time + config[:default_filter_time] || :after + end + + def self.default_filter_time=(before_or_after) + config[:default_filter_time] = before_or_after + end + + def self.timeout + config[:timeout] || 5.minutes + end + + def self.timeout=(seconds) + config[:timeout] = seconds + end + + def self.get_verb(verb) + verbs[verb.to_s] || verbs[verb.to_sym] || {} + end + + def self.verb_type(verb) + type = get_verb(verb.to_s)[:type] || get_verb(verb.to_s)['type'] || :single + verb_types.include?(type.to_sym) ? type : :single + end + + def self.verb_types + [:single, :updatable, :groupable] + end + +end diff --git a/vendor/plugins/action_tracker/lib/action_tracker_model.rb b/vendor/plugins/action_tracker/lib/action_tracker_model.rb new file mode 100644 index 0000000..4f25eb1 --- /dev/null +++ b/vendor/plugins/action_tracker/lib/action_tracker_model.rb @@ -0,0 +1,101 @@ +module ActionTracker + class Record < ActiveRecord::Base + + set_table_name 'action_tracker' + + belongs_to :user, :polymorphic => true + belongs_to :dispatcher, :polymorphic => true + + serialize :params, Hash + + before_validation :stringify_verb + + validates_presence_of :verb + validates_presence_of :user_id + validates_presence_of :user_type + + alias_method :subject, :user + + def self.current_user_from_model + u = new + u.valid? + u.user + end + + def self.update_or_create(params) + u = params[:user] || current_user_from_model + return if u.nil? + l = last :conditions => { :user_id => u.id, :user_type => u.class.base_class.to_s, :verb => params[:verb].to_s } + ( !l.nil? and Time.now - l.updated_at < ActionTrackerConfig.timeout ) ? l.update_attributes(params.merge({ :updated_at => Time.now })) : l = new(params) + l + end + + def self.add_or_create(params) + u = params[:user] || current_user_from_model + return if u.nil? + l = last :conditions => { :user_id => u.id, :user_type => u.class.base_class.to_s, :verb => params[:verb].to_s } + if !l.nil? and Time.now - l.updated_at < ActionTrackerConfig.timeout + params[:params].clone.each { |key, value| params[:params][key] = l.params[key].clone.push(value) } + l.update_attributes params + else + params[:params].clone.each { |key, value| params[:params][key] = [value] } + l = new params + end + l + end + + def self.time_spent(conditions = {}) # In seconds + #FIXME Better if it could be completely done in the database, but SQLite does not support difference between two timestamps + time = 0 + all(:conditions => conditions).each { |action| time += action.updated_at - action.created_at } + time.to_f + end + + def duration # In seconds + ( updated_at - created_at ).to_f + end + + def description + ActionTrackerConfig.get_verb(self.verb)[:description] || "" + end + + def describe + description.gsub(/\{\{([^}]+)\}\}/) { eval $1 } + end + + def predicate + self.params || {} + end + + def phrase + { :subject => self.subject, :verb => self.verb, :predicate => self.predicate } + end + + def method_missing(method, *args, &block) + if method.to_s =~ /^get_(.*)$/ + param = method.to_s.gsub('get_', '') + predicate[param.to_s] || predicate[param.to_sym] + else + super + end + end + + def collect_group_with_index(param) + i = -1 + send("get_#{param}").collect{ |el| yield(el, i += 1) } + end + + protected + + def validate + errors.add_to_base "Verb must be one of the following: #{ActionTrackerConfig.verb_names.join(',')}" unless ActionTrackerConfig.verb_names.include?(self.verb) + end + + private + + def stringify_verb + self.verb = self.verb.to_s unless self.verb.nil? + end + + end +end diff --git a/vendor/plugins/action_tracker/tasks/action_tracker_tasks.rake b/vendor/plugins/action_tracker/tasks/action_tracker_tasks.rake new file mode 100644 index 0000000..3ad71b9 --- /dev/null +++ b/vendor/plugins/action_tracker/tasks/action_tracker_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :action_tracker do +# # Task goes here +# end diff --git a/vendor/plugins/action_tracker/test/action_tracker_config_test.rb b/vendor/plugins/action_tracker/test/action_tracker_config_test.rb new file mode 100755 index 0000000..fffda80 --- /dev/null +++ b/vendor/plugins/action_tracker/test/action_tracker_config_test.rb @@ -0,0 +1,121 @@ +require 'test_helper' + +class ActionTrackerConfigTest < ActiveSupport::TestCase + + def test_has_config + assert_not_nil ActionTrackerConfig + end + + def test_config_is_a_hash + assert_kind_of Hash, ActionTrackerConfig.config + end + + def test_config_can_be_set + c = { :foo => 'bar' } + ActionTrackerConfig.config = c + assert_equal c, ActionTrackerConfig.config + end + + def test_verbs_is_a_hash + assert_kind_of Hash, ActionTrackerConfig.verbs + end + + def test_verbs_can_be_set + v = { :search => {} } + ActionTrackerConfig.verbs = v + assert_equal v, ActionTrackerConfig.verbs + end + + def test_verb_names_is_a_list_of_strings + v = { :search => {}, :delete => {}, "login" => {} } + ActionTrackerConfig.verbs = v + assert_equal 3, ActionTrackerConfig.verb_names.size + %w(search delete login).each { |verb| assert ActionTrackerConfig.verb_names.include?(verb) } + end + + def test_current_user_is_default_method + ActionTrackerConfig.config[:current_user_method] = nil + assert_equal :current_user, ActionTrackerConfig.current_user_method + end + + def test_current_user_can_be_set + ActionTrackerConfig.current_user_method = :logged_in_user + assert_equal :logged_in_user, ActionTrackerConfig.current_user_method + end + + def test_default_filter_time_is_after + ActionTrackerConfig.config[:default_filter_time] = nil + assert_equal :after, ActionTrackerConfig.default_filter_time + end + + def test_default_filter_time_can_be_set + ActionTrackerConfig.default_filter_time = :before + assert_equal :before, ActionTrackerConfig.default_filter_time + end + + def test_default_timeout_is_five_minutes + ActionTrackerConfig.config[:timeout] = nil + assert_equal 5.minutes, ActionTrackerConfig.timeout + end + + def test_timeout_can_be_set + ActionTrackerConfig.timeout = 10.minutes + assert_equal 10.minutes, ActionTrackerConfig.timeout + end + + def test_get_verb_return_hash + assert_kind_of Hash, ActionTrackerConfig.get_verb(:search) + end + + def test_get_verb_symbol_search_by_symbol + ActionTrackerConfig.verbs = { :search => { :description => "Got it" } } + assert_equal "Got it", ActionTrackerConfig.get_verb(:search)[:description] + end + + def test_get_verb_symbol_search_by_string + ActionTrackerConfig.verbs = { :search => { :description => "Got it" } } + assert_equal "Got it", ActionTrackerConfig.get_verb("search")[:description] + end + + def test_get_verb_string_search_by_string + ActionTrackerConfig.verbs = { "search" => { :description => "Got it" } } + assert_equal "Got it", ActionTrackerConfig.get_verb("search")[:description] + end + + def test_get_verb_string_search_by_symbol + ActionTrackerConfig.verbs = { "search" => { :description => "Got it" } } + assert_equal "Got it", ActionTrackerConfig.get_verb(:search)[:description] + end + + def test_default_verb_type_is_single + ActionTrackerConfig.verbs = { "search" => { :description => "Got it" } } + assert_equal :single, ActionTrackerConfig.verb_type(:search) + end + + def test_verb_type_is_single_if_verb_type_not_valid + ActionTrackerConfig.verbs = { "search" => { :type => :not_valid } } + assert_equal :single, ActionTrackerConfig.verb_type(:search) + end + + def test_get_verb_type_by_symbol + ActionTrackerConfig.verbs = { "search" => { :type => :updatable } } + assert_equal :updatable, ActionTrackerConfig.verb_type(:search) + end + + def test_get_verb_type_by_string + ActionTrackerConfig.verbs = { "search" => { "type" => :updatable } } + assert_equal :updatable, ActionTrackerConfig.verb_type(:search) + end + + def test_verb_types_is_a_list + assert_kind_of Array, ActionTrackerConfig.verb_types + end + + def test_valid_verb_types + assert_equal 3, ActionTrackerConfig.verb_types.size + assert ActionTrackerConfig.verb_types.include?(:single) + assert ActionTrackerConfig.verb_types.include?(:updatable) + assert ActionTrackerConfig.verb_types.include?(:groupable) + end + +end diff --git a/vendor/plugins/action_tracker/test/action_tracker_model_test.rb b/vendor/plugins/action_tracker/test/action_tracker_model_test.rb new file mode 100644 index 0000000..7a79941 --- /dev/null +++ b/vendor/plugins/action_tracker/test/action_tracker_model_test.rb @@ -0,0 +1,297 @@ +require 'test_helper' + +ActiveRecord::Base.establish_connection({ + :adapter => "sqlite3", + :database => ":memory:" +}) + +ActiveRecord::Schema.define do + create_table :some_table, :force => true do |t| + t.column :some_column, :integer + end + create_table :action_tracker do |t| + t.belongs_to :user, :polymorphic => true + t.belongs_to :dispatcher, :polymorphic => true + t.text :params + t.string :verb + t.timestamps + end +end + +class SomeModel < ActiveRecord::Base + set_table_name :some_table + acts_as_trackable +end + +class ActionTrackerModelTest < ActiveSupport::TestCase + + def setup + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } } + @mymodel = SomeModel.create! + @othermodel = SomeModel.create! + @tracked_action = ActionTracker::Record.create! :verb => :some_verb, :params => { :user => "foo" }, :user => @mymodel, :dispatcher => @othermodel + end + + def test_has_relationship + assert @mymodel.respond_to?(:tracked_actions) + end + + def test_params_is_a_hash + assert_kind_of Hash, @tracked_action.params + end + + def test_has_a_polymorphic_relation_with_user + assert_equal @mymodel.id, @tracked_action.user_id + assert_equal "SomeModel", @tracked_action.user_type + assert_equal @mymodel, @tracked_action.user + assert_equal [@tracked_action], @mymodel.tracked_actions + end + + def test_has_a_polymorphic_relation_with_dispatcher + assert_equal @othermodel.id, @tracked_action.dispatcher_id + assert_equal "SomeModel", @tracked_action.dispatcher_type + assert_equal @othermodel, @tracked_action.dispatcher + end + + def test_should_stringify_verb_before_validation + ta = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some_verb + assert_equal "some_verb", ta.verb + end + + def test_verb_is_mandatory + ta = ActionTracker::Record.new + ta.valid? + assert ta.errors.on(:verb) + assert_raise ActiveRecord::RecordInvalid do + ta.save! + end + end + + def test_verb_must_be_declared_previously + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } } + assert_raise ActiveRecord::RecordInvalid do + ta = ActionTracker::Record.create! :verb => :undeclared_verb + end + ActionTrackerConfig.verbs = { :declared_verb => { :description => "Did something" } } + assert_nothing_raised do + ta = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :declared_verb + end + end + + def test_update_or_create_create_if_there_is_no_last + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } } + ActionTracker::Record.delete_all + assert_difference "ActionTracker::Record.count" do + ta = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel + ta.save; ta.reload + assert_kind_of ActionTracker::Record, ta + end + assert_equal "some", ActionTracker::Record.last.verb + assert_equal @mymodel, ActionTracker::Record.last.user + end + + def test_update_or_create_create_if_no_timeout + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } } + ActionTrackerConfig.timeout = 5.minutes + ActionTracker::Record.delete_all + ta = nil + assert_difference "ActionTracker::Record.count" do + ta = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel + ta.save; ta.reload + end + assert_kind_of ActionTracker::Record, ta + ta.updated_at = Time.now.ago(6.minutes) + ta.send :update_without_callbacks + t = ta.reload.updated_at + assert_difference "ActionTracker::Record.count" do + ta2 = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel + ta2.save; ta2.reload + assert_kind_of ActionTracker::Record, ta2 + end + assert_equal t, ta.reload.updated_at + end + + def test_update_or_create_update_if_timeout + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } } + ActionTrackerConfig.timeout = 7.minutes + ActionTracker::Record.delete_all + ta = nil + assert_difference "ActionTracker::Record.count" do + ta = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel, :params => { :foo => 2 } + ta.save; ta.reload + end + assert_kind_of ActionTracker::Record, ta + assert_equal 2, ta.get_foo + ta.updated_at = Time.now.ago(6.minutes) + ta.send :update_without_callbacks + t = ta.reload.updated_at + assert_no_difference "ActionTracker::Record.count" do + ta2 = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel, :params => { :foo => 3 } + ta2.save; ta2.reload + assert_kind_of ActionTracker::Record, ta2 + end + assert_not_equal t, ta.reload.updated_at + assert_equal 3, ta.reload.get_foo + end + + def test_add_or_create_create_if_no_timeout + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :groupable } } + ActionTrackerConfig.timeout = 5.minutes + ActionTracker::Record.delete_all + ta = nil + assert_difference "ActionTracker::Record.count" do + ta = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "bar" } + ta.save; ta.reload + end + assert_kind_of ActionTracker::Record, ta + assert_equal ["bar"], ta.reload.params[:foo] + ta.updated_at = Time.now.ago(6.minutes) + ta.send :update_without_callbacks + t = ta.reload.updated_at + assert_difference "ActionTracker::Record.count" do + ta2 = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test" } + ta2.save; ta2.reload + assert_kind_of ActionTracker::Record, ta2 + end + assert_equal t, ta.reload.updated_at + assert_equal ["test"], ActionTracker::Record.last.params[:foo] + end + + def test_add_or_create_update_if_timeout + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } } + ActionTrackerConfig.timeout = 7.minutes + ActionTracker::Record.delete_all + ta = nil + assert_difference "ActionTracker::Record.count" do + ta = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test 1", :bar => 2 } + ta.save; ta.reload + end + assert_kind_of ActionTracker::Record, ta + assert_equal ["test 1"], ta.params[:foo] + assert_equal [2], ta.params[:bar] + ta.updated_at = Time.now.ago(6.minutes) + ta.send :update_without_callbacks + t = ta.reload.updated_at + assert_no_difference "ActionTracker::Record.count" do + ta2 = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test 2", :bar => 1 } + ta2.save; ta2.reload + assert_kind_of ActionTracker::Record, ta2 + end + assert_equal ["test 1", "test 2"], ActionTracker::Record.last.params[:foo] + assert_equal [2, 1], ActionTracker::Record.last.params[:bar] + assert_not_equal t, ta.reload.updated_at + assert_no_difference "ActionTracker::Record.count" do + ta = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test 1", :bar => 1 } + ta.save; ta.reload + end + assert_equal ["test 1", "test 2", "test 1"], ActionTracker::Record.last.params[:foo] + assert_equal [2, 1, 1], ActionTracker::Record.last.params[:bar] + end + + def test_time_spent + ActionTracker::Record.delete_all + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } } + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel + t.save; t.reload + t.created_at = t.created_at.ago(2.days) + t.updated_at = t.created_at.tomorrow + t.send :update_without_callbacks + ActionTrackerConfig.timeout = 5.minutes + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel + t.save; t.reload + t.created_at = t.updated_at.ago(2.hours) + t.send :update_without_callbacks + assert_equal 2, ActionTracker::Record.count + assert_equal 26.hours, ActionTracker::Record.time_spent + end + + def test_duration + ActionTracker::Record.delete_all + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } } + ActionTrackerConfig.timeout = 5.minutes + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel; t.save + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel; t.save + t.reload + t.created_at = t.updated_at.ago(2.minutes) + t.send :update_without_callbacks + assert_equal 1, ActionTracker::Record.count + assert_equal 2.minutes, t.reload.duration + end + + def test_describe + ActionTracker::Record.delete_all + ActionTrackerConfig.verbs = { :some => { :description => "Who done this is from class {{user.class.to_s}} and half of its value is {{params[:value].to_i/2}}" } } + ActionTrackerConfig.timeout = 5.minutes + t = ActionTracker::Record.create! :verb => :some, :user => @mymodel, :params => { :value => "10" } + assert_equal "Who done this is from class SomeModel and half of its value is 5", t.describe + end + + def test_description + ActionTrackerConfig.verbs = { :some => { :description => "Got {{it}}" } } + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some + assert_equal "Got {{it}}", t.description + ActionTrackerConfig.verbs = { :some => nil } + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some + assert_equal "", t.description + end + + def test_subject + ActionTrackerConfig.verbs = { :some => { :description => "Some" } } + u = SomeModel.create! + t = ActionTracker::Record.create! :verb => :some, :user => u + assert_equal u, t.subject + end + + def test_predicate + ActionTrackerConfig.verbs = { :some => { :description => "Some" } } + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => nil + assert_equal({}, t.predicate) + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => { :foo => "bar" } + assert_equal({ :foo => "bar" }, t.predicate) + end + + def test_phrase + ActionTrackerConfig.verbs = { :some => { :description => "Some" } } + u = SomeModel.create! + t = ActionTracker::Record.create! :verb => :some, :params => { :foo => "bar" }, :user => u + assert_equal({ :subject => u, :verb => "some", :predicate => { :foo => "bar" }}, t.phrase) + end + + def test_method_missing + ActionTrackerConfig.verbs = { :some => { :description => "Some" } } + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => { :foo => "test 1", "bar" => "test 2" } + assert_nil t.get_test + assert_equal "test 1", t.get_foo + assert_equal "test 2", t.get_bar + assert_raise NoMethodError do + t.another_unknown_method + end + end + + def test_collect_group_with_index + ActionTrackerConfig.verbs = { :some => { :description => "Some" }, :type => :groupable } + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => { "test" => ["foo", "bar"] } + assert_equal(["foo 1", "bar 2"], t.collect_group_with_index(:test){|x, i| "#{x} #{i+1}" }) + end + + def test_user_id_is_mandatory + ActionTrackerConfig.verbs = { :some => { :description => "Some" } } + ta = ActionTracker::Record.new :user_type => 'SomeModel', :verb => :some + ta.valid? + assert ta.errors.on(:user_id) + assert_raise ActiveRecord::RecordInvalid do + ta.save! + end + end + + def test_user_type_is_mandatory + ActionTrackerConfig.verbs = { :some => { :description => "Some" } } + ta = ActionTracker::Record.new :user_id => 2, :verb => :some + ta.valid? + assert ta.errors.on(:user_type) + assert_raise ActiveRecord::RecordInvalid do + ta.save! + end + end + +end diff --git a/vendor/plugins/action_tracker/test/action_tracker_test.rb b/vendor/plugins/action_tracker/test/action_tracker_test.rb new file mode 100644 index 0000000..aae44ba --- /dev/null +++ b/vendor/plugins/action_tracker/test/action_tracker_test.rb @@ -0,0 +1,559 @@ +require 'test_helper' + +ActiveRecord::Base.establish_connection({ + :adapter => "sqlite3", + :database => ":memory:" +}) + +ActiveRecord::Schema.define do + create_table :some_table, :force => true do |t| + t.column :some_column, :string + end + create_table :other_table, :force => true do |t| + t.column :other_column, :string + t.column :another_column, :integer + end + create_table :action_tracker do |t| + t.belongs_to :user, :polymorphic => true + t.belongs_to :dispatcher, :polymorphic => true + t.text :params + t.string :verb + t.timestamps + end +end + +class SomeModel < ActiveRecord::Base + set_table_name :some_table + acts_as_trackable +end + +class OtherModel < ActiveRecord::Base + set_table_name :other_table +end + +class ThingsController < ActionController::Base + + def index + params[:foo] = params[:foo].to_i + 1 + render :text => "test" + end + + def test + render :text => "test" + end + + def current_user + SomeModel.first || SomeModel.create! + end + + def rescue_action(e) + raise e + end + +end + +ActionController::Routing::Routes.draw { |map| map.resources :things, :collection => { :test => :get } } + +class ActionTrackerTest < ActiveSupport::TestCase + + def setup + UserStamp.creator_attribute = :user + UserStamp.updater_attribute = :user + ActionTrackerConfig.current_user_method = :current_user + ActionTracker::Record.delete_all + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } } + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @controller = ThingsController.new + end + + def test_index + get :index + assert_response :success + end + + def test_track_actions_after_runs_after_action + @controller = create_controller { track_actions_after :some_verb } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => "4" + end + assert_equal 5, ActionTracker::Record.first.params[:foo] + end + + def test_track_actions_after_runs_before_action + @controller = create_controller { track_actions_before :some_verb } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => "4" + end + assert_equal "4", ActionTracker::Record.first.params[:foo] + end + + def test_track_actions_default_is_after + ActionTrackerConfig.default_filter_time = :after + @controller = create_controller { track_actions :some_verb } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => "4" + end + assert_equal 5, ActionTracker::Record.first.params[:foo] + end + + def test_track_actions_default_is_before + ActionTrackerConfig.default_filter_time = :before + @controller = create_controller { track_actions :some_verb } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => "4" + end + assert_equal "4", ActionTracker::Record.first.params[:foo] + end + + def test_track_actions_executes_block + @controller = create_controller do + track_actions :some_verb do + throw :some_symbol + end + end + assert_difference 'ActionTracker::Record.count' do + assert_throws :some_symbol do + get :index, :foo => "4" + end + end + assert_equal "4", ActionTracker::Record.first.params[:foo] + end + + def test_pass_keep_params_as_symbol + @controller = create_controller { track_actions_before :some_verb, :keep_params => [:foo] } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + assert_equal({ "foo" => "5" }, ActionTracker::Record.first.params) + end + + def test_pass_keep_params_as_string + @controller = create_controller { track_actions_before :some_verb, "keep_params" => [:foo, :bar] } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5, :bar => 10 + end + assert_equal({ "foo" => "5", "bar" => "10" }, ActionTracker::Record.first.params) + end + + def test_pass_keep_params_none + @controller = create_controller { track_actions_before :some_verb, :keep_params => :none } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + assert_equal({}, ActionTracker::Record.first.params) + ActionTracker::Record.delete_all + @controller = create_controller { track_actions_before :some_verb, :keep_params => "none" } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + assert_equal({}, ActionTracker::Record.first.params) + end + + def test_pass_keep_params_all + @controller = create_controller { track_actions_before :some_verb, :keep_params => :all } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + assert_equal({"action"=>"index", "foo"=>"5", "controller"=>"things"}, ActionTracker::Record.first.params) + ActionTracker::Record.delete_all + @controller = create_controller { track_actions_before :some_verb, :keep_params => "all" } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + assert_equal({"action"=>"index", "foo"=>"5", "controller"=>"things"}, ActionTracker::Record.first.params) + end + + def test_keep_params_not_set_should_store_all_params + @controller = create_controller { track_actions_before :some_verb } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + assert_equal({"action"=>"index", "foo"=>"5", "controller"=>"things"}, ActionTracker::Record.first.params) + end + + def test_execute_if_some_condition_is_true + @controller = create_controller { track_actions_before :some_verb, :if => Proc.new { 2 < 1 } } + assert_no_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + @controller = create_controller { track_actions_before :some_verb, :if => Proc.new { 2 > 1 } } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + end + + def test_execute_unless_some_condition_is_true + @controller = create_controller { track_actions_before :some_verb, :unless => Proc.new { 2 < 1 } } + assert_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + @controller = create_controller { track_actions_before :some_verb, :unless => Proc.new { 2 > 1 } } + assert_no_difference 'ActionTracker::Record.count' do + get :index, :foo => 5 + end + end + + def test_execute_for_all_actions + @controller = create_controller { track_actions_before :some_verb } + assert_difference 'ActionTracker::Record.count' do + get :index + end + assert_difference 'ActionTracker::Record.count' do + get :test + end + end + + def test_execute_only_for_some_action + @controller = create_controller { track_actions_before :some_verb, :only => [:index] } + assert_difference 'ActionTracker::Record.count' do + get :index + end + assert_no_difference 'ActionTracker::Record.count' do + get :test + end + end + + def test_execute_except_for_some_action + @controller = create_controller { track_actions_before :some_verb, :except => [:index] } + assert_no_difference 'ActionTracker::Record.count' do + get :index + end + assert_difference 'ActionTracker::Record.count' do + get :test + end + end + + def test_store_user + @controller = create_controller do + track_actions_before :some_verb + def current_user + SomeModel.create! :some_column => "test" + end + end + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_equal "test", ActionTracker::Record.last.user.some_column + end + + def test_should_update_when_verb_is_updatable_and_no_timeout + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :updatable } } + ActionTrackerConfig.timeout = 5.minutes + @controller = create_controller { track_actions_before :some_verb } + assert ActionTrackerConfig.verb_type(:some_verb) == :updatable + assert_difference 'ActionTracker::Record.count' do + get :test + end + t = ActionTracker::Record.last + t.updated_at = t.updated_at.ago(2.minutes) + t.send :update_without_callbacks + assert_no_difference 'ActionTracker::Record.count' do + get :test + end + end + + def test_should_create_when_verb_is_updatable_and_timeout + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :updatable } } + ActionTrackerConfig.timeout = 5.minutes + @controller = create_controller { track_actions_before :some_verb } + assert ActionTrackerConfig.verb_type(:some_verb) == :updatable + assert_difference 'ActionTracker::Record.count' do + get :test + end + t = ActionTracker::Record.last + t.updated_at = t.updated_at.ago(6.minutes) + t.send :update_without_callbacks + assert_difference 'ActionTracker::Record.count' do + get :test + end + end + + def test_should_update_when_verb_is_groupable_and_no_timeout + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :groupable } } + ActionTrackerConfig.timeout = 5.minutes + @controller = create_controller { track_actions_before :some_verb, :keep_params => [:foo] } + assert ActionTrackerConfig.verb_type(:some_verb) == :groupable + assert_difference 'ActionTracker::Record.count' do + get :test, :foo => "bar" + end + t = ActionTracker::Record.last + t.updated_at = t.updated_at.ago(2.minutes) + t.send :update_without_callbacks + assert_no_difference 'ActionTracker::Record.count' do + get :test, :foo => "test" + end + end + + def test_should_create_when_verb_is_groupable_and_timeout + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :groupable } } + ActionTrackerConfig.timeout = 5.minutes + @controller = create_controller { track_actions_before :some_verb, :keep_params => [:foo] } + assert ActionTrackerConfig.verb_type(:some_verb) == :groupable + assert_difference 'ActionTracker::Record.count' do + get :test, :foo => "bar" + end + t = ActionTracker::Record.last + t.updated_at = t.updated_at.ago(6.minutes) + t.send :update_without_callbacks + assert_difference 'ActionTracker::Record.count' do + get :test, :foo => "test" + end + end + + def test_should_create_when_verb_is_single + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :single } } + @controller = create_controller { track_actions_before :some_verb } + assert ActionTrackerConfig.verb_type(:some_verb) == :single + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_difference 'ActionTracker::Record.count' do + get :test + end + end + + def test_should_act_as_trackable + m = SomeModel.create! + assert m.respond_to?(:tracked_actions) + assert_kind_of Array, m.tracked_actions + @controller = create_controller { track_actions_before :some_verb } + @controller.stubs(:current_user).returns(m) + get :index + sleep 2 + get :test + assert ActionTracker::Record.last.updated_at > ActionTracker::Record.first.updated_at + assert_equal [ActionTracker::Record.last, ActionTracker::Record.first], m.reload.tracked_actions + end + + def test_should_get_time_spent_doing_something + ActionTrackerConfig.verbs = { :some_verb => { :type => :updatable }, :other_verb => { :type => :updatable } } + m = SomeModel.create! + @controller = create_controller do + track_actions :some_verb + end + @controller.stubs(:current_user).returns(m) + get :index + t1 = ActionTracker::Record.last + t1.updated_at = t1.updated_at.ago(4.hours) + t1.created_at = t1.updated_at.ago(3.hours) + t1.send :update_without_callbacks + get :test + t2 = ActionTracker::Record.last + t2.updated_at = t2.updated_at.ago(3.hours) + t2.created_at = t2.updated_at.ago(2.hours) + t2.send :update_without_callbacks + assert_equal 5.hours, m.time_spent_doing(:some_verb) + assert_equal 3.hours, m.time_spent_doing(:some_verb, :id => t1.id) + assert_equal 0.0, m.time_spent_doing(:other_verb) + assert_equal 0.0, m.time_spent_doing(:other_verb, :verb => :some_verb) + end + + def test_helper_describe_action_tracker_object + ActionTrackerConfig.verbs = { :some_verb => { :description => "Hey, {{link_to 'click here', :controller => :things}} {{ta.user.some_column}}!" } } + view = ActionView::Base.new + view.controller = @controller + @request.env["HTTP_REFERER"] = "http://test.com" + get :index + user = SomeModel.create! :some_column => "test" + t = ActionTracker::Record.create! :verb => :some_verb, :user => user + assert_equal 'Hey, click here test!', view.describe(t) + end + + def test_helper_describe_non_action_tracker_object + view = ActionView::Base.new + view.controller = @controller + @request.env["HTTP_REFERER"] = "http://test.com" + get :index + assert_equal "", view.describe("Something") + end + + def test_track_actions_store_user + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create + end + @controller = create_controller_for_model(model) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_kind_of SomeModel, ActionTracker::Record.last.user + assert_equal "test", ActionTracker::Record.last.user.some_column + end + + def test_track_actions_store_some_params + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create, :keep_params => [:other_column] + end + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_equal "foo", ActionTracker::Record.last.params["other_column"] + assert_nil ActionTracker::Record.last.params["another_column"] + end + + def test_replace_dots_by_underline_in_param_name + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create, :keep_params => ["other_column.size", :another_column] + end + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 5) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_equal 3, ActionTracker::Record.last.params["other_column_size"] + assert_equal 5, ActionTracker::Record.last.params["another_column"] + end + + def test_track_actions_store_all_params + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create, :keep_params => :all + end + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_equal "foo", ActionTracker::Record.last.params["other_column"] + assert_equal 2, ActionTracker::Record.last.params["another_column"] + end + + def test_track_actions_store_all_params_by_default + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create + end + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_equal "foo", ActionTracker::Record.last.params["other_column"] + assert_equal 2, ActionTracker::Record.last.params["another_column"] + end + + def test_track_actions_store_no_params + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create, :keep_params => :none + end + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_nil ActionTracker::Record.last.params["other_column"] + assert_nil ActionTracker::Record.last.params["another_column"] + end + + def test_track_actions_with_options + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model { track_actions :test, :after_create, :keep_params => :all, :if => Proc.new { 2 > 1 } } + @controller = create_controller_for_model(model) + assert_difference('ActionTracker::Record.count') { get :test } + + model = create_model { track_actions :test, :after_create, :keep_params => :all, :if => Proc.new { 2 < 1 } } + @controller = create_controller_for_model(model) + assert_no_difference('ActionTracker::Record.count') { get :test } + + model = create_model { track_actions :test, :after_create, :keep_params => :all, :unless => Proc.new { 2 > 1 } } + @controller = create_controller_for_model(model) + assert_no_difference('ActionTracker::Record.count') { get :test } + + model = create_model { track_actions :test, :after_create, :keep_params => :all, :unless => Proc.new { 2 < 1 } } + @controller = create_controller_for_model(model) + assert_difference('ActionTracker::Record.count') { get :test } + end + + def test_track_actions_post_processing_as_symbol + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create, :post_processing => Proc.new { |ta| OtherModel.create!(:other_column => ta.verb) } + end + @controller = create_controller_for_model(model, :another_column => 2) + assert_difference 'ActionTracker::Record.count' do + assert_difference('OtherModel.count', 2) do + get :test + end + end + assert_equal "test", OtherModel.last.other_column + end + + def test_track_actions_post_processing_as_string + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create, "post_processing" => Proc.new { |ta| OtherModel.create!(:other_column => ta.verb) } + end + @controller = create_controller_for_model(model, :another_column => 2) + assert_difference 'ActionTracker::Record.count' do + assert_difference('OtherModel.count', 2) do + get :test + end + end + assert_equal "test", OtherModel.last.other_column + end + + def test_acts_as_trackable_with_options + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + @@action = create_model do + track_actions :test, :after_create + end + @@user = create_model do + acts_as_trackable :after_add => Proc.new { |x, y| raise 'I was called' } + end + @controller = create_controller do + def test + @@action.create! + render :text => "test" + end + def current_user + @@user.create! + end + end + assert_raise(RuntimeError, 'I was called') do + get :test + end + end + + def test_track_actions_save_dispatcher + ActionTrackerConfig.verbs = { :test => { :description => "Some" } } + model = create_model do + track_actions :test, :after_create + end + @controller = create_controller_for_model(model) + assert_difference 'ActionTracker::Record.count' do + get :test + end + assert_kind_of model.base_class, ActionTracker::Record.last.dispatcher + end + + private + + def create_controller(&block) + klass = Class.new(ThingsController) + klass.module_eval &block + klass.stubs(:controller_path).returns('things') + klass.new + end + + def create_model(&block) + klass = Class.new(OtherModel) + klass.module_eval &block + klass + end + + def create_controller_for_model(model, attributes = {}) + @@model, @@attributes = model, attributes + create_controller do + def test + @@model.create! @@attributes + render :text => "test" + end + + def current_user + SomeModel.create! :some_column => "test" + end + end + end + +end diff --git a/vendor/plugins/action_tracker/test/test_helper.rb b/vendor/plugins/action_tracker/test/test_helper.rb new file mode 100755 index 0000000..4975b1e --- /dev/null +++ b/vendor/plugins/action_tracker/test/test_helper.rb @@ -0,0 +1,5 @@ +ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..' +require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb')) + +ENV["RAILS_ENV"] = "test" +require 'test_help' diff --git a/vendor/plugins/action_tracker/uninstall.rb b/vendor/plugins/action_tracker/uninstall.rb new file mode 100644 index 0000000..9738333 --- /dev/null +++ b/vendor/plugins/action_tracker/uninstall.rb @@ -0,0 +1 @@ +# Uninstall hook code here diff --git a/vendor/plugins/user_stamp/MIT-LICENSE b/vendor/plugins/user_stamp/MIT-LICENSE new file mode 100644 index 0000000..f5eca46 --- /dev/null +++ b/vendor/plugins/user_stamp/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008 [John Nunemaker] + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/user_stamp/README b/vendor/plugins/user_stamp/README new file mode 100644 index 0000000..3f512d6 --- /dev/null +++ b/vendor/plugins/user_stamp/README @@ -0,0 +1,37 @@ += UserStamp + +Rails plugin that makes stamping records with a user when they are +created and updated dirt simple. It assumes that your controller has +a current_user method. It also assumes that any record being stamped +has two attributes--creator_id and updater_id. You can override both +of these assumptions easily. + +== Setup + +1. script/plugin install git://github.com/jnunemaker/user_stamp.git +2. Add user_stamp to application.rb, like the following: + + class ApplicationController < ActionController::Base + user_stamp Post, Asset, Job + end + + +== Defaults + + UserStamp.creator_attribute = :creator_id + UserStamp.updater_attribute = :updater_id + UserStamp.current_user_method = :current_user + +If your user stamped columns and current_user method are different, +just create an initializer such as config/initializers/user_stamp.rb +and copy and paste the defaults above, changing them to fit your app. + +== Problems? + +Use the issue tracker on Github. + +== Docs + +http://rdoc.info/projects/jnunemaker/user_stamp + +Copyright (c) 2008 [John Nunemaker], released under the MIT license diff --git a/vendor/plugins/user_stamp/Rakefile b/vendor/plugins/user_stamp/Rakefile new file mode 100644 index 0000000..cf2dd88 --- /dev/null +++ b/vendor/plugins/user_stamp/Rakefile @@ -0,0 +1,11 @@ +require 'rake' +require 'spec/rake/spectask' + +desc 'Default: run specs.' +task :default => :spec + +desc 'Run the specs' +Spec::Rake::SpecTask.new(:spec) do |t| + t.spec_opts = ['--colour --format progress --loadby mtime --reverse'] + t.spec_files = FileList['spec/**/*_spec.rb'] +end \ No newline at end of file diff --git a/vendor/plugins/user_stamp/init.rb b/vendor/plugins/user_stamp/init.rb new file mode 100644 index 0000000..18d0ac4 --- /dev/null +++ b/vendor/plugins/user_stamp/init.rb @@ -0,0 +1,5 @@ +require 'user_stamp' + +class ActionController::Base + extend UserStamp::ClassMethods +end diff --git a/vendor/plugins/user_stamp/install.rb b/vendor/plugins/user_stamp/install.rb new file mode 100644 index 0000000..d562416 --- /dev/null +++ b/vendor/plugins/user_stamp/install.rb @@ -0,0 +1,15 @@ +instructions = < nil, :updater_id= => nil, :new_record? => true) + record.should_receive(:creator_id=).with(220).once + @sweeper.before_validation(record) + end + + it "should NOT set creator_id if attribute does not exist" do + record = mock('Record', :new_record? => true, :updater_id= => nil, :respond_to? => false) + record.should_receive(:respond_to?).with("creator_id=").and_return(false) + record.should_not_receive(:creator_id=) + @sweeper.before_validation(record) + end + end + + describe "(with non new record)" do + it "should NOT set creator_id if attribute exists" do + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => false) + record.should_not_receive(:creator_id=) + @sweeper.before_validation(record) + end + + it "should NOT set creator_id if attribute does not exist" do + record = mock('Record', :updater_id= => nil, :new_record? => false) + record.should_not_receive(:creator_id=) + @sweeper.before_validation(record) + end + end + + it "should set updater_id if attribute exists" do + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => :false) + record.should_receive(:updater_id=) + @sweeper.before_validation(record) + end + + it "should NOT set updater_id if attribute does not exist" do + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => :false, :respond_to? => false) + record.should_receive(:respond_to?).with("updater_id=").and_return(false) + record.should_not_receive(:updater_id=) + @sweeper.before_validation(record) + end +end + +describe UserStampSweeper, "#before_validation (with custom attribute names)" do + before do + UserStamp.creator_attribute = :created_by + UserStamp.updater_attribute = :updated_by + UserStamp.current_user_method = :current_user + @sweeper = UserStampSweeper.instance + @sweeper.stub!(:controller).and_return(PostsController) + end + + describe "(with new record)" do + it "should set created_by if attribute exists" do + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => true) + record.should_receive(:created_by=).with(220).once + @sweeper.before_validation(record) + end + + it "should NOT set created_by if attribute does not exist" do + record = mock('Record', :new_record? => true, :updated_by= => nil, :respond_to? => false) + record.should_receive(:respond_to?).with("created_by=").and_return(false) + record.should_not_receive(:created_by=) + @sweeper.before_validation(record) + end + end + + describe "(with non new record)" do + it "should NOT set created_by if attribute exists" do + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => false) + record.should_not_receive(:created_by=) + @sweeper.before_validation(record) + end + + it "should NOT set created_by if attribute does not exist" do + record = mock('Record', :updated_by= => nil, :new_record? => false) + record.should_not_receive(:created_by=) + @sweeper.before_validation(record) + end + end + + it "should set updated_by if attribute exists" do + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => :false) + record.should_receive(:updated_by=) + @sweeper.before_validation(record) + end + + it "should NOT set updated_by if attribute does not exist" do + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => :false, :respond_to? => false) + record.should_receive(:respond_to?).with("updated_by=").and_return(false) + record.should_not_receive(:updated_by=) + @sweeper.before_validation(record) + end +end + +describe UserStampSweeper, "#current_user" do + before do + UserStamp.creator_attribute = :creator_id + UserStamp.updater_attribute = :updater_id + UserStamp.current_user_method = :current_user + @sweeper = UserStampSweeper.instance + end + + it "should send current_user if controller responds to it" do + user = mock('User') + controller = mock('Controller', :current_user => user) + @sweeper.stub!(:controller).and_return(controller) + controller.should_receive(:current_user) + @sweeper.send(:current_user) + end + + it "should not send current_user if controller does not respond to it" do + user = mock('User') + controller = mock('Controller', :respond_to? => false) + @sweeper.stub!(:controller).and_return(controller) + controller.should_not_receive(:current_user) + @sweeper.send(:current_user) + end +end + +describe UserStampSweeper, "#current_user (with custom current_user_method)" do + before do + UserStamp.creator_attribute = :creator_id + UserStamp.updater_attribute = :updater_id + UserStamp.current_user_method = :my_user + @sweeper = UserStampSweeper.instance + end + + it "should send current_user if controller responds to it" do + user = mock('User') + controller = mock('Controller', :my_user => user) + @sweeper.stub!(:controller).and_return(controller) + controller.should_receive(:my_user) + @sweeper.send(:current_user) + end + + it "should not send current_user if controller does not respond to it" do + user = mock('User') + controller = mock('Controller', :respond_to? => false) + @sweeper.stub!(:controller).and_return(controller) + controller.should_not_receive(:my_user) + @sweeper.send(:current_user) + end +end \ No newline at end of file -- libgit2 0.21.2