Commit 59338e3fc78861c43612529dce4f979af6112a9e

Authored by Caio SBA
Committed by Antonio Terceiro
1 parent 6fc29038

Profile activity

(ActionItem1303)
Showing 98 changed files with 4712 additions and 167 deletions   Show diff stats
app/controllers/application.rb
... ... @@ -93,8 +93,16 @@ class ApplicationController < ActionController::Base
93 93 verify :method => :post, :only => actions, :redirect_to => redirect
94 94 end
95 95  
  96 + helper_method :current_person, :current_person
  97 +
96 98 protected
97 99  
  100 + def user
  101 + current_user.person if logged_in?
  102 + end
  103 +
  104 + alias :current_person :user
  105 +
98 106 # TODO: move this logic somewhere else (Domain class?)
99 107 def detect_stuff_by_domain
100 108 @domain = Domain.find_by_name(request.host)
... ... @@ -126,10 +134,6 @@ class ApplicationController < ActionController::Base
126 134 render :template => 'shared/access_denied.rhtml', :status => 403
127 135 end
128 136  
129   - def user
130   - current_user.person if logged_in?
131   - end
132   -
133 137 def load_category
134 138 unless params[:category_path].blank?
135 139 path = params[:category_path].join('/')
... ...
app/controllers/public/profile_controller.rb
... ... @@ -3,11 +3,21 @@ class ProfileController < PublicController
3 3 needs_profile
4 4 before_filter :check_access_to_profile, :except => [:join, :index]
5 5 before_filter :store_before_join, :only => [:join]
6   - before_filter :login_required, :only => [:join, :leave, :unblock]
  6 + before_filter :login_required, :only => [:join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities]
  7 +
7 8  
8 9 helper TagsHelper
9 10  
10 11 def index
  12 + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page])
  13 + @network_activities = []
  14 + @wall_items = []
  15 + if !@profile.is_a?(Person)
  16 + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page])
  17 + elsif logged_in? && current_person.follows?(@profile)
  18 + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page])
  19 + @wall_items = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if @profile.is_a?(Person)
  20 + end
11 21 @tags = profile.article_tags
12 22 unless profile.display_info_to?(user)
13 23 profile.visible? ? private_profile : invisible_profile
... ... @@ -123,6 +133,53 @@ class ProfileController < PublicController
123 133 end
124 134 end
125 135  
  136 + def leave_scrap
  137 + sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id])
  138 + receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id])
  139 + @scrap = Scrap.new(params[:scrap])
  140 + @scrap.sender= sender
  141 + @scrap.receiver= receiver
  142 + @tab_action = params[:tab_action]
  143 + @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.")
  144 + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if params[:not_load_scraps].nil?
  145 + render :partial => 'leave_scrap'
  146 + end
  147 +
  148 + def view_more_scraps
  149 + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page])
  150 + render :partial => 'profile_scraps', :locals => {:scraps => @scraps}
  151 + end
  152 +
  153 + def view_more_activities
  154 + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page])
  155 + render :partial => 'profile_activities', :locals => {:activities => @activities}
  156 + end
  157 +
  158 + def view_more_network_activities
  159 + @activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page])
  160 + render :partial => 'profile_network_activities', :locals => {:network_activities => @activities}
  161 + end
  162 +
  163 + def remove_scrap
  164 + begin
  165 + scrap = current_user.person.scraps(params[:scrap_id])
  166 + scrap.destroy
  167 + render :text => _('Scrap successfully removed.')
  168 + rescue
  169 + render :text => _('You could not remove this scrap')
  170 + end
  171 + end
  172 +
  173 + def remove_activity
  174 + begin
  175 + activity = current_user.person.tracked_actions.find(params[:activity_id])
  176 + ActionTrackerNotification.find(:first, :conditions => {:profile_id => current_user.person, :action_tracker_id => activity}).destroy
  177 + render :text => _('Activity successfully removed.')
  178 + rescue
  179 + render :text => _('You could not remove this activity')
  180 + end
  181 + end
  182 +
126 183 protected
127 184  
128 185 def check_access_to_profile
... ...
app/helpers/application_helper.rb
... ... @@ -414,6 +414,7 @@ module ApplicationHelper
414 414 #
415 415 # If the profile has no image set yet, then a default image is used.
416 416 def profile_image(profile, size=:portrait, opt={})
  417 + return '' if profile.nil?
417 418 opt[:alt] ||= profile.name()
418 419 opt[:title] ||= ''
419 420 opt[:class] ||= ''
... ... @@ -515,14 +516,14 @@ module ApplicationHelper
515 516 if profile.kind_of?(Person)
516 517 [
517 518 {_('Home Page') => url_for(profile.url)},
518   - {_('Profile') => url_for(profile.public_profile_url)},
  519 + {_('Wall') => url_for(profile.public_profile_url)},
519 520 {_('Friends') => url_for(:controller => :profile, :action => :friends, :profile => profile.identifier)},
520 521 {_('Communities') => url_for(:controller => :profile, :action => :communities, :profile => profile.identifier)}
521 522 ]
522 523 elsif profile.kind_of?(Community)
523 524 [
524 525 {_('Home Page') => url_for(profile.url)},
525   - {_('Profile') => url_for(profile.public_profile_url)},
  526 + {_('Wall') => url_for(profile.public_profile_url)},
526 527 {_('Members') => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)},
527 528 {_('Agenda') => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}
528 529 ]
... ... @@ -1027,8 +1028,12 @@ module ApplicationHelper
1027 1028 link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button'))
1028 1029 end
1029 1030  
  1031 + def jquery_theme
  1032 + theme_option(:jquery_theme) || 'smoothness_mod'
  1033 + end
  1034 +
1030 1035 def jquery_ui_theme_stylesheet_path
1031   - 'jquery.ui/sunny-mod/jquery-ui-1.8.2.custom'
  1036 + 'jquery.ui/' + jquery_theme + '/jquery-ui-1.8.2.custom'
1032 1037 end
1033 1038  
1034 1039 def ui_error(message)
... ... @@ -1131,4 +1136,20 @@ module ApplicationHelper
1131 1136 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
1132 1137 end
1133 1138  
  1139 + def limited_text_area(object_name, method, limit, text_area_id, options = {})
  1140 + content_tag(:div, [
  1141 + text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)),
  1142 + content_tag(:p, content_tag(:span, limit) + _(' characters left'), :id => text_area_id + '_left'),
  1143 + content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none')
  1144 + ], :class => 'limited-text-area')
  1145 + end
  1146 +
  1147 + def pluralize_without_count(count, singular, plural = nil)
  1148 + count == 1 ? singular : (plural || singular.pluralize)
  1149 + end
  1150 +
  1151 + def unique_with_count(list, connector = 'for')
  1152 + list.sort.inject(Hash.new(0)){|h,i| h[i] += 1; h }.collect{ |x, n| [n, connector, x].join(" ") }.sort
  1153 + end
  1154 +
1134 1155 end
... ...
app/models/action_tracker_notification.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class ActionTrackerNotification < ActiveRecord::Base
  2 +
  3 + belongs_to :profile
  4 + belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id'
  5 +
  6 + validates_presence_of :profile_id, :action_tracker_id
  7 + validates_uniqueness_of :action_tracker_id, :scope => :profile_id
  8 +
  9 +end
  10 +
  11 +ActionTracker::Record.has_many :action_tracker_notifications, :class_name => 'ActionTrackerNotification', :foreign_key => 'action_tracker_id', :dependent => :destroy
... ...
app/models/add_friend.rb
... ... @@ -15,8 +15,8 @@ class AddFriend &lt; Task
15 15 alias :friend= :target=
16 16  
17 17 def perform
18   - requestor.add_friend(target, group_for_person)
19 18 target.add_friend(requestor, group_for_friend)
  19 + requestor.add_friend(target, group_for_person)
20 20 end
21 21  
22 22 def description
... ...
app/models/article.rb
... ... @@ -2,6 +2,11 @@ require &#39;hpricot&#39;
2 2  
3 3 class Article < ActiveRecord::Base
4 4  
  5 + track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.published? && !a.profile.is_a?(Community) && !a.image? }
  6 + track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.published? && (a.body_changed? || a.name_changed?) }
  7 + track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => :published?
  8 + 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? }
  9 +
5 10 # xss_terminate plugin can't sanitize array fields
6 11 before_save :sanitize_tag_list
7 12  
... ...
app/models/comment.rb
1 1 class Comment < ActiveRecord::Base
2   -
  2 +
  3 + track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"]
  4 +
3 5 validates_presence_of :title, :body
4 6 belongs_to :article, :counter_cache => true
5 7 belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id'
... ...
app/models/community.rb
... ... @@ -67,4 +67,12 @@ class Community &lt; Organization
67 67 def blocks_to_expire_cache
68 68 [MembersBlock]
69 69 end
  70 +
  71 + def each_member(offset=0)
  72 + while member = self.members.first(:order => :id, :offset => offset)
  73 + yield member
  74 + offset = offset + 1
  75 + end
  76 + end
  77 +
70 78 end
... ...
app/models/friendship.rb
1 1 class Friendship < ActiveRecord::Base
  2 + 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) }
  3 +
2 4 belongs_to :person, :foreign_key => :person_id
3 5 belongs_to :friend, :class_name => 'Person', :foreign_key => 'friend_id'
4   -
5 6 end
... ...
app/models/person.rb
1 1 # A person is the profile of an user holding all relationships with the rest of the system
2 2 class Person < Profile
3 3  
  4 + acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
4 5 acts_as_accessor
5 6  
6 7 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 &lt; Profile
16 17  
17 18 has_many :mailings
18 19  
  20 + has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC"
  21 + has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id
  22 +
19 23 named_scope :more_popular,
20 24 :select => "#{Profile.qualified_column_names}, count(friend_id) as total",
21 25 :group => Profile.qualified_column_names,
... ... @@ -31,6 +35,23 @@ class Person &lt; Profile
31 35 self.user.destroy if self.user
32 36 end
33 37  
  38 + def scraps(scrap=nil)
  39 + scrap = scrap.is_a?(Scrap) ? scrap.id : scrap
  40 + scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
  41 + end
  42 +
  43 + def can_control_scrap?(scrap)
  44 + begin
  45 + !self.scraps(scrap).nil?
  46 + rescue
  47 + false
  48 + end
  49 + end
  50 +
  51 + def can_control_activity?(activity)
  52 + self.tracked_notifications.exists?(activity)
  53 + end
  54 +
34 55 # Sets the identifier for this person. Raises an exception when called on a
35 56 # existing person (since peoples' identifiers cannot be changed)
36 57 def identifier=(value)
... ... @@ -305,4 +326,34 @@ class Person &lt; Profile
305 326 }[amount] || _("%s friends") % amount
306 327 end
307 328  
  329 + def self.notify_activity(tracked_action)
  330 + profile = tracked_action.dispatcher.profile if tracked_action.dispatcher.is_a?(Article)
  331 + profile = tracked_action.dispatcher.resource if tracked_action.dispatcher.is_a?(RoleAssignment)
  332 + profile = tracked_action.dispatcher.article.profile if tracked_action.dispatcher.is_a?(Comment)
  333 + profile_id = profile.nil? ? nil : profile.id
  334 + Delayed::Job.enqueue NotifyActivityToProfilesJob.new(tracked_action.id, profile_id)
  335 + ActionTrackerNotification.create(:action_tracker => tracked_action, :profile => tracked_action.user)
  336 + end
  337 +
  338 + def is_member_of?(profile)
  339 + profile.members.include?(self)
  340 + end
  341 +
  342 + def follows?(profile)
  343 + profile.followed_by?(self)
  344 + end
  345 +
  346 + def each_friend(offset=0)
  347 + while friend = self.friends.first(:order => :id, :offset => offset)
  348 + yield friend
  349 + offset = offset + 1
  350 + end
  351 + end
  352 +
  353 + protected
  354 +
  355 + def followed_by?(profile)
  356 + self == profile || self.is_a_friend?(profile)
  357 + end
  358 +
308 359 end
... ...
app/models/profile.rb
... ... @@ -87,6 +87,11 @@ class Profile &lt; ActiveRecord::Base
87 87 :order => "total DESC, total_comments DESC",
88 88 :conditions => ["articles.created_at BETWEEN ? AND ?", 7.days.ago, DateTime.now]
89 89  
  90 + acts_as_trackable :dependent => :destroy
  91 +
  92 + has_many :action_tracker_notifications, :foreign_key => 'profile_id'
  93 + has_many :tracked_notifications, :through => :action_tracker_notifications, :source => :action_tracker, :order => 'updated_at DESC'
  94 +
90 95 # FIXME ugly workaround
91 96 def self.human_attribute_name(attrib)
92 97 _(self.superclass.human_attribute_name(attrib))
... ... @@ -743,8 +748,16 @@ private :generate_url, :url_options
743 748 }[amount] || _("%s members") % amount
744 749 end
745 750  
  751 + def profile_custom_icon
  752 + self.image.public_filename(:icon) unless self.image.blank?
  753 + end
  754 +
746 755 protected
747 756  
  757 + def followed_by?(person)
  758 + person.is_member_of?(self)
  759 + end
  760 +
748 761 def display_private_info_to?(user)
749 762 if user.nil?
750 763 false
... ...
app/models/scrap.rb 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +class Scrap < ActiveRecord::Base
  2 + validates_presence_of :content
  3 + validates_presence_of :sender_id, :receiver_id
  4 +
  5 + belongs_to :receiver, :class_name => 'Person', :foreign_key => 'receiver_id'
  6 + belongs_to :sender, :class_name => 'Person', :foreign_key => 'sender_id'
  7 + has_many :replies, :class_name => 'Scrap', :foreign_key => 'scrap_id', :dependent => :destroy
  8 + belongs_to :root, :class_name => 'Scrap', :foreign_key => 'scrap_id'
  9 +
  10 + named_scope :all_scraps, lambda {|profile| {:conditions => ["receiver_id = ? OR sender_id = ?", profile, profile], :limit => 30}}
  11 +
  12 + named_scope :not_replies, :conditions => {:scrap_id => nil}
  13 +
  14 + track_actions :leave_scrap, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.receiver != s.sender}
  15 + track_actions :leave_scrap_to_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.receiver == s.sender}
  16 +
  17 + after_create do |scrap|
  18 + scrap.root.update_attribute('updated_at', DateTime.now) unless scrap.root.nil?
  19 + end
  20 +
  21 + before_validation :strip_all_html_tags
  22 +
  23 + def strip_all_html_tags
  24 + sanitizer = HTML::WhiteListSanitizer.new
  25 + self.content = sanitizer.sanitize(self.content, :tags => [])
  26 + end
  27 +
  28 +end
... ...
app/models/uploaded_file.rb
... ... @@ -4,6 +4,8 @@
4 4 # of the file itself is kept. (FIXME?)
5 5 class UploadedFile < Article
6 6  
  7 + 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? }
  8 +
7 9 include ShortFilename
8 10  
9 11 settings_items :title, :type => 'string'
... ... @@ -14,6 +16,10 @@ class UploadedFile &lt; Article
14 16  
15 17 validates_size_of :title, :maximum => 60, :if => (lambda { |file| !file.title.blank? })
16 18  
  19 + def thumbnail_path
  20 + self.image? ? self.full_filename(:thumb).gsub(File.join(RAILS_ROOT, 'public'), '') : nil
  21 + end
  22 +
17 23 def display_title
18 24 title.blank? ? name : title
19 25 end
... ...
app/views/profile/_common.rhtml 0 → 100644
... ... @@ -0,0 +1,53 @@
  1 + <% unless @action %>
  2 + <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours.from_now) do %>
  3 + <tr>
  4 + <th colspan='2'>
  5 + <%= _('Content') %>
  6 + </th>
  7 + </tr>
  8 +
  9 + <% profile.blogs.each do |blog| %>
  10 + <tr>
  11 + <td><%= blog.name + ':' %></td>
  12 + <td>
  13 + <%= link_to(n_('One post', '%{num} posts', blog.posts.published.count) % { :num => blog.posts.published.count }, blog.url) %>
  14 + </td>
  15 + </tr>
  16 + <% end %>
  17 + <% profile.image_galleries.each do |gallery| %>
  18 + <tr>
  19 + <td><%= gallery.name + ':' %></td>
  20 + <td>
  21 + <%= link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) %>
  22 + </td>
  23 + </tr>
  24 + <% end %>
  25 +
  26 + <tr>
  27 + <td><%= _('Events:') %></td>
  28 + <td>
  29 + <%= link_to profile.events.published.count, :controller => 'events', :action => 'events' %>
  30 + </td>
  31 + </tr>
  32 + <tr>
  33 + <td>
  34 + <%= _('Tags:') %>
  35 + </td>
  36 + <td>
  37 + <%= tag_cloud @tags, :id, { :action => 'tags' }, :max_size => 18, :min_size => 10%>
  38 + </td>
  39 + </tr>
  40 +
  41 + <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %>
  42 + <tr>
  43 + <th colspan='2'><%= _('Interests') %></th>
  44 + </tr>
  45 + <% profile.interests.each do |item| %>
  46 + <tr>
  47 + <td></td>
  48 + <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td>
  49 + </tr>
  50 + <% end %>
  51 + <% end %>
  52 + <% end %>
  53 + <% end %>
... ...
app/views/profile/_leave_scrap.rhtml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<%= @message %>
  2 +<% unless @scraps.nil? %>
  3 + <%= render :partial => 'profile_scraps', :locals => {:scraps => @scraps} %>
  4 +<% end %>
... ...
app/views/profile/_organization.rhtml
1   -<tr>
2   - <th colspan='2'><%= _('Basic information')%></th>
3   -</tr>
4   -
5   -<tr>
6   - <td class='field-name'><%= _('Members') %></td>
7   - <td>
8   - <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %>
9   - </td>
10   -</tr>
11   -
12   -<%= display_field(_('Type:'), profile, :privacy_setting, true) %>
13   -
14   -<%= display_field(_('Location:'), profile, :location, true) %>
15 1  
16 2 <tr>
17   - <td class='field-name'><%= _('Created at:') %></td>
18   - <td><%= show_date(profile.created_at) %></td>
19   -</tr>
  3 + <td colspan='2'>
  4 + <div class='ui-tabs'>
  5 +
  6 + <ul>
  7 + <li class='tab'><a href='#profile-network'><%= _('Network') %></a></li>
  8 + <li class='tab'><a href='#community-profile'><%= _('Profile') %></a></li>
  9 + </ul>
20 10  
21   -<% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %>
22   - <tr>
23   - <td></td>
24   - <td>
25   - <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %>
26   - </td>
27   - </tr>
28   -<% end %>
  11 + <%= render :partial => 'profile_network' %>
29 12  
30   -<tr>
31   - <td class='field-name'><%= _('Administrators:') %></td>
32   - <td>
33   - <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %>
  13 + <div id='community-profile'>
  14 + <table>
  15 + <tr>
  16 + <th colspan='2'><%= _('Basic information')%></th>
  17 + </tr>
  18 +
  19 + <tr>
  20 + <td class='field-name'><%= _('Members') %></td>
  21 + <td>
  22 + <%= link_to profile.members.count, :controller => 'profile', :action => 'members' %>
  23 + </td>
  24 + </tr>
  25 +
  26 + <%= display_field(_('Type:'), profile, :privacy_setting, true) %>
  27 +
  28 + <%= display_field(_('Location:'), profile, :location, true) %>
  29 +
  30 + <tr>
  31 + <td class='field-name'><%= _('Created at:') %></td>
  32 + <td><%= show_date(profile.created_at) %></td>
  33 + </tr>
  34 +
  35 + <% if profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises') %>
  36 + <tr>
  37 + <td></td>
  38 + <td>
  39 + <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %>
  40 + </td>
  41 + </tr>
  42 + <% end %>
  43 +
  44 + <tr>
  45 + <td class='field-name'><%= _('Administrators:') %></td>
  46 + <td>
  47 + <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %>
  48 + </td>
  49 + </tr>
  50 +
  51 + <%= render :partial => 'common' %>
  52 + </table>
  53 + </div>
  54 + </div>
34 55 </td>
35 56 </tr>
... ...
app/views/profile/_person.rhtml
1 1 <tr>
2   - <th colspan='2'><%= _('Basic information')%></th>
3   -</tr>
4   -<%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>
5   -<%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%>
6   -<%= display_field(_('Location:'), profile, :location, true) %>
7   -
8   -<%= display_field(_('Type:'), profile, :privacy_setting, true) %>
9   -
10   -<tr>
11   - <td class='field-name'><%= _('Created at:') %></td>
12   - <td><%= show_date(profile.created_at) %></td>
13   -</tr>
14   -
15   -<% if profile == user || profile.friends.include?(user) %>
16   - <tr>
17   - <th colspan='2'><%= _('Contact')%></th>
18   - </tr>
19   - <%= display_field(_('Address:'), profile, :address) %>
20   - <%= display_field(_('ZIP code:'), profile, :zip_code) %>
21   - <%= display_field(_('Contact phone:'), profile, :contact_phone) %>
22   - <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %>
23   -<% end %>
  2 + <td colspan='2'>
  3 + <div class='ui-tabs'>
  4 +
  5 + <ul>
  6 + <% if logged_in? && current_person.follows?(@profile) %>
  7 + <li class='tab'><a href='#profile-wall'><%= _('Wall') %></a></li>
  8 + <li class='tab'><a href='#profile-network'><%= _('Network') %></a></li>
  9 + <% end %>
  10 + <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %>
  11 + <li class='tab'><a href='#profile-activity'><%= _('Activity') %></a></li>
  12 + <% end %>
  13 + <li class='tab'><a href='#person-profile'><%= _('Profile') %></a></li>
  14 + </ul>
24 15  
25   -<% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %>
26   - <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %>
27   - <tr>
28   - <th colspan='2'><%= _('Work')%></th>
29   - </tr>
30   - <% end %>
31   - <%= display_field(_('Organization:'), profile, :organization) %>
32   - <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%>
  16 + <% if logged_in? && current_person.follows?(@profile) %>
  17 + <%= render :partial => 'profile_wall' %>
  18 + <%= render :partial => 'profile_network' %>
  19 + <% end %>
33 20  
  21 + <% if @profile.public? || (logged_in? && current_person.follows?(@profile)) %>
  22 + <%= render :partial => 'profile_activity' %>
  23 + <% end %>
34 24  
35   - <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %>
36   - <tr>
37   - <th colspan='2'><%= __('Enterprises') %></th>
38   - </tr>
39   - <% profile.enterprises.each do |item| %>
40   - <tr>
41   - <td></td>
42   - <td><%= button 'menu-enterprise', item.name, item.url %></td>
43   - </tr>
44   - <% end %>
45   - <% end %>
  25 + <div id='person-profile'>
  26 + <table>
  27 + <tr>
  28 + <th colspan='2'><%= _('Basic information')%></th>
  29 + </tr>
  30 + <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>
  31 + <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%>
  32 + <%= display_field(_('Location:'), profile, :location, true) %>
  33 +
  34 + <%= display_field(_('Type:'), profile, :privacy_setting, true) %>
  35 +
  36 + <tr>
  37 + <td class='field-name'><%= _('Created at:') %></td>
  38 + <td><%= show_date(profile.created_at) %></td>
  39 + </tr>
  40 +
  41 + <% if profile == user || profile.friends.include?(user) %>
  42 + <tr>
  43 + <th colspan='2'><%= _('Contact')%></th>
  44 + </tr>
  45 + <%= display_field(_('Address:'), profile, :address) %>
  46 + <%= display_field(_('ZIP code:'), profile, :zip_code) %>
  47 + <%= display_field(_('Contact phone:'), profile, :contact_phone) %>
  48 + <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %>
  49 + <% end %>
  50 +
  51 + <% cache_timeout(profile.relationships_cache_key, 4.hours.from_now) do %>
  52 + <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %>
  53 + <tr>
  54 + <th colspan='2'><%= _('Work')%></th>
  55 + </tr>
  56 + <% end %>
  57 + <%= display_field(_('Organization:'), profile, :organization) %>
  58 + <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%>
  59 +
  60 +
  61 + <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %>
  62 + <tr>
  63 + <th colspan='2'><%= __('Enterprises') %></th>
  64 + </tr>
  65 + <% profile.enterprises.each do |item| %>
  66 + <tr>
  67 + <td></td>
  68 + <td><%= button 'menu-enterprise', item.name, item.url %></td>
  69 + </tr>
  70 + <% end %>
  71 + <% end %>
  72 +
  73 + <tr>
  74 + <th colspan='2'><%= _('Network')%></th>
  75 + </tr>
  76 + <tr>
  77 + <td><%= __('Friends') + ':' %></td>
  78 + <td><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %></td>
  79 + </tr>
  80 + <tr>
  81 + <td><%= __('Communities') + ':' %></td>
  82 + <td><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %></td>
  83 + </tr>
  84 +
  85 + <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %>
  86 + <tr>
  87 + <th colspan='2'><%= _('Interests') %></th>
  88 + </tr>
  89 + <% profile.interests.each do |item| %>
  90 + <tr>
  91 + <td></td>
  92 + <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td>
  93 + </tr>
  94 + <% end %>
  95 + <% end %>
46 96  
47   - <tr>
48   - <th colspan='2'><%= _('Network')%></th>
49   - </tr>
50   - <tr>
51   - <td><%= __('Friends') + ':' %></td>
52   - <td><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %></td>
53   - </tr>
54   - <tr>
55   - <td><%= __('Communities') + ':' %></td>
56   - <td><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %></td>
57   - </tr>
  97 + <%= render :partial => 'common' %>
58 98  
59   - <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %>
60   - <tr>
61   - <th colspan='2'><%= _('Interests') %></th>
62   - </tr>
63   - <% profile.interests.each do |item| %>
64   - <tr>
65   - <td></td>
66   - <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td>
67   - </tr>
68   - <% end %>
69   - <% end %>
70   -
71   -<% end %>
  99 + </table>
  100 + <% end %>
  101 + </div>
  102 + </div>
  103 + </td>
  104 +</tr>
... ...
app/views/profile/_profile_activities.rhtml 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<% activities.each do |activity| %>
  2 + <li class='profile-activity-item <%= activity.verb %>' id='profile-activity-item-<%= activity.id %>'>
  3 + <div class='profile-activity-image'>
  4 + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
  5 + </div>
  6 + <div class='profile-activity-description'>
  7 + <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) + ' ' + _('ago') %></p>
  8 + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
  9 + <%= 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 %>
  10 + </div>
  11 + <hr />
  12 + </li>
  13 +<% end %>
  14 +<% if activities.current_page < activities.total_pages %>
  15 + <div id='profile_activities_page_<%= activities.current_page %>'>
  16 + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1)}, :update => "profile_activities_page_#{activities.current_page}" %>
  17 + </div>
  18 +<% end %>
... ...
app/views/profile/_profile_activity.rhtml 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<div id='profile-activity'>
  2 + <h3><%= _("%s's activity") % @profile.name %></h3>
  3 + <ul>
  4 + <%= render :partial => 'profile_activities', :locals => {:activities => @activities} %>
  5 + </ul>
  6 +</div>
... ...
app/views/profile/_profile_network.rhtml 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<div id='profile-network'>
  2 + <h3><%= _("%s's network activity") % @profile.name %></h3>
  3 + <ul>
  4 + <%= render :partial => 'profile_network_activities', :locals => {:network_activities => @network_activities} %>
  5 + </ul>
  6 +</div>
... ...
app/views/profile/_profile_network_activities.rhtml 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 + <% network_activities.each do |activity| %>
  2 + <li class='profile-network-item <%= activity.verb %>' id='profile-network-item-<%= activity.id %>'>
  3 + <div class='profile-network-image'>
  4 + <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
  5 + <% if logged_in? && current_person.follows?(activity.user) && current_person != activity.user %>
  6 + <p class='profile-network-send-message'><%= 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" %></p>
  7 + <% end %>
  8 + </div>
  9 + <div class='profile-network-description'>
  10 + <p class='profile-network-time'><%= time_ago_in_words(activity.created_at) + ' ' + _('ago') %></p>
  11 + <p class='profile-network-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
  12 + </div>
  13 + <div id='profile-network-message-<%= activity.id%>' style='display:none;'>
  14 + <div id='profile-network-form-<%= activity.id%>' style='display:none;'>
  15 + <p class='profile-network-message'>
  16 + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :not_load_scraps => true}, :update => "profile-network-message-response-#{activity.id}", :success =>"hide_and_show(['#profile-network-form-#{activity.id}'],['#profile-network-message-response-#{activity.id}'])" do %>
  17 + <%= limited_text_area :scrap, :content, 420, "content_#{activity.id}", :cols => 50, :rows => 2 %>
  18 + <%= hidden_field_tag 'receiver_id', activity.user.id %>
  19 + <%= submit_button :add, _('Leave a message') %>
  20 + <%= button_to_function :cancel, _('Cancel'), "hide_and_show(['#profile-network-message-#{activity.id}'],[]);return false" %>
  21 + <% end %>
  22 + </p>
  23 + </div>
  24 + <div id='profile-network-message-response-<%=activity.id%>' class='profile-network-message-response'></div>
  25 + </div>
  26 + <hr />
  27 + </li>
  28 + <% end %>
  29 +<% if network_activities.current_page < network_activities.total_pages %>
  30 + <div id='profile_network_activities_page_<%= network_activities.current_page %>'>
  31 + <%= 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}" %>
  32 + </div>
  33 +<% end %>
... ...
app/views/profile/_profile_scrap.rhtml 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +<li class='profile-wall-item' id='profile-wall-item-<%= scrap.id %>'>
  2 + <div class='profile-wall-image'>
  3 + <%= link_to(profile_image(scrap.sender, :minor), scrap.sender.url) %>
  4 + <% if logged_in? && current_person.follows?(scrap.sender) && current_person != scrap.sender %>
  5 + <p class='profile-wall-send-message'><%= 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" %></p>
  6 + <% end %>
  7 + </div>
  8 + <div class='profile-wall-description'>
  9 + <p class='profile-wall-sender'><%= link_to scrap.sender.name, scrap.sender.url %></p>
  10 + <p class='profile-wall-time'><%= time_ago_in_words(scrap.created_at) + ' ' + _('ago') %></p>
  11 + <p class='profile-wall-text'><%= auto_link_urls scrap.content %></p>
  12 + <%= 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) %>
  13 + <% if logged_in? && current_person.follows?(scrap.sender) && scrap.root.nil? %>
  14 + <p class='profile-wall-send-reply'><%= 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" %></p>
  15 + <% end %>
  16 + <ul>
  17 + <% scrap.replies.map do |reply| %>
  18 + <%= render :partial => 'profile_scrap', :locals => {:scrap => reply} %>
  19 + <% end %>
  20 + </ul>
  21 + </div>
  22 + <div id='profile-wall-message-<%= scrap.id%>' style='display:none;'>
  23 + <div id='profile-wall-form-<%= scrap.id%>' style='display:none;'>
  24 + <p class='profile-wall-message'>
  25 + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :not_load_scraps => true}, :update => "profile-wall-message-response-#{scrap.id}", :success =>"hide_and_show(['#profile-wall-form-#{scrap.id}'],['#profile-wall-message-response-#{scrap.id}'])" do %>
  26 + <%= limited_text_area :scrap, :content, 420, "content_#{scrap.id}", :cols => 50, :rows => 2 %>
  27 + <%= hidden_field_tag 'receiver_id', scrap.sender.id %>
  28 + <%= submit_button :add, _('Leave a scrap') %>
  29 + <%= button_to_function :cancel, _('Cancel'), "hide_and_show(['#profile-wall-message-#{scrap.id}'],[]);return false" %>
  30 + <% end %>
  31 + </p>
  32 + </div>
  33 + <div id='profile-wall-message-response-<%=scrap.id%>' class='profile-wall-message-response'></div>
  34 + </div>
  35 + <div id='profile-wall-reply-<%= scrap.id%>' style='display:none;'>
  36 + <div id='profile-wall-reply-form-<%= scrap.id%>' style='display:none;'>
  37 + <p class='profile-wall-reply'>
  38 + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap'}, :update => "profile_scraps", :success =>"hide_and_show(['#profile-wall-reply-form-#{scrap.id}'],['#profile-wall-reply-response-#{scrap.id}'])" do %>
  39 + <%= limited_text_area :scrap, :content, 420, "reply_content_#{scrap.id}", :cols => 50, :rows => 2 %>
  40 + <%= hidden_field :scrap, :scrap_id, :id => "scrap_id_#{scrap.id}" %>
  41 + <%= hidden_field_tag 'receiver_id', scrap.sender.id %>
  42 + <%= submit_button :add, _('Leave a scrap') %>
  43 + <%= button_to_function :cancel, _('Cancel'), "hide_and_show(['#profile-wall-reply-#{scrap.id}'],[]);return false" %>
  44 + <% end %>
  45 + </p>
  46 + </div>
  47 + <div id='profile-wall-message-response-<%=scrap.id%>' class='profile-wall-message-response'></div>
  48 + </div>
  49 + <hr />
  50 +</li>
... ...
app/views/profile/_profile_scraps.rhtml 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<% scraps.map do |scrap| %>
  2 + <%= render :partial => 'profile_scrap', :locals => {:scrap => scrap} %>
  3 +<% end %>
  4 +<% if scraps.current_page < scraps.total_pages %>
  5 + <div id='profile_scraps_page_<%= scraps.current_page %>'>
  6 + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_scraps', :page => (scraps.current_page + 1)}, :update => "profile_scraps_page_#{scraps.current_page}" %>
  7 + </div>
  8 +<% end %>
... ...
app/views/profile/_profile_wall.rhtml 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +<div id='profile-wall'>
  2 + <h3><%= _("%s's wall") % @profile.name %></h3>
  3 + <div id='leave_scrap'>
  4 + <%= flash[:error] %>
  5 + <% form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_scraps', :success => "$('leave_scrap_content').value=''" do %>
  6 + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %>
  7 + <%= submit_button :scrap, _('Leave a scrap') %>
  8 + <% end %>
  9 + </div>
  10 + <div id='leave_scrap_response'></div>
  11 + <ul id='profile_scraps'>
  12 + <%= render :partial => 'profile_scraps', :locals => {:scraps => @wall_items} %>
  13 + </ul>
  14 +</div>
... ...
app/views/profile/index.rhtml
... ... @@ -14,58 +14,4 @@
14 14  
15 15 <table class='profile'>
16 16 <%= render :partial => partial_for_class(profile.class) %>
17   -
18   - <% unless @action %>
19   - <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours.from_now) do %>
20   - <tr>
21   - <th colspan='2'>
22   - <%= _('Content') %>
23   - </th>
24   - </tr>
25   -
26   - <% profile.blogs.each do |blog| %>
27   - <tr>
28   - <td><%= blog.name + ':' %></td>
29   - <td>
30   - <%= link_to(n_('One post', '%{num} posts', blog.posts.published.count) % { :num => blog.posts.published.count }, blog.url) %>
31   - </td>
32   - </tr>
33   - <% end %>
34   - <% profile.image_galleries.each do |gallery| %>
35   - <tr>
36   - <td><%= gallery.name + ':' %></td>
37   - <td>
38   - <%= link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) %>
39   - </td>
40   - </tr>
41   - <% end %>
42   -
43   - <tr>
44   - <td><%= _('Events:') %></td>
45   - <td>
46   - <%= link_to profile.events.published.count, :controller => 'events', :action => 'events' %>
47   - </td>
48   - </tr>
49   - <tr>
50   - <td>
51   - <%= _('Tags:') %>
52   - </td>
53   - <td>
54   - <%= tag_cloud @tags, :id, { :action => 'tags' }, :max_size => 18, :min_size => 10%>
55   - </td>
56   - </tr>
57   -
58   - <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %>
59   - <tr>
60   - <th colspan='2'><%= _('Interests') %></th>
61   - </tr>
62   - <% profile.interests.each do |item| %>
63   - <tr>
64   - <td></td>
65   - <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td>
66   - </tr>
67   - <% end %>
68   - <% end %>
69   - <% end %>
70   - <% end %>
71 17 </table>
... ...
config/initializers/action_tracker.rb 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +require 'noosfero/i18n'
  2 +
  3 +# ActionTracker plugin stuff
  4 +
  5 +ActionTrackerConfig.verbs = {
  6 + :create_article => {
  7 + :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"))}}'],
  8 + :type => :groupable
  9 + },
  10 + :update_article => {
  11 + :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"))}}'],
  12 + :type => :groupable
  13 + },
  14 + :remove_article => {
  15 + :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"))}}'],
  16 + :type => :groupable
  17 + },
  18 + :publish_article_in_community => {
  19 + :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"))}}'],
  20 + :type => :groupable
  21 + },
  22 + :new_friendship => {
  23 + :description => _('has made %s %s:<br />%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}}'],
  24 + :type => :groupable
  25 + },
  26 + :join_community => {
  27 + :description => _('has joined %s %s:<br />%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}}'],
  28 + :type => :groupable
  29 + },
  30 + :leave_community => {
  31 + :description => _('has left %s %s:<br />%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}}'],
  32 + :type => :groupable
  33 + },
  34 + :upload_image => {
  35 + :description => _('uploaded %s %s:<br />%s<br />%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("<br />")}}'],
  36 + :type => :groupable
  37 + },
  38 + :leave_comment => {
  39 + :description => _('has left the following comment "%s" on the article %s: <br /> "%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)}}"]
  40 + },
  41 + :leave_scrap => {
  42 + :description => _('sent a message to %s: <br /> "%s"') % ["{{link_to(ta.get_receiver_name, ta.get_receiver_url)}}", "{{auto_link_urls(ta.get_content)}}"]
  43 + },
  44 + :leave_scrap_to_self => {
  45 + :description => _('wrote: <br /> "%s"') % "{{auto_link_urls(ta.get_content)}}"
  46 + }
  47 +}
  48 +
  49 +ActionTrackerConfig.current_user_method = :current_person
  50 +
  51 +ActionTrackerConfig.timeout = 24.hours
... ...
db/migrate/20100910205341_create_scraps.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class CreateScraps < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :scraps do |t|
  4 + t.text :content
  5 + t.integer :sender_id, :receiver_id
  6 + t.integer :scrap_id
  7 + t.timestamps
  8 + end
  9 + end
  10 +
  11 + def self.down
  12 + drop_table :scraps
  13 + end
  14 +end
... ...
db/migrate/20100910205408_create_action_tracker.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +class CreateActionTracker < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :action_tracker do |t|
  4 + t.belongs_to :user, :polymorphic => true
  5 + t.belongs_to :dispatcher, :polymorphic => true
  6 + t.text :params
  7 + t.string :verb
  8 + t.timestamps
  9 + end
  10 +
  11 + change_table :action_tracker do |t|
  12 + t.index [:user_id, :user_type]
  13 + t.index [:dispatcher_id, :dispatcher_type]
  14 + t.index :verb
  15 + end
  16 + end
  17 +
  18 + def self.down
  19 + drop_table :action_tracker
  20 + end
  21 +end
... ...
db/migrate/20100910205427_create_action_tracker_notifications.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +class CreateActionTrackerNotifications < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :action_tracker_notifications do |t|
  4 + t.references :action_tracker
  5 + t.references :profile
  6 + end
  7 + end
  8 +
  9 + def self.down
  10 + drop_table :action_tracker_notifications
  11 + end
  12 +end
... ...
lib/notify_activity_job.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +class NotifyActivityJob < Struct.new(:tracked_action_id, :profile_id)
  2 + def perform
  3 + tracked_action = ActionTracker::Record.find(tracked_action_id)
  4 + profile = Profile.find(profile_id)
  5 + ActionTrackerNotification.create(:action_tracker => tracked_action, :profile => profile)
  6 + end
  7 +end
... ...
lib/notify_activity_to_profiles_job.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id, :target_profile_id)
  2 + def perform
  3 + profile = Profile.find(target_profile_id) unless target_profile_id.nil?
  4 + tracked_action = ActionTracker::Record.find(tracked_action_id)
  5 + tracked_action.user.each_friend do |friend|
  6 + Delayed::Job.enqueue NotifyActivityJob.new(tracked_action_id, friend.id)
  7 + end
  8 + if profile.is_a?(Community)
  9 + profile.each_member do |member|
  10 + next if member == tracked_action.user
  11 + Delayed::Job.enqueue NotifyActivityJob.new(tracked_action_id, member.id)
  12 + end
  13 + ActionTrackerNotification.create(:action_tracker => tracked_action, :profile => profile)
  14 + end
  15 + end
  16 +end
... ...
public/designs/icons/tango/style.css
... ... @@ -71,3 +71,5 @@
71 71 .icon-media-next { background-image: url(Tango/16x16/actions/media-skip-forward.png) }
72 72 .icon-lock { background-image: url(Tango/16x16/actions/lock.png) }
73 73 .icon-chat { background-image: url(Tango/16x16/apps/internet-group-chat.png) }
  74 +.icon-scrap { background-image: url(Tango/16x16/actions/format-justify-left.png) }
  75 +.icon-reply { background-image: url(Tango/16x16/actions/edit-redo.png) }
... ...
public/designs/themes/noosfero/theme.yml
1 1 theme: "Noosfero default theme"
2 2 layout: "application-ng"
  3 +jquery_theme: "smoothness_mod"
3 4 icon_theme: [default, pidgin]
... ...
public/images/scrap-bg-gray.png 0 → 100644

289 Bytes

public/images/scrap-bg.png 0 → 100644

290 Bytes

public/javascripts/application.js
... ... @@ -545,3 +545,29 @@ jQuery(function($) {
545 545 $('body').addClass('webkit');
546 546 }
547 547 });
  548 +
  549 +function hide_and_show(hide_elements, show_elements) {
  550 + for(i=0; i < hide_elements.length; i++){
  551 + jQuery(hide_elements[i]).hide();
  552 + }
  553 + for(i=0; i < show_elements.length; i++){
  554 + jQuery(show_elements[i]).show();
  555 + }
  556 +}
  557 +
  558 +function limited_text_area(textid, limit) {
  559 + var text = jQuery('#' + textid).val();
  560 + jQuery('#' + textid).css('height', jQuery('#' + textid).attr('scrollHeight') + 'px');
  561 + var textlength = text.length;
  562 + jQuery('#' + textid + '_left span').html(limit - textlength);
  563 + if (textlength > limit) {
  564 + jQuery('#' + textid + '_left').hide();
  565 + jQuery('#' + textid + '_limit').show();
  566 + jQuery('#' + textid).val(text.substr(0,limit));
  567 + return false;
  568 + } else {
  569 + jQuery('#' + textid + '_left').show();
  570 + jQuery('#' + textid + '_limit').hide();
  571 + return true;
  572 + }
  573 +}
... ...
public/stylesheets/application.css
... ... @@ -265,7 +265,7 @@ table.profile th {
265 265 }
266 266 table.profile td {
267 267 border: none;
268   - padding-left: 0px;
  268 + padding: 0px;
269 269 }
270 270 table.profile tr:hover td {
271 271 background: none;
... ... @@ -4617,3 +4617,261 @@ h1#agenda-title {
4617 4617 text-align: center;
4618 4618 display: block;
4619 4619 }
  4620 +
  4621 +#profile-activity ul, #profile-network ul, #profile-wall ul {
  4622 + padding-left: 0;
  4623 + clear: both;
  4624 +}
  4625 +
  4626 +#profile-activity li, #profile-network li, #profile-wall li {
  4627 + display: block;
  4628 + padding: 0;
  4629 + margin-bottom: 8px;
  4630 +}
  4631 +
  4632 +#profile-activity .profile-activity-image, #profile-network .profile-network-image, #profile-wall .profile-wall-image {
  4633 + float: left;
  4634 + width: 100px;
  4635 + height: 80px;
  4636 + margin: 0;
  4637 + padding: 0;
  4638 + border: 1px solid transparent;
  4639 + text-align: center;
  4640 +}
  4641 +
  4642 +#profile-activity .profile-activity-description, #profile-network .profile-network-description, #profile-wall .profile-wall-description {
  4643 + float: left;
  4644 + min-height: 80px;
  4645 + margin: 0;
  4646 + padding: 0;
  4647 + border: 1px solid #ccc;
  4648 + width: 355px;
  4649 + overflow: hidden;
  4650 + background-color: #fff;
  4651 + position: relative;
  4652 +}
  4653 +
  4654 +#profile-activity .profile-activity-description .icon-delete span,
  4655 +#profile-network .profile-network-description .icon-delete span,
  4656 +#profile-wall .profile-wall-description .icon-delete span {
  4657 + display: none;
  4658 +}
  4659 +
  4660 +#profile-activity .profile-activity-description .icon-delete,
  4661 +#profile-network .profile-network-description .icon-delete,
  4662 +#profile-wall .profile-wall-description .icon-delete {
  4663 + position: absolute;
  4664 + top: 4px;
  4665 + right: 4px;
  4666 + background-position: center center;
  4667 +}
  4668 +
  4669 +#profile-activity .profile-activity-text, #profile-network .profile-network-text, #profile-wall .profile-wall-text {
  4670 + font-size: 13px;
  4671 + margin: 5px;
  4672 + padding-top: 15px;
  4673 +}
  4674 +
  4675 +#profile-wall .profile-wall-text {
  4676 + padding-top: 0;
  4677 +}
  4678 +
  4679 +#profile-activity .profile-activity-time, #profile-network .profile-network-time, #profile-wall .profile-wall-time {
  4680 + font-size: 11px;
  4681 + margin: 5px;
  4682 + color: #333;
  4683 +}
  4684 +
  4685 +#profile-activity hr, #profile-network hr, #profile-wall hr {
  4686 + clear: both;
  4687 + border: 0;
  4688 +}
  4689 +
  4690 +#profile-activity .profile-activity-send-message, #profile-network .profile-network-send-message, #profile-wall .profile-wall-send-message {
  4691 + text-decoration: none;
  4692 + font-size: 11px;
  4693 + color: #000;
  4694 + margin: 10px 0 0 0;
  4695 +}
  4696 +
  4697 +#profile-activity .profile-activity-send-message a, #profile-network .profile-network-send-message a, #profile-wall .profile-wall-send-message a {
  4698 + text-decoration: none;
  4699 +}
  4700 +
  4701 +#profile-activity .profile-activity-text img, #profile-network .profile-network-text img, #profile-wall .profile-wall-text img {
  4702 + padding: 1px;
  4703 + border: 1px solid #ccc;
  4704 + margin: 4px 3px 0 0;
  4705 +}
  4706 +
  4707 +.profile-wall-image,
  4708 +.leave_scrap_to_self .profile-network-image,
  4709 +.leave_scrap .profile-network-image,
  4710 +.leave_scrap_to_self .profile-activity-image,
  4711 +.leave_scrap .profile-activity-image {
  4712 + background: transparent url(/images/scrap-bg.png) 104% 10px no-repeat;
  4713 + position: relative;
  4714 + left: 1px;
  4715 + z-index: 2;
  4716 +}
  4717 +
  4718 +#leave_scrap_content {
  4719 + width: 450px;
  4720 + border: 1px solid #ccc;
  4721 + padding: 2px;
  4722 +}
  4723 +
  4724 +#profile-network .upload_image .profile-network-text span,
  4725 +#profile-activity .upload_image .profile-activity-text span {
  4726 + width: 109px;
  4727 + height: 109px;
  4728 + overflow: hidden;
  4729 + display: inline-block;
  4730 + padding: 0;
  4731 + margin: 4px 3px 0 0;
  4732 + text-align: center;
  4733 +}
  4734 +
  4735 +#profile-network .upload_image .profile-network-text img,
  4736 +#profile-activity .upload_image .profile-activity-text img {
  4737 + padding: 0;
  4738 + border: 0;
  4739 + margin: 0;
  4740 +}
  4741 +
  4742 +#leave_scrap_response {
  4743 + font-size: 12px;
  4744 + text-align: right;
  4745 + font-weight: bold;
  4746 + color: #333;
  4747 + margin: 4px;
  4748 +}
  4749 +
  4750 +.profile-network-message-response, .profile-wall-message-response {
  4751 + color: #204a87;
  4752 + padding: 4px;
  4753 + color: #333;
  4754 + text-align: right;
  4755 + font-size: 12px;
  4756 +}
  4757 +
  4758 +.profile-wall-sender,
  4759 +.profile-wall-time,
  4760 +.profile-wall-description,
  4761 +.profile-activity-sender,
  4762 +.profile-activity-time,
  4763 +.profile-activity-description,
  4764 +.profile-network-sender,
  4765 +.profile-network-time,
  4766 +.profile-network-description {
  4767 + padding-left: 5px;
  4768 +}
  4769 +
  4770 +.profile-network-sender,
  4771 +.profile-wall-sender {
  4772 + margin: 2px 0;
  4773 +}
  4774 +
  4775 +#profile-activity .profile-activity-time,
  4776 +#profile-network .profile-network-time,
  4777 +#profile-wall .profile-wall-time {
  4778 + margin: 0;
  4779 +}
  4780 +
  4781 +#profile-network textarea,
  4782 +#profile-wall textarea {
  4783 + margin: 4px 0;
  4784 + width: 453px;
  4785 + border: 1px solid #ccc;
  4786 + padding: 2px;
  4787 + overflow: hidden;
  4788 +}
  4789 +
  4790 +.profile-network-message,
  4791 +.profile-wall-message {
  4792 + margin: 0;
  4793 +}
  4794 +
  4795 +.profile-send-message {
  4796 + color: #555;
  4797 + border: 1px solid #ccc;
  4798 + background-color: #eee;
  4799 + padding: 4px 4px 4px 20px;
  4800 + background-repeat: no-repeat;
  4801 + background-position: 4px center;
  4802 +}
  4803 +
  4804 +.ui-widget-content .profile-send-message:hover,
  4805 +.profile-send-message:hover {
  4806 + color: #eee;
  4807 + background-color: #555;
  4808 +}
  4809 +
  4810 +.limited-text-area p {
  4811 + margin: 0;
  4812 + font-size: 11px;
  4813 + color: #333;
  4814 + text-align: right;
  4815 +}
  4816 +
  4817 +#leave_scrap input.button {
  4818 + float: right;
  4819 + margin-bottom: 10px;
  4820 +}
  4821 +
  4822 +#leave_scrap_content_limit,
  4823 +#leave_scrap_content_left {
  4824 + float: left;
  4825 +}
  4826 +
  4827 +#leave_scrap {
  4828 + float: left;
  4829 + padding: 4px;
  4830 + border: 1px dotted #aaa;
  4831 + margin-bottom: 10px;
  4832 +}
  4833 +
  4834 +#leave_scrap textarea {
  4835 + width: 440px;
  4836 + overflow: hidden;
  4837 +}
  4838 +
  4839 +.profile-send-reply {
  4840 + background-color: #eee;
  4841 + border: 1px solid #aaa;
  4842 + padding: 2px;
  4843 + padding-left: 20px;
  4844 + background-repeat: no-repeat;
  4845 + background-position: 2px center;
  4846 + color: #aaa;
  4847 + text-decoration: none;
  4848 + margin-left: 8px;
  4849 +}
  4850 +
  4851 +#content .profile-send-reply:hover {
  4852 + text-decoration: none;
  4853 +}
  4854 +
  4855 +#profile-wall .profile-wall-description .profile-wall-image {
  4856 + background-image: url(/images/scrap-bg-gray.png);
  4857 +}
  4858 +
  4859 +#profile-wall .profile-wall-description .profile-wall-description {
  4860 + width: 242px;
  4861 + background-color: #eee;
  4862 +}
  4863 +
  4864 +#profile-network .profile-wall-description textarea,
  4865 +#profile-wall .profile-wall-description textarea {
  4866 + width: 338px;
  4867 + margin-left: 2px;
  4868 +}
  4869 +
  4870 +#profile-network .profile-wall-description .limited-text-area p,
  4871 +#profile-wall .profile-wall-description .limited-text-area p {
  4872 + margin-right: 10px;
  4873 +}
  4874 +
  4875 +.limited-text-area div.fieldWithErrors {
  4876 + background: transparent;
  4877 +}
... ...
public/stylesheets/jquery.ui/smoothness_mod/images/ui-anim_basic_16x16.gif 0 → 100644

1.52 KB

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_0_aaaaaa_40x100.png 0 → 100644

180 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_flat_75_ffffff_40x100.png 0 → 100644

178 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_55_fbf9ee_1x400.png 0 → 100644

120 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_65_ffffff_1x400.png 0 → 100644

105 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_dadada_1x400.png 0 → 100644

111 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_75_e6e6e6_1x400.png 0 → 100644

110 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_glass_95_fef1ec_1x400.png 0 → 100644

119 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-bg_highlight-soft_75_cccccc_1x100.png 0 → 100644

101 Bytes

public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_222222_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_2e83ff_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_454545_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_888888_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/smoothness_mod/images/ui-icons_cd0a0a_256x240.png 0 → 100644

4.27 KB

public/stylesheets/jquery.ui/smoothness_mod/jquery-ui-1.8.2.custom.css 0 → 100644
... ... @@ -0,0 +1,489 @@
  1 +/*
  2 +* jQuery UI CSS Framework
  3 +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  4 +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
  5 +*/
  6 +
  7 +/* Layout helpers
  8 +----------------------------------*/
  9 +.ui-helper-hidden { display: none; }
  10 +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
  11 +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
  12 +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
  13 +.ui-helper-clearfix { display: inline-block; }
  14 +/* required comment for clearfix to work in Opera \*/
  15 +* html .ui-helper-clearfix { height:1%; }
  16 +.ui-helper-clearfix { display:block; }
  17 +/* end clearfix */
  18 +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
  19 +
  20 +
  21 +/* Interaction Cues
  22 +----------------------------------*/
  23 +.ui-state-disabled { cursor: default !important; }
  24 +
  25 +
  26 +/* Icons
  27 +----------------------------------*/
  28 +
  29 +/* states and images */
  30 +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
  31 +
  32 +
  33 +/* Misc visuals
  34 +----------------------------------*/
  35 +
  36 +/* Overlays */
  37 +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
  38 +
  39 +
  40 +/*
  41 +* jQuery UI CSS Framework
  42 +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
  43 +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
  44 +* 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
  45 +*/
  46 +
  47 +
  48 +/* Component containers
  49 +----------------------------------*/
  50 +.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
  51 +.ui-widget .ui-widget { font-size: 1em; }
  52 +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
  53 +.ui-widget-content { background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
  54 +.ui-widget-content a { color: #222222; }
  55 +.ui-widget-header { border-bottom: 1px solid #aaaaaa; color: #222222; font-weight: bold; }
  56 +.ui-widget-header a { color: #222222; }
  57 +
  58 +/* Interaction states
  59 +----------------------------------*/
  60 +.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; }
  61 +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
  62 +.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; }
  63 +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
  64 +.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; }
  65 +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
  66 +.ui-widget :active { outline: none; }
  67 +
  68 +/* Interaction Cues
  69 +----------------------------------*/
  70 +.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; }
  71 +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
  72 +.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; }
  73 +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
  74 +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
  75 +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
  76 +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
  77 +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
  78 +
  79 +/* Icons
  80 +----------------------------------*/
  81 +
  82 +/* states and images */
  83 +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
  84 +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
  85 +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
  86 +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
  87 +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
  88 +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
  89 +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
  90 +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
  91 +
  92 +/* positioning */
  93 +.ui-icon-carat-1-n { background-position: 0 0; }
  94 +.ui-icon-carat-1-ne { background-position: -16px 0; }
  95 +.ui-icon-carat-1-e { background-position: -32px 0; }
  96 +.ui-icon-carat-1-se { background-position: -48px 0; }
  97 +.ui-icon-carat-1-s { background-position: -64px 0; }
  98 +.ui-icon-carat-1-sw { background-position: -80px 0; }
  99 +.ui-icon-carat-1-w { background-position: -96px 0; }
  100 +.ui-icon-carat-1-nw { background-position: -112px 0; }
  101 +.ui-icon-carat-2-n-s { background-position: -128px 0; }
  102 +.ui-icon-carat-2-e-w { background-position: -144px 0; }
  103 +.ui-icon-triangle-1-n { background-position: 0 -16px; }
  104 +.ui-icon-triangle-1-ne { background-position: -16px -16px; }
  105 +.ui-icon-triangle-1-e { background-position: -32px -16px; }
  106 +.ui-icon-triangle-1-se { background-position: -48px -16px; }
  107 +.ui-icon-triangle-1-s { background-position: -64px -16px; }
  108 +.ui-icon-triangle-1-sw { background-position: -80px -16px; }
  109 +.ui-icon-triangle-1-w { background-position: -96px -16px; }
  110 +.ui-icon-triangle-1-nw { background-position: -112px -16px; }
  111 +.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
  112 +.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
  113 +.ui-icon-arrow-1-n { background-position: 0 -32px; }
  114 +.ui-icon-arrow-1-ne { background-position: -16px -32px; }
  115 +.ui-icon-arrow-1-e { background-position: -32px -32px; }
  116 +.ui-icon-arrow-1-se { background-position: -48px -32px; }
  117 +.ui-icon-arrow-1-s { background-position: -64px -32px; }
  118 +.ui-icon-arrow-1-sw { background-position: -80px -32px; }
  119 +.ui-icon-arrow-1-w { background-position: -96px -32px; }
  120 +.ui-icon-arrow-1-nw { background-position: -112px -32px; }
  121 +.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
  122 +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
  123 +.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
  124 +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
  125 +.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
  126 +.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
  127 +.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
  128 +.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
  129 +.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
  130 +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
  131 +.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
  132 +.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
  133 +.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
  134 +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
  135 +.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
  136 +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
  137 +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
  138 +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
  139 +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
  140 +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
  141 +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
  142 +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
  143 +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
  144 +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
  145 +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
  146 +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
  147 +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
  148 +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
  149 +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
  150 +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
  151 +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
  152 +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
  153 +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
  154 +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
  155 +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
  156 +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
  157 +.ui-icon-arrow-4 { background-position: 0 -80px; }
  158 +.ui-icon-arrow-4-diag { background-position: -16px -80px; }
  159 +.ui-icon-extlink { background-position: -32px -80px; }
  160 +.ui-icon-newwin { background-position: -48px -80px; }
  161 +.ui-icon-refresh { background-position: -64px -80px; }
  162 +.ui-icon-shuffle { background-position: -80px -80px; }
  163 +.ui-icon-transfer-e-w { background-position: -96px -80px; }
  164 +.ui-icon-transferthick-e-w { background-position: -112px -80px; }
  165 +.ui-icon-folder-collapsed { background-position: 0 -96px; }
  166 +.ui-icon-folder-open { background-position: -16px -96px; }
  167 +.ui-icon-document { background-position: -32px -96px; }
  168 +.ui-icon-document-b { background-position: -48px -96px; }
  169 +.ui-icon-note { background-position: -64px -96px; }
  170 +.ui-icon-mail-closed { background-position: -80px -96px; }
  171 +.ui-icon-mail-open { background-position: -96px -96px; }
  172 +.ui-icon-suitcase { background-position: -112px -96px; }
  173 +.ui-icon-comment { background-position: -128px -96px; }
  174 +.ui-icon-person { background-position: -144px -96px; }
  175 +.ui-icon-print { background-position: -160px -96px; }
  176 +.ui-icon-trash { background-position: -176px -96px; }
  177 +.ui-icon-locked { background-position: -192px -96px; }
  178 +.ui-icon-unlocked { background-position: -208px -96px; }
  179 +.ui-icon-bookmark { background-position: -224px -96px; }
  180 +.ui-icon-tag { background-position: -240px -96px; }
  181 +.ui-icon-home { background-position: 0 -112px; }
  182 +.ui-icon-flag { background-position: -16px -112px; }
  183 +.ui-icon-calendar { background-position: -32px -112px; }
  184 +.ui-icon-cart { background-position: -48px -112px; }
  185 +.ui-icon-pencil { background-position: -64px -112px; }
  186 +.ui-icon-clock { background-position: -80px -112px; }
  187 +.ui-icon-disk { background-position: -96px -112px; }
  188 +.ui-icon-calculator { background-position: -112px -112px; }
  189 +.ui-icon-zoomin { background-position: -128px -112px; }
  190 +.ui-icon-zoomout { background-position: -144px -112px; }
  191 +.ui-icon-search { background-position: -160px -112px; }
  192 +.ui-icon-wrench { background-position: -176px -112px; }
  193 +.ui-icon-gear { background-position: -192px -112px; }
  194 +.ui-icon-heart { background-position: -208px -112px; }
  195 +.ui-icon-star { background-position: -224px -112px; }
  196 +.ui-icon-link { background-position: -240px -112px; }
  197 +.ui-icon-cancel { background-position: 0 -128px; }
  198 +.ui-icon-plus { background-position: -16px -128px; }
  199 +.ui-icon-plusthick { background-position: -32px -128px; }
  200 +.ui-icon-minus { background-position: -48px -128px; }
  201 +.ui-icon-minusthick { background-position: -64px -128px; }
  202 +.ui-icon-close { background-position: -80px -128px; }
  203 +.ui-icon-closethick { background-position: -96px -128px; }
  204 +.ui-icon-key { background-position: -112px -128px; }
  205 +.ui-icon-lightbulb { background-position: -128px -128px; }
  206 +.ui-icon-scissors { background-position: -144px -128px; }
  207 +.ui-icon-clipboard { background-position: -160px -128px; }
  208 +.ui-icon-copy { background-position: -176px -128px; }
  209 +.ui-icon-contact { background-position: -192px -128px; }
  210 +.ui-icon-image { background-position: -208px -128px; }
  211 +.ui-icon-video { background-position: -224px -128px; }
  212 +.ui-icon-script { background-position: -240px -128px; }
  213 +.ui-icon-alert { background-position: 0 -144px; }
  214 +.ui-icon-info { background-position: -16px -144px; }
  215 +.ui-icon-notice { background-position: -32px -144px; }
  216 +.ui-icon-help { background-position: -48px -144px; }
  217 +.ui-icon-check { background-position: -64px -144px; }
  218 +.ui-icon-bullet { background-position: -80px -144px; }
  219 +.ui-icon-radio-off { background-position: -96px -144px; }
  220 +.ui-icon-radio-on { background-position: -112px -144px; }
  221 +.ui-icon-pin-w { background-position: -128px -144px; }
  222 +.ui-icon-pin-s { background-position: -144px -144px; }
  223 +.ui-icon-play { background-position: 0 -160px; }
  224 +.ui-icon-pause { background-position: -16px -160px; }
  225 +.ui-icon-seek-next { background-position: -32px -160px; }
  226 +.ui-icon-seek-prev { background-position: -48px -160px; }
  227 +.ui-icon-seek-end { background-position: -64px -160px; }
  228 +.ui-icon-seek-start { background-position: -80px -160px; }
  229 +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
  230 +.ui-icon-seek-first { background-position: -80px -160px; }
  231 +.ui-icon-stop { background-position: -96px -160px; }
  232 +.ui-icon-eject { background-position: -112px -160px; }
  233 +.ui-icon-volume-off { background-position: -128px -160px; }
  234 +.ui-icon-volume-on { background-position: -144px -160px; }
  235 +.ui-icon-power { background-position: 0 -176px; }
  236 +.ui-icon-signal-diag { background-position: -16px -176px; }
  237 +.ui-icon-signal { background-position: -32px -176px; }
  238 +.ui-icon-battery-0 { background-position: -48px -176px; }
  239 +.ui-icon-battery-1 { background-position: -64px -176px; }
  240 +.ui-icon-battery-2 { background-position: -80px -176px; }
  241 +.ui-icon-battery-3 { background-position: -96px -176px; }
  242 +.ui-icon-circle-plus { background-position: 0 -192px; }
  243 +.ui-icon-circle-minus { background-position: -16px -192px; }
  244 +.ui-icon-circle-close { background-position: -32px -192px; }
  245 +.ui-icon-circle-triangle-e { background-position: -48px -192px; }
  246 +.ui-icon-circle-triangle-s { background-position: -64px -192px; }
  247 +.ui-icon-circle-triangle-w { background-position: -80px -192px; }
  248 +.ui-icon-circle-triangle-n { background-position: -96px -192px; }
  249 +.ui-icon-circle-arrow-e { background-position: -112px -192px; }
  250 +.ui-icon-circle-arrow-s { background-position: -128px -192px; }
  251 +.ui-icon-circle-arrow-w { background-position: -144px -192px; }
  252 +.ui-icon-circle-arrow-n { background-position: -160px -192px; }
  253 +.ui-icon-circle-zoomin { background-position: -176px -192px; }
  254 +.ui-icon-circle-zoomout { background-position: -192px -192px; }
  255 +.ui-icon-circle-check { background-position: -208px -192px; }
  256 +.ui-icon-circlesmall-plus { background-position: 0 -208px; }
  257 +.ui-icon-circlesmall-minus { background-position: -16px -208px; }
  258 +.ui-icon-circlesmall-close { background-position: -32px -208px; }
  259 +.ui-icon-squaresmall-plus { background-position: -48px -208px; }
  260 +.ui-icon-squaresmall-minus { background-position: -64px -208px; }
  261 +.ui-icon-squaresmall-close { background-position: -80px -208px; }
  262 +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
  263 +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
  264 +.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
  265 +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
  266 +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
  267 +.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
  268 +
  269 +
  270 +/* Misc visuals
  271 +----------------------------------*/
  272 +
  273 +/* Corner radius */
  274 +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
  275 +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
  276 +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
  277 +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
  278 +.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; }
  279 +.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; }
  280 +.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; }
  281 +.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; }
  282 +.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;}
  283 +
  284 +/* Overlays */
  285 +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
  286 +.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
  287 +----------------------------------*/
  288 +.ui-resizable { position: relative;}
  289 +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
  290 +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
  291 +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
  292 +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
  293 +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
  294 +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
  295 +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
  296 +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
  297 +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
  298 +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Selectable
  299 +----------------------------------*/
  300 +.ui-selectable-helper { border:1px dotted black }
  301 +/* Accordion
  302 +----------------------------------*/
  303 +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
  304 +.ui-accordion .ui-accordion-li-fix { display: inline; }
  305 +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
  306 +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
  307 +/* IE7-/Win - Fix extra vertical space in lists */
  308 +.ui-accordion a { zoom: 1; }
  309 +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
  310 +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
  311 +.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; }
  312 +.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete
  313 +----------------------------------*/
  314 +.ui-autocomplete { position: absolute; cursor: default; }
  315 +.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
  316 +
  317 +/* workarounds */
  318 +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
  319 +
  320 +/* Menu
  321 +----------------------------------*/
  322 +.ui-menu {
  323 + list-style:none;
  324 + padding: 2px;
  325 + margin: 0;
  326 + display:block;
  327 +}
  328 +.ui-menu .ui-menu {
  329 + margin-top: -3px;
  330 +}
  331 +.ui-menu .ui-menu-item {
  332 + margin:0;
  333 + padding: 0;
  334 + zoom: 1;
  335 + float: left;
  336 + clear: left;
  337 + width: 100%;
  338 +}
  339 +.ui-menu .ui-menu-item a {
  340 + text-decoration:none;
  341 + display:block;
  342 + padding:.2em .4em;
  343 + line-height:1.5;
  344 + zoom:1;
  345 +}
  346 +.ui-menu .ui-menu-item a.ui-state-hover,
  347 +.ui-menu .ui-menu-item a.ui-state-active {
  348 + font-weight: normal;
  349 + margin: -1px;
  350 +}
  351 +/* Button
  352 +----------------------------------*/
  353 +
  354 +.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 */
  355 +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
  356 +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
  357 +.ui-button-icons-only { width: 3.4em; }
  358 +button.ui-button-icons-only { width: 3.7em; }
  359 +
  360 +/*button text element */
  361 +.ui-button .ui-button-text { display: block; line-height: 1.4; }
  362 +.ui-button-text-only .ui-button-text { padding: .4em 1em; }
  363 +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
  364 +.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
  365 +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
  366 +/* no icon support for input elements, provide padding by default */
  367 +input.ui-button { padding: .4em 1em; }
  368 +
  369 +/*button icon element(s) */
  370 +.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; }
  371 +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
  372 +.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; }
  373 +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
  374 +
  375 +/*button sets*/
  376 +.ui-buttonset { margin-right: 7px; }
  377 +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
  378 +
  379 +/* workarounds */
  380 +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
  381 +
  382 +
  383 +
  384 +
  385 +
  386 +/* Dialog
  387 +----------------------------------*/
  388 +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
  389 +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
  390 +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
  391 +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
  392 +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
  393 +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
  394 +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
  395 +.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; }
  396 +.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; }
  397 +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
  398 +.ui-draggable .ui-dialog-titlebar { cursor: move; }
  399 +/* Slider
  400 +----------------------------------*/
  401 +.ui-slider { position: relative; text-align: left; }
  402 +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
  403 +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
  404 +
  405 +.ui-slider-horizontal { height: .8em; }
  406 +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
  407 +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
  408 +.ui-slider-horizontal .ui-slider-range-min { left: 0; }
  409 +.ui-slider-horizontal .ui-slider-range-max { right: 0; }
  410 +
  411 +.ui-slider-vertical { width: .8em; height: 100px; }
  412 +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
  413 +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
  414 +.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
  415 +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
  416 +----------------------------------*/
  417 +.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") */
  418 +.ui-tabs .ui-tabs-nav { margin: 0; padding: 0; }
  419 +.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; }
  420 +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
  421 +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
  422 +.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; }
  423 +.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... */
  424 +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; border: 1px solid #aaa; border-top: 0; }
  425 +.ui-tabs .ui-tabs-hide { display: none !important; }
  426 +/* Datepicker
  427 +----------------------------------*/
  428 +.ui-datepicker { width: 17em; padding: .2em .2em 0; }
  429 +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
  430 +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
  431 +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
  432 +.ui-datepicker .ui-datepicker-prev { left:2px; }
  433 +.ui-datepicker .ui-datepicker-next { right:2px; }
  434 +.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
  435 +.ui-datepicker .ui-datepicker-next-hover { right:1px; }
  436 +.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; }
  437 +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
  438 +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
  439 +.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
  440 +.ui-datepicker select.ui-datepicker-month,
  441 +.ui-datepicker select.ui-datepicker-year { width: 49%;}
  442 +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
  443 +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
  444 +.ui-datepicker td { border: 0; padding: 1px; }
  445 +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
  446 +.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; }
  447 +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
  448 +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
  449 +
  450 +/* with multiple calendars */
  451 +.ui-datepicker.ui-datepicker-multi { width:auto; }
  452 +.ui-datepicker-multi .ui-datepicker-group { float:left; }
  453 +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
  454 +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
  455 +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
  456 +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
  457 +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
  458 +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
  459 +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
  460 +.ui-datepicker-row-break { clear:both; width:100%; }
  461 +
  462 +/* RTL support */
  463 +.ui-datepicker-rtl { direction: rtl; }
  464 +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
  465 +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
  466 +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
  467 +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
  468 +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
  469 +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
  470 +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
  471 +.ui-datepicker-rtl .ui-datepicker-group { float:right; }
  472 +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
  473 +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
  474 +
  475 +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
  476 +.ui-datepicker-cover {
  477 + display: none; /*sorry for IE5*/
  478 + display/**/: block; /*sorry for IE5*/
  479 + position: absolute; /*must have*/
  480 + z-index: -1; /*must have*/
  481 + filter: mask(); /*must have*/
  482 + top: -4px; /*must have*/
  483 + left: -4px; /*must have*/
  484 + width: 200px; /*must have*/
  485 + height: 200px; /*must have*/
  486 +}/* Progressbar
  487 +----------------------------------*/
  488 +.ui-progressbar { height:2em; text-align: left; }
  489 +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
... ...
test/action_tracker_test_helper.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class UserStampSweeper < ActionController::Caching::Sweeper
  2 + private
  3 + def current_user
  4 + Person.first
  5 + end
  6 +end
  7 +
  8 +module ActionTracker
  9 + class Record
  10 + def back_in_time(time = 25.hours)
  11 + self.updated_at = Time.now.ago(time)
  12 + self.send :update_without_callbacks
  13 + self
  14 + end
  15 + end
  16 +end
... ...
test/factories.rb
1 1 module Noosfero::Factory
2 2  
3 3 def fast_create(name, attrs = {}, options = {})
4   - data = defaults_for(name).merge(attrs)
  4 + data = defaults_for(name.to_s.gsub('::','')).merge(attrs)
5 5 klass = name.to_s.camelize.constantize
6 6 if klass.superclass != ActiveRecord::Base
7 7 data[:type] = klass.to_s
... ... @@ -360,4 +360,44 @@ module Noosfero::Factory
360 360  
361 361 alias :defaults_for_certifier :defaults_for_qualifier
362 362  
  363 + ###############################################
  364 + # Scrap
  365 + ###############################################
  366 +
  367 + def defaults_for_scrap(params = {})
  368 + { :content => 'soment content ', :sender_id => 1, :receiver_id => 1, :created_at => DateTime.now }.merge(params)
  369 + end
  370 +
  371 + ###############################################
  372 + # ActionTrackerNotification
  373 + ###############################################
  374 +
  375 + def defaults_for_action_tracker_notification(params = {})
  376 + { :action_tracker_id => 1, :profile_id => 1 }.merge(params)
  377 + end
  378 +
  379 + ###############################################
  380 + # ActionTracker
  381 + ###############################################
  382 +
  383 + def defaults_for_action_tracker_record(params = {})
  384 + { :created_at => DateTime.now, :verb => :leave_comment, :user_type => 'Profile', :user_id => 1 }.merge(params)
  385 + end
  386 +
  387 + ###############################################
  388 + # Friendship
  389 + ###############################################
  390 +
  391 + def defaults_for_friendship(params = {})
  392 + { :created_at => DateTime.now, :person_id => 1, :friend_id => 2 }.merge(params)
  393 + end
  394 +
  395 + ###############################################
  396 + # RoleAssignment
  397 + ###############################################
  398 +
  399 + def defaults_for_role_assignment(params = {})
  400 + { :role_id => 1, :accessor_id => 1, :accessor_type => 'Profile', :resource_id => 2, :resource_type => 'Profile' }.merge(params)
  401 + end
  402 +
363 403 end
... ...
test/functional/profile_controller_test.rb
... ... @@ -709,4 +709,496 @@ class ProfileControllerTest &lt; Test::Unit::TestCase
709 709 assert_match(/Last post/, @response.body) # Latest post must come in the feed
710 710 end
711 711  
  712 + should "be logged in to leave a scrap" do
  713 + count = Scrap.count
  714 + post :leave_scrap, :profile => profile.identifier, :scrap => {:content => 'something'}
  715 + assert_equal count, Scrap.count
  716 + assert_redirected_to :controller => 'account', :action => 'login'
  717 + end
  718 +
  719 + should "leave a scrap in the own profile" do
  720 + login_as(profile.identifier)
  721 + count = Scrap.count
  722 + assert profile.scraps_received.empty?
  723 + post :leave_scrap, :profile => profile.identifier, :scrap => {:content => 'something'}
  724 + assert_equal count + 1, Scrap.count
  725 + assert_response :success
  726 + assert_equal "Message successfully sent.", assigns(:message)
  727 + profile.reload
  728 + assert !profile.scraps_received.empty?
  729 + end
  730 +
  731 + should "leave a scrap on another profile" do
  732 + login_as(profile.identifier)
  733 + count = Scrap.count
  734 + another_person = fast_create(Person)
  735 + assert another_person.scraps_received.empty?
  736 + post :leave_scrap, :profile => another_person.identifier, :scrap => {:content => 'something'}
  737 + assert_equal count + 1, Scrap.count
  738 + assert_response :success
  739 + assert_equal "Message successfully sent.", assigns(:message)
  740 + another_person.reload
  741 + assert !another_person.scraps_received.empty?
  742 + end
  743 +
  744 + should "the owner of scrap could remove it" do
  745 + login_as(profile.identifier)
  746 + scrap = fast_create(Scrap, :sender_id => profile.id)
  747 + count = Scrap
  748 + assert_difference Scrap, :count, -1 do
  749 + post :remove_scrap, :profile => profile.identifier, :scrap_id => scrap.id
  750 + end
  751 + end
  752 +
  753 + should "the receiver scrap remove it" do
  754 + login_as(profile.identifier)
  755 + scrap = fast_create(Scrap, :receiver_id => profile.id)
  756 + count = Scrap
  757 + assert_difference Scrap, :count, -1 do
  758 + post :remove_scrap, :profile => profile.identifier, :scrap_id => scrap.id
  759 + end
  760 + end
  761 +
  762 + should "not remove others scraps" do
  763 + login_as(profile.identifier)
  764 + person = fast_create(Person)
  765 + scrap = fast_create(Scrap, :sender_id => person.id, :receiver_id => person.id)
  766 + count = Scrap
  767 + assert_difference Scrap, :count, 0 do
  768 + post :remove_scrap, :profile => profile.identifier, :scrap_id => scrap.id
  769 + end
  770 + end
  771 +
  772 + should "be logged in to remove a scrap" do
  773 + count = Scrap.count
  774 + post :remove_scrap, :profile => profile.identifier, :scrap => {:content => 'something'}
  775 + assert_equal count, Scrap.count
  776 + assert_redirected_to :controller => 'account', :action => 'login'
  777 + end
  778 +
  779 + should "not remove an scrap of another user" do
  780 + login_as(profile.identifier)
  781 + person = fast_create(Person)
  782 + scrap = fast_create(Scrap, :receiver_id => person.id)
  783 + count = Scrap.count
  784 + post :remove_scrap, :profile => person.identifier, :scrap_id => scrap.id
  785 + assert_equal count, Scrap.count
  786 + end
  787 +
  788 + should "the sender be the logged user by default" do
  789 + login_as(profile.identifier)
  790 + count = Scrap.count
  791 + another_person = fast_create(Person)
  792 + post :leave_scrap, :profile => another_person.identifier, :scrap => {:content => 'something'}
  793 + last = Scrap.last
  794 + assert_equal profile, last.sender
  795 + end
  796 +
  797 + should "the receiver be the current profile by default" do
  798 + login_as(profile.identifier)
  799 + count = Scrap.count
  800 + another_person = fast_create(Person)
  801 + post :leave_scrap, :profile => another_person.identifier, :scrap => {:content => 'something'}
  802 + last = Scrap.last
  803 + assert_equal another_person, last.receiver
  804 + end
  805 +
  806 + should "report to user the scrap errors on creation" do
  807 + login_as(profile.identifier)
  808 + count = Scrap.count
  809 + post :leave_scrap, :profile => profile.identifier, :scrap => {:content => ''}
  810 + assert_response :success
  811 + assert_equal "You can't leave an empty message.", assigns(:message)
  812 + end
  813 +
  814 + should 'see all activities of the current profile' do
  815 + p1= Person.first
  816 + p2= fast_create(Person)
  817 + assert !p1.is_a_friend?(p2)
  818 + p3= fast_create(Person)
  819 + assert !p1.is_a_friend?(p3)
  820 + ActionTracker::Record.destroy_all
  821 + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))
  822 + a1 = ActionTracker::Record.last
  823 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  824 + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3))
  825 + a2 = ActionTracker::Record.last
  826 + UserStampSweeper.any_instance.stubs(:current_user).returns(p3)
  827 + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1))
  828 + a3 = ActionTracker::Record.last
  829 + login_as(profile.identifier)
  830 + get :index, :profile => p1.identifier
  831 + assert_not_nil assigns(:activities)
  832 + assert_equal [a1], assigns(:activities)
  833 + end
  834 +
  835 + should 'see the activities_items paginated' do
  836 + p1= Person.first
  837 + ActionTracker::Record.destroy_all
  838 + 40.times{Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))}
  839 + login_as(p1.identifier)
  840 + get :index, :profile => p1.identifier
  841 + assert_equal 30, assigns(:activities).count
  842 + end
  843 +
  844 + should 'see not see the friends activities in the current profile activity' do
  845 + p1= Person.first
  846 + p2= fast_create(Person)
  847 + assert !p1.is_a_friend?(p2)
  848 + p3= fast_create(Person)
  849 + p1.add_friend(p3)
  850 + assert p1.is_a_friend?(p3)
  851 + ActionTracker::Record.destroy_all
  852 + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))
  853 + a1 = ActionTracker::Record.last
  854 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  855 + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3))
  856 + a2 = ActionTracker::Record.last
  857 + UserStampSweeper.any_instance.stubs(:current_user).returns(p3)
  858 + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1))
  859 + a3 = ActionTracker::Record.last
  860 + login_as(profile.identifier)
  861 + get :index, :profile => p1.identifier
  862 + assert_not_nil assigns(:activities)
  863 + assert_equal [a1], assigns(:activities)
  864 + end
  865 +
  866 + should 'see all the activities in the current profile network' do
  867 + p1= Person.first
  868 + p2= fast_create(Person)
  869 + assert !p1.is_a_friend?(p2)
  870 + p3= fast_create(Person)
  871 + p3.add_friend(p1)
  872 + assert p3.is_a_friend?(p1)
  873 + ActionTracker::Record.destroy_all
  874 + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))
  875 + a1 = ActionTracker::Record.last
  876 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  877 + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3))
  878 + a2 = ActionTracker::Record.last
  879 + UserStampSweeper.any_instance.stubs(:current_user).returns(p3)
  880 + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1))
  881 + a3 = ActionTracker::Record.last
  882 +
  883 +
  884 + @controller.stubs(:logged_in?).returns(true)
  885 + user = mock()
  886 + user.stubs(:person).returns(p3)
  887 + user.stubs(:login).returns('some')
  888 + @controller.stubs(:current_user).returns(user)
  889 + Person.any_instance.stubs(:follows?).returns(true)
  890 +
  891 + process_delayed_job_queue
  892 + get :index, :profile => p1.identifier
  893 + assert_not_nil assigns(:network_activities)
  894 + assert_equal [], [a1,a3] - assigns(:network_activities)
  895 + assert_equal assigns(:network_activities) - [a1, a3], []
  896 + end
  897 +
  898 + should 'the network activity be visible only to profile followers' do
  899 + p1= Person.first
  900 + p2= fast_create(Person)
  901 + assert !p1.is_a_friend?(p2)
  902 + p3= fast_create(Person)
  903 + p3.add_friend(p1)
  904 + assert p3.is_a_friend?(p1)
  905 + ActionTracker::Record.destroy_all
  906 + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))
  907 + a1 = ActionTracker::Record.last
  908 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  909 + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3))
  910 + a2 = ActionTracker::Record.last
  911 + UserStampSweeper.any_instance.stubs(:current_user).returns(p3)
  912 + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1))
  913 + a3 = ActionTracker::Record.last
  914 +
  915 + @controller.stubs(:logged_in?).returns(true)
  916 + user = mock()
  917 + user.stubs(:person).returns(p2)
  918 + user.stubs(:login).returns('some')
  919 + @controller.stubs(:current_user).returns(user)
  920 + get :index, :profile => p1.identifier
  921 + assert_equal [], assigns(:network_activities)
  922 +
  923 + user = mock()
  924 + user.stubs(:person).returns(p3)
  925 + user.stubs(:login).returns('some')
  926 + @controller.stubs(:current_user).returns(user)
  927 + Person.any_instance.stubs(:follows?).returns(true)
  928 + process_delayed_job_queue
  929 + get :index, :profile => p3.identifier
  930 + assert_equal [], [a1,a3] - assigns(:network_activities)
  931 + assert_equal assigns(:network_activities) - [a1, a3], []
  932 + end
  933 +
  934 + should 'the network activity be paginated' do
  935 + p1= Person.first
  936 + 40.times{Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))}
  937 +
  938 + @controller.stubs(:logged_in?).returns(true)
  939 + user = mock()
  940 + user.stubs(:person).returns(p1)
  941 + user.stubs(:login).returns('some')
  942 + @controller.stubs(:current_user).returns(user)
  943 + get :index, :profile => p1.identifier
  944 + assert_equal 30, assigns(:network_activities).count
  945 + end
  946 +
  947 + should 'the network activity be visible only to logged users' do
  948 + p1= ActionTracker::Record.current_user_from_model
  949 + p2= fast_create(Person)
  950 + assert !p1.is_a_friend?(p2)
  951 + p3= fast_create(Person)
  952 + p3.add_friend(p1)
  953 + assert p3.is_a_friend?(p1)
  954 + ActionTracker::Record.destroy_all
  955 + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))
  956 + a1 = ActionTracker::Record.last
  957 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  958 + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3))
  959 + a2 = ActionTracker::Record.last
  960 + UserStampSweeper.any_instance.stubs(:current_user).returns(p3)
  961 + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1))
  962 + a3 = ActionTracker::Record.last
  963 +
  964 + login_as(profile.identifier)
  965 + get :index, :profile => p1.identifier
  966 + assert_equal [], assigns(:network_activities)
  967 + assert_response :success
  968 + assert_template 'index'
  969 +
  970 + get :index, :profile => p2.identifier
  971 + assert_equal [], assigns(:network_activities)
  972 + assert_response :success
  973 + assert_template 'index'
  974 +
  975 + get :index, :profile => p3.identifier
  976 + assert_equal [], assigns(:network_activities)
  977 + assert_response :success
  978 + assert_template 'index'
  979 + end
  980 +
  981 + should 'the network activity be visible to uses not logged in on communities and enteprises' do
  982 + p1= Person.first
  983 + community = fast_create(Community)
  984 + p2= fast_create(Person)
  985 + assert !p1.is_a_friend?(p2)
  986 + community.add_member(p1)
  987 + community.add_member(p2)
  988 + ActionTracker::Record.destroy_all
  989 + Article.create! :name => 'a', :profile_id => community.id
  990 + Article.create! :name => 'b', :profile_id => community.id
  991 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  992 + Article.create! :name => 'c', :profile_id => community.id
  993 + process_delayed_job_queue
  994 +
  995 + get :index, :profile => community.identifier
  996 + assert_not_equal [], assigns(:network_items)
  997 + assert_response :success
  998 + assert_template 'index'
  999 + end
  1000 +
  1001 + should 'the network activity be paginated on communities' do
  1002 + community = fast_create(Community)
  1003 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1004 + 40.times{ fast_create(ActionTrackerNotification, :profile_id => community.id, :action_tracker_id => at.id) }
  1005 + get :index, :profile => community.identifier
  1006 + assert_equal 30, assigns(:network_activities).count
  1007 + end
  1008 +
  1009 + should 'the self activity not crashes with user not logged in' do
  1010 + p1= Person.first
  1011 + p2= fast_create(Person)
  1012 + assert !p1.is_a_friend?(p2)
  1013 + p3= fast_create(Person)
  1014 + p3.add_friend(p1)
  1015 + assert p3.is_a_friend?(p1)
  1016 + ActionTracker::Record.destroy_all
  1017 + Scrap.create!(defaults_for_scrap(:sender => p1, :receiver => p1))
  1018 + a1 = ActionTracker::Record.last
  1019 + UserStampSweeper.any_instance.stubs(:current_user).returns(p2)
  1020 + Scrap.create!(defaults_for_scrap(:sender => p2, :receiver => p3))
  1021 + a2 = ActionTracker::Record.last
  1022 + UserStampSweeper.any_instance.stubs(:current_user).returns(p3)
  1023 + Scrap.create!(defaults_for_scrap(:sender => p3, :receiver => p1))
  1024 + a3 = ActionTracker::Record.last
  1025 +
  1026 + get :index, :profile => p1.identifier
  1027 + assert_response :success
  1028 + assert_template 'index'
  1029 + end
  1030 +
  1031 + should 'have wall_itens defined' do
  1032 + p1= ActionTracker::Record.current_user_from_model
  1033 + get :index, :profile => p1.identifier
  1034 + assert_equal [], assigns(:wall_items)
  1035 + end
  1036 +
  1037 + should 'the wall_itens be the received scraps' do
  1038 + p1 = ActionTracker::Record.current_user_from_model
  1039 + p2 = fast_create(Person)
  1040 + p3 = fast_create(Person)
  1041 + s1 = fast_create(Scrap, :sender_id => p1.id, :receiver_id => p2.id)
  1042 + s2 = fast_create(Scrap, :sender_id => p2.id, :receiver_id => p1.id)
  1043 + s3 = fast_create(Scrap, :sender_id => p3.id, :receiver_id => p1.id)
  1044 +
  1045 + @controller.stubs(:logged_in?).returns(true)
  1046 + user = mock()
  1047 + user.stubs(:person).returns(p1)
  1048 + user.stubs(:login).returns('some')
  1049 + @controller.stubs(:current_user).returns(user)
  1050 + Person.any_instance.stubs(:follows?).returns(true)
  1051 + get :index, :profile => p1.identifier
  1052 + assert_equal [s2,s3], assigns(:wall_items)
  1053 + end
  1054 +
  1055 + should 'the wall_itens be paginated' do
  1056 + p1 = Person.first
  1057 + 40.times{fast_create(Scrap, :sender_id => p1.id)}
  1058 +
  1059 + @controller.stubs(:logged_in?).returns(true)
  1060 + user = mock()
  1061 + user.stubs(:person).returns(p1)
  1062 + user.stubs(:login).returns('some')
  1063 + @controller.stubs(:current_user).returns(user)
  1064 + Person.any_instance.stubs(:follows?).returns(true)
  1065 + assert_equal 40, p1.scraps_received.not_replies.count
  1066 + get :index, :profile => p1.identifier
  1067 + assert_equal 30, assigns(:wall_items).count
  1068 + end
  1069 +
  1070 + should "the owner of activity could remove it" do
  1071 + login_as(profile.identifier)
  1072 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1073 + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id)
  1074 + assert_difference ActionTrackerNotification, :count, -1 do
  1075 + post :remove_activity, :profile => profile.identifier, :activity_id => at.id
  1076 + end
  1077 + end
  1078 +
  1079 + should "not remove others activities" do
  1080 + login_as(profile.identifier)
  1081 + person = fast_create(Person)
  1082 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1083 + atn = fast_create(ActionTrackerNotification, :profile_id => person.id, :action_tracker_id => at.id)
  1084 + count = ActionTrackerNotification
  1085 + assert_difference ActionTrackerNotification, :count, 0 do
  1086 + post :remove_activity, :profile => profile.identifier, :activity_id => at.id
  1087 + end
  1088 + end
  1089 +
  1090 + should "be logged in to remove the activity" do
  1091 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1092 + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id)
  1093 + count = ActionTrackerNotification.count
  1094 + post :remove_activity, :profile => profile.identifier, :activity_id => at.id
  1095 + assert_equal count, ActionTrackerNotification.count
  1096 + assert_redirected_to :controller => 'account', :action => 'login'
  1097 + end
  1098 +
  1099 + should "not remove an activity of another user" do
  1100 + login_as(profile.identifier)
  1101 + person = fast_create(Person)
  1102 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1103 + atn = fast_create(ActionTrackerNotification, :profile_id => person.id, :action_tracker_id => at.id)
  1104 + count = ActionTrackerNotification.count
  1105 + post :remove_activity, :profile => person.identifier, :activity_id => at.id
  1106 + assert_equal count, ActionTrackerNotification.count
  1107 + end
  1108 +
  1109 + should "not show the scrap button on network activity if the user don't follow the user" do
  1110 + login_as(profile.identifier)
  1111 + person = fast_create(Person)
  1112 + at = fast_create(ActionTracker::Record, :user_id => person.id)
  1113 + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id)
  1114 + get :index, :profile => profile.identifier
  1115 + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-network-send-message'}
  1116 +
  1117 + person.add_friend(profile)
  1118 + get :index, :profile => profile.identifier
  1119 + assert_tag :tag => 'p', :attributes => {:class => 'profile-network-send-message'}
  1120 + end
  1121 +
  1122 + should "not show the scrap button on network activity if the user is himself" do
  1123 + login_as(profile.identifier)
  1124 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1125 + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id)
  1126 + get :index, :profile => profile.identifier
  1127 + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-network-send-message'}
  1128 + end
  1129 +
  1130 + should "not show the scrap button on wall activity if the user don't follow the user" do
  1131 + login_as(profile.identifier)
  1132 + person = fast_create(Person)
  1133 + scrap = fast_create(Scrap, :sender_id => person.id, :receiver_id => profile.id)
  1134 + get :index, :profile => profile.identifier
  1135 + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-wall-send-message'}
  1136 +
  1137 + person.add_friend(profile)
  1138 + get :index, :profile => profile.identifier
  1139 + assert_tag :tag => 'p', :attributes => {:class => 'profile-wall-send-message'}
  1140 + end
  1141 +
  1142 + should "not show the scrap button on wall activity if the user is himself" do
  1143 + login_as(profile.identifier)
  1144 + scrap = fast_create(Scrap, :sender_id => profile.id, :receiver_id => profile.id)
  1145 + get :index, :profile => profile.identifier
  1146 + assert_no_tag :tag => 'p', :attributes => {:class => 'profile-wall-send-message'}
  1147 + end
  1148 +
  1149 + should "not show the activities to offline users if the profile is private" do
  1150 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1151 + profile.public_profile=false
  1152 + profile.save
  1153 + atn = fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id)
  1154 + get :index, :profile => profile.identifier
  1155 + assert_equal [at], profile.tracked_actions
  1156 + assert_no_tag :tag => 'li', :attributes => {:id => "profile-activity-item-#{atn.id}"}
  1157 + end
  1158 +
  1159 + should "view more scraps paginate the scraps" do
  1160 + login_as(profile.identifier)
  1161 + 40.times{fast_create(Scrap, :receiver_id => profile.id)}
  1162 + get :view_more_scraps, :profile => profile.identifier, :page => 2
  1163 + assert_response :success
  1164 + assert_template '_profile_scraps'
  1165 + assert_equal 10, assigns(:scraps).count
  1166 + end
  1167 +
  1168 + should "be logged in to access the view_more_scraps action" do
  1169 + get :view_more_scraps, :profile => profile.identifier
  1170 + assert_redirected_to :controller => 'account', :action => 'login'
  1171 + end
  1172 +
  1173 + should "view more activities paginated" do
  1174 + login_as(profile.identifier)
  1175 + 40.times{ fast_create(ActionTracker::Record, :user_id => profile.id)}
  1176 + assert_equal 40, profile.tracked_actions.count
  1177 + get :view_more_activities, :profile => profile.identifier, :page => 2
  1178 + assert_response :success
  1179 + assert_template '_profile_activities'
  1180 + assert_equal 10, assigns(:activities).count
  1181 + end
  1182 +
  1183 + should "be logged in to access the view_more_activities action" do
  1184 + get :view_more_activities, :profile => profile.identifier
  1185 + assert_redirected_to :controller => 'account', :action => 'login'
  1186 + end
  1187 +
  1188 + should "view more network activities paginated" do
  1189 + login_as(profile.identifier)
  1190 + at = fast_create(ActionTracker::Record, :user_id => profile.id)
  1191 + 40.times{fast_create(ActionTrackerNotification, :profile_id => profile.id, :action_tracker_id => at.id) }
  1192 + assert_equal 40, profile.tracked_notifications.count
  1193 + get :view_more_network_activities, :profile => profile.identifier, :page => 2
  1194 + assert_response :success
  1195 + assert_template '_profile_network_activities'
  1196 + assert_equal 10, assigns(:activities).count
  1197 + end
  1198 +
  1199 + should "be logged in to access the view_more_network_activities action" do
  1200 + get :view_more_network_activities, :profile => profile.identifier
  1201 + assert_redirected_to :controller => 'account', :action => 'login'
  1202 + end
  1203 +
712 1204 end
... ...
test/test_helper.rb
... ... @@ -8,6 +8,7 @@ require &#39;hpricot&#39;
8 8 require 'noosfero/test'
9 9 require File.dirname(__FILE__) + '/factories'
10 10 require File.dirname(__FILE__) + '/noosfero_doc_test'
  11 +require File.dirname(__FILE__) + '/action_tracker_test_helper'
11 12  
12 13 FileUtils.rm_rf(File.join(RAILS_ROOT, 'index', 'test'))
13 14  
... ...
test/unit/action_tracker_notification_test.rb 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class ActionTrackerNotificationTest < ActiveSupport::TestCase
  4 +
  5 + should "have the profile" do
  6 + a = ActionTrackerNotification.new
  7 + a.valid?
  8 + assert a.errors.invalid?(:profile_id)
  9 +
  10 + a.profile_id= 1
  11 + a.valid?
  12 + assert !a.errors.invalid?(:profile_id)
  13 + end
  14 +
  15 + should "have the action tracker" do
  16 + a = ActionTrackerNotification.new
  17 + a.valid?
  18 + assert a.errors.invalid?(:action_tracker_id)
  19 +
  20 + a.action_tracker_id= 1
  21 + a.valid?
  22 + assert !a.errors.invalid?(:action_tracker_id)
  23 + end
  24 +
  25 + should "be associated to Person" do
  26 + person = fast_create(Person)
  27 + a = ActionTrackerNotification.new
  28 + assert_nothing_raised do
  29 + a.profile = person
  30 + end
  31 + end
  32 +
  33 + should "be associated to ActionTracker" do
  34 + action_tracker = ActionTracker::Record.new
  35 + a = ActionTrackerNotification.new
  36 + assert_nothing_raised do
  37 + a.action_tracker= action_tracker
  38 + end
  39 + end
  40 +
  41 + should "destroy the notifications if the activity is destroyed" do
  42 + action_tracker = fast_create(ActionTracker::Record)
  43 + count = ActionTrackerNotification.count
  44 + fast_create(ActionTrackerNotification, :action_tracker_id => action_tracker.id)
  45 + fast_create(ActionTrackerNotification, :action_tracker_id => action_tracker.id)
  46 + fast_create(ActionTrackerNotification, :action_tracker_id => action_tracker.id)
  47 + action_tracker.destroy
  48 + assert_equal count, ActionTrackerNotification.count
  49 + end
  50 +
  51 +end
... ...
test/unit/application_helper_test.rb
... ... @@ -484,7 +484,7 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
484 484 person.stubs(:url).returns('url for person')
485 485 person.stubs(:public_profile_url).returns('url for person')
486 486 links = links_for_balloon(person)
487   - assert_equal ['Home Page', 'Profile', 'Friends', 'Communities'], links.map{|i| i.keys.first}
  487 + assert_equal ['Home Page', 'Wall', 'Friends', 'Communities'], links.map{|i| i.keys.first}
488 488 end
489 489  
490 490 should 'return ordered list of links to balloon to Community' do
... ... @@ -495,7 +495,7 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
495 495 community.stubs(:url).returns('url for community')
496 496 community.stubs(:public_profile_url).returns('url for community')
497 497 links = links_for_balloon(community)
498   - assert_equal ['Home Page', 'Profile', 'Members', 'Agenda'], links.map{|i| i.keys.first}
  498 + assert_equal ['Home Page', 'Wall', 'Members', 'Agenda'], links.map{|i| i.keys.first}
499 499 end
500 500  
501 501 should 'return ordered list of links to balloon to Enterprise' do
... ... @@ -577,6 +577,16 @@ class ApplicationHelperTest &lt; Test::Unit::TestCase
577 577 assert_not_nil mime
578 578 end
579 579  
  580 + should 'pluralize without count' do
  581 + assert_equal "tests", pluralize_without_count(2, "test")
  582 + assert_equal "test", pluralize_without_count(1, "test")
  583 + assert_equal "testes", pluralize_without_count(2, "test", "testes")
  584 + end
  585 +
  586 + should 'unique with count' do
  587 + assert_equal ["1 for b", "2 for c", "3 for a"], unique_with_count(%w(a b c a c a))
  588 + end
  589 +
580 590 protected
581 591  
582 592 def url_for(args = {})
... ...
test/unit/article_test.rb
... ... @@ -918,4 +918,80 @@ class ArticleTest &lt; Test::Unit::TestCase
918 918 assert_equal '', a.lead
919 919 end
920 920  
  921 + should 'track action when a published article is created outside a community' do
  922 + article = Article.create! :name => 'Tracked Article', :profile_id => profile.id
  923 + assert article.published?
  924 + assert_kind_of Person, article.profile
  925 + ta = ActionTracker::Record.last
  926 + assert_equal 'Tracked Article', ta.get_name.last
  927 + assert_equal article.url, ta.get_url.last
  928 + assert_kind_of Person, ta.user
  929 + ta.back_in_time(26.hours)
  930 + article = Article.create! :name => 'Another Tracked Article', :profile_id => profile.id
  931 + ta = ActionTracker::Record.last
  932 + assert_equal ['Another Tracked Article'], ta.get_name
  933 + assert_equal [article.url], ta.get_url
  934 + end
  935 +
  936 + should 'track action when a published article is created in a community' do
  937 + community = fast_create(Community)
  938 + p1 = ActionTracker::Record.current_user_from_model
  939 + p2 = fast_create(Person)
  940 + p3 = fast_create(Person)
  941 + community.add_member(p1)
  942 + community.add_member(p2)
  943 + assert p1.is_member_of?(community)
  944 + assert p2.is_member_of?(community)
  945 + assert !p3.is_member_of?(community)
  946 + Article.destroy_all
  947 + ActionTracker::Record.destroy_all
  948 + article = Article.create! :name => 'Tracked Article', :profile_id => community.id
  949 + assert article.published?
  950 + assert_kind_of Community, article.profile
  951 + ta = ActionTracker::Record.last
  952 + assert_equal 'Tracked Article', ta.get_name.last
  953 + assert_equal article.url, ta.get_url.last
  954 + assert_kind_of Person, ta.user
  955 + process_delayed_job_queue
  956 + assert_equal 3, ActionTrackerNotification.count
  957 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  958 + assert [p1,p2,community].include?(profile)
  959 + end
  960 + end
  961 +
  962 + should 'track action when a published article is updated' do
  963 + a = Article.create! :name => 'a', :profile_id => profile.id
  964 + a.update_attributes! :name => 'b'
  965 + ta = ActionTracker::Record.last
  966 + assert_equal ['b'], ta.get_name
  967 + assert_equal [a.reload.url], ta.get_url
  968 + a.update_attributes! :name => 'c'
  969 + ta = ActionTracker::Record.last
  970 + assert_equal ['b','c'], ta.get_name
  971 + assert_equal [a.url,a.reload.url], ta.get_url
  972 + a.update_attributes! :body => 'test'
  973 + ta = ActionTracker::Record.last
  974 + assert_equal ['b','c','c'], ta.get_name
  975 + assert_equal [a.url,a.reload.url,a.reload.url], ta.get_url
  976 + a.update_attributes! :hits => 50
  977 + ta = ActionTracker::Record.last
  978 + assert_equal ['b','c','c'], ta.get_name
  979 + assert_equal [a.url,a.reload.url,a.reload.url], ta.get_url
  980 + end
  981 +
  982 + should 'track action when a published article is removed' do
  983 + a = Article.create! :name => 'a', :profile_id => profile.id
  984 + a.destroy
  985 + ta = ActionTracker::Record.last
  986 + assert_equal ['a'], ta.get_name
  987 + a = Article.create! :name => 'b', :profile_id => profile.id
  988 + a.destroy
  989 + ta = ActionTracker::Record.last
  990 + assert_equal ['a','b'], ta.get_name
  991 + a = Article.create! :name => 'c', :profile_id => profile.id, :published => false
  992 + a.destroy
  993 + ta = ActionTracker::Record.last
  994 + assert_equal ['a','b'], ta.get_name
  995 + end
  996 +
921 997 end
... ...
test/unit/comment_test.rb
... ... @@ -214,4 +214,16 @@ class CommentTest &lt; Test::Unit::TestCase
214 214 assert File.exists?(File.join(Rails.root, 'public', image)), "#{image} does not exist."
215 215 end
216 216  
  217 + should 'track action when comment is created' do
  218 + owner = create_user('testuser').person
  219 + article = owner.articles.create!(:name => 'test', :body => '...')
  220 + comment = article.comments.create!(:article => article, :name => 'foo', :title => 'bar', :body => 'my comment', :email => 'cracker@test.org')
  221 + ta = ActionTracker::Record.last
  222 + assert_equal 'bar', ta.get_title
  223 + assert_equal 'my comment', ta.get_body
  224 + assert_equal 'test', ta.get_article_title
  225 + assert_equal article.url, ta.get_article_url
  226 + assert_equal comment.url, ta.get_url
  227 + end
  228 +
217 229 end
... ...
test/unit/community_test.rb
... ... @@ -215,4 +215,63 @@ class CommunityTest &lt; Test::Unit::TestCase
215 215 assert_no_match /[<>]/, community.description
216 216 end
217 217  
  218 + should "the followed_by method be protected and true to the community members by default" do
  219 + c = fast_create(Community)
  220 + p1 = fast_create(Person)
  221 + p2 = fast_create(Person)
  222 + p3 = fast_create(Person)
  223 +
  224 + assert !p1.is_member_of?(c)
  225 + c.add_member(p1)
  226 + assert p1.is_member_of?(c)
  227 +
  228 + assert !p3.is_member_of?(c)
  229 + c.add_member(p3)
  230 + assert p3.is_member_of?(c)
  231 +
  232 + assert_equal true, c.send(:followed_by?,p1)
  233 + assert_equal true, c.send(:followed_by?,p3)
  234 + assert_equal false, c.send(:followed_by?,p2)
  235 + end
  236 +
  237 + should "be created an tracked action when the user is join to the community" do
  238 + p1 = Person.first
  239 + community = fast_create(Community)
  240 + p2 = fast_create(Person)
  241 + p3 = fast_create(Person)
  242 +
  243 + RoleAssignment.delete_all
  244 + ActionTrackerNotification.delete_all
  245 + RoleAssignment.any_instance.stubs(:role_id).returns(3)
  246 + assert_difference(ActionTrackerNotification, :count, 3) do
  247 + community.add_member(p1)
  248 + community.add_member(p3)
  249 + assert p1.is_member_of?(community)
  250 + assert !p2.is_member_of?(community)
  251 + assert p3.is_member_of?(community)
  252 + process_delayed_job_queue
  253 + end
  254 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  255 + assert [community,p1,p3].include?(profile)
  256 + end
  257 + end
  258 +
  259 + should "be created an tracked action to the community when an community's article is commented" do
  260 + ActionTrackerNotification.delete_all
  261 + p1 = Person.first
  262 + community = fast_create(Community)
  263 + p2 = fast_create(Person)
  264 + p3 = fast_create(Person)
  265 + community.add_member(p3)
  266 + article = fast_create(Article, :profile_id => community.id)
  267 + ActionTracker::Record.destroy_all
  268 + assert_difference(ActionTrackerNotification, :count, 3) do
  269 + Comment.create!(:article_id => article.id, :title => 'some', :body => 'some', :author_id => p2.id)
  270 + process_delayed_job_queue
  271 + end
  272 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  273 + assert [community,p1,p3].include?(profile)
  274 + end
  275 + end
  276 +
218 277 end
... ...
test/unit/enterprise_test.rb
... ... @@ -398,4 +398,24 @@ class EnterpriseTest &lt; Test::Unit::TestCase
398 398 assert_equal product.inputs, enterprise.inputs
399 399 end
400 400  
  401 + should "the followed_by? be true only to members" do
  402 + e = fast_create(Enterprise)
  403 + e.stubs(:closed?).returns(false)
  404 + p1 = fast_create(Person)
  405 + p2 = fast_create(Person)
  406 + p3 = fast_create(Person)
  407 +
  408 + assert !p1.is_member_of?(e)
  409 + e.add_member(p1)
  410 + assert p1.is_member_of?(e)
  411 +
  412 + assert !p3.is_member_of?(e)
  413 + e.add_member(p3)
  414 + assert p3.is_member_of?(e)
  415 +
  416 + assert_equal true, e.send(:followed_by?,p1)
  417 + assert_equal true, e.send(:followed_by?,p3)
  418 + assert_equal false, e.send(:followed_by?,p2)
  419 + end
  420 +
401 421 end
... ...
test/unit/friendship_test.rb
... ... @@ -22,4 +22,24 @@ class FriendshipTest &lt; Test::Unit::TestCase
22 22  
23 23 end
24 24  
  25 + should 'create tracked action' do
  26 + f = Friendship.create! :person => create_user('a').person, :friend => create_user('b').person
  27 + ta = ActionTracker::Record.last
  28 + person = Person.first
  29 + assert_equal person.name, ta.user.name
  30 + assert_equal 'b', ta.get_friend_name[0]
  31 + f = Friendship.create! :person => create_user('a').person, :friend => create_user('c').person
  32 + ta = ActionTracker::Record.last
  33 + assert_equal person.name, ta.user.name
  34 + assert_equal 'c', ta.get_friend_name[1]
  35 + end
  36 +
  37 + should 'create tracked action only if they are not friends yet' do
  38 + a, b = create_user('a').person, create_user('b').person
  39 + f = Friendship.create! :person => a, :friend => b
  40 + assert_equal ['b'], ActionTracker::Record.last.get_friend_name
  41 + f = Friendship.create! :person => b, :friend => a
  42 + assert_equal ['b'], ActionTracker::Record.last.get_friend_name
  43 + end
  44 +
25 45 end
... ...
test/unit/notify_activity_job_test.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class NotifyActivityJobTest < ActiveSupport::TestCase
  4 +
  5 + should 'create the ActionTrackerNotification' do
  6 + action_tracker = fast_create(ActionTracker::Record)
  7 + profile = fast_create(Profile)
  8 + count = ActionTrackerNotification.count
  9 + job = NotifyActivityJob.new(action_tracker.id, profile.id)
  10 + job.perform
  11 +
  12 + assert_equal count + 1, ActionTrackerNotification.count
  13 + last = ActionTrackerNotification.last
  14 + assert_equal action_tracker, last.action_tracker
  15 + assert_equal profile, last.profile
  16 + end
  17 +
  18 +end
... ...
test/unit/notify_activity_to_profiles_job_test.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase
  4 +
  5 + should 'create the ActionTrackerNotification' do
  6 + person = fast_create(Person)
  7 + community = fast_create(Community)
  8 + action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id)
  9 + p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
  10 + fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
  11 + fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
  12 + fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id)
  13 + fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
  14 + ActionTrackerNotification.delete_all
  15 + job = NotifyActivityToProfilesJob.new(action_tracker.id, community.id)
  16 + job.perform
  17 + process_delayed_job_queue
  18 + assert_equal 5, ActionTrackerNotification.count
  19 + end
  20 +
  21 +end
... ...
test/unit/organization_test.rb
... ... @@ -268,4 +268,23 @@ class OrganizationTest &lt; Test::Unit::TestCase
268 268 assert_no_match /[<>]/, organization.management_information
269 269 end
270 270  
  271 + should "the followed_by? be true only to members" do
  272 + o = fast_create(Organization)
  273 + p1 = fast_create(Person)
  274 + p2 = fast_create(Person)
  275 + p3 = fast_create(Person)
  276 +
  277 + assert !p1.is_member_of?(o)
  278 + o.add_member(p1)
  279 + assert p1.is_member_of?(o)
  280 +
  281 + assert !p3.is_member_of?(o)
  282 + o.add_member(p3)
  283 + assert p3.is_member_of?(o)
  284 +
  285 + assert_equal true, o.send(:followed_by?,p1)
  286 + assert_equal true, o.send(:followed_by?,p3)
  287 + assert_equal false, o.send(:followed_by?,p2)
  288 + end
  289 +
271 290 end
... ...
test/unit/person_test.rb
... ... @@ -642,4 +642,337 @@ class PersonTest &lt; Test::Unit::TestCase
642 642 end
643 643 end
644 644  
  645 + should "see get all sent scraps" do
  646 + p1 = fast_create(Person)
  647 + assert_equal [], p1.scraps_sent
  648 + fast_create(Scrap, :sender_id => p1.id)
  649 + fast_create(Scrap, :sender_id => p1.id)
  650 + assert_equal 2, p1.scraps_sent.count
  651 + p2 = fast_create(Person)
  652 + fast_create(Scrap, :sender_id => p2.id)
  653 + assert_equal 2, p1.scraps_sent.count
  654 + fast_create(Scrap, :sender_id => p1.id)
  655 + assert_equal 3, p1.scraps_sent.count
  656 + fast_create(Scrap, :receiver_id => p1.id)
  657 + assert_equal 3, p1.scraps_sent.count
  658 + end
  659 +
  660 + should "see get all received scraps" do
  661 + p1 = fast_create(Person)
  662 + assert_equal [], p1.scraps_received
  663 + fast_create(Scrap, :receiver_id => p1.id)
  664 + fast_create(Scrap, :receiver_id => p1.id)
  665 + assert_equal 2, p1.scraps_received.count
  666 + p2 = fast_create(Person)
  667 + fast_create(Scrap, :receiver_id => p2.id)
  668 + assert_equal 2, p1.scraps_received.count
  669 + fast_create(Scrap, :receiver_id => p1.id)
  670 + assert_equal 3, p1.scraps_received.count
  671 + fast_create(Scrap, :sender_id => p1.id)
  672 + assert_equal 3, p1.scraps_received.count
  673 + end
  674 +
  675 + should "see get all received scraps that are not replies" do
  676 + p1 = fast_create(Person)
  677 + s1 = fast_create(Scrap, :receiver_id => p1.id)
  678 + s2 = fast_create(Scrap, :receiver_id => p1.id)
  679 + s3 = fast_create(Scrap, :receiver_id => p1.id, :scrap_id => s1.id)
  680 + assert_equal 3, p1.scraps_received.count
  681 + assert_equal [s1,s2], p1.scraps_received.not_replies
  682 + p2 = fast_create(Person)
  683 + s4 = fast_create(Scrap, :receiver_id => p2.id)
  684 + s5 = fast_create(Scrap, :receiver_id => p2.id, :scrap_id => s4.id)
  685 + assert_equal 2, p2.scraps_received.count
  686 + assert_equal [s4], p2.scraps_received.not_replies
  687 + end
  688 +
  689 + should "the followed_by method be protected and true to the person friends and herself by default" do
  690 + p1 = fast_create(Person)
  691 + p2 = fast_create(Person)
  692 + p3 = fast_create(Person)
  693 + p4 = fast_create(Person)
  694 +
  695 + p1.add_friend(p2)
  696 + assert p1.is_a_friend?(p2)
  697 + p1.add_friend(p4)
  698 + assert p1.is_a_friend?(p4)
  699 +
  700 + assert_equal true, p1.send(:followed_by?,p1)
  701 + assert_equal true, p1.send(:followed_by?,p2)
  702 + assert_equal true, p1.send(:followed_by?,p4)
  703 + assert_equal false, p1.send(:followed_by?,p3)
  704 + end
  705 +
  706 + should "the person follows her friends and herself by default" do
  707 + p1 = fast_create(Person)
  708 + p2 = fast_create(Person)
  709 + p3 = fast_create(Person)
  710 + p4 = fast_create(Person)
  711 +
  712 + p2.add_friend(p1)
  713 + assert p2.is_a_friend?(p1)
  714 + p4.add_friend(p1)
  715 + assert p4.is_a_friend?(p1)
  716 +
  717 + assert_equal true, p1.follows?(p1)
  718 + assert_equal true, p1.follows?(p2)
  719 + assert_equal true, p1.follows?(p4)
  720 + assert_equal false, p1.follows?(p3)
  721 + end
  722 +
  723 + should "a person member of a community follows the community" do
  724 + c = fast_create(Community)
  725 + p1 = fast_create(Person)
  726 + p2 = fast_create(Person)
  727 + p3 = fast_create(Person)
  728 +
  729 + assert !p1.is_member_of?(c)
  730 + c.add_member(p1)
  731 + assert p1.is_member_of?(c)
  732 +
  733 + assert !p3.is_member_of?(c)
  734 + c.add_member(p3)
  735 + assert p3.is_member_of?(c)
  736 +
  737 + assert_equal true, p1.follows?(c)
  738 + assert_equal true, p3.follows?(c)
  739 + assert_equal false, p2.follows?(c)
  740 + end
  741 +
  742 + should "the person member of a enterprise follows the enterprise" do
  743 + e = fast_create(Enterprise)
  744 + e.stubs(:closed?).returns(false)
  745 + p1 = fast_create(Person)
  746 + p2 = fast_create(Person)
  747 + p3 = fast_create(Person)
  748 +
  749 + assert !p1.is_member_of?(e)
  750 + e.add_member(p1)
  751 + assert p1.is_member_of?(e)
  752 +
  753 + assert !p3.is_member_of?(e)
  754 + e.add_member(p3)
  755 + assert p3.is_member_of?(e)
  756 +
  757 + assert_equal true, p1.follows?(e)
  758 + assert_equal true, p3.follows?(e)
  759 + assert_equal false, p2.follows?(e)
  760 + end
  761 +
  762 + should "the person see all of your scraps" do
  763 + person = fast_create(Person)
  764 + s1 = fast_create(Scrap, :sender_id => person.id)
  765 + assert_equal [s1], person.scraps
  766 + s2 = fast_create(Scrap, :sender_id => person.id)
  767 + assert_equal [s1,s2], person.scraps
  768 + s3 = fast_create(Scrap, :receiver_id => person.id)
  769 + assert_equal [s1,s2,s3], person.scraps
  770 + end
  771 +
  772 + should "the person browse for a scrap with a Scrap object" do
  773 + person = fast_create(Person)
  774 + s1 = fast_create(Scrap, :sender_id => person.id)
  775 + s2 = fast_create(Scrap, :sender_id => person.id)
  776 + s3 = fast_create(Scrap, :receiver_id => person.id)
  777 + assert_equal s2, person.scraps(s2)
  778 + end
  779 +
  780 + should "the person browse for a scrap with an inter and string id" do
  781 + person = fast_create(Person)
  782 + s1 = fast_create(Scrap, :sender_id => person.id)
  783 + s2 = fast_create(Scrap, :sender_id => person.id)
  784 + s3 = fast_create(Scrap, :receiver_id => person.id)
  785 + assert_equal s2, person.scraps(s2.id)
  786 + assert_equal s2, person.scraps(s2.id.to_s)
  787 + end
  788 +
  789 + should "the tracked action be notified to person friends and herself" do
  790 + p1 = Person.first
  791 + p2 = fast_create(Person)
  792 + p3 = fast_create(Person)
  793 + p4 = fast_create(Person)
  794 +
  795 + p1.add_friend(p2)
  796 + assert p1.is_a_friend?(p2)
  797 + assert !p1.is_a_friend?(p3)
  798 + p1.add_friend(p4)
  799 + assert p1.is_a_friend?(p4)
  800 +
  801 + action_tracker = fast_create(ActionTracker::Record)
  802 + ActionTrackerNotification.delete_all
  803 + count = ActionTrackerNotification.count
  804 + Delayed::Job.destroy_all
  805 + Person.notify_activity(action_tracker)
  806 + process_delayed_job_queue
  807 + assert_equal count + 3, ActionTrackerNotification.count
  808 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  809 + [p1,p2,p4].include?(profile)
  810 + end
  811 + end
  812 +
  813 + should "the tracked action be notified to friends with delayed job" do
  814 + p1 = Person.first
  815 + p2 = fast_create(Person)
  816 + p3 = fast_create(Person)
  817 + p4 = fast_create(Person)
  818 +
  819 + p1.add_friend(p2)
  820 + assert p1.is_a_friend?(p2)
  821 + assert !p1.is_a_friend?(p3)
  822 + p1.add_friend(p4)
  823 + assert p1.is_a_friend?(p4)
  824 +
  825 + action_tracker = fast_create(ActionTracker::Record)
  826 +
  827 + assert_difference(Delayed::Job, :count, 1) do
  828 + Person.notify_activity(action_tracker)
  829 + end
  830 + end
  831 +
  832 + should "the tracked action notify friends with one delayed job process followed by others jobs created after run the firt job" do
  833 + p1 = Person.first
  834 + p2 = fast_create(Person)
  835 + p3 = fast_create(Person)
  836 + p4 = fast_create(Person)
  837 +
  838 + p1.add_friend(p2)
  839 + assert p1.is_a_friend?(p2)
  840 + assert !p1.is_a_friend?(p3)
  841 + p1.add_friend(p4)
  842 + assert p1.is_a_friend?(p4)
  843 +
  844 + action_tracker = fast_create(ActionTracker::Record)
  845 +
  846 + Delayed::Job.delete_all
  847 + assert_difference(Delayed::Job, :count, 1) do
  848 + Person.notify_activity(action_tracker)
  849 + end
  850 +
  851 + assert_difference(ActionTrackerNotification, :count, 2) do
  852 + process_delayed_job_queue
  853 + end
  854 + end
  855 +
  856 + should "the community tracked action be notified to the author and to community members" do
  857 + p1 = Person.first
  858 + community = fast_create(Community)
  859 + p2 = fast_create(Person)
  860 + p3 = fast_create(Person)
  861 +
  862 + community.add_member(p1)
  863 + assert p1.is_member_of?(community)
  864 + community.add_member(p3)
  865 + assert p3.is_member_of?(community)
  866 + assert !p2.is_member_of?(community)
  867 + process_delayed_job_queue
  868 +
  869 + action_tracker = fast_create(ActionTracker::Record)
  870 + article = mock()
  871 + action_tracker.stubs(:dispatcher).returns(article)
  872 + article.stubs(:is_a?).with(Article).returns(true)
  873 + article.stubs(:is_a?).with(RoleAssignment).returns(false)
  874 + article.stubs(:is_a?).with(Comment).returns(false)
  875 + article.stubs(:profile).returns(community)
  876 + ActionTrackerNotification.delete_all
  877 + assert_difference(ActionTrackerNotification, :count, 3) do
  878 + Person.notify_activity(action_tracker)
  879 + process_delayed_job_queue
  880 + end
  881 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  882 + assert [community,p1,p3].include?(profile)
  883 + end
  884 + end
  885 +
  886 + should "the community tracked action be notified to members with delayed job" do
  887 + p1 = Person.first
  888 + community = fast_create(Community)
  889 + p2 = fast_create(Person)
  890 + p3 = fast_create(Person)
  891 + p4 = fast_create(Person)
  892 +
  893 + community.add_member(p1)
  894 + assert p1.is_member_of?(community)
  895 + community.add_member(p3)
  896 + assert p3.is_member_of?(community)
  897 + community.add_member(p4)
  898 + assert p4.is_member_of?(community)
  899 + assert !p2.is_member_of?(community)
  900 +
  901 + action_tracker = fast_create(ActionTracker::Record)
  902 + article = mock()
  903 + action_tracker.stubs(:dispatcher).returns(article)
  904 + article.stubs(:is_a?).with(Article).returns(true)
  905 + article.stubs(:is_a?).with(RoleAssignment).returns(false)
  906 + article.stubs(:is_a?).with(Comment).returns(false)
  907 + article.stubs(:profile).returns(community)
  908 + ActionTrackerNotification.delete_all
  909 +
  910 + assert_difference(Delayed::Job, :count, 2) do
  911 + Person.notify_activity(action_tracker)
  912 + end
  913 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  914 + assert [community,p1,p3,p4].include?(profile)
  915 + end
  916 + end
  917 +
  918 + should "remove activities if the person is destroyed" do
  919 + ActionTracker::Record.destroy_all
  920 + ActionTrackerNotification.destroy_all
  921 + person = fast_create(Person)
  922 + a1 = fast_create(ActionTracker::Record, :user_id => person.id )
  923 + a2 = fast_create(ActionTracker::Record, :user_id => person.id )
  924 + a3 = fast_create(ActionTracker::Record)
  925 + assert_equal 3, ActionTracker::Record.count
  926 + fast_create(ActionTrackerNotification, :action_tracker_id => a1.id)
  927 + fast_create(ActionTrackerNotification, :action_tracker_id => a1.id)
  928 + fast_create(ActionTrackerNotification, :action_tracker_id => a3.id)
  929 + fast_create(ActionTrackerNotification, :action_tracker_id => a2.id)
  930 + assert_equal 4, ActionTrackerNotification.count
  931 + person.destroy
  932 + assert_equal 1, ActionTracker::Record.count
  933 + assert_equal 1, ActionTrackerNotification.count
  934 + end
  935 +
  936 + should "control scrap if is sender or receiver" do
  937 + p1, p2 = fast_create(Person), fast_create(Person)
  938 + s = fast_create(Scrap, :sender_id => p1.id, :receiver_id => p2.id)
  939 + assert p1.can_control_scrap?(s)
  940 + assert p2.can_control_scrap?(s)
  941 + end
  942 +
  943 + should "not control scrap if is not sender or receiver" do
  944 + p1, p2 = fast_create(Person), fast_create(Person)
  945 + s = fast_create(Scrap, :sender_id => p1.id, :receiver_id => p1.id)
  946 + assert p1.can_control_scrap?(s)
  947 + assert !p2.can_control_scrap?(s)
  948 + end
  949 +
  950 + should "control activity or not" do
  951 + p1, p2 = fast_create(Person), fast_create(Person)
  952 + a = fast_create(ActionTracker::Record, :user_id => p2.id)
  953 + n = fast_create(ActionTrackerNotification, :profile_id => p2.id, :action_tracker_id => a.id)
  954 + assert !p1.reload.can_control_activity?(a)
  955 + assert p2.reload.can_control_activity?(a)
  956 + end
  957 +
  958 + should 'track only one action when a person joins a community' do
  959 + ActionTracker::Record.delete_all
  960 + p = create_user('test_user').person
  961 + c = fast_create(Community, :name => "Foo")
  962 + c.add_member(p)
  963 + assert_equal ["Foo"], ActionTracker::Record.last.get_resource_name
  964 + c.reload.add_moderator(p.reload)
  965 + assert_equal ["Foo"], ActionTracker::Record.last.get_resource_name
  966 + end
  967 +
  968 + should 'track only one action when a person leaves a community' do
  969 + p = create_user('test_user').person
  970 + c = fast_create(Community, :name => "Foo")
  971 + c.add_member(p)
  972 + c.add_moderator(p)
  973 + ActionTracker::Record.delete_all
  974 + c.remove_member(p)
  975 + assert_equal ["Foo"], ActionTracker::Record.last.get_resource_name
  976 + end
  977 +
645 978 end
... ...
test/unit/profile_test.rb
... ... @@ -1798,6 +1798,13 @@ class ProfileTest &lt; Test::Unit::TestCase
1798 1798 assert_equal [f1], p.image_galleries
1799 1799 end
1800 1800  
  1801 + should 'get custom profile icon' do
  1802 + profile = build(Profile, :image_builder => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')})
  1803 + assert_kind_of String, profile.profile_custom_icon
  1804 + profile = build(Profile, :image_builder => {:uploaded_data => nil})
  1805 + assert_nil profile.profile_custom_icon
  1806 + end
  1807 +
1801 1808 private
1802 1809  
1803 1810 def assert_invalid_identifier(id)
... ...
test/unit/rss_feed_test.rb
... ... @@ -123,9 +123,7 @@ class RssFeedTest &lt; Test::Unit::TestCase
123 123 feed.profile = profile
124 124 feed.save!
125 125  
126   - profile.environment.expects(:default_hostname).returns('mysite.net').at_least_once
127   -
128   - assert_match "<link>http://mysite.net/testuser</link>", feed.data
  126 + assert_match "<link>http://colivre.net/testuser</link>", feed.data
129 127 end
130 128  
131 129 should 'provide link to each article' do
... ...
test/unit/scrap_test.rb 0 → 100644
... ... @@ -0,0 +1,197 @@
  1 +require File.join(File.dirname(__FILE__), '..', 'test_helper')
  2 +
  3 +class ScrapTest < ActiveSupport::TestCase
  4 + should "have the content" do
  5 + s = Scrap.new
  6 + s.valid?
  7 + assert s.errors.invalid?(:content)
  8 +
  9 + s.content = ''
  10 + s.valid?
  11 + assert s.errors.invalid?(:content)
  12 +
  13 + s.content = 'some content'
  14 + s.valid?
  15 + assert !s.errors.invalid?(:content)
  16 + end
  17 +
  18 + should "have the sender" do
  19 + s = Scrap.new
  20 + s.valid?
  21 + assert s.errors.invalid?(:sender_id)
  22 +
  23 + s.sender_id = 1
  24 + s.valid?
  25 + assert !s.errors.invalid?(:sender_id)
  26 + end
  27 +
  28 + should "have the receiver" do
  29 + s = Scrap.new
  30 + s.valid?
  31 + assert s.errors.invalid?(:receiver_id)
  32 +
  33 + s.receiver_id = 1
  34 + s.valid?
  35 + assert !s.errors.invalid?(:receiver_id)
  36 + end
  37 +
  38 + should "be associated to Person as sender" do
  39 + person = fast_create(Person)
  40 + s = Scrap.new
  41 + assert_nothing_raised do
  42 + s.sender = person
  43 + end
  44 + end
  45 +
  46 + should "be associated to Person as receiver" do
  47 + person = fast_create(Person)
  48 + s = Scrap.new
  49 + assert_nothing_raised do
  50 + s.receiver = person
  51 + end
  52 + end
  53 +
  54 + should "collect all scraps sent and received of a person" do
  55 + person = fast_create(Person)
  56 + s1 = fast_create(Scrap, :sender_id => person.id)
  57 + assert_equal [s1], Scrap.all_scraps(person)
  58 + s2 = fast_create(Scrap, :sender_id => person.id)
  59 + assert_equal [s1,s2], Scrap.all_scraps(person)
  60 + s3 = fast_create(Scrap, :receiver_id => person.id)
  61 + assert_equal [s1,s2,s3], Scrap.all_scraps(person)
  62 + end
  63 +
  64 + should "create the leave_scrap action tracker verb on scrap creation of one user to another" do
  65 + p1 = ActionTracker::Record.current_user_from_model
  66 + p2 = fast_create(Person)
  67 + s = Scrap.new
  68 + s.sender= p1
  69 + s.receiver= p2
  70 + s.content = 'some content'
  71 + s.save!
  72 + ta = ActionTracker::Record.last
  73 + assert_equal s.content, ta.params['content']
  74 + assert_equal s.sender.name, ta.params['sender_name']
  75 + assert_equal s.receiver.name, ta.params['receiver_name']
  76 + assert_equal s.receiver.url, ta.params['receiver_url']
  77 + assert_equal 'leave_scrap', ta.verb
  78 + assert_equal p1, ta.user
  79 + end
  80 +
  81 + should "notify leave_scrap action tracker verb to friends and itself" do
  82 + p1 = ActionTracker::Record.current_user_from_model
  83 + p2 = fast_create(Person)
  84 + p1.add_friend(p2)
  85 + ActionTrackerNotification.destroy_all
  86 + Delayed::Job.destroy_all
  87 + s = Scrap.new
  88 + s.sender= p1
  89 + s.receiver= p2
  90 + s.content = 'some content'
  91 + s.save!
  92 + process_delayed_job_queue
  93 + assert_equal 2, ActionTrackerNotification.count
  94 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  95 + assert [p1,p2].include?(profile)
  96 + end
  97 + end
  98 +
  99 + should "create the leave_scrap_to_self action tracker verb on scrap creation of one user to itself" do
  100 + p1 = Person.first
  101 + s = Scrap.new
  102 + s.sender= p1
  103 + s.receiver= p1
  104 + s.content = 'some content'
  105 + s.save!
  106 + ta = ActionTracker::Record.last
  107 + assert_equal s.content, ta.params['content']
  108 + assert_equal s.sender.name, ta.params['sender_name']
  109 + assert_equal 'leave_scrap_to_self', ta.verb
  110 + assert_equal p1, ta.user
  111 + end
  112 +
  113 + should "notify leave_scrap_to_self action tracker verb to friends and itself" do
  114 + p1 = Person.first
  115 + p2 = fast_create(Person)
  116 + p1.add_friend(p2)
  117 + ActionTrackerNotification.destroy_all
  118 + Delayed::Job.destroy_all
  119 + s = Scrap.new
  120 + s.sender= p1
  121 + s.receiver= p1
  122 + s.content = 'some content'
  123 + s.save!
  124 + process_delayed_job_queue
  125 + assert_equal 2, ActionTrackerNotification.count
  126 + ActionTrackerNotification.all.map{|a|a.profile}.map do |profile|
  127 + assert [p1,p2].include?(profile)
  128 + end
  129 + end
  130 +
  131 + should "get replies of a scrap" do
  132 + s = fast_create(Scrap)
  133 + s1 = fast_create(Scrap, :scrap_id => s.id)
  134 + s2 = fast_create(Scrap)
  135 + s3 = fast_create(Scrap, :scrap_id => s.id)
  136 + assert_equal [s1,s3], s.replies
  137 + end
  138 +
  139 + should "get only replies scrap" do
  140 + s0 = fast_create(Scrap)
  141 + s1 = fast_create(Scrap, :scrap_id => s0.id)
  142 + s2 = fast_create(Scrap)
  143 + s3 = fast_create(Scrap, :scrap_id => s0.id)
  144 + assert_equal [s0,s2], Scrap.not_replies
  145 + end
  146 +
  147 + should "remove the replies is the root is removed" do
  148 + Scrap.delete_all
  149 + s = fast_create(Scrap)
  150 + s1 = fast_create(Scrap, :scrap_id => s.id)
  151 + s2 = fast_create(Scrap, :scrap_id => s.id)
  152 + assert_equal [s1,s2], s.replies
  153 + assert_equal 3, Scrap.count
  154 + s.destroy
  155 + assert_equal 0, Scrap.count
  156 + end
  157 +
  158 + should "update the scrap on reply creation" do
  159 + Scrap.delete_all
  160 + s = fast_create(Scrap, :updated_at => DateTime.parse('2010-01-01'))
  161 + assert_equal DateTime.parse('2010-01-01'), s.updated_at.strftime('%Y-%m-%d')
  162 + DateTime.stubs(:now).returns(DateTime.parse('2010-09-07'))
  163 + s1 = Scrap.create(defaults_for_scrap(:scrap_id => s.id))
  164 + s.reload
  165 + assert_not_equal DateTime.parse('2010-01-01'), s.updated_at.strftime('%Y-%m-%d')
  166 + end
  167 +
  168 + should "have the root defined" do
  169 + s = fast_create(Scrap)
  170 + s1 = fast_create(Scrap, :scrap_id => s.id)
  171 + s2 = fast_create(Scrap, :scrap_id => s.id)
  172 + assert_equal s, s1.root
  173 + assert_equal s, s2.root
  174 + end
  175 +
  176 + should 'strip all html tags' do
  177 + s, r = fast_create(Person), fast_create(Person)
  178 + s = Scrap.new :sender => s, :receiver => r, :content => "<p>Test <b>Rails</b></p>"
  179 + assert_equal "Test Rails", s.strip_all_html_tags
  180 + end
  181 +
  182 + should 'strip html before save' do
  183 + s, r = fast_create(Person), fast_create(Person)
  184 + s = Scrap.new :sender => s, :receiver => r, :content => "<p>Test <b>Rails</b></p>"
  185 + s.save!
  186 + assert_equal "Test Rails", s.reload.content
  187 + end
  188 +
  189 + should 'strip html before validate' do
  190 + s, r = fast_create(Person), fast_create(Person)
  191 + s = Scrap.new :sender => s, :receiver => r, :content => "<p><b></b></p>"
  192 + assert !s.valid?
  193 + s.content = "<p>Test</p>"
  194 + assert s.valid?
  195 + end
  196 +
  197 +end
... ...
test/unit/uploaded_file_test.rb
... ... @@ -228,4 +228,55 @@ class UploadedFileTest &lt; Test::Unit::TestCase
228 228 assert File.exists?(image)
229 229 end
230 230 end
  231 +
  232 + should 'return a thumbnail for images' do
  233 + f = UploadedFile.new
  234 + f.expects(:image?).returns(true)
  235 + f.expects(:full_filename).with(:thumb).returns(File.join(RAILS_ROOT, 'public', 'images', '0000', '0005', 'x.png'))
  236 + assert_equal '/images/0000/0005/x.png', f.thumbnail_path
  237 + f = UploadedFile.new
  238 + f.stubs(:full_filename).with(:thumb).returns(File.join(RAILS_ROOT, 'public', 'images', '0000', '0005', 'x.png'))
  239 + f.expects(:image?).returns(false)
  240 + assert_nil f.thumbnail_path
  241 + end
  242 +
  243 + should 'track action when a published image is uploaded in a gallery' do
  244 + p = fast_create(Folder, :profile_id => @profile.id)
  245 + p.view_as = 'image_gallery'; p.save!
  246 + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => p, :profile => @profile)
  247 + ta = ActionTracker::Record.last(:conditions => { :verb => "upload_image" })
  248 + assert_kind_of String, ta.get_thumbnail_path[0]
  249 + assert_equal [f.reload.view_url], ta.get_view_url
  250 + assert_equal [p.reload.url], ta.get_parent_url
  251 + assert_equal [p.name], ta.get_parent_name
  252 + end
  253 +
  254 + should 'not track action when is not image' do
  255 + ActionTracker::Record.delete_all
  256 + p = fast_create(Folder, :profile_id => @profile.id)
  257 + p.view_as = 'image_gallery'; p.save!
  258 + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :parent => p, :profile => @profile)
  259 + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" })
  260 + end
  261 +
  262 + should 'not track action when has no parent' do
  263 + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => nil, :profile => @profile)
  264 + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" })
  265 + end
  266 +
  267 + should 'not track action when is not published' do
  268 + ActionTracker::Record.delete_all
  269 + p = fast_create(Folder, :profile_id => @profile.id)
  270 + p.view_as = 'image_gallery'; p.save!
  271 + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => p, :profile => @profile, :published => false)
  272 + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" })
  273 + end
  274 +
  275 + should 'not track action when parent is not gallery' do
  276 + ActionTracker::Record.delete_all
  277 + p = fast_create(Folder, :profile_id => @profile.id)
  278 + f = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :parent => p, :profile => @profile)
  279 + assert_nil ActionTracker::Record.last(:conditions => { :verb => "upload_image" })
  280 + end
  281 +
231 282 end
... ...
vendor/plugins/access_control/lib/role_assignment.rb
... ... @@ -4,7 +4,11 @@ class RoleAssignment &lt; ActiveRecord::Base
4 4 belongs_to :resource, :polymorphic => true
5 5  
6 6 validates_presence_of :role, :accessor
7   -
  7 +
  8 + 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 }
  9 +
  10 + 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 }
  11 +
8 12 def has_permission?(perm, res)
9 13 return false unless role.has_permission?(perm.to_s) && (resource || is_global)
10 14 return true if is_global
... ...
vendor/plugins/action_tracker/MIT-LICENSE 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +Copyright (c) 2010 [name of plugin creator]
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
vendor/plugins/action_tracker/README 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +=============
  2 +ActionTracker
  3 +=============
  4 +Installation
  5 +=============
  6 +$ ./script/generate action_tracker
  7 +
  8 +The migration will be created.
  9 +
  10 +$ rake db:migrate
  11 +
  12 +The table will be created.
  13 +
  14 +Copyright (c) 2010 Caio SBA <caiosba@gmail.com>, released under the MIT license
... ...
vendor/plugins/action_tracker/Rakefile 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +require 'rake'
  2 +require 'rake/testtask'
  3 +require 'rake/rdoctask'
  4 +
  5 +desc 'Default: run unit tests.'
  6 +task :default => :test
  7 +
  8 +desc 'Test the action_tracker plugin.'
  9 +Rake::TestTask.new(:test) do |t|
  10 + t.libs << 'lib'
  11 + t.libs << 'test'
  12 + t.pattern = 'test/**/*_test.rb'
  13 + t.verbose = true
  14 +end
  15 +
  16 +desc 'Generate documentation for the action_tracker plugin.'
  17 +Rake::RDocTask.new(:rdoc) do |rdoc|
  18 + rdoc.rdoc_dir = 'rdoc'
  19 + rdoc.title = 'ActionTracker'
  20 + rdoc.options << '--line-numbers' << '--inline-source'
  21 + rdoc.rdoc_files.include('README')
  22 + rdoc.rdoc_files.include('lib/**/*.rb')
  23 +end
... ...
vendor/plugins/action_tracker/generators/action_tracker/action_tracker_generator.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +class ActionTrackerGenerator < Rails::Generator::Base
  2 + def manifest
  3 + record do |m|
  4 + m.migration_template 'migration.rb', File.join('db', 'migrate'), :migration_file_name => 'create_action_tracker'
  5 + end
  6 + end
  7 +end
... ...
vendor/plugins/action_tracker/generators/action_tracker/templates/migration.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +class CreateActionTracker < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :action_tracker do |t|
  4 + t.belongs_to :user, :polymorphic => true
  5 + t.belongs_to :dispatcher, :polymorphic => true
  6 + t.text :params
  7 + t.string :verb
  8 + t.timestamps
  9 + end
  10 +
  11 + change_table :action_tracker do |t|
  12 + t.index [:user_id, :user_type]
  13 + t.index [:dispatcher_id, :dispatcher_type]
  14 + t.index :verb
  15 + end
  16 + end
  17 +
  18 + def self.down
  19 + drop_table :action_tracker
  20 + end
  21 +end
... ...
vendor/plugins/action_tracker/init.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +require "user_stamp"
  2 +
  3 +UserStamp.creator_attribute = :user
  4 +UserStamp.updater_attribute = :user
  5 +
  6 +class ActionController::Base
  7 + extend UserStamp::ClassMethods
  8 +end
  9 +
  10 +require "action_tracker"
... ...
vendor/plugins/action_tracker/install.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +# Install hook code here
... ...
vendor/plugins/action_tracker/lib/action_tracker.rb 0 → 100644
... ... @@ -0,0 +1,137 @@
  1 +require File.join(File.dirname(__FILE__), 'action_tracker_model.rb')
  2 +
  3 +module ActionTracker
  4 +
  5 + module ControllerMethods
  6 +
  7 + def self.included(base)
  8 + base.send :user_stamp, ActionTracker::Record
  9 + base.send :extend, ClassMethods
  10 + end
  11 +
  12 + module ClassMethods
  13 +
  14 + def track_actions_after(verb, options = {}, &block)
  15 + track_actions_by_time(verb, :after, options, &block)
  16 + end
  17 +
  18 + def track_actions_before(verb, options = {}, &block)
  19 + track_actions_by_time(verb, :before, options, &block)
  20 + end
  21 +
  22 + def track_actions(verb, options = {}, &block)
  23 + track_actions_by_time(verb, ActionTrackerConfig.default_filter_time, options, &block)
  24 + end
  25 +
  26 + def track_actions_by_time(verb, time, options = {}, &block)
  27 + keep_params = options.delete(:keep_params) || options.delete('keep_params') || :all
  28 + send("#{time}_filter", options) do |x|
  29 + x.save_action_for_verb(verb.to_s, keep_params)
  30 + block.call(x) unless block.nil?
  31 + end
  32 + send :include, InstanceMethods
  33 + end
  34 + end
  35 +
  36 + module InstanceMethods
  37 + def save_action_for_verb(verb, keep_params = :all)
  38 + if keep_params.is_a? Array
  39 + stored_params = params.reject { |key, value| !keep_params.include?(key.to_sym) and !keep_params.include?(key.to_s) }
  40 + elsif keep_params.to_s == 'none'
  41 + stored_params = {}
  42 + elsif keep_params.to_s == 'all'
  43 + stored_params = params
  44 + end
  45 + user = send ActionTrackerConfig.current_user_method
  46 + tracked_action = case ActionTrackerConfig.verb_type(verb)
  47 + when :groupable
  48 + Record.add_or_create :verb => verb, :user => user, :params => stored_params
  49 + when :updatable
  50 + Record.update_or_create :verb => verb, :user => user, :params => stored_params
  51 + when :single
  52 + Record.new :verb => verb, :user => user, :params => stored_params
  53 + end
  54 + user.tracked_actions << tracked_action
  55 + end
  56 + end
  57 +
  58 + end
  59 +
  60 + module ModelMethods
  61 +
  62 + def self.included(base)
  63 + base.send :extend, ClassMethods
  64 + end
  65 +
  66 + module ClassMethods
  67 + def track_actions(verb, callback, options = {}, &block)
  68 + keep_params = options.delete(:keep_params) || options.delete('keep_params') || :all
  69 + post_proc = options.delete(:post_processing) || options.delete('post_processing') || Proc.new{}
  70 + send(callback, Proc.new { |tracked| tracked.save_action_for_verb(verb.to_s, keep_params, post_proc) }, options)
  71 + send :include, InstanceMethods
  72 + end
  73 +
  74 + def acts_as_trackable(options = {})
  75 + has_many :tracked_actions, { :class_name => "ActionTracker::Record", :order => "updated_at DESC", :foreign_key => :user_id }.merge(options)
  76 + send :include, InstanceMethods
  77 + end
  78 + end
  79 +
  80 + module InstanceMethods
  81 + def time_spent_doing(verb, conditions = {})
  82 + time = 0
  83 + tracked_actions.all(:conditions => conditions.merge({ :verb => verb.to_s })).each do |t|
  84 + time += t.updated_at - t.created_at
  85 + end
  86 + time.to_f
  87 + end
  88 +
  89 + def save_action_for_verb(verb, keep_params = :all, post_proc = Proc.new{})
  90 + user = ActionTracker::Record.current_user_from_model
  91 + return nil if user.nil?
  92 + if keep_params.is_a? Array
  93 + stored_params = {}
  94 + keep_params.each do |param|
  95 + result = self
  96 + param.to_s.split('.').each { |m| result = result.send(m) }
  97 + stored_params[param.to_s.gsub(/\./, '_')] = result
  98 + end
  99 + elsif keep_params.to_s == 'none'
  100 + stored_params = {}
  101 + elsif keep_params.to_s == 'all'
  102 + stored_params = self.attributes
  103 + end
  104 + tracked_action = case ActionTrackerConfig.verb_type(verb)
  105 + when :groupable
  106 + Record.add_or_create :verb => verb, :params => stored_params
  107 + when :updatable
  108 + Record.update_or_create :verb => verb, :params => stored_params
  109 + when :single
  110 + Record.new :verb => verb, :params => stored_params
  111 + end
  112 + tracked_action.dispatcher = self
  113 + user.tracked_actions << tracked_action
  114 + post_proc.call tracked_action.reload
  115 + end
  116 +
  117 + end
  118 +
  119 + end
  120 +
  121 + module ViewHelper
  122 + def describe(ta)
  123 + returning "" do |result|
  124 + if ta.is_a?(ActionTracker::Record)
  125 + result << ta.description.gsub(/\{\{(.*?)\}\}/) { eval $1 }
  126 + else
  127 + result << ""
  128 + end
  129 + end
  130 + end
  131 + end
  132 +
  133 +end
  134 +
  135 +ActionController::Base.send :include, ActionTracker::ControllerMethods
  136 +ActiveRecord::Base.send :include, ActionTracker::ModelMethods
  137 +ActionView::Base.send :include, ActionTracker::ViewHelper
... ...
vendor/plugins/action_tracker/lib/action_tracker_config.rb 0 → 100644
... ... @@ -0,0 +1,60 @@
  1 +class ActionTrackerConfig
  2 +
  3 + def self.config
  4 + @action_tracker_config ||= {}
  5 + end
  6 +
  7 + def self.config=(h)
  8 + @action_tracker_config = h
  9 + end
  10 +
  11 + def self.verbs
  12 + config[:verbs] || {}
  13 + end
  14 +
  15 + def self.verbs=(h)
  16 + config[:verbs] = h
  17 + end
  18 +
  19 + def self.verb_names
  20 + verbs.keys.map(&:to_s)
  21 + end
  22 +
  23 + def self.current_user_method
  24 + config[:current_user_method] || :current_user
  25 + end
  26 +
  27 + def self.current_user_method=(method_name)
  28 + UserStamp.current_user_method = config[:current_user_method] = method_name
  29 + end
  30 +
  31 + def self.default_filter_time
  32 + config[:default_filter_time] || :after
  33 + end
  34 +
  35 + def self.default_filter_time=(before_or_after)
  36 + config[:default_filter_time] = before_or_after
  37 + end
  38 +
  39 + def self.timeout
  40 + config[:timeout] || 5.minutes
  41 + end
  42 +
  43 + def self.timeout=(seconds)
  44 + config[:timeout] = seconds
  45 + end
  46 +
  47 + def self.get_verb(verb)
  48 + verbs[verb.to_s] || verbs[verb.to_sym] || {}
  49 + end
  50 +
  51 + def self.verb_type(verb)
  52 + type = get_verb(verb.to_s)[:type] || get_verb(verb.to_s)['type'] || :single
  53 + verb_types.include?(type.to_sym) ? type : :single
  54 + end
  55 +
  56 + def self.verb_types
  57 + [:single, :updatable, :groupable]
  58 + end
  59 +
  60 +end
... ...
vendor/plugins/action_tracker/lib/action_tracker_model.rb 0 → 100644
... ... @@ -0,0 +1,101 @@
  1 +module ActionTracker
  2 + class Record < ActiveRecord::Base
  3 +
  4 + set_table_name 'action_tracker'
  5 +
  6 + belongs_to :user, :polymorphic => true
  7 + belongs_to :dispatcher, :polymorphic => true
  8 +
  9 + serialize :params, Hash
  10 +
  11 + before_validation :stringify_verb
  12 +
  13 + validates_presence_of :verb
  14 + validates_presence_of :user_id
  15 + validates_presence_of :user_type
  16 +
  17 + alias_method :subject, :user
  18 +
  19 + def self.current_user_from_model
  20 + u = new
  21 + u.valid?
  22 + u.user
  23 + end
  24 +
  25 + def self.update_or_create(params)
  26 + u = params[:user] || current_user_from_model
  27 + return if u.nil?
  28 + l = last :conditions => { :user_id => u.id, :user_type => u.class.base_class.to_s, :verb => params[:verb].to_s }
  29 + ( !l.nil? and Time.now - l.updated_at < ActionTrackerConfig.timeout ) ? l.update_attributes(params.merge({ :updated_at => Time.now })) : l = new(params)
  30 + l
  31 + end
  32 +
  33 + def self.add_or_create(params)
  34 + u = params[:user] || current_user_from_model
  35 + return if u.nil?
  36 + l = last :conditions => { :user_id => u.id, :user_type => u.class.base_class.to_s, :verb => params[:verb].to_s }
  37 + if !l.nil? and Time.now - l.updated_at < ActionTrackerConfig.timeout
  38 + params[:params].clone.each { |key, value| params[:params][key] = l.params[key].clone.push(value) }
  39 + l.update_attributes params
  40 + else
  41 + params[:params].clone.each { |key, value| params[:params][key] = [value] }
  42 + l = new params
  43 + end
  44 + l
  45 + end
  46 +
  47 + def self.time_spent(conditions = {}) # In seconds
  48 + #FIXME Better if it could be completely done in the database, but SQLite does not support difference between two timestamps
  49 + time = 0
  50 + all(:conditions => conditions).each { |action| time += action.updated_at - action.created_at }
  51 + time.to_f
  52 + end
  53 +
  54 + def duration # In seconds
  55 + ( updated_at - created_at ).to_f
  56 + end
  57 +
  58 + def description
  59 + ActionTrackerConfig.get_verb(self.verb)[:description] || ""
  60 + end
  61 +
  62 + def describe
  63 + description.gsub(/\{\{([^}]+)\}\}/) { eval $1 }
  64 + end
  65 +
  66 + def predicate
  67 + self.params || {}
  68 + end
  69 +
  70 + def phrase
  71 + { :subject => self.subject, :verb => self.verb, :predicate => self.predicate }
  72 + end
  73 +
  74 + def method_missing(method, *args, &block)
  75 + if method.to_s =~ /^get_(.*)$/
  76 + param = method.to_s.gsub('get_', '')
  77 + predicate[param.to_s] || predicate[param.to_sym]
  78 + else
  79 + super
  80 + end
  81 + end
  82 +
  83 + def collect_group_with_index(param)
  84 + i = -1
  85 + send("get_#{param}").collect{ |el| yield(el, i += 1) }
  86 + end
  87 +
  88 + protected
  89 +
  90 + def validate
  91 + errors.add_to_base "Verb must be one of the following: #{ActionTrackerConfig.verb_names.join(',')}" unless ActionTrackerConfig.verb_names.include?(self.verb)
  92 + end
  93 +
  94 + private
  95 +
  96 + def stringify_verb
  97 + self.verb = self.verb.to_s unless self.verb.nil?
  98 + end
  99 +
  100 + end
  101 +end
... ...
vendor/plugins/action_tracker/tasks/action_tracker_tasks.rake 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +# desc "Explaining what the task does"
  2 +# task :action_tracker do
  3 +# # Task goes here
  4 +# end
... ...
vendor/plugins/action_tracker/test/action_tracker_config_test.rb 0 → 100755
... ... @@ -0,0 +1,121 @@
  1 +require 'test_helper'
  2 +
  3 +class ActionTrackerConfigTest < ActiveSupport::TestCase
  4 +
  5 + def test_has_config
  6 + assert_not_nil ActionTrackerConfig
  7 + end
  8 +
  9 + def test_config_is_a_hash
  10 + assert_kind_of Hash, ActionTrackerConfig.config
  11 + end
  12 +
  13 + def test_config_can_be_set
  14 + c = { :foo => 'bar' }
  15 + ActionTrackerConfig.config = c
  16 + assert_equal c, ActionTrackerConfig.config
  17 + end
  18 +
  19 + def test_verbs_is_a_hash
  20 + assert_kind_of Hash, ActionTrackerConfig.verbs
  21 + end
  22 +
  23 + def test_verbs_can_be_set
  24 + v = { :search => {} }
  25 + ActionTrackerConfig.verbs = v
  26 + assert_equal v, ActionTrackerConfig.verbs
  27 + end
  28 +
  29 + def test_verb_names_is_a_list_of_strings
  30 + v = { :search => {}, :delete => {}, "login" => {} }
  31 + ActionTrackerConfig.verbs = v
  32 + assert_equal 3, ActionTrackerConfig.verb_names.size
  33 + %w(search delete login).each { |verb| assert ActionTrackerConfig.verb_names.include?(verb) }
  34 + end
  35 +
  36 + def test_current_user_is_default_method
  37 + ActionTrackerConfig.config[:current_user_method] = nil
  38 + assert_equal :current_user, ActionTrackerConfig.current_user_method
  39 + end
  40 +
  41 + def test_current_user_can_be_set
  42 + ActionTrackerConfig.current_user_method = :logged_in_user
  43 + assert_equal :logged_in_user, ActionTrackerConfig.current_user_method
  44 + end
  45 +
  46 + def test_default_filter_time_is_after
  47 + ActionTrackerConfig.config[:default_filter_time] = nil
  48 + assert_equal :after, ActionTrackerConfig.default_filter_time
  49 + end
  50 +
  51 + def test_default_filter_time_can_be_set
  52 + ActionTrackerConfig.default_filter_time = :before
  53 + assert_equal :before, ActionTrackerConfig.default_filter_time
  54 + end
  55 +
  56 + def test_default_timeout_is_five_minutes
  57 + ActionTrackerConfig.config[:timeout] = nil
  58 + assert_equal 5.minutes, ActionTrackerConfig.timeout
  59 + end
  60 +
  61 + def test_timeout_can_be_set
  62 + ActionTrackerConfig.timeout = 10.minutes
  63 + assert_equal 10.minutes, ActionTrackerConfig.timeout
  64 + end
  65 +
  66 + def test_get_verb_return_hash
  67 + assert_kind_of Hash, ActionTrackerConfig.get_verb(:search)
  68 + end
  69 +
  70 + def test_get_verb_symbol_search_by_symbol
  71 + ActionTrackerConfig.verbs = { :search => { :description => "Got it" } }
  72 + assert_equal "Got it", ActionTrackerConfig.get_verb(:search)[:description]
  73 + end
  74 +
  75 + def test_get_verb_symbol_search_by_string
  76 + ActionTrackerConfig.verbs = { :search => { :description => "Got it" } }
  77 + assert_equal "Got it", ActionTrackerConfig.get_verb("search")[:description]
  78 + end
  79 +
  80 + def test_get_verb_string_search_by_string
  81 + ActionTrackerConfig.verbs = { "search" => { :description => "Got it" } }
  82 + assert_equal "Got it", ActionTrackerConfig.get_verb("search")[:description]
  83 + end
  84 +
  85 + def test_get_verb_string_search_by_symbol
  86 + ActionTrackerConfig.verbs = { "search" => { :description => "Got it" } }
  87 + assert_equal "Got it", ActionTrackerConfig.get_verb(:search)[:description]
  88 + end
  89 +
  90 + def test_default_verb_type_is_single
  91 + ActionTrackerConfig.verbs = { "search" => { :description => "Got it" } }
  92 + assert_equal :single, ActionTrackerConfig.verb_type(:search)
  93 + end
  94 +
  95 + def test_verb_type_is_single_if_verb_type_not_valid
  96 + ActionTrackerConfig.verbs = { "search" => { :type => :not_valid } }
  97 + assert_equal :single, ActionTrackerConfig.verb_type(:search)
  98 + end
  99 +
  100 + def test_get_verb_type_by_symbol
  101 + ActionTrackerConfig.verbs = { "search" => { :type => :updatable } }
  102 + assert_equal :updatable, ActionTrackerConfig.verb_type(:search)
  103 + end
  104 +
  105 + def test_get_verb_type_by_string
  106 + ActionTrackerConfig.verbs = { "search" => { "type" => :updatable } }
  107 + assert_equal :updatable, ActionTrackerConfig.verb_type(:search)
  108 + end
  109 +
  110 + def test_verb_types_is_a_list
  111 + assert_kind_of Array, ActionTrackerConfig.verb_types
  112 + end
  113 +
  114 + def test_valid_verb_types
  115 + assert_equal 3, ActionTrackerConfig.verb_types.size
  116 + assert ActionTrackerConfig.verb_types.include?(:single)
  117 + assert ActionTrackerConfig.verb_types.include?(:updatable)
  118 + assert ActionTrackerConfig.verb_types.include?(:groupable)
  119 + end
  120 +
  121 +end
... ...
vendor/plugins/action_tracker/test/action_tracker_model_test.rb 0 → 100644
... ... @@ -0,0 +1,297 @@
  1 +require 'test_helper'
  2 +
  3 +ActiveRecord::Base.establish_connection({
  4 + :adapter => "sqlite3",
  5 + :database => ":memory:"
  6 +})
  7 +
  8 +ActiveRecord::Schema.define do
  9 + create_table :some_table, :force => true do |t|
  10 + t.column :some_column, :integer
  11 + end
  12 + create_table :action_tracker do |t|
  13 + t.belongs_to :user, :polymorphic => true
  14 + t.belongs_to :dispatcher, :polymorphic => true
  15 + t.text :params
  16 + t.string :verb
  17 + t.timestamps
  18 + end
  19 +end
  20 +
  21 +class SomeModel < ActiveRecord::Base
  22 + set_table_name :some_table
  23 + acts_as_trackable
  24 +end
  25 +
  26 +class ActionTrackerModelTest < ActiveSupport::TestCase
  27 +
  28 + def setup
  29 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } }
  30 + @mymodel = SomeModel.create!
  31 + @othermodel = SomeModel.create!
  32 + @tracked_action = ActionTracker::Record.create! :verb => :some_verb, :params => { :user => "foo" }, :user => @mymodel, :dispatcher => @othermodel
  33 + end
  34 +
  35 + def test_has_relationship
  36 + assert @mymodel.respond_to?(:tracked_actions)
  37 + end
  38 +
  39 + def test_params_is_a_hash
  40 + assert_kind_of Hash, @tracked_action.params
  41 + end
  42 +
  43 + def test_has_a_polymorphic_relation_with_user
  44 + assert_equal @mymodel.id, @tracked_action.user_id
  45 + assert_equal "SomeModel", @tracked_action.user_type
  46 + assert_equal @mymodel, @tracked_action.user
  47 + assert_equal [@tracked_action], @mymodel.tracked_actions
  48 + end
  49 +
  50 + def test_has_a_polymorphic_relation_with_dispatcher
  51 + assert_equal @othermodel.id, @tracked_action.dispatcher_id
  52 + assert_equal "SomeModel", @tracked_action.dispatcher_type
  53 + assert_equal @othermodel, @tracked_action.dispatcher
  54 + end
  55 +
  56 + def test_should_stringify_verb_before_validation
  57 + ta = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some_verb
  58 + assert_equal "some_verb", ta.verb
  59 + end
  60 +
  61 + def test_verb_is_mandatory
  62 + ta = ActionTracker::Record.new
  63 + ta.valid?
  64 + assert ta.errors.on(:verb)
  65 + assert_raise ActiveRecord::RecordInvalid do
  66 + ta.save!
  67 + end
  68 + end
  69 +
  70 + def test_verb_must_be_declared_previously
  71 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } }
  72 + assert_raise ActiveRecord::RecordInvalid do
  73 + ta = ActionTracker::Record.create! :verb => :undeclared_verb
  74 + end
  75 + ActionTrackerConfig.verbs = { :declared_verb => { :description => "Did something" } }
  76 + assert_nothing_raised do
  77 + ta = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :declared_verb
  78 + end
  79 + end
  80 +
  81 + def test_update_or_create_create_if_there_is_no_last
  82 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } }
  83 + ActionTracker::Record.delete_all
  84 + assert_difference "ActionTracker::Record.count" do
  85 + ta = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel
  86 + ta.save; ta.reload
  87 + assert_kind_of ActionTracker::Record, ta
  88 + end
  89 + assert_equal "some", ActionTracker::Record.last.verb
  90 + assert_equal @mymodel, ActionTracker::Record.last.user
  91 + end
  92 +
  93 + def test_update_or_create_create_if_no_timeout
  94 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } }
  95 + ActionTrackerConfig.timeout = 5.minutes
  96 + ActionTracker::Record.delete_all
  97 + ta = nil
  98 + assert_difference "ActionTracker::Record.count" do
  99 + ta = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel
  100 + ta.save; ta.reload
  101 + end
  102 + assert_kind_of ActionTracker::Record, ta
  103 + ta.updated_at = Time.now.ago(6.minutes)
  104 + ta.send :update_without_callbacks
  105 + t = ta.reload.updated_at
  106 + assert_difference "ActionTracker::Record.count" do
  107 + ta2 = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel
  108 + ta2.save; ta2.reload
  109 + assert_kind_of ActionTracker::Record, ta2
  110 + end
  111 + assert_equal t, ta.reload.updated_at
  112 + end
  113 +
  114 + def test_update_or_create_update_if_timeout
  115 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } }
  116 + ActionTrackerConfig.timeout = 7.minutes
  117 + ActionTracker::Record.delete_all
  118 + ta = nil
  119 + assert_difference "ActionTracker::Record.count" do
  120 + ta = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel, :params => { :foo => 2 }
  121 + ta.save; ta.reload
  122 + end
  123 + assert_kind_of ActionTracker::Record, ta
  124 + assert_equal 2, ta.get_foo
  125 + ta.updated_at = Time.now.ago(6.minutes)
  126 + ta.send :update_without_callbacks
  127 + t = ta.reload.updated_at
  128 + assert_no_difference "ActionTracker::Record.count" do
  129 + ta2 = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel, :params => { :foo => 3 }
  130 + ta2.save; ta2.reload
  131 + assert_kind_of ActionTracker::Record, ta2
  132 + end
  133 + assert_not_equal t, ta.reload.updated_at
  134 + assert_equal 3, ta.reload.get_foo
  135 + end
  136 +
  137 + def test_add_or_create_create_if_no_timeout
  138 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :groupable } }
  139 + ActionTrackerConfig.timeout = 5.minutes
  140 + ActionTracker::Record.delete_all
  141 + ta = nil
  142 + assert_difference "ActionTracker::Record.count" do
  143 + ta = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "bar" }
  144 + ta.save; ta.reload
  145 + end
  146 + assert_kind_of ActionTracker::Record, ta
  147 + assert_equal ["bar"], ta.reload.params[:foo]
  148 + ta.updated_at = Time.now.ago(6.minutes)
  149 + ta.send :update_without_callbacks
  150 + t = ta.reload.updated_at
  151 + assert_difference "ActionTracker::Record.count" do
  152 + ta2 = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test" }
  153 + ta2.save; ta2.reload
  154 + assert_kind_of ActionTracker::Record, ta2
  155 + end
  156 + assert_equal t, ta.reload.updated_at
  157 + assert_equal ["test"], ActionTracker::Record.last.params[:foo]
  158 + end
  159 +
  160 + def test_add_or_create_update_if_timeout
  161 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } }
  162 + ActionTrackerConfig.timeout = 7.minutes
  163 + ActionTracker::Record.delete_all
  164 + ta = nil
  165 + assert_difference "ActionTracker::Record.count" do
  166 + ta = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test 1", :bar => 2 }
  167 + ta.save; ta.reload
  168 + end
  169 + assert_kind_of ActionTracker::Record, ta
  170 + assert_equal ["test 1"], ta.params[:foo]
  171 + assert_equal [2], ta.params[:bar]
  172 + ta.updated_at = Time.now.ago(6.minutes)
  173 + ta.send :update_without_callbacks
  174 + t = ta.reload.updated_at
  175 + assert_no_difference "ActionTracker::Record.count" do
  176 + ta2 = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test 2", :bar => 1 }
  177 + ta2.save; ta2.reload
  178 + assert_kind_of ActionTracker::Record, ta2
  179 + end
  180 + assert_equal ["test 1", "test 2"], ActionTracker::Record.last.params[:foo]
  181 + assert_equal [2, 1], ActionTracker::Record.last.params[:bar]
  182 + assert_not_equal t, ta.reload.updated_at
  183 + assert_no_difference "ActionTracker::Record.count" do
  184 + ta = ActionTracker::Record.add_or_create :verb => :some, :user => @mymodel, :params => { :foo => "test 1", :bar => 1 }
  185 + ta.save; ta.reload
  186 + end
  187 + assert_equal ["test 1", "test 2", "test 1"], ActionTracker::Record.last.params[:foo]
  188 + assert_equal [2, 1, 1], ActionTracker::Record.last.params[:bar]
  189 + end
  190 +
  191 + def test_time_spent
  192 + ActionTracker::Record.delete_all
  193 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } }
  194 + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel
  195 + t.save; t.reload
  196 + t.created_at = t.created_at.ago(2.days)
  197 + t.updated_at = t.created_at.tomorrow
  198 + t.send :update_without_callbacks
  199 + ActionTrackerConfig.timeout = 5.minutes
  200 + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel
  201 + t.save; t.reload
  202 + t.created_at = t.updated_at.ago(2.hours)
  203 + t.send :update_without_callbacks
  204 + assert_equal 2, ActionTracker::Record.count
  205 + assert_equal 26.hours, ActionTracker::Record.time_spent
  206 + end
  207 +
  208 + def test_duration
  209 + ActionTracker::Record.delete_all
  210 + ActionTrackerConfig.verbs = { :some => { :description => "Something", :type => :updatable } }
  211 + ActionTrackerConfig.timeout = 5.minutes
  212 + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel; t.save
  213 + t = ActionTracker::Record.update_or_create :verb => :some, :user => @mymodel; t.save
  214 + t.reload
  215 + t.created_at = t.updated_at.ago(2.minutes)
  216 + t.send :update_without_callbacks
  217 + assert_equal 1, ActionTracker::Record.count
  218 + assert_equal 2.minutes, t.reload.duration
  219 + end
  220 +
  221 + def test_describe
  222 + ActionTracker::Record.delete_all
  223 + 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}}" } }
  224 + ActionTrackerConfig.timeout = 5.minutes
  225 + t = ActionTracker::Record.create! :verb => :some, :user => @mymodel, :params => { :value => "10" }
  226 + assert_equal "Who done this is from class SomeModel and half of its value is 5", t.describe
  227 + end
  228 +
  229 + def test_description
  230 + ActionTrackerConfig.verbs = { :some => { :description => "Got {{it}}" } }
  231 + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some
  232 + assert_equal "Got {{it}}", t.description
  233 + ActionTrackerConfig.verbs = { :some => nil }
  234 + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some
  235 + assert_equal "", t.description
  236 + end
  237 +
  238 + def test_subject
  239 + ActionTrackerConfig.verbs = { :some => { :description => "Some" } }
  240 + u = SomeModel.create!
  241 + t = ActionTracker::Record.create! :verb => :some, :user => u
  242 + assert_equal u, t.subject
  243 + end
  244 +
  245 + def test_predicate
  246 + ActionTrackerConfig.verbs = { :some => { :description => "Some" } }
  247 + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => nil
  248 + assert_equal({}, t.predicate)
  249 + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => { :foo => "bar" }
  250 + assert_equal({ :foo => "bar" }, t.predicate)
  251 + end
  252 +
  253 + def test_phrase
  254 + ActionTrackerConfig.verbs = { :some => { :description => "Some" } }
  255 + u = SomeModel.create!
  256 + t = ActionTracker::Record.create! :verb => :some, :params => { :foo => "bar" }, :user => u
  257 + assert_equal({ :subject => u, :verb => "some", :predicate => { :foo => "bar" }}, t.phrase)
  258 + end
  259 +
  260 + def test_method_missing
  261 + ActionTrackerConfig.verbs = { :some => { :description => "Some" } }
  262 + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => { :foo => "test 1", "bar" => "test 2" }
  263 + assert_nil t.get_test
  264 + assert_equal "test 1", t.get_foo
  265 + assert_equal "test 2", t.get_bar
  266 + assert_raise NoMethodError do
  267 + t.another_unknown_method
  268 + end
  269 + end
  270 +
  271 + def test_collect_group_with_index
  272 + ActionTrackerConfig.verbs = { :some => { :description => "Some" }, :type => :groupable }
  273 + t = ActionTracker::Record.create! :user => SomeModel.create!, :verb => :some, :params => { "test" => ["foo", "bar"] }
  274 + assert_equal(["foo 1", "bar 2"], t.collect_group_with_index(:test){|x, i| "#{x} #{i+1}" })
  275 + end
  276 +
  277 + def test_user_id_is_mandatory
  278 + ActionTrackerConfig.verbs = { :some => { :description => "Some" } }
  279 + ta = ActionTracker::Record.new :user_type => 'SomeModel', :verb => :some
  280 + ta.valid?
  281 + assert ta.errors.on(:user_id)
  282 + assert_raise ActiveRecord::RecordInvalid do
  283 + ta.save!
  284 + end
  285 + end
  286 +
  287 + def test_user_type_is_mandatory
  288 + ActionTrackerConfig.verbs = { :some => { :description => "Some" } }
  289 + ta = ActionTracker::Record.new :user_id => 2, :verb => :some
  290 + ta.valid?
  291 + assert ta.errors.on(:user_type)
  292 + assert_raise ActiveRecord::RecordInvalid do
  293 + ta.save!
  294 + end
  295 + end
  296 +
  297 +end
... ...
vendor/plugins/action_tracker/test/action_tracker_test.rb 0 → 100644
... ... @@ -0,0 +1,559 @@
  1 +require 'test_helper'
  2 +
  3 +ActiveRecord::Base.establish_connection({
  4 + :adapter => "sqlite3",
  5 + :database => ":memory:"
  6 +})
  7 +
  8 +ActiveRecord::Schema.define do
  9 + create_table :some_table, :force => true do |t|
  10 + t.column :some_column, :string
  11 + end
  12 + create_table :other_table, :force => true do |t|
  13 + t.column :other_column, :string
  14 + t.column :another_column, :integer
  15 + end
  16 + create_table :action_tracker do |t|
  17 + t.belongs_to :user, :polymorphic => true
  18 + t.belongs_to :dispatcher, :polymorphic => true
  19 + t.text :params
  20 + t.string :verb
  21 + t.timestamps
  22 + end
  23 +end
  24 +
  25 +class SomeModel < ActiveRecord::Base
  26 + set_table_name :some_table
  27 + acts_as_trackable
  28 +end
  29 +
  30 +class OtherModel < ActiveRecord::Base
  31 + set_table_name :other_table
  32 +end
  33 +
  34 +class ThingsController < ActionController::Base
  35 +
  36 + def index
  37 + params[:foo] = params[:foo].to_i + 1
  38 + render :text => "test"
  39 + end
  40 +
  41 + def test
  42 + render :text => "test"
  43 + end
  44 +
  45 + def current_user
  46 + SomeModel.first || SomeModel.create!
  47 + end
  48 +
  49 + def rescue_action(e)
  50 + raise e
  51 + end
  52 +
  53 +end
  54 +
  55 +ActionController::Routing::Routes.draw { |map| map.resources :things, :collection => { :test => :get } }
  56 +
  57 +class ActionTrackerTest < ActiveSupport::TestCase
  58 +
  59 + def setup
  60 + UserStamp.creator_attribute = :user
  61 + UserStamp.updater_attribute = :user
  62 + ActionTrackerConfig.current_user_method = :current_user
  63 + ActionTracker::Record.delete_all
  64 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } }
  65 + @request = ActionController::TestRequest.new
  66 + @response = ActionController::TestResponse.new
  67 + @controller = ThingsController.new
  68 + end
  69 +
  70 + def test_index
  71 + get :index
  72 + assert_response :success
  73 + end
  74 +
  75 + def test_track_actions_after_runs_after_action
  76 + @controller = create_controller { track_actions_after :some_verb }
  77 + assert_difference 'ActionTracker::Record.count' do
  78 + get :index, :foo => "4"
  79 + end
  80 + assert_equal 5, ActionTracker::Record.first.params[:foo]
  81 + end
  82 +
  83 + def test_track_actions_after_runs_before_action
  84 + @controller = create_controller { track_actions_before :some_verb }
  85 + assert_difference 'ActionTracker::Record.count' do
  86 + get :index, :foo => "4"
  87 + end
  88 + assert_equal "4", ActionTracker::Record.first.params[:foo]
  89 + end
  90 +
  91 + def test_track_actions_default_is_after
  92 + ActionTrackerConfig.default_filter_time = :after
  93 + @controller = create_controller { track_actions :some_verb }
  94 + assert_difference 'ActionTracker::Record.count' do
  95 + get :index, :foo => "4"
  96 + end
  97 + assert_equal 5, ActionTracker::Record.first.params[:foo]
  98 + end
  99 +
  100 + def test_track_actions_default_is_before
  101 + ActionTrackerConfig.default_filter_time = :before
  102 + @controller = create_controller { track_actions :some_verb }
  103 + assert_difference 'ActionTracker::Record.count' do
  104 + get :index, :foo => "4"
  105 + end
  106 + assert_equal "4", ActionTracker::Record.first.params[:foo]
  107 + end
  108 +
  109 + def test_track_actions_executes_block
  110 + @controller = create_controller do
  111 + track_actions :some_verb do
  112 + throw :some_symbol
  113 + end
  114 + end
  115 + assert_difference 'ActionTracker::Record.count' do
  116 + assert_throws :some_symbol do
  117 + get :index, :foo => "4"
  118 + end
  119 + end
  120 + assert_equal "4", ActionTracker::Record.first.params[:foo]
  121 + end
  122 +
  123 + def test_pass_keep_params_as_symbol
  124 + @controller = create_controller { track_actions_before :some_verb, :keep_params => [:foo] }
  125 + assert_difference 'ActionTracker::Record.count' do
  126 + get :index, :foo => 5
  127 + end
  128 + assert_equal({ "foo" => "5" }, ActionTracker::Record.first.params)
  129 + end
  130 +
  131 + def test_pass_keep_params_as_string
  132 + @controller = create_controller { track_actions_before :some_verb, "keep_params" => [:foo, :bar] }
  133 + assert_difference 'ActionTracker::Record.count' do
  134 + get :index, :foo => 5, :bar => 10
  135 + end
  136 + assert_equal({ "foo" => "5", "bar" => "10" }, ActionTracker::Record.first.params)
  137 + end
  138 +
  139 + def test_pass_keep_params_none
  140 + @controller = create_controller { track_actions_before :some_verb, :keep_params => :none }
  141 + assert_difference 'ActionTracker::Record.count' do
  142 + get :index, :foo => 5
  143 + end
  144 + assert_equal({}, ActionTracker::Record.first.params)
  145 + ActionTracker::Record.delete_all
  146 + @controller = create_controller { track_actions_before :some_verb, :keep_params => "none" }
  147 + assert_difference 'ActionTracker::Record.count' do
  148 + get :index, :foo => 5
  149 + end
  150 + assert_equal({}, ActionTracker::Record.first.params)
  151 + end
  152 +
  153 + def test_pass_keep_params_all
  154 + @controller = create_controller { track_actions_before :some_verb, :keep_params => :all }
  155 + assert_difference 'ActionTracker::Record.count' do
  156 + get :index, :foo => 5
  157 + end
  158 + assert_equal({"action"=>"index", "foo"=>"5", "controller"=>"things"}, ActionTracker::Record.first.params)
  159 + ActionTracker::Record.delete_all
  160 + @controller = create_controller { track_actions_before :some_verb, :keep_params => "all" }
  161 + assert_difference 'ActionTracker::Record.count' do
  162 + get :index, :foo => 5
  163 + end
  164 + assert_equal({"action"=>"index", "foo"=>"5", "controller"=>"things"}, ActionTracker::Record.first.params)
  165 + end
  166 +
  167 + def test_keep_params_not_set_should_store_all_params
  168 + @controller = create_controller { track_actions_before :some_verb }
  169 + assert_difference 'ActionTracker::Record.count' do
  170 + get :index, :foo => 5
  171 + end
  172 + assert_equal({"action"=>"index", "foo"=>"5", "controller"=>"things"}, ActionTracker::Record.first.params)
  173 + end
  174 +
  175 + def test_execute_if_some_condition_is_true
  176 + @controller = create_controller { track_actions_before :some_verb, :if => Proc.new { 2 < 1 } }
  177 + assert_no_difference 'ActionTracker::Record.count' do
  178 + get :index, :foo => 5
  179 + end
  180 + @controller = create_controller { track_actions_before :some_verb, :if => Proc.new { 2 > 1 } }
  181 + assert_difference 'ActionTracker::Record.count' do
  182 + get :index, :foo => 5
  183 + end
  184 + end
  185 +
  186 + def test_execute_unless_some_condition_is_true
  187 + @controller = create_controller { track_actions_before :some_verb, :unless => Proc.new { 2 < 1 } }
  188 + assert_difference 'ActionTracker::Record.count' do
  189 + get :index, :foo => 5
  190 + end
  191 + @controller = create_controller { track_actions_before :some_verb, :unless => Proc.new { 2 > 1 } }
  192 + assert_no_difference 'ActionTracker::Record.count' do
  193 + get :index, :foo => 5
  194 + end
  195 + end
  196 +
  197 + def test_execute_for_all_actions
  198 + @controller = create_controller { track_actions_before :some_verb }
  199 + assert_difference 'ActionTracker::Record.count' do
  200 + get :index
  201 + end
  202 + assert_difference 'ActionTracker::Record.count' do
  203 + get :test
  204 + end
  205 + end
  206 +
  207 + def test_execute_only_for_some_action
  208 + @controller = create_controller { track_actions_before :some_verb, :only => [:index] }
  209 + assert_difference 'ActionTracker::Record.count' do
  210 + get :index
  211 + end
  212 + assert_no_difference 'ActionTracker::Record.count' do
  213 + get :test
  214 + end
  215 + end
  216 +
  217 + def test_execute_except_for_some_action
  218 + @controller = create_controller { track_actions_before :some_verb, :except => [:index] }
  219 + assert_no_difference 'ActionTracker::Record.count' do
  220 + get :index
  221 + end
  222 + assert_difference 'ActionTracker::Record.count' do
  223 + get :test
  224 + end
  225 + end
  226 +
  227 + def test_store_user
  228 + @controller = create_controller do
  229 + track_actions_before :some_verb
  230 + def current_user
  231 + SomeModel.create! :some_column => "test"
  232 + end
  233 + end
  234 + assert_difference 'ActionTracker::Record.count' do
  235 + get :test
  236 + end
  237 + assert_equal "test", ActionTracker::Record.last.user.some_column
  238 + end
  239 +
  240 + def test_should_update_when_verb_is_updatable_and_no_timeout
  241 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :updatable } }
  242 + ActionTrackerConfig.timeout = 5.minutes
  243 + @controller = create_controller { track_actions_before :some_verb }
  244 + assert ActionTrackerConfig.verb_type(:some_verb) == :updatable
  245 + assert_difference 'ActionTracker::Record.count' do
  246 + get :test
  247 + end
  248 + t = ActionTracker::Record.last
  249 + t.updated_at = t.updated_at.ago(2.minutes)
  250 + t.send :update_without_callbacks
  251 + assert_no_difference 'ActionTracker::Record.count' do
  252 + get :test
  253 + end
  254 + end
  255 +
  256 + def test_should_create_when_verb_is_updatable_and_timeout
  257 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :updatable } }
  258 + ActionTrackerConfig.timeout = 5.minutes
  259 + @controller = create_controller { track_actions_before :some_verb }
  260 + assert ActionTrackerConfig.verb_type(:some_verb) == :updatable
  261 + assert_difference 'ActionTracker::Record.count' do
  262 + get :test
  263 + end
  264 + t = ActionTracker::Record.last
  265 + t.updated_at = t.updated_at.ago(6.minutes)
  266 + t.send :update_without_callbacks
  267 + assert_difference 'ActionTracker::Record.count' do
  268 + get :test
  269 + end
  270 + end
  271 +
  272 + def test_should_update_when_verb_is_groupable_and_no_timeout
  273 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :groupable } }
  274 + ActionTrackerConfig.timeout = 5.minutes
  275 + @controller = create_controller { track_actions_before :some_verb, :keep_params => [:foo] }
  276 + assert ActionTrackerConfig.verb_type(:some_verb) == :groupable
  277 + assert_difference 'ActionTracker::Record.count' do
  278 + get :test, :foo => "bar"
  279 + end
  280 + t = ActionTracker::Record.last
  281 + t.updated_at = t.updated_at.ago(2.minutes)
  282 + t.send :update_without_callbacks
  283 + assert_no_difference 'ActionTracker::Record.count' do
  284 + get :test, :foo => "test"
  285 + end
  286 + end
  287 +
  288 + def test_should_create_when_verb_is_groupable_and_timeout
  289 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :groupable } }
  290 + ActionTrackerConfig.timeout = 5.minutes
  291 + @controller = create_controller { track_actions_before :some_verb, :keep_params => [:foo] }
  292 + assert ActionTrackerConfig.verb_type(:some_verb) == :groupable
  293 + assert_difference 'ActionTracker::Record.count' do
  294 + get :test, :foo => "bar"
  295 + end
  296 + t = ActionTracker::Record.last
  297 + t.updated_at = t.updated_at.ago(6.minutes)
  298 + t.send :update_without_callbacks
  299 + assert_difference 'ActionTracker::Record.count' do
  300 + get :test, :foo => "test"
  301 + end
  302 + end
  303 +
  304 + def test_should_create_when_verb_is_single
  305 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something", :type => :single } }
  306 + @controller = create_controller { track_actions_before :some_verb }
  307 + assert ActionTrackerConfig.verb_type(:some_verb) == :single
  308 + assert_difference 'ActionTracker::Record.count' do
  309 + get :test
  310 + end
  311 + assert_difference 'ActionTracker::Record.count' do
  312 + get :test
  313 + end
  314 + end
  315 +
  316 + def test_should_act_as_trackable
  317 + m = SomeModel.create!
  318 + assert m.respond_to?(:tracked_actions)
  319 + assert_kind_of Array, m.tracked_actions
  320 + @controller = create_controller { track_actions_before :some_verb }
  321 + @controller.stubs(:current_user).returns(m)
  322 + get :index
  323 + sleep 2
  324 + get :test
  325 + assert ActionTracker::Record.last.updated_at > ActionTracker::Record.first.updated_at
  326 + assert_equal [ActionTracker::Record.last, ActionTracker::Record.first], m.reload.tracked_actions
  327 + end
  328 +
  329 + def test_should_get_time_spent_doing_something
  330 + ActionTrackerConfig.verbs = { :some_verb => { :type => :updatable }, :other_verb => { :type => :updatable } }
  331 + m = SomeModel.create!
  332 + @controller = create_controller do
  333 + track_actions :some_verb
  334 + end
  335 + @controller.stubs(:current_user).returns(m)
  336 + get :index
  337 + t1 = ActionTracker::Record.last
  338 + t1.updated_at = t1.updated_at.ago(4.hours)
  339 + t1.created_at = t1.updated_at.ago(3.hours)
  340 + t1.send :update_without_callbacks
  341 + get :test
  342 + t2 = ActionTracker::Record.last
  343 + t2.updated_at = t2.updated_at.ago(3.hours)
  344 + t2.created_at = t2.updated_at.ago(2.hours)
  345 + t2.send :update_without_callbacks
  346 + assert_equal 5.hours, m.time_spent_doing(:some_verb)
  347 + assert_equal 3.hours, m.time_spent_doing(:some_verb, :id => t1.id)
  348 + assert_equal 0.0, m.time_spent_doing(:other_verb)
  349 + assert_equal 0.0, m.time_spent_doing(:other_verb, :verb => :some_verb)
  350 + end
  351 +
  352 + def test_helper_describe_action_tracker_object
  353 + ActionTrackerConfig.verbs = { :some_verb => { :description => "Hey, {{link_to 'click here', :controller => :things}} {{ta.user.some_column}}!" } }
  354 + view = ActionView::Base.new
  355 + view.controller = @controller
  356 + @request.env["HTTP_REFERER"] = "http://test.com"
  357 + get :index
  358 + user = SomeModel.create! :some_column => "test"
  359 + t = ActionTracker::Record.create! :verb => :some_verb, :user => user
  360 + assert_equal 'Hey, <a href="/things">click here</a> test!', view.describe(t)
  361 + end
  362 +
  363 + def test_helper_describe_non_action_tracker_object
  364 + view = ActionView::Base.new
  365 + view.controller = @controller
  366 + @request.env["HTTP_REFERER"] = "http://test.com"
  367 + get :index
  368 + assert_equal "", view.describe("Something")
  369 + end
  370 +
  371 + def test_track_actions_store_user
  372 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  373 + model = create_model do
  374 + track_actions :test, :after_create
  375 + end
  376 + @controller = create_controller_for_model(model)
  377 + assert_difference 'ActionTracker::Record.count' do
  378 + get :test
  379 + end
  380 + assert_kind_of SomeModel, ActionTracker::Record.last.user
  381 + assert_equal "test", ActionTracker::Record.last.user.some_column
  382 + end
  383 +
  384 + def test_track_actions_store_some_params
  385 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  386 + model = create_model do
  387 + track_actions :test, :after_create, :keep_params => [:other_column]
  388 + end
  389 + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2)
  390 + assert_difference 'ActionTracker::Record.count' do
  391 + get :test
  392 + end
  393 + assert_equal "foo", ActionTracker::Record.last.params["other_column"]
  394 + assert_nil ActionTracker::Record.last.params["another_column"]
  395 + end
  396 +
  397 + def test_replace_dots_by_underline_in_param_name
  398 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  399 + model = create_model do
  400 + track_actions :test, :after_create, :keep_params => ["other_column.size", :another_column]
  401 + end
  402 + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 5)
  403 + assert_difference 'ActionTracker::Record.count' do
  404 + get :test
  405 + end
  406 + assert_equal 3, ActionTracker::Record.last.params["other_column_size"]
  407 + assert_equal 5, ActionTracker::Record.last.params["another_column"]
  408 + end
  409 +
  410 + def test_track_actions_store_all_params
  411 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  412 + model = create_model do
  413 + track_actions :test, :after_create, :keep_params => :all
  414 + end
  415 + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2)
  416 + assert_difference 'ActionTracker::Record.count' do
  417 + get :test
  418 + end
  419 + assert_equal "foo", ActionTracker::Record.last.params["other_column"]
  420 + assert_equal 2, ActionTracker::Record.last.params["another_column"]
  421 + end
  422 +
  423 + def test_track_actions_store_all_params_by_default
  424 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  425 + model = create_model do
  426 + track_actions :test, :after_create
  427 + end
  428 + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2)
  429 + assert_difference 'ActionTracker::Record.count' do
  430 + get :test
  431 + end
  432 + assert_equal "foo", ActionTracker::Record.last.params["other_column"]
  433 + assert_equal 2, ActionTracker::Record.last.params["another_column"]
  434 + end
  435 +
  436 + def test_track_actions_store_no_params
  437 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  438 + model = create_model do
  439 + track_actions :test, :after_create, :keep_params => :none
  440 + end
  441 + @controller = create_controller_for_model(model, :other_column => "foo", :another_column => 2)
  442 + assert_difference 'ActionTracker::Record.count' do
  443 + get :test
  444 + end
  445 + assert_nil ActionTracker::Record.last.params["other_column"]
  446 + assert_nil ActionTracker::Record.last.params["another_column"]
  447 + end
  448 +
  449 + def test_track_actions_with_options
  450 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  451 + model = create_model { track_actions :test, :after_create, :keep_params => :all, :if => Proc.new { 2 > 1 } }
  452 + @controller = create_controller_for_model(model)
  453 + assert_difference('ActionTracker::Record.count') { get :test }
  454 +
  455 + model = create_model { track_actions :test, :after_create, :keep_params => :all, :if => Proc.new { 2 < 1 } }
  456 + @controller = create_controller_for_model(model)
  457 + assert_no_difference('ActionTracker::Record.count') { get :test }
  458 +
  459 + model = create_model { track_actions :test, :after_create, :keep_params => :all, :unless => Proc.new { 2 > 1 } }
  460 + @controller = create_controller_for_model(model)
  461 + assert_no_difference('ActionTracker::Record.count') { get :test }
  462 +
  463 + model = create_model { track_actions :test, :after_create, :keep_params => :all, :unless => Proc.new { 2 < 1 } }
  464 + @controller = create_controller_for_model(model)
  465 + assert_difference('ActionTracker::Record.count') { get :test }
  466 + end
  467 +
  468 + def test_track_actions_post_processing_as_symbol
  469 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  470 + model = create_model do
  471 + track_actions :test, :after_create, :post_processing => Proc.new { |ta| OtherModel.create!(:other_column => ta.verb) }
  472 + end
  473 + @controller = create_controller_for_model(model, :another_column => 2)
  474 + assert_difference 'ActionTracker::Record.count' do
  475 + assert_difference('OtherModel.count', 2) do
  476 + get :test
  477 + end
  478 + end
  479 + assert_equal "test", OtherModel.last.other_column
  480 + end
  481 +
  482 + def test_track_actions_post_processing_as_string
  483 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  484 + model = create_model do
  485 + track_actions :test, :after_create, "post_processing" => Proc.new { |ta| OtherModel.create!(:other_column => ta.verb) }
  486 + end
  487 + @controller = create_controller_for_model(model, :another_column => 2)
  488 + assert_difference 'ActionTracker::Record.count' do
  489 + assert_difference('OtherModel.count', 2) do
  490 + get :test
  491 + end
  492 + end
  493 + assert_equal "test", OtherModel.last.other_column
  494 + end
  495 +
  496 + def test_acts_as_trackable_with_options
  497 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  498 + @@action = create_model do
  499 + track_actions :test, :after_create
  500 + end
  501 + @@user = create_model do
  502 + acts_as_trackable :after_add => Proc.new { |x, y| raise 'I was called' }
  503 + end
  504 + @controller = create_controller do
  505 + def test
  506 + @@action.create!
  507 + render :text => "test"
  508 + end
  509 + def current_user
  510 + @@user.create!
  511 + end
  512 + end
  513 + assert_raise(RuntimeError, 'I was called') do
  514 + get :test
  515 + end
  516 + end
  517 +
  518 + def test_track_actions_save_dispatcher
  519 + ActionTrackerConfig.verbs = { :test => { :description => "Some" } }
  520 + model = create_model do
  521 + track_actions :test, :after_create
  522 + end
  523 + @controller = create_controller_for_model(model)
  524 + assert_difference 'ActionTracker::Record.count' do
  525 + get :test
  526 + end
  527 + assert_kind_of model.base_class, ActionTracker::Record.last.dispatcher
  528 + end
  529 +
  530 + private
  531 +
  532 + def create_controller(&block)
  533 + klass = Class.new(ThingsController)
  534 + klass.module_eval &block
  535 + klass.stubs(:controller_path).returns('things')
  536 + klass.new
  537 + end
  538 +
  539 + def create_model(&block)
  540 + klass = Class.new(OtherModel)
  541 + klass.module_eval &block
  542 + klass
  543 + end
  544 +
  545 + def create_controller_for_model(model, attributes = {})
  546 + @@model, @@attributes = model, attributes
  547 + create_controller do
  548 + def test
  549 + @@model.create! @@attributes
  550 + render :text => "test"
  551 + end
  552 +
  553 + def current_user
  554 + SomeModel.create! :some_column => "test"
  555 + end
  556 + end
  557 + end
  558 +
  559 +end
... ...
vendor/plugins/action_tracker/test/test_helper.rb 0 → 100755
... ... @@ -0,0 +1,5 @@
  1 +ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
  2 +require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
  3 +
  4 +ENV["RAILS_ENV"] = "test"
  5 +require 'test_help'
... ...
vendor/plugins/action_tracker/uninstall.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +# Uninstall hook code here
... ...
vendor/plugins/user_stamp/MIT-LICENSE 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +Copyright (c) 2008 [John Nunemaker]
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
vendor/plugins/user_stamp/README 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 += UserStamp
  2 +
  3 +Rails plugin that makes stamping records with a user when they are
  4 +created and updated dirt simple. It assumes that your controller has
  5 +a current_user method. It also assumes that any record being stamped
  6 +has two attributes--creator_id and updater_id. You can override both
  7 +of these assumptions easily.
  8 +
  9 +== Setup
  10 +
  11 +1. script/plugin install git://github.com/jnunemaker/user_stamp.git
  12 +2. Add user_stamp to application.rb, like the following:
  13 +
  14 + class ApplicationController < ActionController::Base
  15 + user_stamp Post, Asset, Job
  16 + end
  17 +
  18 +
  19 +== Defaults
  20 +
  21 + UserStamp.creator_attribute = :creator_id
  22 + UserStamp.updater_attribute = :updater_id
  23 + UserStamp.current_user_method = :current_user
  24 +
  25 +If your user stamped columns and current_user method are different,
  26 +just create an initializer such as config/initializers/user_stamp.rb
  27 +and copy and paste the defaults above, changing them to fit your app.
  28 +
  29 +== Problems?
  30 +
  31 +Use the issue tracker on Github.
  32 +
  33 +== Docs
  34 +
  35 +http://rdoc.info/projects/jnunemaker/user_stamp
  36 +
  37 +Copyright (c) 2008 [John Nunemaker], released under the MIT license
... ...
vendor/plugins/user_stamp/Rakefile 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +require 'rake'
  2 +require 'spec/rake/spectask'
  3 +
  4 +desc 'Default: run specs.'
  5 +task :default => :spec
  6 +
  7 +desc 'Run the specs'
  8 +Spec::Rake::SpecTask.new(:spec) do |t|
  9 + t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
  10 + t.spec_files = FileList['spec/**/*_spec.rb']
  11 +end
0 12 \ No newline at end of file
... ...
vendor/plugins/user_stamp/init.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +require 'user_stamp'
  2 +
  3 +class ActionController::Base
  4 + extend UserStamp::ClassMethods
  5 +end
... ...
vendor/plugins/user_stamp/install.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +instructions = <<EOF
  2 +
  3 +#{'*' * 62}
  4 +Don't forget to add user stamp to your application controller.
  5 +
  6 + class ApplicationController < ActionController::Base
  7 + user_stamp Post, Asset, Job
  8 + end
  9 +
  10 +View the README for more information.
  11 +#{'*' * 62}
  12 +
  13 +EOF
  14 +
  15 +puts instructions
0 16 \ No newline at end of file
... ...
vendor/plugins/user_stamp/lib/user_stamp.rb 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +module UserStamp
  2 + mattr_accessor :creator_attribute
  3 + mattr_accessor :updater_attribute
  4 + mattr_accessor :current_user_method
  5 +
  6 + def self.creator_assignment_method
  7 + "#{UserStamp.creator_attribute}="
  8 + end
  9 +
  10 + def self.updater_assignment_method
  11 + "#{UserStamp.updater_attribute}="
  12 + end
  13 +
  14 + module ClassMethods
  15 + def user_stamp(*models)
  16 + models.each { |klass| klass.add_observer(UserStampSweeper.instance) }
  17 +
  18 + class_eval do
  19 + cache_sweeper :user_stamp_sweeper
  20 + end
  21 + end
  22 + end
  23 +end
  24 +
  25 +UserStamp.creator_attribute = :creator_id
  26 +UserStamp.updater_attribute = :updater_id
  27 +UserStamp.current_user_method = :current_user
  28 +
  29 +class UserStampSweeper < ActionController::Caching::Sweeper
  30 + def before_validation(record)
  31 + return unless current_user
  32 +
  33 + if record.respond_to?(UserStamp.creator_assignment_method) && record.new_record?
  34 + record.send(UserStamp.creator_assignment_method, current_user)
  35 + end
  36 +
  37 + if record.respond_to?(UserStamp.updater_assignment_method)
  38 + record.send(UserStamp.updater_assignment_method, current_user)
  39 + end
  40 + end
  41 +
  42 + private
  43 + def current_user
  44 + if controller.respond_to?(UserStamp.current_user_method)
  45 + controller.send UserStamp.current_user_method
  46 + end
  47 + end
  48 +end
... ...
vendor/plugins/user_stamp/spec/spec_helper.rb 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +require 'rubygems'
  2 +
  3 +gem 'rspec'
  4 +require 'spec'
  5 +
  6 +%w[activesupport activerecord actionpack].each do |lib|
  7 + gem lib
  8 + require lib
  9 +end
  10 +
  11 +require 'action_controller'
  12 +
  13 +$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
  14 +require 'user_stamp'
  15 +
  16 +UserStampSweeper.instance
  17 +
  18 +class User
  19 + attr_accessor :id
  20 +
  21 + def initialize(id);
  22 + @id = id
  23 + end
  24 +end
... ...
vendor/plugins/user_stamp/spec/user_stamp_class_methods_spec.rb 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +require File.dirname(__FILE__) + '/spec_helper'
  2 +
  3 +class FauxModelBase
  4 + def self.add_observer(observer_instance); end
  5 +end
  6 +
  7 +class Post < FauxModelBase; end
  8 +class Category < FauxModelBase; end
  9 +class Label < FauxModelBase; end
  10 +
  11 +class FauxApplicationController
  12 + def self.cache_sweeper(sweepers); end
  13 +
  14 + def self.current_user
  15 + User.new(220)
  16 + end
  17 +end
  18 +
  19 +class PostsController < FauxApplicationController
  20 + extend UserStamp::ClassMethods
  21 +end
  22 +
  23 +describe UserStamp::ClassMethods do
  24 + before do
  25 + UserStamp.creator_attribute = :creator_id
  26 + UserStamp.updater_attribute = :updater_id
  27 + UserStamp.current_user_method = :current_user
  28 + end
  29 +
  30 + it "should add user_stamp method" do
  31 + PostsController.respond_to?(:user_stamp).should == true
  32 + end
  33 +
  34 + def user_stamp
  35 + PostsController.user_stamp Post, Category, Label
  36 + end
  37 +
  38 + describe "#user_stamp" do
  39 + it "should add UserStampSweeper as observer for each model" do
  40 + [Post, Category, Label].each do |klass|
  41 + klass.should_receive(:add_observer).with(UserStampSweeper.instance).once
  42 + end
  43 + user_stamp
  44 + end
  45 +
  46 + it "should setup cache sweeper for controller" do
  47 + PostsController.should_receive(:cache_sweeper).with(:user_stamp_sweeper).once
  48 + user_stamp
  49 + end
  50 + end
  51 +end
0 52 \ No newline at end of file
... ...
vendor/plugins/user_stamp/spec/user_stamp_spec.rb 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +require File.dirname(__FILE__) + '/spec_helper'
  2 +
  3 +describe UserStamp do
  4 + before do
  5 + UserStamp.creator_attribute = :creator_id
  6 + UserStamp.updater_attribute = :updater_id
  7 + UserStamp.current_user_method = :current_user
  8 + end
  9 +
  10 + it "should default creator_attribute to creator_id" do
  11 + UserStamp.creator_attribute.should == :creator_id
  12 + end
  13 +
  14 + it "should default updater_attribute to updater_id" do
  15 + UserStamp.updater_attribute.should == :updater_id
  16 + end
  17 +
  18 + it "should default current_user_method to current_user" do
  19 + UserStamp.current_user_method.should == :current_user
  20 + end
  21 +
  22 + it "should have accessor for creator_attribute" do
  23 + UserStamp.creator_attribute = 'mofo_id'
  24 + UserStamp.creator_attribute.should == 'mofo_id'
  25 + end
  26 +
  27 + it "should have accessor for updater_attribute" do
  28 + UserStamp.updater_attribute = 'mofo_id'
  29 + UserStamp.updater_attribute.should == 'mofo_id'
  30 + end
  31 +
  32 + it "should have accessor for current_user_method" do
  33 + UserStamp.current_user_method = 'my_current_user'
  34 + UserStamp.current_user_method.should == 'my_current_user'
  35 + end
  36 +
  37 + describe "assignment methods" do
  38 + before do
  39 + UserStamp.creator_attribute = 'creator_mofo_id'
  40 + UserStamp.updater_attribute = 'updater_mofo_id'
  41 + end
  42 +
  43 + it "should include creator assignment method" do
  44 + UserStamp.creator_assignment_method.should == 'creator_mofo_id='
  45 + end
  46 +
  47 + it "should include updater assignment method" do
  48 + UserStamp.updater_assignment_method.should == 'updater_mofo_id='
  49 + end
  50 + end
  51 +end
0 52 \ No newline at end of file
... ...
vendor/plugins/user_stamp/spec/user_stamp_sweeper_spec.rb 0 → 100644
... ... @@ -0,0 +1,161 @@
  1 +require File.dirname(__FILE__) + '/spec_helper'
  2 +
  3 +class PostsController
  4 + def self.current_user
  5 + User.new(220)
  6 + end
  7 +end
  8 +
  9 +describe UserStampSweeper, "#before_validation" do
  10 + before do
  11 + UserStamp.creator_attribute = :creator_id
  12 + UserStamp.updater_attribute = :updater_id
  13 + UserStamp.current_user_method = :current_user
  14 + @sweeper = UserStampSweeper.instance
  15 + @sweeper.stub!(:controller).and_return(PostsController)
  16 + end
  17 +
  18 + describe "(with new record)" do
  19 + it "should set creator_id if attribute exists" do
  20 + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => true)
  21 + record.should_receive(:creator_id=).with(220).once
  22 + @sweeper.before_validation(record)
  23 + end
  24 +
  25 + it "should NOT set creator_id if attribute does not exist" do
  26 + record = mock('Record', :new_record? => true, :updater_id= => nil, :respond_to? => false)
  27 + record.should_receive(:respond_to?).with("creator_id=").and_return(false)
  28 + record.should_not_receive(:creator_id=)
  29 + @sweeper.before_validation(record)
  30 + end
  31 + end
  32 +
  33 + describe "(with non new record)" do
  34 + it "should NOT set creator_id if attribute exists" do
  35 + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => false)
  36 + record.should_not_receive(:creator_id=)
  37 + @sweeper.before_validation(record)
  38 + end
  39 +
  40 + it "should NOT set creator_id if attribute does not exist" do
  41 + record = mock('Record', :updater_id= => nil, :new_record? => false)
  42 + record.should_not_receive(:creator_id=)
  43 + @sweeper.before_validation(record)
  44 + end
  45 + end
  46 +
  47 + it "should set updater_id if attribute exists" do
  48 + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => :false)
  49 + record.should_receive(:updater_id=)
  50 + @sweeper.before_validation(record)
  51 + end
  52 +
  53 + it "should NOT set updater_id if attribute does not exist" do
  54 + record = mock('Record', :creator_id= => nil, :updater_id= => nil, :new_record? => :false, :respond_to? => false)
  55 + record.should_receive(:respond_to?).with("updater_id=").and_return(false)
  56 + record.should_not_receive(:updater_id=)
  57 + @sweeper.before_validation(record)
  58 + end
  59 +end
  60 +
  61 +describe UserStampSweeper, "#before_validation (with custom attribute names)" do
  62 + before do
  63 + UserStamp.creator_attribute = :created_by
  64 + UserStamp.updater_attribute = :updated_by
  65 + UserStamp.current_user_method = :current_user
  66 + @sweeper = UserStampSweeper.instance
  67 + @sweeper.stub!(:controller).and_return(PostsController)
  68 + end
  69 +
  70 + describe "(with new record)" do
  71 + it "should set created_by if attribute exists" do
  72 + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => true)
  73 + record.should_receive(:created_by=).with(220).once
  74 + @sweeper.before_validation(record)
  75 + end
  76 +
  77 + it "should NOT set created_by if attribute does not exist" do
  78 + record = mock('Record', :new_record? => true, :updated_by= => nil, :respond_to? => false)
  79 + record.should_receive(:respond_to?).with("created_by=").and_return(false)
  80 + record.should_not_receive(:created_by=)
  81 + @sweeper.before_validation(record)
  82 + end
  83 + end
  84 +
  85 + describe "(with non new record)" do
  86 + it "should NOT set created_by if attribute exists" do
  87 + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => false)
  88 + record.should_not_receive(:created_by=)
  89 + @sweeper.before_validation(record)
  90 + end
  91 +
  92 + it "should NOT set created_by if attribute does not exist" do
  93 + record = mock('Record', :updated_by= => nil, :new_record? => false)
  94 + record.should_not_receive(:created_by=)
  95 + @sweeper.before_validation(record)
  96 + end
  97 + end
  98 +
  99 + it "should set updated_by if attribute exists" do
  100 + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => :false)
  101 + record.should_receive(:updated_by=)
  102 + @sweeper.before_validation(record)
  103 + end
  104 +
  105 + it "should NOT set updated_by if attribute does not exist" do
  106 + record = mock('Record', :created_by= => nil, :updated_by= => nil, :new_record? => :false, :respond_to? => false)
  107 + record.should_receive(:respond_to?).with("updated_by=").and_return(false)
  108 + record.should_not_receive(:updated_by=)
  109 + @sweeper.before_validation(record)
  110 + end
  111 +end
  112 +
  113 +describe UserStampSweeper, "#current_user" do
  114 + before do
  115 + UserStamp.creator_attribute = :creator_id
  116 + UserStamp.updater_attribute = :updater_id
  117 + UserStamp.current_user_method = :current_user
  118 + @sweeper = UserStampSweeper.instance
  119 + end
  120 +
  121 + it "should send current_user if controller responds to it" do
  122 + user = mock('User')
  123 + controller = mock('Controller', :current_user => user)
  124 + @sweeper.stub!(:controller).and_return(controller)
  125 + controller.should_receive(:current_user)
  126 + @sweeper.send(:current_user)
  127 + end
  128 +
  129 + it "should not send current_user if controller does not respond to it" do
  130 + user = mock('User')
  131 + controller = mock('Controller', :respond_to? => false)
  132 + @sweeper.stub!(:controller).and_return(controller)
  133 + controller.should_not_receive(:current_user)
  134 + @sweeper.send(:current_user)
  135 + end
  136 +end
  137 +
  138 +describe UserStampSweeper, "#current_user (with custom current_user_method)" do
  139 + before do
  140 + UserStamp.creator_attribute = :creator_id
  141 + UserStamp.updater_attribute = :updater_id
  142 + UserStamp.current_user_method = :my_user
  143 + @sweeper = UserStampSweeper.instance
  144 + end
  145 +
  146 + it "should send current_user if controller responds to it" do
  147 + user = mock('User')
  148 + controller = mock('Controller', :my_user => user)
  149 + @sweeper.stub!(:controller).and_return(controller)
  150 + controller.should_receive(:my_user)
  151 + @sweeper.send(:current_user)
  152 + end
  153 +
  154 + it "should not send current_user if controller does not respond to it" do
  155 + user = mock('User')
  156 + controller = mock('Controller', :respond_to? => false)
  157 + @sweeper.stub!(:controller).and_return(controller)
  158 + controller.should_not_receive(:my_user)
  159 + @sweeper.send(:current_user)
  160 + end
  161 +end
0 162 \ No newline at end of file
... ...