##// END OF EJS Templates
Fixed: wiki pages in search results are referenced by project number, not by project identifier (#4456)....
Jean-Philippe Lang -
r3117:5e1dc78d7539
parent child
Show More
@@ -1,193 +1,193
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'diff'
18 require 'diff'
19 require 'enumerator'
19 require 'enumerator'
20
20
21 class WikiPage < ActiveRecord::Base
21 class WikiPage < ActiveRecord::Base
22 belongs_to :wiki
22 belongs_to :wiki
23 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
23 has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
24 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
24 acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
25 acts_as_tree :dependent => :nullify, :order => 'title'
25 acts_as_tree :dependent => :nullify, :order => 'title'
26
26
27 acts_as_watchable
27 acts_as_watchable
28 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
28 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
29 :description => :text,
29 :description => :text,
30 :datetime => :created_on,
30 :datetime => :created_on,
31 :url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project_id, :page => o.title}}
31 :url => Proc.new {|o| {:controller => 'wiki', :id => o.wiki.project, :page => o.title}}
32
32
33 acts_as_searchable :columns => ['title', 'text'],
33 acts_as_searchable :columns => ['title', 'text'],
34 :include => [{:wiki => :project}, :content],
34 :include => [{:wiki => :project}, :content],
35 :project_key => "#{Wiki.table_name}.project_id"
35 :project_key => "#{Wiki.table_name}.project_id"
36
36
37 attr_accessor :redirect_existing_links
37 attr_accessor :redirect_existing_links
38
38
39 validates_presence_of :title
39 validates_presence_of :title
40 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
40 validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/
41 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
41 validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false
42 validates_associated :content
42 validates_associated :content
43
43
44 def visible?(user=User.current)
44 def visible?(user=User.current)
45 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
45 !user.nil? && user.allowed_to?(:view_wiki_pages, project)
46 end
46 end
47
47
48 def title=(value)
48 def title=(value)
49 value = Wiki.titleize(value)
49 value = Wiki.titleize(value)
50 @previous_title = read_attribute(:title) if @previous_title.blank?
50 @previous_title = read_attribute(:title) if @previous_title.blank?
51 write_attribute(:title, value)
51 write_attribute(:title, value)
52 end
52 end
53
53
54 def before_save
54 def before_save
55 self.title = Wiki.titleize(title)
55 self.title = Wiki.titleize(title)
56 # Manage redirects if the title has changed
56 # Manage redirects if the title has changed
57 if !@previous_title.blank? && (@previous_title != title) && !new_record?
57 if !@previous_title.blank? && (@previous_title != title) && !new_record?
58 # Update redirects that point to the old title
58 # Update redirects that point to the old title
59 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
59 wiki.redirects.find_all_by_redirects_to(@previous_title).each do |r|
60 r.redirects_to = title
60 r.redirects_to = title
61 r.title == r.redirects_to ? r.destroy : r.save
61 r.title == r.redirects_to ? r.destroy : r.save
62 end
62 end
63 # Remove redirects for the new title
63 # Remove redirects for the new title
64 wiki.redirects.find_all_by_title(title).each(&:destroy)
64 wiki.redirects.find_all_by_title(title).each(&:destroy)
65 # Create a redirect to the new title
65 # Create a redirect to the new title
66 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
66 wiki.redirects << WikiRedirect.new(:title => @previous_title, :redirects_to => title) unless redirect_existing_links == "0"
67 @previous_title = nil
67 @previous_title = nil
68 end
68 end
69 end
69 end
70
70
71 def before_destroy
71 def before_destroy
72 # Remove redirects to this page
72 # Remove redirects to this page
73 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
73 wiki.redirects.find_all_by_redirects_to(title).each(&:destroy)
74 end
74 end
75
75
76 def pretty_title
76 def pretty_title
77 WikiPage.pretty_title(title)
77 WikiPage.pretty_title(title)
78 end
78 end
79
79
80 def content_for_version(version=nil)
80 def content_for_version(version=nil)
81 result = content.versions.find_by_version(version.to_i) if version
81 result = content.versions.find_by_version(version.to_i) if version
82 result ||= content
82 result ||= content
83 result
83 result
84 end
84 end
85
85
86 def diff(version_to=nil, version_from=nil)
86 def diff(version_to=nil, version_from=nil)
87 version_to = version_to ? version_to.to_i : self.content.version
87 version_to = version_to ? version_to.to_i : self.content.version
88 version_from = version_from ? version_from.to_i : version_to - 1
88 version_from = version_from ? version_from.to_i : version_to - 1
89 version_to, version_from = version_from, version_to unless version_from < version_to
89 version_to, version_from = version_from, version_to unless version_from < version_to
90
90
91 content_to = content.versions.find_by_version(version_to)
91 content_to = content.versions.find_by_version(version_to)
92 content_from = content.versions.find_by_version(version_from)
92 content_from = content.versions.find_by_version(version_from)
93
93
94 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
94 (content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
95 end
95 end
96
96
97 def annotate(version=nil)
97 def annotate(version=nil)
98 version = version ? version.to_i : self.content.version
98 version = version ? version.to_i : self.content.version
99 c = content.versions.find_by_version(version)
99 c = content.versions.find_by_version(version)
100 c ? WikiAnnotate.new(c) : nil
100 c ? WikiAnnotate.new(c) : nil
101 end
101 end
102
102
103 def self.pretty_title(str)
103 def self.pretty_title(str)
104 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
104 (str && str.is_a?(String)) ? str.tr('_', ' ') : str
105 end
105 end
106
106
107 def project
107 def project
108 wiki.project
108 wiki.project
109 end
109 end
110
110
111 def text
111 def text
112 content.text if content
112 content.text if content
113 end
113 end
114
114
115 # Returns true if usr is allowed to edit the page, otherwise false
115 # Returns true if usr is allowed to edit the page, otherwise false
116 def editable_by?(usr)
116 def editable_by?(usr)
117 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
117 !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
118 end
118 end
119
119
120 def attachments_deletable?(usr=User.current)
120 def attachments_deletable?(usr=User.current)
121 editable_by?(usr) && super(usr)
121 editable_by?(usr) && super(usr)
122 end
122 end
123
123
124 def parent_title
124 def parent_title
125 @parent_title || (self.parent && self.parent.pretty_title)
125 @parent_title || (self.parent && self.parent.pretty_title)
126 end
126 end
127
127
128 def parent_title=(t)
128 def parent_title=(t)
129 @parent_title = t
129 @parent_title = t
130 parent_page = t.blank? ? nil : self.wiki.find_page(t)
130 parent_page = t.blank? ? nil : self.wiki.find_page(t)
131 self.parent = parent_page
131 self.parent = parent_page
132 end
132 end
133
133
134 protected
134 protected
135
135
136 def validate
136 def validate
137 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
137 errors.add(:parent_title, :invalid) if !@parent_title.blank? && parent.nil?
138 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
138 errors.add(:parent_title, :circular_dependency) if parent && (parent == self || parent.ancestors.include?(self))
139 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
139 errors.add(:parent_title, :not_same_project) if parent && (parent.wiki_id != wiki_id)
140 end
140 end
141 end
141 end
142
142
143 class WikiDiff
143 class WikiDiff
144 attr_reader :diff, :words, :content_to, :content_from
144 attr_reader :diff, :words, :content_to, :content_from
145
145
146 def initialize(content_to, content_from)
146 def initialize(content_to, content_from)
147 @content_to = content_to
147 @content_to = content_to
148 @content_from = content_from
148 @content_from = content_from
149 @words = content_to.text.split(/(\s+)/)
149 @words = content_to.text.split(/(\s+)/)
150 @words = @words.select {|word| word != ' '}
150 @words = @words.select {|word| word != ' '}
151 words_from = content_from.text.split(/(\s+)/)
151 words_from = content_from.text.split(/(\s+)/)
152 words_from = words_from.select {|word| word != ' '}
152 words_from = words_from.select {|word| word != ' '}
153 @diff = words_from.diff @words
153 @diff = words_from.diff @words
154 end
154 end
155 end
155 end
156
156
157 class WikiAnnotate
157 class WikiAnnotate
158 attr_reader :lines, :content
158 attr_reader :lines, :content
159
159
160 def initialize(content)
160 def initialize(content)
161 @content = content
161 @content = content
162 current = content
162 current = content
163 current_lines = current.text.split(/\r?\n/)
163 current_lines = current.text.split(/\r?\n/)
164 @lines = current_lines.collect {|t| [nil, nil, t]}
164 @lines = current_lines.collect {|t| [nil, nil, t]}
165 positions = []
165 positions = []
166 current_lines.size.times {|i| positions << i}
166 current_lines.size.times {|i| positions << i}
167 while (current.previous)
167 while (current.previous)
168 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
168 d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
169 d.each_slice(3) do |s|
169 d.each_slice(3) do |s|
170 sign, line = s[0], s[1]
170 sign, line = s[0], s[1]
171 if sign == '+' && positions[line] && positions[line] != -1
171 if sign == '+' && positions[line] && positions[line] != -1
172 if @lines[positions[line]][0].nil?
172 if @lines[positions[line]][0].nil?
173 @lines[positions[line]][0] = current.version
173 @lines[positions[line]][0] = current.version
174 @lines[positions[line]][1] = current.author
174 @lines[positions[line]][1] = current.author
175 end
175 end
176 end
176 end
177 end
177 end
178 d.each_slice(3) do |s|
178 d.each_slice(3) do |s|
179 sign, line = s[0], s[1]
179 sign, line = s[0], s[1]
180 if sign == '-'
180 if sign == '-'
181 positions.insert(line, -1)
181 positions.insert(line, -1)
182 else
182 else
183 positions[line] = nil
183 positions[line] = nil
184 end
184 end
185 end
185 end
186 positions.compact!
186 positions.compact!
187 # Stop if every line is annotated
187 # Stop if every line is annotated
188 break unless @lines.detect { |line| line[0].nil? }
188 break unless @lines.detect { |line| line[0].nil? }
189 current = current.previous
189 current = current.previous
190 end
190 end
191 @lines.each { |line| line[0] ||= current.version }
191 @lines.each { |line| line[0] ||= current.version }
192 end
192 end
193 end
193 end
General Comments 0
You need to be logged in to leave comments. Login now