##// END OF EJS Templates
Makes user unwatch what he can no longer view after its permissions have changed (#3589)....
Jean-Philippe Lang -
r3053:f33231181f95
parent child
Show More
@@ -0,0 +1,9
1 desc 'Removes watchers from what they can no longer view.'
2
3 namespace :redmine do
4 namespace :watchers do
5 task :prune => :environment do
6 Watcher.prune
7 end
8 end
9 end
@@ -1,46 +1,50
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Board < ActiveRecord::Base
19 19 belongs_to :project
20 20 has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC"
21 21 has_many :messages, :dependent => :delete_all, :order => "#{Message.table_name}.created_on DESC"
22 22 belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id
23 23 acts_as_list :scope => :project_id
24 24 acts_as_watchable
25 25
26 26 validates_presence_of :name, :description
27 27 validates_length_of :name, :maximum => 30
28 28 validates_length_of :description, :maximum => 255
29 29
30 def visible?(user=User.current)
31 !user.nil? && user.allowed_to?(:view_messages, project)
32 end
33
30 34 def to_s
31 35 name
32 36 end
33 37
34 38 def reset_counters!
35 39 self.class.reset_counters!(id)
36 40 end
37 41
38 42 # Updates topics_count, messages_count and last_message_id attributes for +board_id+
39 43 def self.reset_counters!(board_id)
40 44 board_id = board_id.to_i
41 45 update_all("topics_count = (SELECT COUNT(*) FROM #{Message.table_name} WHERE board_id=#{board_id} AND parent_id IS NULL)," +
42 46 " messages_count = (SELECT COUNT(*) FROM #{Message.table_name} WHERE board_id=#{board_id})," +
43 47 " last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=#{board_id})",
44 48 ["id = ?", board_id])
45 49 end
46 50 end
@@ -1,66 +1,81
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Member < ActiveRecord::Base
19 19 belongs_to :user
20 20 belongs_to :principal, :foreign_key => 'user_id'
21 21 has_many :member_roles, :dependent => :destroy
22 22 has_many :roles, :through => :member_roles
23 23 belongs_to :project
24 24
25 25 validates_presence_of :principal, :project
26 26 validates_uniqueness_of :user_id, :scope => :project_id
27 27
28 after_destroy :unwatch_from_permission_change
29
28 30 def name
29 31 self.user.name
30 32 end
31 33
32 34 alias :base_role_ids= :role_ids=
33 35 def role_ids=(arg)
34 36 ids = (arg || []).collect(&:to_i) - [0]
35 37 # Keep inherited roles
36 38 ids += member_roles.select {|mr| !mr.inherited_from.nil?}.collect(&:role_id)
37 39
38 40 new_role_ids = ids - role_ids
39 41 # Add new roles
40 42 new_role_ids.each {|id| member_roles << MemberRole.new(:role_id => id) }
41 43 # Remove roles (Rails' #role_ids= will not trigger MemberRole#on_destroy)
42 member_roles.select {|mr| !ids.include?(mr.role_id)}.each(&:destroy)
44 member_roles_to_destroy = member_roles.select {|mr| !ids.include?(mr.role_id)}
45 if member_roles_to_destroy.any?
46 member_roles_to_destroy.each(&:destroy)
47 unwatch_from_permission_change
48 end
43 49 end
44 50
45 51 def <=>(member)
46 52 a, b = roles.sort.first, member.roles.sort.first
47 53 a == b ? (principal <=> member.principal) : (a <=> b)
48 54 end
49 55
50 56 def deletable?
51 57 member_roles.detect {|mr| mr.inherited_from}.nil?
52 58 end
53 59
54 60 def before_destroy
55 61 if user
56 62 # remove category based auto assignments for this member
57 63 IssueCategory.update_all "assigned_to_id = NULL", ["project_id = ? AND assigned_to_id = ?", project.id, user.id]
58 64 end
59 65 end
60 66
61 67 protected
62 68
63 69 def validate
64 70 errors.add_to_base "Role can't be blank" if member_roles.empty? && roles.empty?
65 71 end
72
73 private
74
75 # Unwatch things that the user is no longer allowed to view inside project
76 def unwatch_from_permission_change
77 if user
78 Watcher.prune(:user => user, :project => project)
79 end
80 end
66 81 end
@@ -1,54 +1,59
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class MemberRole < ActiveRecord::Base
19 19 belongs_to :member
20 20 belongs_to :role
21 21
22 22 after_destroy :remove_member_if_empty
23 23
24 24 after_create :add_role_to_group_users
25 25 after_destroy :remove_role_from_group_users
26 26
27 27 validates_presence_of :role
28 28
29 29 def validate
30 30 errors.add :role_id, :invalid if role && !role.member?
31 31 end
32 32
33 33 private
34 34
35 35 def remove_member_if_empty
36 36 if member.roles.empty?
37 37 member.destroy
38 38 end
39 39 end
40 40
41 41 def add_role_to_group_users
42 42 if member.principal.is_a?(Group)
43 43 member.principal.users.each do |user|
44 44 user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id)
45 45 user_member.member_roles << MemberRole.new(:role => role, :inherited_from => id)
46 46 user_member.save!
47 47 end
48 48 end
49 49 end
50 50
51 51 def remove_role_from_group_users
52 MemberRole.find(:all, :conditions => { :inherited_from => id }).each(&:destroy)
52 MemberRole.find(:all, :conditions => { :inherited_from => id }).group_by(&:member).each do |member, member_roles|
53 member_roles.each(&:destroy)
54 if member && member.user
55 Watcher.prune(:user => member.user, :project => member.project)
56 end
57 end
53 58 end
54 59 end
@@ -1,94 +1,98
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Message < ActiveRecord::Base
19 19 belongs_to :board
20 20 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
21 21 acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
22 22 acts_as_attachable
23 23 belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
24 24
25 25 acts_as_searchable :columns => ['subject', 'content'],
26 26 :include => {:board => :project},
27 27 :project_key => 'project_id',
28 28 :date_column => "#{table_name}.created_on"
29 29 acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
30 30 :description => :content,
31 31 :type => Proc.new {|o| o.parent_id.nil? ? 'message' : 'reply'},
32 32 :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
33 33 {:id => o.parent_id, :anchor => "message-#{o.id}"})}
34 34
35 35 acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
36 36 :author_key => :author_id
37 37 acts_as_watchable
38 38
39 39 attr_protected :locked, :sticky
40 40 validates_presence_of :board, :subject, :content
41 41 validates_length_of :subject, :maximum => 255
42 42
43 43 after_create :add_author_as_watcher
44 44
45 def visible?(user=User.current)
46 !user.nil? && user.allowed_to?(:view_messages, project)
47 end
48
45 49 def validate_on_create
46 50 # Can not reply to a locked topic
47 51 errors.add_to_base 'Topic is locked' if root.locked? && self != root
48 52 end
49 53
50 54 def after_create
51 55 if parent
52 56 parent.reload.update_attribute(:last_reply_id, self.id)
53 57 end
54 58 board.reset_counters!
55 59 end
56 60
57 61 def after_update
58 62 if board_id_changed?
59 63 Message.update_all("board_id = #{board_id}", ["id = ? OR parent_id = ?", root.id, root.id])
60 64 Board.reset_counters!(board_id_was)
61 65 Board.reset_counters!(board_id)
62 66 end
63 67 end
64 68
65 69 def after_destroy
66 70 board.reset_counters!
67 71 end
68 72
69 73 def sticky=(arg)
70 74 write_attribute :sticky, (arg == true || arg.to_s == '1' ? 1 : 0)
71 75 end
72 76
73 77 def sticky?
74 78 sticky == 1
75 79 end
76 80
77 81 def project
78 82 board.project
79 83 end
80 84
81 85 def editable_by?(usr)
82 86 usr && usr.logged? && (usr.allowed_to?(:edit_messages, project) || (self.author == usr && usr.allowed_to?(:edit_own_messages, project)))
83 87 end
84 88
85 89 def destroyable_by?(usr)
86 90 usr && usr.logged? && (usr.allowed_to?(:delete_messages, project) || (self.author == usr && usr.allowed_to?(:delete_own_messages, project)))
87 91 end
88 92
89 93 private
90 94
91 95 def add_author_as_watcher
92 96 Watcher.create(:watchable => self.root, :user => author)
93 97 end
94 98 end
@@ -1,30 +1,65
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Watcher < ActiveRecord::Base
19 19 belongs_to :watchable, :polymorphic => true
20 20 belongs_to :user
21 21
22 22 validates_presence_of :user
23 23 validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
24 24
25 # Unwatch things that users are no longer allowed to view
26 def self.prune(options={})
27 if options.has_key?(:user)
28 prune_single_user(options[:user], options)
29 else
30 pruned = 0
31 User.find(:all, :conditions => "id IN (SELECT DISTINCT user_id FROM #{table_name})").each do |user|
32 pruned += prune_single_user(user, options)
33 end
34 pruned
35 end
36 end
37
25 38 protected
26 39
27 40 def validate
28 41 errors.add :user_id, :invalid unless user.nil? || user.active?
29 42 end
43
44 private
45
46 def self.prune_single_user(user, options={})
47 return unless user.is_a?(User)
48 pruned = 0
49 find(:all, :conditions => {:user_id => user.id}).each do |watcher|
50 next if watcher.watchable.nil?
51
52 if options.has_key?(:project)
53 next unless watcher.watchable.respond_to?(:project) && watcher.watchable.project == options[:project]
54 end
55
56 if watcher.watchable.respond_to?(:visible?)
57 unless watcher.watchable.visible?(user)
58 watcher.destroy
59 pruned += 1
60 end
61 end
62 end
63 pruned
64 end
30 65 end
@@ -1,75 +1,79
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Wiki < ActiveRecord::Base
19 19 belongs_to :project
20 20 has_many :pages, :class_name => 'WikiPage', :dependent => :destroy, :order => 'title'
21 21 has_many :redirects, :class_name => 'WikiRedirect', :dependent => :delete_all
22 22
23 23 acts_as_watchable
24 24
25 25 validates_presence_of :start_page
26 26 validates_format_of :start_page, :with => /^[^,\.\/\?\;\|\:]*$/
27 27
28 def visible?(user=User.current)
29 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
30 end
31
28 32 # find the page with the given title
29 33 # if page doesn't exist, return a new page
30 34 def find_or_new_page(title)
31 35 title = start_page if title.blank?
32 36 find_page(title) || WikiPage.new(:wiki => self, :title => Wiki.titleize(title))
33 37 end
34 38
35 39 # find the page with the given title
36 40 def find_page(title, options = {})
37 41 title = start_page if title.blank?
38 42 title = Wiki.titleize(title)
39 43 page = pages.find_by_title(title)
40 44 if !page && !(options[:with_redirect] == false)
41 45 # search for a redirect
42 46 redirect = redirects.find_by_title(title)
43 47 page = find_page(redirect.redirects_to, :with_redirect => false) if redirect
44 48 end
45 49 page
46 50 end
47 51
48 52 # Finds a page by title
49 53 # The given string can be of one of the forms: "title" or "project:title"
50 54 # Examples:
51 55 # Wiki.find_page("bar", project => foo)
52 56 # Wiki.find_page("foo:bar")
53 57 def self.find_page(title, options = {})
54 58 project = options[:project]
55 59 if title.to_s =~ %r{^([^\:]+)\:(.*)$}
56 60 project_identifier, title = $1, $2
57 61 project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
58 62 end
59 63 if project && project.wiki
60 64 page = project.wiki.find_page(title)
61 65 if page && page.content
62 66 page
63 67 end
64 68 end
65 69 end
66 70
67 71 # turn a string into a valid page title
68 72 def self.titleize(title)
69 73 # replace spaces with _ and remove unwanted caracters
70 74 title = title.gsub(/\s+/, '_').delete(',./?;|:') if title
71 75 # upcase the first letter
72 76 title = (title.slice(0..0).upcase + (title.slice(1..-1) || '')) if title
73 77 title
74 78 end
75 79 end
@@ -1,189 +1,193
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'diff'
19 19 require 'enumerator'
20 20
21 21 class WikiPage < ActiveRecord::Base
22 22 belongs_to :wiki
23 23 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
24 24 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
25 25 acts_as_tree :dependent => :nullify, :order => 'title'
26 26
27 27 acts_as_watchable
28 28 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
29 29 :description => :text,
30 30 :datetime => :created_on,
31 31 :url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project_id, :page => o.title}}
32 32
33 33 acts_as_searchable :columns => ['title', 'text'],
34 34 :include => [{:wiki => :project}, :content],
35 35 :project_key => "#{Wiki.table_name}.project_id"
36 36
37 37 attr_accessor :redirect_existing_links
38 38
39 39 validates_presence_of :title
40 40 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
41 41 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
42 42 validates_associated :content
43 43
44 def visible?(user=User.current)
45 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
46 end
47
44 48 def title=(value)
45 49 value = Wiki.titleize(value)
46 50 @previous_title = read_attribute(:title) if @previous_title.blank?
47 51 write_attribute(:title, value)
48 52 end
49 53
50 54 def before_save
51 55 self.title = Wiki.titleize(title)
52 56 # Manage redirects if the title has changed
53 57 if !@previous_title.blank? && (@previous_title != title) && !new_record?
54 58 # Update redirects that point to the old title
55 59 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
56 60 r.redirects_to = title
57 61 r.title == r.redirects_to ? r.destroy : r.save
58 62 end
59 63 # Remove redirects for the new title
60 64 wiki.redirects.find_all_by_title(title).each(&:destroy)
61 65 # Create a redirect to the new title
62 66 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
63 67 @previous_title = nil
64 68 end
65 69 end
66 70
67 71 def before_destroy
68 72 # Remove redirects to this page
69 73 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
70 74 end
71 75
72 76 def pretty_title
73 77 WikiPage.pretty_title(title)
74 78 end
75 79
76 80 def content_for_version(version=nil)
77 81 result = content.versions.find_by_version(version.to_i) if version
78 82 result ||= content
79 83 result
80 84 end
81 85
82 86 def diff(version_to=nil, version_from=nil)
83 87 version_to = version_to ? version_to.to_i : self.content.version
84 88 version_from = version_from ? version_from.to_i : version_to - 1
85 89 version_to, version_from = version_from, version_to unless version_from < version_to
86 90
87 91 content_to = content.versions.find_by_version(version_to)
88 92 content_from = content.versions.find_by_version(version_from)
89 93
90 94 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
91 95 end
92 96
93 97 def annotate(version=nil)
94 98 version = version ? version.to_i : self.content.version
95 99 c = content.versions.find_by_version(version)
96 100 c ? WikiAnnotate.new(c) : nil
97 101 end
98 102
99 103 def self.pretty_title(str)
100 104 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
101 105 end
102 106
103 107 def project
104 108 wiki.project
105 109 end
106 110
107 111 def text
108 112 content.text if content
109 113 end
110 114
111 115 # Returns true if usr is allowed to edit the page, otherwise false
112 116 def editable_by?(usr)
113 117 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
114 118 end
115 119
116 120 def attachments_deletable?(usr=User.current)
117 121 editable_by?(usr) && super(usr)
118 122 end
119 123
120 124 def parent_title
121 125 @parent_title || (self.parent && self.parent.pretty_title)
122 126 end
123 127
124 128 def parent_title=(t)
125 129 @parent_title = t
126 130 parent_page = t.blank? ? nil : self.wiki.find_page(t)
127 131 self.parent = parent_page
128 132 end
129 133
130 134 protected
131 135
132 136 def validate
133 137 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
134 138 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
135 139 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
136 140 end
137 141 end
138 142
139 143 class WikiDiff
140 144 attr_reader :diff, :words, :content_to, :content_from
141 145
142 146 def initialize(content_to, content_from)
143 147 @content_to = content_to
144 148 @content_from = content_from
145 149 @words = content_to.text.split(/(\s+)/)
146 150 @words = @words.select {|word| word != ' '}
147 151 words_from = content_from.text.split(/(\s+)/)
148 152 words_from = words_from.select {|word| word != ' '}
149 153 @diff = words_from.diff @words
150 154 end
151 155 end
152 156
153 157 class WikiAnnotate
154 158 attr_reader :lines, :content
155 159
156 160 def initialize(content)
157 161 @content = content
158 162 current = content
159 163 current_lines = current.text.split(/\r?\n/)
160 164 @lines = current_lines.collect {|t| [nil, nil, t]}
161 165 positions = []
162 166 current_lines.size.times {|i| positions << i}
163 167 while (current.previous)
164 168 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
165 169 d.each_slice(3) do |s|
166 170 sign, line = s[0], s[1]
167 171 if sign == '+' && positions[line] && positions[line] != -1
168 172 if @lines[positions[line]][0].nil?
169 173 @lines[positions[line]][0] = current.version
170 174 @lines[positions[line]][1] = current.author
171 175 end
172 176 end
173 177 end
174 178 d.each_slice(3) do |s|
175 179 sign, line = s[0], s[1]
176 180 if sign == '-'
177 181 positions.insert(line, -1)
178 182 else
179 183 positions[line] = nil
180 184 end
181 185 end
182 186 positions.compact!
183 187 # Stop if every line is annotated
184 188 break unless @lines.detect { |line| line[0].nil? }
185 189 current = current.previous
186 190 end
187 191 @lines.each { |line| line[0] ||= current.version }
188 192 end
189 193 end
@@ -1,61 +1,65
1 1 ---
2 2 enabled_modules_001:
3 3 name: issue_tracking
4 4 project_id: 1
5 5 id: 1
6 6 enabled_modules_002:
7 7 name: time_tracking
8 8 project_id: 1
9 9 id: 2
10 10 enabled_modules_003:
11 11 name: news
12 12 project_id: 1
13 13 id: 3
14 14 enabled_modules_004:
15 15 name: documents
16 16 project_id: 1
17 17 id: 4
18 18 enabled_modules_005:
19 19 name: files
20 20 project_id: 1
21 21 id: 5
22 22 enabled_modules_006:
23 23 name: wiki
24 24 project_id: 1
25 25 id: 6
26 26 enabled_modules_007:
27 27 name: repository
28 28 project_id: 1
29 29 id: 7
30 30 enabled_modules_008:
31 31 name: boards
32 32 project_id: 1
33 33 id: 8
34 34 enabled_modules_009:
35 35 name: repository
36 36 project_id: 3
37 37 id: 9
38 38 enabled_modules_010:
39 39 name: wiki
40 40 project_id: 3
41 41 id: 10
42 42 enabled_modules_011:
43 43 name: issue_tracking
44 44 project_id: 2
45 45 id: 11
46 46 enabled_modules_012:
47 47 name: time_tracking
48 48 project_id: 3
49 49 id: 12
50 50 enabled_modules_013:
51 51 name: issue_tracking
52 52 project_id: 3
53 53 id: 13
54 54 enabled_modules_014:
55 55 name: issue_tracking
56 56 project_id: 5
57 57 id: 14
58 58 enabled_modules_015:
59 59 name: wiki
60 60 project_id: 2
61 61 id: 15
62 enabled_modules_016:
63 name: boards
64 project_id: 2
65 id: 16
@@ -1,68 +1,79
1 1 ---
2 2 messages_001:
3 3 created_on: 2007-05-12 17:15:32 +02:00
4 4 updated_on: 2007-05-12 17:15:32 +02:00
5 5 subject: First post
6 6 id: 1
7 7 replies_count: 2
8 8 last_reply_id: 3
9 9 content: "This is the very first post\n\
10 10 in the forum"
11 11 author_id: 1
12 12 parent_id:
13 13 board_id: 1
14 14 messages_002:
15 15 created_on: 2007-05-12 17:18:00 +02:00
16 16 updated_on: 2007-05-12 17:18:00 +02:00
17 17 subject: First reply
18 18 id: 2
19 19 replies_count: 0
20 20 last_reply_id:
21 21 content: "Reply to the first post"
22 22 author_id: 1
23 23 parent_id: 1
24 24 board_id: 1
25 25 messages_003:
26 26 created_on: 2007-05-12 17:18:02 +02:00
27 27 updated_on: 2007-05-12 17:18:02 +02:00
28 28 subject: "RE: First post"
29 29 id: 3
30 30 replies_count: 0
31 31 last_reply_id:
32 32 content: "An other reply"
33 33 author_id: 2
34 34 parent_id: 1
35 35 board_id: 1
36 36 messages_004:
37 37 created_on: 2007-08-12 17:15:32 +02:00
38 38 updated_on: 2007-08-12 17:15:32 +02:00
39 39 subject: Post 2
40 40 id: 4
41 41 replies_count: 2
42 42 last_reply_id: 6
43 43 content: "This is an other post"
44 44 author_id:
45 45 parent_id:
46 46 board_id: 1
47 47 messages_005:
48 48 created_on: <%= 3.days.ago.to_date.to_s(:db) %>
49 49 updated_on: <%= 3.days.ago.to_date.to_s(:db) %>
50 50 subject: 'RE: post 2'
51 51 id: 5
52 52 replies_count: 0
53 53 last_reply_id:
54 54 content: "Reply to the second post"
55 55 author_id: 1
56 56 parent_id: 4
57 57 board_id: 1
58 58 messages_006:
59 59 created_on: <%= 2.days.ago.to_date.to_s(:db) %>
60 60 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
61 61 subject: 'RE: post 2'
62 62 id: 6
63 63 replies_count: 0
64 64 last_reply_id:
65 65 content: "Another reply to the second post"
66 66 author_id: 3
67 67 parent_id: 4
68 68 board_id: 1
69 messages_007:
70 created_on: <%= 2.days.ago.to_date.to_s(:db) %>
71 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
72 subject: 'Message on a private project'
73 id: 7
74 replies_count: 0
75 last_reply_id:
76 content: "This is a private message"
77 author_id: 1
78 parent_id:
79 board_id: 3
@@ -1,71 +1,137
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class MemberTest < ActiveSupport::TestCase
21 fixtures :users, :projects, :roles, :members, :member_roles
21 fixtures :all
22 22
23 23 def setup
24 24 @jsmith = Member.find(1)
25 25 end
26 26
27 27 def test_create
28 28 member = Member.new(:project_id => 1, :user_id => 4, :role_ids => [1, 2])
29 29 assert member.save
30 30 member.reload
31 31
32 32 assert_equal 2, member.roles.size
33 33 assert_equal Role.find(1), member.roles.sort.first
34 34 end
35 35
36 36 def test_update
37 37 assert_equal "eCookbook", @jsmith.project.name
38 38 assert_equal "Manager", @jsmith.roles.first.name
39 39 assert_equal "jsmith", @jsmith.user.login
40 40
41 41 @jsmith.mail_notification = !@jsmith.mail_notification
42 42 assert @jsmith.save
43 43 end
44 44
45 45 def test_update_roles
46 46 assert_equal 1, @jsmith.roles.size
47 47 @jsmith.role_ids = [1, 2]
48 48 assert @jsmith.save
49 49 assert_equal 2, @jsmith.reload.roles.size
50 50 end
51 51
52 52 def test_validate
53 53 member = Member.new(:project_id => 1, :user_id => 2, :role_ids => [2])
54 54 # same use can't have more than one membership for a project
55 55 assert !member.save
56 56
57 57 member = Member.new(:project_id => 1, :user_id => 2, :role_ids => [])
58 58 # must have one role at least
59 59 assert !member.save
60 60 end
61 61
62 62 def test_destroy
63 63 assert_difference 'Member.count', -1 do
64 64 assert_difference 'MemberRole.count', -1 do
65 65 @jsmith.destroy
66 66 end
67 67 end
68 68
69 69 assert_raise(ActiveRecord::RecordNotFound) { Member.find(@jsmith.id) }
70 70 end
71
72 context "removing permissions" do
73 setup do
74 Watcher.delete_all("user_id = 9")
75 user = User.find(9)
76 # public
77 Watcher.create!(:watchable => Issue.find(1), :user_id => user)
78 # private
79 Watcher.create!(:watchable => Issue.find(4), :user => user)
80 Watcher.create!(:watchable => Message.find(7), :user => user)
81 Watcher.create!(:watchable => Wiki.find(2), :user => user)
82 Watcher.create!(:watchable => WikiPage.find(3), :user => user)
83 end
84
85 context "of user" do
86 setup do
87 @member = Member.create!(:project => Project.find(2), :principal => User.find(9), :role_ids => [1, 2])
88 end
89
90 context "by deleting membership" do
91 should "prune watchers" do
92 assert_difference 'Watcher.count', -4 do
93 @member.destroy
94 end
95 end
96 end
97
98 context "by updating roles" do
99 should "prune watchers" do
100 Role.find(2).remove_permission! :view_wiki_pages
101 member = Member.first(:order => 'id desc')
102 assert_difference 'Watcher.count', -2 do
103 member.role_ids = [2]
104 member.save
105 end
106 assert !Message.find(7).watched_by?(@user)
107 end
108 end
109 end
110
111 context "of group" do
112 setup do
113 group = Group.find(10)
114 @member = Member.create!(:project => Project.find(2), :principal => group, :role_ids => [1, 2])
115 group.users << User.find(9)
116 end
117
118 context "by deleting membership" do
119 should "prune watchers" do
120 assert_difference 'Watcher.count', -4 do
121 @member.destroy
122 end
123 end
124 end
125
126 context "by updating roles" do
127 should "prune watchers" do
128 Role.find(2).remove_permission! :view_wiki_pages
129 assert_difference 'Watcher.count', -2 do
130 @member.role_ids = [2]
131 @member.save
132 end
133 end
134 end
135 end
136 end
71 137 end
@@ -1,69 +1,105
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class WatcherTest < ActiveSupport::TestCase
21 fixtures :issues, :users
21 fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules,
22 :issues,
23 :boards, :messages,
24 :wikis, :wiki_pages,
25 :watchers
22 26
23 27 def setup
24 28 @user = User.find(1)
25 29 @issue = Issue.find(1)
26 30 end
27 31
28 32 def test_watch
29 33 assert @issue.add_watcher(@user)
30 34 @issue.reload
31 35 assert @issue.watchers.detect { |w| w.user == @user }
32 36 end
33 37
34 38 def test_cant_watch_twice
35 39 assert @issue.add_watcher(@user)
36 40 assert !@issue.add_watcher(@user)
37 41 end
38 42
39 43 def test_watched_by
40 44 assert @issue.add_watcher(@user)
41 45 @issue.reload
42 46 assert @issue.watched_by?(@user)
43 47 assert Issue.watched_by(@user).include?(@issue)
44 48 end
45 49
46 50 def test_recipients
47 51 @issue.watchers.delete_all
48 52 @issue.reload
49 53
50 54 assert @issue.watcher_recipients.empty?
51 55 assert @issue.add_watcher(@user)
52 56
53 57 @user.mail_notification = true
54 58 @user.save
55 59 @issue.reload
56 60 assert @issue.watcher_recipients.include?(@user.mail)
57 61
58 62 @user.mail_notification = false
59 63 @user.save
60 64 @issue.reload
61 65 assert @issue.watcher_recipients.include?(@user.mail)
62 66 end
63 67
64 68 def test_unwatch
65 69 assert @issue.add_watcher(@user)
66 70 @issue.reload
67 71 assert_equal 1, @issue.remove_watcher(@user)
68 72 end
73
74 def test_prune
75 Watcher.delete_all("user_id = 9")
76 user = User.find(9)
77
78 # public
79 Watcher.create!(:watchable => Issue.find(1), :user => user)
80 Watcher.create!(:watchable => Issue.find(2), :user => user)
81 Watcher.create!(:watchable => Message.find(1), :user => user)
82 Watcher.create!(:watchable => Wiki.find(1), :user => user)
83 Watcher.create!(:watchable => WikiPage.find(2), :user => user)
84
85 # private project (id: 2)
86 Member.create!(:project => Project.find(2), :principal => user, :role_ids => [1])
87 Watcher.create!(:watchable => Issue.find(4), :user => user)
88 Watcher.create!(:watchable => Message.find(7), :user => user)
89 Watcher.create!(:watchable => Wiki.find(2), :user => user)
90 Watcher.create!(:watchable => WikiPage.find(3), :user => user)
91
92 assert_no_difference 'Watcher.count' do
93 Watcher.prune(:user => User.find(9))
94 end
95
96 Member.delete_all
97
98 assert_difference 'Watcher.count', -4 do
99 Watcher.prune(:user => User.find(9))
100 end
101
102 assert Issue.find(1).watched_by?(user)
103 assert !Issue.find(4).watched_by?(user)
104 end
69 105 end
General Comments 0
You need to be logged in to leave comments. Login now