##// END OF EJS Templates
Handle the case of a text formatter that doesn't support section edit (#2222)....
Jean-Philippe Lang -
r7711:1e8a9da13168
parent child
Show More
@@ -1,306 +1,309
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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
19
20 # The WikiController follows the Rails REST controller pattern but with
20 # The WikiController follows the Rails REST controller pattern but with
21 # a few differences
21 # a few differences
22 #
22 #
23 # * index - shows a list of WikiPages grouped by page or date
23 # * index - shows a list of WikiPages grouped by page or date
24 # * new - not used
24 # * new - not used
25 # * create - not used
25 # * create - not used
26 # * show - will also show the form for creating a new wiki page
26 # * show - will also show the form for creating a new wiki page
27 # * edit - used to edit an existing or new page
27 # * edit - used to edit an existing or new page
28 # * update - used to save a wiki page update to the database, including new pages
28 # * update - used to save a wiki page update to the database, including new pages
29 # * destroy - normal
29 # * destroy - normal
30 #
30 #
31 # Other member and collection methods are also used
31 # Other member and collection methods are also used
32 #
32 #
33 # TODO: still being worked on
33 # TODO: still being worked on
34 class WikiController < ApplicationController
34 class WikiController < ApplicationController
35 default_search_scope :wiki_pages
35 default_search_scope :wiki_pages
36 before_filter :find_wiki, :authorize
36 before_filter :find_wiki, :authorize
37 before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
37 before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
38 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
38 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
39
39
40 helper :attachments
40 helper :attachments
41 include AttachmentsHelper
41 include AttachmentsHelper
42 helper :watchers
42 helper :watchers
43 include Redmine::Export::PDF
43 include Redmine::Export::PDF
44
44
45 # List of pages, sorted alphabetically and by parent (hierarchy)
45 # List of pages, sorted alphabetically and by parent (hierarchy)
46 def index
46 def index
47 load_pages_for_index
47 load_pages_for_index
48 @pages_by_parent_id = @pages.group_by(&:parent_id)
48 @pages_by_parent_id = @pages.group_by(&:parent_id)
49 end
49 end
50
50
51 # List of page, by last update
51 # List of page, by last update
52 def date_index
52 def date_index
53 load_pages_for_index
53 load_pages_for_index
54 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
54 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
55 end
55 end
56
56
57 # display a page (in editing mode if it doesn't exist)
57 # display a page (in editing mode if it doesn't exist)
58 def show
58 def show
59 if @page.new_record?
59 if @page.new_record?
60 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
60 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
61 edit
61 edit
62 render :action => 'edit'
62 render :action => 'edit'
63 else
63 else
64 render_404
64 render_404
65 end
65 end
66 return
66 return
67 end
67 end
68 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
68 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
69 # Redirects user to the current version if he's not allowed to view previous versions
69 # Redirects user to the current version if he's not allowed to view previous versions
70 redirect_to :version => nil
70 redirect_to :version => nil
71 return
71 return
72 end
72 end
73 @content = @page.content_for_version(params[:version])
73 @content = @page.content_for_version(params[:version])
74 if User.current.allowed_to?(:export_wiki_pages, @project)
74 if User.current.allowed_to?(:export_wiki_pages, @project)
75 if params[:format] == 'pdf'
75 if params[:format] == 'pdf'
76 send_data(wiki_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
76 send_data(wiki_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
77 return
77 return
78 elsif params[:format] == 'html'
78 elsif params[:format] == 'html'
79 export = render_to_string :action => 'export', :layout => false
79 export = render_to_string :action => 'export', :layout => false
80 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
80 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
81 return
81 return
82 elsif params[:format] == 'txt'
82 elsif params[:format] == 'txt'
83 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
83 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
84 return
84 return
85 end
85 end
86 end
86 end
87 @editable = editable?
87 @editable = editable?
88 @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) && params[:version].nil?
88 @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) &&
89 params[:version].nil? &&
90 Redmine::WikiFormatting.supports_section_edit?
91
89 render :action => 'show'
92 render :action => 'show'
90 end
93 end
91
94
92 # edit an existing page or a new one
95 # edit an existing page or a new one
93 def edit
96 def edit
94 return render_403 unless editable?
97 return render_403 unless editable?
95 @page.content = WikiContent.new(:page => @page) if @page.new_record?
98 @page.content = WikiContent.new(:page => @page) if @page.new_record?
96
99
97 @content = @page.content_for_version(params[:version])
100 @content = @page.content_for_version(params[:version])
98 @content.text = initial_page_content(@page) if @content.text.blank?
101 @content.text = initial_page_content(@page) if @content.text.blank?
99 # don't keep previous comment
102 # don't keep previous comment
100 @content.comments = nil
103 @content.comments = nil
101
104
102 # To prevent StaleObjectError exception when reverting to a previous version
105 # To prevent StaleObjectError exception when reverting to a previous version
103 @content.version = @page.content.version
106 @content.version = @page.content.version
104
107
105 @text = @content.text
108 @text = @content.text
106 if params[:section].present?
109 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
107 @section = params[:section].to_i
110 @section = params[:section].to_i
108 @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
111 @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
109 render_404 if @text.blank?
112 render_404 if @text.blank?
110 end
113 end
111 end
114 end
112
115
113 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
116 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
114 # Creates a new page or updates an existing one
117 # Creates a new page or updates an existing one
115 def update
118 def update
116 return render_403 unless editable?
119 return render_403 unless editable?
117 @page.content = WikiContent.new(:page => @page) if @page.new_record?
120 @page.content = WikiContent.new(:page => @page) if @page.new_record?
118
121
119 @content = @page.content_for_version(params[:version])
122 @content = @page.content_for_version(params[:version])
120 @content.text = initial_page_content(@page) if @content.text.blank?
123 @content.text = initial_page_content(@page) if @content.text.blank?
121 # don't keep previous comment
124 # don't keep previous comment
122 @content.comments = nil
125 @content.comments = nil
123
126
124 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
127 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
125 attachments = Attachment.attach_files(@page, params[:attachments])
128 attachments = Attachment.attach_files(@page, params[:attachments])
126 render_attachment_warning_if_needed(@page)
129 render_attachment_warning_if_needed(@page)
127 # don't save if text wasn't changed
130 # don't save if text wasn't changed
128 redirect_to :action => 'show', :project_id => @project, :id => @page.title
131 redirect_to :action => 'show', :project_id => @project, :id => @page.title
129 return
132 return
130 end
133 end
131
134
132 @content.comments = params[:content][:comments]
135 @content.comments = params[:content][:comments]
133 @text = params[:content][:text]
136 @text = params[:content][:text]
134 if params[:section].present?
137 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
135 @section = params[:section].to_i
138 @section = params[:section].to_i
136 @section_hash = params[:section_hash]
139 @section_hash = params[:section_hash]
137 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
140 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
138 else
141 else
139 @content.version = params[:content][:version]
142 @content.version = params[:content][:version]
140 @content.text = @text
143 @content.text = @text
141 end
144 end
142 @content.author = User.current
145 @content.author = User.current
143 # if page is new @page.save will also save content, but not if page isn't a new record
146 # if page is new @page.save will also save content, but not if page isn't a new record
144 if (@page.new_record? ? @page.save : @content.save)
147 if (@page.new_record? ? @page.save : @content.save)
145 attachments = Attachment.attach_files(@page, params[:attachments])
148 attachments = Attachment.attach_files(@page, params[:attachments])
146 render_attachment_warning_if_needed(@page)
149 render_attachment_warning_if_needed(@page)
147 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
150 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
148 redirect_to :action => 'show', :project_id => @project, :id => @page.title
151 redirect_to :action => 'show', :project_id => @project, :id => @page.title
149 else
152 else
150 render :action => 'edit'
153 render :action => 'edit'
151 end
154 end
152
155
153 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
156 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
154 # Optimistic locking exception
157 # Optimistic locking exception
155 flash.now[:error] = l(:notice_locking_conflict)
158 flash.now[:error] = l(:notice_locking_conflict)
156 render :action => 'edit'
159 render :action => 'edit'
157 end
160 end
158
161
159 # rename a page
162 # rename a page
160 def rename
163 def rename
161 return render_403 unless editable?
164 return render_403 unless editable?
162 @page.redirect_existing_links = true
165 @page.redirect_existing_links = true
163 # used to display the *original* title if some AR validation errors occur
166 # used to display the *original* title if some AR validation errors occur
164 @original_title = @page.pretty_title
167 @original_title = @page.pretty_title
165 if request.post? && @page.update_attributes(params[:wiki_page])
168 if request.post? && @page.update_attributes(params[:wiki_page])
166 flash[:notice] = l(:notice_successful_update)
169 flash[:notice] = l(:notice_successful_update)
167 redirect_to :action => 'show', :project_id => @project, :id => @page.title
170 redirect_to :action => 'show', :project_id => @project, :id => @page.title
168 end
171 end
169 end
172 end
170
173
171 verify :method => :post, :only => :protect, :redirect_to => { :action => :show }
174 verify :method => :post, :only => :protect, :redirect_to => { :action => :show }
172 def protect
175 def protect
173 @page.update_attribute :protected, params[:protected]
176 @page.update_attribute :protected, params[:protected]
174 redirect_to :action => 'show', :project_id => @project, :id => @page.title
177 redirect_to :action => 'show', :project_id => @project, :id => @page.title
175 end
178 end
176
179
177 # show page history
180 # show page history
178 def history
181 def history
179 @version_count = @page.content.versions.count
182 @version_count = @page.content.versions.count
180 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
183 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
181 # don't load text
184 # don't load text
182 @versions = @page.content.versions.find :all,
185 @versions = @page.content.versions.find :all,
183 :select => "id, author_id, comments, updated_on, version",
186 :select => "id, author_id, comments, updated_on, version",
184 :order => 'version DESC',
187 :order => 'version DESC',
185 :limit => @version_pages.items_per_page + 1,
188 :limit => @version_pages.items_per_page + 1,
186 :offset => @version_pages.current.offset
189 :offset => @version_pages.current.offset
187
190
188 render :layout => false if request.xhr?
191 render :layout => false if request.xhr?
189 end
192 end
190
193
191 def diff
194 def diff
192 @diff = @page.diff(params[:version], params[:version_from])
195 @diff = @page.diff(params[:version], params[:version_from])
193 render_404 unless @diff
196 render_404 unless @diff
194 end
197 end
195
198
196 def annotate
199 def annotate
197 @annotate = @page.annotate(params[:version])
200 @annotate = @page.annotate(params[:version])
198 render_404 unless @annotate
201 render_404 unless @annotate
199 end
202 end
200
203
201 verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
204 verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
202 # Removes a wiki page and its history
205 # Removes a wiki page and its history
203 # Children can be either set as root pages, removed or reassigned to another parent page
206 # Children can be either set as root pages, removed or reassigned to another parent page
204 def destroy
207 def destroy
205 return render_403 unless editable?
208 return render_403 unless editable?
206
209
207 @descendants_count = @page.descendants.size
210 @descendants_count = @page.descendants.size
208 if @descendants_count > 0
211 if @descendants_count > 0
209 case params[:todo]
212 case params[:todo]
210 when 'nullify'
213 when 'nullify'
211 # Nothing to do
214 # Nothing to do
212 when 'destroy'
215 when 'destroy'
213 # Removes all its descendants
216 # Removes all its descendants
214 @page.descendants.each(&:destroy)
217 @page.descendants.each(&:destroy)
215 when 'reassign'
218 when 'reassign'
216 # Reassign children to another parent page
219 # Reassign children to another parent page
217 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
220 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
218 return unless reassign_to
221 return unless reassign_to
219 @page.children.each do |child|
222 @page.children.each do |child|
220 child.update_attribute(:parent, reassign_to)
223 child.update_attribute(:parent, reassign_to)
221 end
224 end
222 else
225 else
223 @reassignable_to = @wiki.pages - @page.self_and_descendants
226 @reassignable_to = @wiki.pages - @page.self_and_descendants
224 return
227 return
225 end
228 end
226 end
229 end
227 @page.destroy
230 @page.destroy
228 redirect_to :action => 'index', :project_id => @project
231 redirect_to :action => 'index', :project_id => @project
229 end
232 end
230
233
231 # Export wiki to a single html file
234 # Export wiki to a single html file
232 def export
235 def export
233 if User.current.allowed_to?(:export_wiki_pages, @project)
236 if User.current.allowed_to?(:export_wiki_pages, @project)
234 @pages = @wiki.pages.find :all, :order => 'title'
237 @pages = @wiki.pages.find :all, :order => 'title'
235 export = render_to_string :action => 'export_multiple', :layout => false
238 export = render_to_string :action => 'export_multiple', :layout => false
236 send_data(export, :type => 'text/html', :filename => "wiki.html")
239 send_data(export, :type => 'text/html', :filename => "wiki.html")
237 else
240 else
238 redirect_to :action => 'show', :project_id => @project, :id => nil
241 redirect_to :action => 'show', :project_id => @project, :id => nil
239 end
242 end
240 end
243 end
241
244
242 def preview
245 def preview
243 page = @wiki.find_page(params[:id])
246 page = @wiki.find_page(params[:id])
244 # page is nil when previewing a new page
247 # page is nil when previewing a new page
245 return render_403 unless page.nil? || editable?(page)
248 return render_403 unless page.nil? || editable?(page)
246 if page
249 if page
247 @attachements = page.attachments
250 @attachements = page.attachments
248 @previewed = page.content
251 @previewed = page.content
249 end
252 end
250 @text = params[:content][:text]
253 @text = params[:content][:text]
251 render :partial => 'common/preview'
254 render :partial => 'common/preview'
252 end
255 end
253
256
254 def add_attachment
257 def add_attachment
255 return render_403 unless editable?
258 return render_403 unless editable?
256 attachments = Attachment.attach_files(@page, params[:attachments])
259 attachments = Attachment.attach_files(@page, params[:attachments])
257 render_attachment_warning_if_needed(@page)
260 render_attachment_warning_if_needed(@page)
258 redirect_to :action => 'show', :id => @page.title, :project_id => @project
261 redirect_to :action => 'show', :id => @page.title, :project_id => @project
259 end
262 end
260
263
261 private
264 private
262
265
263 def find_wiki
266 def find_wiki
264 @project = Project.find(params[:project_id])
267 @project = Project.find(params[:project_id])
265 @wiki = @project.wiki
268 @wiki = @project.wiki
266 render_404 unless @wiki
269 render_404 unless @wiki
267 rescue ActiveRecord::RecordNotFound
270 rescue ActiveRecord::RecordNotFound
268 render_404
271 render_404
269 end
272 end
270
273
271 # Finds the requested page or a new page if it doesn't exist
274 # Finds the requested page or a new page if it doesn't exist
272 def find_existing_or_new_page
275 def find_existing_or_new_page
273 @page = @wiki.find_or_new_page(params[:id])
276 @page = @wiki.find_or_new_page(params[:id])
274 if @wiki.page_found_with_redirect?
277 if @wiki.page_found_with_redirect?
275 redirect_to params.update(:id => @page.title)
278 redirect_to params.update(:id => @page.title)
276 end
279 end
277 end
280 end
278
281
279 # Finds the requested page and returns a 404 error if it doesn't exist
282 # Finds the requested page and returns a 404 error if it doesn't exist
280 def find_existing_page
283 def find_existing_page
281 @page = @wiki.find_page(params[:id])
284 @page = @wiki.find_page(params[:id])
282 if @page.nil?
285 if @page.nil?
283 render_404
286 render_404
284 return
287 return
285 end
288 end
286 if @wiki.page_found_with_redirect?
289 if @wiki.page_found_with_redirect?
287 redirect_to params.update(:id => @page.title)
290 redirect_to params.update(:id => @page.title)
288 end
291 end
289 end
292 end
290
293
291 # Returns true if the current user is allowed to edit the page, otherwise false
294 # Returns true if the current user is allowed to edit the page, otherwise false
292 def editable?(page = @page)
295 def editable?(page = @page)
293 page.editable_by?(User.current)
296 page.editable_by?(User.current)
294 end
297 end
295
298
296 # Returns the default content of a new wiki page
299 # Returns the default content of a new wiki page
297 def initial_page_content(page)
300 def initial_page_content(page)
298 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
301 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
299 extend helper unless self.instance_of?(helper)
302 extend helper unless self.instance_of?(helper)
300 helper.instance_method(:initial_page_content).bind(self).call(page)
303 helper.instance_method(:initial_page_content).bind(self).call(page)
301 end
304 end
302
305
303 def load_pages_for_index
306 def load_pages_for_index
304 @pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
307 @pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
305 end
308 end
306 end
309 end
@@ -1,107 +1,112
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 module Redmine
18 module Redmine
19 module WikiFormatting
19 module WikiFormatting
20 class StaleSectionError < Exception; end
20 class StaleSectionError < Exception; end
21
21
22 @@formatters = {}
22 @@formatters = {}
23
23
24 class << self
24 class << self
25 def map
25 def map
26 yield self
26 yield self
27 end
27 end
28
28
29 def register(name, formatter, helper)
29 def register(name, formatter, helper)
30 raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_s]
30 raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_s]
31 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
31 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
32 end
32 end
33
33
34 def formatter
34 def formatter
35 formatter_for(Setting.text_formatting)
35 formatter_for(Setting.text_formatting)
36 end
36 end
37
37
38 def formatter_for(name)
38 def formatter_for(name)
39 entry = @@formatters[name.to_s]
39 entry = @@formatters[name.to_s]
40 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
40 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
41 end
41 end
42
42
43 def helper_for(name)
43 def helper_for(name)
44 entry = @@formatters[name.to_s]
44 entry = @@formatters[name.to_s]
45 (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
45 (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
46 end
46 end
47
47
48 def format_names
48 def format_names
49 @@formatters.keys.map
49 @@formatters.keys.map
50 end
50 end
51
51
52 def to_html(format, text, options = {})
52 def to_html(format, text, options = {})
53 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
53 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
54 # Text retrieved from the cache store may be frozen
54 # Text retrieved from the cache store may be frozen
55 # We need to dup it so we can do in-place substitutions with gsub!
55 # We need to dup it so we can do in-place substitutions with gsub!
56 cache_store.fetch cache_key do
56 cache_store.fetch cache_key do
57 formatter_for(format).new(text).to_html
57 formatter_for(format).new(text).to_html
58 end.dup
58 end.dup
59 else
59 else
60 formatter_for(format).new(text).to_html
60 formatter_for(format).new(text).to_html
61 end
61 end
62 text
62 text
63 end
63 end
64
64
65 # Returns true if the text formatter supports single section edit
66 def supports_section_edit?
67 (formatter.instance_methods & ['update_section', :update_section]).any?
68 end
69
65 # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done
70 # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done
66 def cache_key_for(format, object, attribute)
71 def cache_key_for(format, object, attribute)
67 if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank?
72 if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank?
68 "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}"
73 "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}"
69 end
74 end
70 end
75 end
71
76
72 # Returns the cache store used to cache HTML output
77 # Returns the cache store used to cache HTML output
73 def cache_store
78 def cache_store
74 ActionController::Base.cache_store
79 ActionController::Base.cache_store
75 end
80 end
76 end
81 end
77
82
78 # Default formatter module
83 # Default formatter module
79 module NullFormatter
84 module NullFormatter
80 class Formatter
85 class Formatter
81 include ActionView::Helpers::TagHelper
86 include ActionView::Helpers::TagHelper
82 include ActionView::Helpers::TextHelper
87 include ActionView::Helpers::TextHelper
83 include ActionView::Helpers::UrlHelper
88 include ActionView::Helpers::UrlHelper
84
89
85 def initialize(text)
90 def initialize(text)
86 @text = text
91 @text = text
87 end
92 end
88
93
89 def to_html(*args)
94 def to_html(*args)
90 simple_format(auto_link(CGI::escapeHTML(@text)))
95 simple_format(auto_link(CGI::escapeHTML(@text)))
91 end
96 end
92 end
97 end
93
98
94 module Helper
99 module Helper
95 def wikitoolbar_for(field_id)
100 def wikitoolbar_for(field_id)
96 end
101 end
97
102
98 def heads_for_wiki_formatter
103 def heads_for_wiki_formatter
99 end
104 end
100
105
101 def initial_page_content(page)
106 def initial_page_content(page)
102 page.pretty_title.to_s
107 page.pretty_title.to_s
103 end
108 end
104 end
109 end
105 end
110 end
106 end
111 end
107 end
112 end
@@ -1,45 +1,55
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 File.expand_path('../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../test_helper', __FILE__)
19
19
20 class Redmine::WikiFormattingTest < ActiveSupport::TestCase
20 class Redmine::WikiFormattingTest < ActiveSupport::TestCase
21
21
22 def test_textile_formatter
22 def test_textile_formatter
23 assert_equal Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting.formatter_for('textile')
23 assert_equal Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting.formatter_for('textile')
24 assert_equal Redmine::WikiFormatting::Textile::Helper, Redmine::WikiFormatting.helper_for('textile')
24 assert_equal Redmine::WikiFormatting::Textile::Helper, Redmine::WikiFormatting.helper_for('textile')
25 end
25 end
26
26
27 def test_null_formatter
27 def test_null_formatter
28 assert_equal Redmine::WikiFormatting::NullFormatter::Formatter, Redmine::WikiFormatting.formatter_for('')
28 assert_equal Redmine::WikiFormatting::NullFormatter::Formatter, Redmine::WikiFormatting.formatter_for('')
29 assert_equal Redmine::WikiFormatting::NullFormatter::Helper, Redmine::WikiFormatting.helper_for('')
29 assert_equal Redmine::WikiFormatting::NullFormatter::Helper, Redmine::WikiFormatting.helper_for('')
30 end
30 end
31
31
32 def test_should_link_urls_and_email_addresses
32 def test_should_link_urls_and_email_addresses
33 raw = <<-DIFF
33 raw = <<-DIFF
34 This is a sample *text* with a link: http://www.redmine.org
34 This is a sample *text* with a link: http://www.redmine.org
35 and an email address foo@example.net
35 and an email address foo@example.net
36 DIFF
36 DIFF
37
37
38 expected = <<-EXPECTED
38 expected = <<-EXPECTED
39 <p>This is a sample *text* with a link: <a href="http://www.redmine.org">http://www.redmine.org</a><br />
39 <p>This is a sample *text* with a link: <a href="http://www.redmine.org">http://www.redmine.org</a><br />
40 and an email address <a href="mailto:foo@example.net">foo@example.net</a></p>
40 and an email address <a href="mailto:foo@example.net">foo@example.net</a></p>
41 EXPECTED
41 EXPECTED
42
42
43 assert_equal expected.gsub(%r{[\r\n\t]}, ''), Redmine::WikiFormatting::NullFormatter::Formatter.new(raw).to_html.gsub(%r{[\r\n\t]}, '')
43 assert_equal expected.gsub(%r{[\r\n\t]}, ''), Redmine::WikiFormatting::NullFormatter::Formatter.new(raw).to_html.gsub(%r{[\r\n\t]}, '')
44 end
44 end
45
46 def test_supports_section_edit
47 with_settings :text_formatting => 'textile' do
48 assert_equal true, Redmine::WikiFormatting.supports_section_edit?
49 end
50
51 with_settings :text_formatting => '' do
52 assert_equal false, Redmine::WikiFormatting.supports_section_edit?
53 end
54 end
45 end
55 end
General Comments 0
You need to be logged in to leave comments. Login now