##// END OF EJS Templates
Fix wiki links on the activity page. #6746...
Eric Davis -
r4180:17eab0f5f9ec
parent child
Show More
@@ -1,110 +1,110
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 require 'zlib'
19 19
20 20 class WikiContent < ActiveRecord::Base
21 21 set_locking_column :version
22 22 belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
23 23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
24 24 validates_presence_of :text
25 25 validates_length_of :comments, :maximum => 255, :allow_nil => true
26 26
27 27 acts_as_versioned
28 28
29 29 def visible?(user=User.current)
30 30 page.visible?(user)
31 31 end
32 32
33 33 def project
34 34 page.project
35 35 end
36 36
37 37 def attachments
38 38 page.nil? ? [] : page.attachments
39 39 end
40 40
41 41 # Returns the mail adresses of users that should be notified
42 42 def recipients
43 43 notified = project.notified_users
44 44 notified.reject! {|user| !visible?(user)}
45 45 notified.collect(&:mail)
46 46 end
47 47
48 48 class Version
49 49 belongs_to :page, :class_name => '::WikiPage', :foreign_key => 'page_id'
50 50 belongs_to :author, :class_name => '::User', :foreign_key => 'author_id'
51 51 attr_protected :data
52 52
53 53 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
54 54 :description => :comments,
55 55 :datetime => :updated_on,
56 56 :type => 'wiki-page',
57 :url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
57 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.page.wiki.project, :page => o.page.title, :version => o.version}}
58 58
59 59 acts_as_activity_provider :type => 'wiki_edits',
60 60 :timestamp => "#{WikiContent.versioned_table_name}.updated_on",
61 61 :author_key => "#{WikiContent.versioned_table_name}.author_id",
62 62 :permission => :view_wiki_edits,
63 63 :find_options => {:select => "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
64 64 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
65 65 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
66 66 "#{WikiContent.versioned_table_name}.id",
67 67 :joins => "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
68 68 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
69 69 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"}
70 70
71 71 def text=(plain)
72 72 case Setting.wiki_compression
73 73 when 'gzip'
74 74 begin
75 75 self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
76 76 self.compression = 'gzip'
77 77 rescue
78 78 self.data = plain
79 79 self.compression = ''
80 80 end
81 81 else
82 82 self.data = plain
83 83 self.compression = ''
84 84 end
85 85 plain
86 86 end
87 87
88 88 def text
89 89 @text ||= case compression
90 90 when 'gzip'
91 91 Zlib::Inflate.inflate(data)
92 92 else
93 93 # uncompressed data
94 94 data
95 95 end
96 96 end
97 97
98 98 def project
99 99 page.project
100 100 end
101 101
102 102 # Returns the previous version or nil
103 103 def previous
104 104 @previous ||= WikiContent::Version.find(:first,
105 105 :order => 'version DESC',
106 106 :include => :author,
107 107 :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
108 108 end
109 109 end
110 110 end
@@ -1,202 +1,202
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 :url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project, :page => o.title}}
31 :url => Proc.new {|o| {:controller => 'wiki', :action => 'show', :project_id => o.wiki.project, :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 44 # Wiki pages that are protected by default
45 45 DEFAULT_PROTECTED_PAGES = %w(sidebar)
46 46
47 47 def after_initialize
48 48 if new_record? && DEFAULT_PROTECTED_PAGES.include?(title.to_s.downcase)
49 49 self.protected = true
50 50 end
51 51 end
52 52
53 53 def visible?(user=User.current)
54 54 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
55 55 end
56 56
57 57 def title=(value)
58 58 value = Wiki.titleize(value)
59 59 @previous_title = read_attribute(:title) if @previous_title.blank?
60 60 write_attribute(:title, value)
61 61 end
62 62
63 63 def before_save
64 64 self.title = Wiki.titleize(title)
65 65 # Manage redirects if the title has changed
66 66 if !@previous_title.blank? && (@previous_title != title) && !new_record?
67 67 # Update redirects that point to the old title
68 68 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
69 69 r.redirects_to = title
70 70 r.title == r.redirects_to ? r.destroy : r.save
71 71 end
72 72 # Remove redirects for the new title
73 73 wiki.redirects.find_all_by_title(title).each(&:destroy)
74 74 # Create a redirect to the new title
75 75 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
76 76 @previous_title = nil
77 77 end
78 78 end
79 79
80 80 def before_destroy
81 81 # Remove redirects to this page
82 82 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
83 83 end
84 84
85 85 def pretty_title
86 86 WikiPage.pretty_title(title)
87 87 end
88 88
89 89 def content_for_version(version=nil)
90 90 result = content.versions.find_by_version(version.to_i) if version
91 91 result ||= content
92 92 result
93 93 end
94 94
95 95 def diff(version_to=nil, version_from=nil)
96 96 version_to = version_to ? version_to.to_i : self.content.version
97 97 version_from = version_from ? version_from.to_i : version_to - 1
98 98 version_to, version_from = version_from, version_to unless version_from < version_to
99 99
100 100 content_to = content.versions.find_by_version(version_to)
101 101 content_from = content.versions.find_by_version(version_from)
102 102
103 103 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
104 104 end
105 105
106 106 def annotate(version=nil)
107 107 version = version ? version.to_i : self.content.version
108 108 c = content.versions.find_by_version(version)
109 109 c ? WikiAnnotate.new(c) : nil
110 110 end
111 111
112 112 def self.pretty_title(str)
113 113 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
114 114 end
115 115
116 116 def project
117 117 wiki.project
118 118 end
119 119
120 120 def text
121 121 content.text if content
122 122 end
123 123
124 124 # Returns true if usr is allowed to edit the page, otherwise false
125 125 def editable_by?(usr)
126 126 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
127 127 end
128 128
129 129 def attachments_deletable?(usr=User.current)
130 130 editable_by?(usr) && super(usr)
131 131 end
132 132
133 133 def parent_title
134 134 @parent_title || (self.parent && self.parent.pretty_title)
135 135 end
136 136
137 137 def parent_title=(t)
138 138 @parent_title = t
139 139 parent_page = t.blank? ? nil : self.wiki.find_page(t)
140 140 self.parent = parent_page
141 141 end
142 142
143 143 protected
144 144
145 145 def validate
146 146 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
147 147 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
148 148 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
149 149 end
150 150 end
151 151
152 152 class WikiDiff
153 153 attr_reader :diff, :words, :content_to, :content_from
154 154
155 155 def initialize(content_to, content_from)
156 156 @content_to = content_to
157 157 @content_from = content_from
158 158 @words = content_to.text.split(/(\s+)/)
159 159 @words = @words.select {|word| word != ' '}
160 160 words_from = content_from.text.split(/(\s+)/)
161 161 words_from = words_from.select {|word| word != ' '}
162 162 @diff = words_from.diff @words
163 163 end
164 164 end
165 165
166 166 class WikiAnnotate
167 167 attr_reader :lines, :content
168 168
169 169 def initialize(content)
170 170 @content = content
171 171 current = content
172 172 current_lines = current.text.split(/\r?\n/)
173 173 @lines = current_lines.collect {|t| [nil, nil, t]}
174 174 positions = []
175 175 current_lines.size.times {|i| positions << i}
176 176 while (current.previous)
177 177 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
178 178 d.each_slice(3) do |s|
179 179 sign, line = s[0], s[1]
180 180 if sign == '+' && positions[line] && positions[line] != -1
181 181 if @lines[positions[line]][0].nil?
182 182 @lines[positions[line]][0] = current.version
183 183 @lines[positions[line]][1] = current.author
184 184 end
185 185 end
186 186 end
187 187 d.each_slice(3) do |s|
188 188 sign, line = s[0], s[1]
189 189 if sign == '-'
190 190 positions.insert(line, -1)
191 191 else
192 192 positions[line] = nil
193 193 end
194 194 end
195 195 positions.compact!
196 196 # Stop if every line is annotated
197 197 break unless @lines.detect { |line| line[0].nil? }
198 198 current = current.previous
199 199 end
200 200 @lines.each { |line| line[0] ||= current.version }
201 201 end
202 202 end
General Comments 0
You need to be logged in to leave comments. Login now