Commit 59338e3fc78861c43612529dce4f979af6112a9e
Committed by
Antonio Terceiro
1 parent
6fc29038
Exists in
master
and in
29 other branches
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 | ... | ... |
... | ... | @@ -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 < 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 'hpricot' |
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 < 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 < 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 < 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 < 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 < 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 | ... | ... |
... | ... | @@ -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 < 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 | ... | ... |
... | ... | @@ -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/_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> | ... | ... |
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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
289 Bytes
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%; } | ... | ... |
... | ... | @@ -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 < 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 'hpricot' |
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 | ... | ... |
... | ... | @@ -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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 < 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 < 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 < 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 < 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 | ... | ... |
... | ... | @@ -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 < 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 < 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 | ... | ... |
... | ... | @@ -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. | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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
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 | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +# Install hook code here | ... | ... |
... | ... | @@ -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
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 | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +# Uninstall hook code here | ... | ... |
... | ... | @@ -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. | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |