##// END OF EJS Templates
Refactor: convert WikiController to a REST resource...
Eric Davis -
r4189:c514dd6885af
parent child
Show More
@@ -1,277 +1,277
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
37 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
38
38
39 verify :method => :post, :only => [:protect], :redirect_to => { :action => :show }
39 verify :method => :post, :only => [:protect], :redirect_to => { :action => :show }
40
40
41 helper :attachments
41 helper :attachments
42 include AttachmentsHelper
42 include AttachmentsHelper
43 helper :watchers
43 helper :watchers
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_grouped_by_date_without_content
47 load_pages_grouped_by_date_without_content
48 end
48 end
49
49
50 # display a page (in editing mode if it doesn't exist)
50 # display a page (in editing mode if it doesn't exist)
51 def show
51 def show
52 page_title = params[:id]
52 page_title = params[:id]
53 @page = @wiki.find_or_new_page(page_title)
53 @page = @wiki.find_or_new_page(page_title)
54 if @page.new_record?
54 if @page.new_record?
55 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
55 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
56 edit
56 edit
57 render :action => 'edit'
57 render :action => 'edit'
58 else
58 else
59 render_404
59 render_404
60 end
60 end
61 return
61 return
62 end
62 end
63 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
63 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
64 # Redirects user to the current version if he's not allowed to view previous versions
64 # Redirects user to the current version if he's not allowed to view previous versions
65 redirect_to :version => nil
65 redirect_to :version => nil
66 return
66 return
67 end
67 end
68 @content = @page.content_for_version(params[:version])
68 @content = @page.content_for_version(params[:version])
69 if User.current.allowed_to?(:export_wiki_pages, @project)
69 if User.current.allowed_to?(:export_wiki_pages, @project)
70 if params[:format] == 'html'
70 if params[:format] == 'html'
71 export = render_to_string :action => 'export', :layout => false
71 export = render_to_string :action => 'export', :layout => false
72 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
72 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
73 return
73 return
74 elsif params[:format] == 'txt'
74 elsif params[:format] == 'txt'
75 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
75 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
76 return
76 return
77 end
77 end
78 end
78 end
79 @editable = editable?
79 @editable = editable?
80 render :action => 'show'
80 render :action => 'show'
81 end
81 end
82
82
83 # edit an existing page or a new one
83 # edit an existing page or a new one
84 def edit
84 def edit
85 @page = @wiki.find_or_new_page(params[:id])
85 @page = @wiki.find_or_new_page(params[:id])
86 return render_403 unless editable?
86 return render_403 unless editable?
87 @page.content = WikiContent.new(:page => @page) if @page.new_record?
87 @page.content = WikiContent.new(:page => @page) if @page.new_record?
88
88
89 @content = @page.content_for_version(params[:version])
89 @content = @page.content_for_version(params[:version])
90 @content.text = initial_page_content(@page) if @content.text.blank?
90 @content.text = initial_page_content(@page) if @content.text.blank?
91 # don't keep previous comment
91 # don't keep previous comment
92 @content.comments = nil
92 @content.comments = nil
93
93
94 # To prevent StaleObjectError exception when reverting to a previous version
94 # To prevent StaleObjectError exception when reverting to a previous version
95 @content.version = @page.content.version
95 @content.version = @page.content.version
96 rescue ActiveRecord::StaleObjectError
96 rescue ActiveRecord::StaleObjectError
97 # Optimistic locking exception
97 # Optimistic locking exception
98 flash[:error] = l(:notice_locking_conflict)
98 flash[:error] = l(:notice_locking_conflict)
99 end
99 end
100
100
101 verify :method => :post, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
101 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
102 # Creates a new page or updates an existing one
102 # Creates a new page or updates an existing one
103 def update
103 def update
104 @page = @wiki.find_or_new_page(params[:id])
104 @page = @wiki.find_or_new_page(params[:id])
105 return render_403 unless editable?
105 return render_403 unless editable?
106 @page.content = WikiContent.new(:page => @page) if @page.new_record?
106 @page.content = WikiContent.new(:page => @page) if @page.new_record?
107
107
108 @content = @page.content_for_version(params[:version])
108 @content = @page.content_for_version(params[:version])
109 @content.text = initial_page_content(@page) if @content.text.blank?
109 @content.text = initial_page_content(@page) if @content.text.blank?
110 # don't keep previous comment
110 # don't keep previous comment
111 @content.comments = nil
111 @content.comments = nil
112
112
113 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
113 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
114 attachments = Attachment.attach_files(@page, params[:attachments])
114 attachments = Attachment.attach_files(@page, params[:attachments])
115 render_attachment_warning_if_needed(@page)
115 render_attachment_warning_if_needed(@page)
116 # don't save if text wasn't changed
116 # don't save if text wasn't changed
117 redirect_to :action => 'show', :project_id => @project, :id => @page.title
117 redirect_to :action => 'show', :project_id => @project, :id => @page.title
118 return
118 return
119 end
119 end
120 @content.attributes = params[:content]
120 @content.attributes = params[:content]
121 @content.author = User.current
121 @content.author = User.current
122 # if page is new @page.save will also save content, but not if page isn't a new record
122 # if page is new @page.save will also save content, but not if page isn't a new record
123 if (@page.new_record? ? @page.save : @content.save)
123 if (@page.new_record? ? @page.save : @content.save)
124 attachments = Attachment.attach_files(@page, params[:attachments])
124 attachments = Attachment.attach_files(@page, params[:attachments])
125 render_attachment_warning_if_needed(@page)
125 render_attachment_warning_if_needed(@page)
126 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
126 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
127 redirect_to :action => 'show', :project_id => @project, :id => @page.title
127 redirect_to :action => 'show', :project_id => @project, :id => @page.title
128 end
128 end
129
129
130 rescue ActiveRecord::StaleObjectError
130 rescue ActiveRecord::StaleObjectError
131 # Optimistic locking exception
131 # Optimistic locking exception
132 flash[:error] = l(:notice_locking_conflict)
132 flash[:error] = l(:notice_locking_conflict)
133 end
133 end
134
134
135 # rename a page
135 # rename a page
136 def rename
136 def rename
137 return render_403 unless editable?
137 return render_403 unless editable?
138 @page.redirect_existing_links = true
138 @page.redirect_existing_links = true
139 # used to display the *original* title if some AR validation errors occur
139 # used to display the *original* title if some AR validation errors occur
140 @original_title = @page.pretty_title
140 @original_title = @page.pretty_title
141 if request.post? && @page.update_attributes(params[:wiki_page])
141 if request.post? && @page.update_attributes(params[:wiki_page])
142 flash[:notice] = l(:notice_successful_update)
142 flash[:notice] = l(:notice_successful_update)
143 redirect_to :action => 'show', :project_id => @project, :id => @page.title
143 redirect_to :action => 'show', :project_id => @project, :id => @page.title
144 end
144 end
145 end
145 end
146
146
147 def protect
147 def protect
148 @page.update_attribute :protected, params[:protected]
148 @page.update_attribute :protected, params[:protected]
149 redirect_to :action => 'show', :project_id => @project, :id => @page.title
149 redirect_to :action => 'show', :project_id => @project, :id => @page.title
150 end
150 end
151
151
152 # show page history
152 # show page history
153 def history
153 def history
154 @version_count = @page.content.versions.count
154 @version_count = @page.content.versions.count
155 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
155 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
156 # don't load text
156 # don't load text
157 @versions = @page.content.versions.find :all,
157 @versions = @page.content.versions.find :all,
158 :select => "id, author_id, comments, updated_on, version",
158 :select => "id, author_id, comments, updated_on, version",
159 :order => 'version DESC',
159 :order => 'version DESC',
160 :limit => @version_pages.items_per_page + 1,
160 :limit => @version_pages.items_per_page + 1,
161 :offset => @version_pages.current.offset
161 :offset => @version_pages.current.offset
162
162
163 render :layout => false if request.xhr?
163 render :layout => false if request.xhr?
164 end
164 end
165
165
166 def diff
166 def diff
167 @diff = @page.diff(params[:version], params[:version_from])
167 @diff = @page.diff(params[:version], params[:version_from])
168 render_404 unless @diff
168 render_404 unless @diff
169 end
169 end
170
170
171 def annotate
171 def annotate
172 @annotate = @page.annotate(params[:version])
172 @annotate = @page.annotate(params[:version])
173 render_404 unless @annotate
173 render_404 unless @annotate
174 end
174 end
175
175
176 verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
176 verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
177 # Removes a wiki page and its history
177 # Removes a wiki page and its history
178 # Children can be either set as root pages, removed or reassigned to another parent page
178 # Children can be either set as root pages, removed or reassigned to another parent page
179 def destroy
179 def destroy
180 return render_403 unless editable?
180 return render_403 unless editable?
181
181
182 @descendants_count = @page.descendants.size
182 @descendants_count = @page.descendants.size
183 if @descendants_count > 0
183 if @descendants_count > 0
184 case params[:todo]
184 case params[:todo]
185 when 'nullify'
185 when 'nullify'
186 # Nothing to do
186 # Nothing to do
187 when 'destroy'
187 when 'destroy'
188 # Removes all its descendants
188 # Removes all its descendants
189 @page.descendants.each(&:destroy)
189 @page.descendants.each(&:destroy)
190 when 'reassign'
190 when 'reassign'
191 # Reassign children to another parent page
191 # Reassign children to another parent page
192 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
192 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
193 return unless reassign_to
193 return unless reassign_to
194 @page.children.each do |child|
194 @page.children.each do |child|
195 child.update_attribute(:parent, reassign_to)
195 child.update_attribute(:parent, reassign_to)
196 end
196 end
197 else
197 else
198 @reassignable_to = @wiki.pages - @page.self_and_descendants
198 @reassignable_to = @wiki.pages - @page.self_and_descendants
199 return
199 return
200 end
200 end
201 end
201 end
202 @page.destroy
202 @page.destroy
203 redirect_to :action => 'index', :project_id => @project
203 redirect_to :action => 'index', :project_id => @project
204 end
204 end
205
205
206 # Export wiki to a single html file
206 # Export wiki to a single html file
207 def export
207 def export
208 if User.current.allowed_to?(:export_wiki_pages, @project)
208 if User.current.allowed_to?(:export_wiki_pages, @project)
209 @pages = @wiki.pages.find :all, :order => 'title'
209 @pages = @wiki.pages.find :all, :order => 'title'
210 export = render_to_string :action => 'export_multiple', :layout => false
210 export = render_to_string :action => 'export_multiple', :layout => false
211 send_data(export, :type => 'text/html', :filename => "wiki.html")
211 send_data(export, :type => 'text/html', :filename => "wiki.html")
212 else
212 else
213 redirect_to :action => 'show', :project_id => @project, :id => nil
213 redirect_to :action => 'show', :project_id => @project, :id => nil
214 end
214 end
215 end
215 end
216
216
217 def date_index
217 def date_index
218 load_pages_grouped_by_date_without_content
218 load_pages_grouped_by_date_without_content
219 end
219 end
220
220
221 def preview
221 def preview
222 page = @wiki.find_page(params[:id])
222 page = @wiki.find_page(params[:id])
223 # page is nil when previewing a new page
223 # page is nil when previewing a new page
224 return render_403 unless page.nil? || editable?(page)
224 return render_403 unless page.nil? || editable?(page)
225 if page
225 if page
226 @attachements = page.attachments
226 @attachements = page.attachments
227 @previewed = page.content
227 @previewed = page.content
228 end
228 end
229 @text = params[:content][:text]
229 @text = params[:content][:text]
230 render :partial => 'common/preview'
230 render :partial => 'common/preview'
231 end
231 end
232
232
233 def add_attachment
233 def add_attachment
234 return render_403 unless editable?
234 return render_403 unless editable?
235 attachments = Attachment.attach_files(@page, params[:attachments])
235 attachments = Attachment.attach_files(@page, params[:attachments])
236 render_attachment_warning_if_needed(@page)
236 render_attachment_warning_if_needed(@page)
237 redirect_to :action => 'show', :id => @page.title
237 redirect_to :action => 'show', :id => @page.title, :project_id => @project
238 end
238 end
239
239
240 private
240 private
241
241
242 def find_wiki
242 def find_wiki
243 @project = Project.find(params[:project_id])
243 @project = Project.find(params[:project_id])
244 @wiki = @project.wiki
244 @wiki = @project.wiki
245 render_404 unless @wiki
245 render_404 unless @wiki
246 rescue ActiveRecord::RecordNotFound
246 rescue ActiveRecord::RecordNotFound
247 render_404
247 render_404
248 end
248 end
249
249
250 # Finds the requested page and returns a 404 error if it doesn't exist
250 # Finds the requested page and returns a 404 error if it doesn't exist
251 def find_existing_page
251 def find_existing_page
252 @page = @wiki.find_page(params[:id])
252 @page = @wiki.find_page(params[:id])
253 render_404 if @page.nil?
253 render_404 if @page.nil?
254 end
254 end
255
255
256 # Returns true if the current user is allowed to edit the page, otherwise false
256 # Returns true if the current user is allowed to edit the page, otherwise false
257 def editable?(page = @page)
257 def editable?(page = @page)
258 page.editable_by?(User.current)
258 page.editable_by?(User.current)
259 end
259 end
260
260
261 # Returns the default content of a new wiki page
261 # Returns the default content of a new wiki page
262 def initial_page_content(page)
262 def initial_page_content(page)
263 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
263 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
264 extend helper unless self.instance_of?(helper)
264 extend helper unless self.instance_of?(helper)
265 helper.instance_method(:initial_page_content).bind(self).call(page)
265 helper.instance_method(:initial_page_content).bind(self).call(page)
266 end
266 end
267
267
268 # eager load information about last updates, without loading text
268 # eager load information about last updates, without loading text
269 def load_pages_grouped_by_date_without_content
269 def load_pages_grouped_by_date_without_content
270 @pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
270 @pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on",
271 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
271 :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id",
272 :order => 'title'
272 :order => 'title'
273 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
273 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
274 @pages_by_parent_id = @pages.group_by(&:parent_id)
274 @pages_by_parent_id = @pages.group_by(&:parent_id)
275 end
275 end
276
276
277 end
277 end
@@ -1,840 +1,841
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 'forwardable'
18 require 'forwardable'
19 require 'cgi'
19 require 'cgi'
20
20
21 module ApplicationHelper
21 module ApplicationHelper
22 include Redmine::WikiFormatting::Macros::Definitions
22 include Redmine::WikiFormatting::Macros::Definitions
23 include Redmine::I18n
23 include Redmine::I18n
24 include GravatarHelper::PublicMethods
24 include GravatarHelper::PublicMethods
25
25
26 extend Forwardable
26 extend Forwardable
27 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
27 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
28
28
29 # Return true if user is authorized for controller/action, otherwise false
29 # Return true if user is authorized for controller/action, otherwise false
30 def authorize_for(controller, action)
30 def authorize_for(controller, action)
31 User.current.allowed_to?({:controller => controller, :action => action}, @project)
31 User.current.allowed_to?({:controller => controller, :action => action}, @project)
32 end
32 end
33
33
34 # Display a link if user is authorized
34 # Display a link if user is authorized
35 #
35 #
36 # @param [String] name Anchor text (passed to link_to)
36 # @param [String] name Anchor text (passed to link_to)
37 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
37 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
38 # @param [optional, Hash] html_options Options passed to link_to
38 # @param [optional, Hash] html_options Options passed to link_to
39 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
39 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
42 end
42 end
43
43
44 # Display a link to remote if user is authorized
44 # Display a link to remote if user is authorized
45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
46 url = options[:url] || {}
46 url = options[:url] || {}
47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
48 end
48 end
49
49
50 # Displays a link to user's account page if active
50 # Displays a link to user's account page if active
51 def link_to_user(user, options={})
51 def link_to_user(user, options={})
52 if user.is_a?(User)
52 if user.is_a?(User)
53 name = h(user.name(options[:format]))
53 name = h(user.name(options[:format]))
54 if user.active?
54 if user.active?
55 link_to name, :controller => 'users', :action => 'show', :id => user
55 link_to name, :controller => 'users', :action => 'show', :id => user
56 else
56 else
57 name
57 name
58 end
58 end
59 else
59 else
60 h(user.to_s)
60 h(user.to_s)
61 end
61 end
62 end
62 end
63
63
64 # Displays a link to +issue+ with its subject.
64 # Displays a link to +issue+ with its subject.
65 # Examples:
65 # Examples:
66 #
66 #
67 # link_to_issue(issue) # => Defect #6: This is the subject
67 # link_to_issue(issue) # => Defect #6: This is the subject
68 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
68 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
69 # link_to_issue(issue, :subject => false) # => Defect #6
69 # link_to_issue(issue, :subject => false) # => Defect #6
70 # link_to_issue(issue, :project => true) # => Foo - Defect #6
70 # link_to_issue(issue, :project => true) # => Foo - Defect #6
71 #
71 #
72 def link_to_issue(issue, options={})
72 def link_to_issue(issue, options={})
73 title = nil
73 title = nil
74 subject = nil
74 subject = nil
75 if options[:subject] == false
75 if options[:subject] == false
76 title = truncate(issue.subject, :length => 60)
76 title = truncate(issue.subject, :length => 60)
77 else
77 else
78 subject = issue.subject
78 subject = issue.subject
79 if options[:truncate]
79 if options[:truncate]
80 subject = truncate(subject, :length => options[:truncate])
80 subject = truncate(subject, :length => options[:truncate])
81 end
81 end
82 end
82 end
83 s = link_to "#{issue.tracker} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
83 s = link_to "#{issue.tracker} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
84 :class => issue.css_classes,
84 :class => issue.css_classes,
85 :title => title
85 :title => title
86 s << ": #{h subject}" if subject
86 s << ": #{h subject}" if subject
87 s = "#{h issue.project} - " + s if options[:project]
87 s = "#{h issue.project} - " + s if options[:project]
88 s
88 s
89 end
89 end
90
90
91 # Generates a link to an attachment.
91 # Generates a link to an attachment.
92 # Options:
92 # Options:
93 # * :text - Link text (default to attachment filename)
93 # * :text - Link text (default to attachment filename)
94 # * :download - Force download (default: false)
94 # * :download - Force download (default: false)
95 def link_to_attachment(attachment, options={})
95 def link_to_attachment(attachment, options={})
96 text = options.delete(:text) || attachment.filename
96 text = options.delete(:text) || attachment.filename
97 action = options.delete(:download) ? 'download' : 'show'
97 action = options.delete(:download) ? 'download' : 'show'
98
98
99 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
99 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
100 end
100 end
101
101
102 # Generates a link to a SCM revision
102 # Generates a link to a SCM revision
103 # Options:
103 # Options:
104 # * :text - Link text (default to the formatted revision)
104 # * :text - Link text (default to the formatted revision)
105 def link_to_revision(revision, project, options={})
105 def link_to_revision(revision, project, options={})
106 text = options.delete(:text) || format_revision(revision)
106 text = options.delete(:text) || format_revision(revision)
107
107
108 link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
108 link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision))
109 end
109 end
110
110
111 # Generates a link to a project if active
111 # Generates a link to a project if active
112 # Examples:
112 # Examples:
113 #
113 #
114 # link_to_project(project) # => link to the specified project overview
114 # link_to_project(project) # => link to the specified project overview
115 # link_to_project(project, :action=>'settings') # => link to project settings
115 # link_to_project(project, :action=>'settings') # => link to project settings
116 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
116 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
117 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
117 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
118 #
118 #
119 def link_to_project(project, options={}, html_options = nil)
119 def link_to_project(project, options={}, html_options = nil)
120 if project.active?
120 if project.active?
121 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
121 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
122 link_to(h(project), url, html_options)
122 link_to(h(project), url, html_options)
123 else
123 else
124 h(project)
124 h(project)
125 end
125 end
126 end
126 end
127
127
128 def toggle_link(name, id, options={})
128 def toggle_link(name, id, options={})
129 onclick = "Element.toggle('#{id}'); "
129 onclick = "Element.toggle('#{id}'); "
130 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
130 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
131 onclick << "return false;"
131 onclick << "return false;"
132 link_to(name, "#", :onclick => onclick)
132 link_to(name, "#", :onclick => onclick)
133 end
133 end
134
134
135 def image_to_function(name, function, html_options = {})
135 def image_to_function(name, function, html_options = {})
136 html_options.symbolize_keys!
136 html_options.symbolize_keys!
137 tag(:input, html_options.merge({
137 tag(:input, html_options.merge({
138 :type => "image", :src => image_path(name),
138 :type => "image", :src => image_path(name),
139 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
139 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
140 }))
140 }))
141 end
141 end
142
142
143 def prompt_to_remote(name, text, param, url, html_options = {})
143 def prompt_to_remote(name, text, param, url, html_options = {})
144 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
144 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
145 link_to name, {}, html_options
145 link_to name, {}, html_options
146 end
146 end
147
147
148 def format_activity_title(text)
148 def format_activity_title(text)
149 h(truncate_single_line(text, :length => 100))
149 h(truncate_single_line(text, :length => 100))
150 end
150 end
151
151
152 def format_activity_day(date)
152 def format_activity_day(date)
153 date == Date.today ? l(:label_today).titleize : format_date(date)
153 date == Date.today ? l(:label_today).titleize : format_date(date)
154 end
154 end
155
155
156 def format_activity_description(text)
156 def format_activity_description(text)
157 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
157 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
158 end
158 end
159
159
160 def format_version_name(version)
160 def format_version_name(version)
161 if version.project == @project
161 if version.project == @project
162 h(version)
162 h(version)
163 else
163 else
164 h("#{version.project} - #{version}")
164 h("#{version.project} - #{version}")
165 end
165 end
166 end
166 end
167
167
168 def due_date_distance_in_words(date)
168 def due_date_distance_in_words(date)
169 if date
169 if date
170 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
170 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
171 end
171 end
172 end
172 end
173
173
174 def render_page_hierarchy(pages, node=nil)
174 def render_page_hierarchy(pages, node=nil)
175 content = ''
175 content = ''
176 if pages[node]
176 if pages[node]
177 content << "<ul class=\"pages-hierarchy\">\n"
177 content << "<ul class=\"pages-hierarchy\">\n"
178 pages[node].each do |page|
178 pages[node].each do |page|
179 content << "<li>"
179 content << "<li>"
180 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
180 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
181 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
181 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
182 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
182 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
183 content << "</li>\n"
183 content << "</li>\n"
184 end
184 end
185 content << "</ul>\n"
185 content << "</ul>\n"
186 end
186 end
187 content
187 content
188 end
188 end
189
189
190 # Renders flash messages
190 # Renders flash messages
191 def render_flash_messages
191 def render_flash_messages
192 s = ''
192 s = ''
193 flash.each do |k,v|
193 flash.each do |k,v|
194 s << content_tag('div', v, :class => "flash #{k}")
194 s << content_tag('div', v, :class => "flash #{k}")
195 end
195 end
196 s
196 s
197 end
197 end
198
198
199 # Renders tabs and their content
199 # Renders tabs and their content
200 def render_tabs(tabs)
200 def render_tabs(tabs)
201 if tabs.any?
201 if tabs.any?
202 render :partial => 'common/tabs', :locals => {:tabs => tabs}
202 render :partial => 'common/tabs', :locals => {:tabs => tabs}
203 else
203 else
204 content_tag 'p', l(:label_no_data), :class => "nodata"
204 content_tag 'p', l(:label_no_data), :class => "nodata"
205 end
205 end
206 end
206 end
207
207
208 # Renders the project quick-jump box
208 # Renders the project quick-jump box
209 def render_project_jump_box
209 def render_project_jump_box
210 # Retrieve them now to avoid a COUNT query
210 # Retrieve them now to avoid a COUNT query
211 projects = User.current.projects.all
211 projects = User.current.projects.all
212 if projects.any?
212 if projects.any?
213 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
213 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
214 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
214 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
215 '<option value="" disabled="disabled">---</option>'
215 '<option value="" disabled="disabled">---</option>'
216 s << project_tree_options_for_select(projects, :selected => @project) do |p|
216 s << project_tree_options_for_select(projects, :selected => @project) do |p|
217 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
217 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
218 end
218 end
219 s << '</select>'
219 s << '</select>'
220 s
220 s
221 end
221 end
222 end
222 end
223
223
224 def project_tree_options_for_select(projects, options = {})
224 def project_tree_options_for_select(projects, options = {})
225 s = ''
225 s = ''
226 project_tree(projects) do |project, level|
226 project_tree(projects) do |project, level|
227 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
227 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
228 tag_options = {:value => project.id}
228 tag_options = {:value => project.id}
229 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
229 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
230 tag_options[:selected] = 'selected'
230 tag_options[:selected] = 'selected'
231 else
231 else
232 tag_options[:selected] = nil
232 tag_options[:selected] = nil
233 end
233 end
234 tag_options.merge!(yield(project)) if block_given?
234 tag_options.merge!(yield(project)) if block_given?
235 s << content_tag('option', name_prefix + h(project), tag_options)
235 s << content_tag('option', name_prefix + h(project), tag_options)
236 end
236 end
237 s
237 s
238 end
238 end
239
239
240 # Yields the given block for each project with its level in the tree
240 # Yields the given block for each project with its level in the tree
241 #
241 #
242 # Wrapper for Project#project_tree
242 # Wrapper for Project#project_tree
243 def project_tree(projects, &block)
243 def project_tree(projects, &block)
244 Project.project_tree(projects, &block)
244 Project.project_tree(projects, &block)
245 end
245 end
246
246
247 def project_nested_ul(projects, &block)
247 def project_nested_ul(projects, &block)
248 s = ''
248 s = ''
249 if projects.any?
249 if projects.any?
250 ancestors = []
250 ancestors = []
251 projects.sort_by(&:lft).each do |project|
251 projects.sort_by(&:lft).each do |project|
252 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
252 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
253 s << "<ul>\n"
253 s << "<ul>\n"
254 else
254 else
255 ancestors.pop
255 ancestors.pop
256 s << "</li>"
256 s << "</li>"
257 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
257 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
258 ancestors.pop
258 ancestors.pop
259 s << "</ul></li>\n"
259 s << "</ul></li>\n"
260 end
260 end
261 end
261 end
262 s << "<li>"
262 s << "<li>"
263 s << yield(project).to_s
263 s << yield(project).to_s
264 ancestors << project
264 ancestors << project
265 end
265 end
266 s << ("</li></ul>\n" * ancestors.size)
266 s << ("</li></ul>\n" * ancestors.size)
267 end
267 end
268 s
268 s
269 end
269 end
270
270
271 def principals_check_box_tags(name, principals)
271 def principals_check_box_tags(name, principals)
272 s = ''
272 s = ''
273 principals.sort.each do |principal|
273 principals.sort.each do |principal|
274 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
274 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
275 end
275 end
276 s
276 s
277 end
277 end
278
278
279 # Truncates and returns the string as a single line
279 # Truncates and returns the string as a single line
280 def truncate_single_line(string, *args)
280 def truncate_single_line(string, *args)
281 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
281 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
282 end
282 end
283
283
284 # Truncates at line break after 250 characters or options[:length]
284 # Truncates at line break after 250 characters or options[:length]
285 def truncate_lines(string, options={})
285 def truncate_lines(string, options={})
286 length = options[:length] || 250
286 length = options[:length] || 250
287 if string.to_s =~ /\A(.{#{length}}.*?)$/m
287 if string.to_s =~ /\A(.{#{length}}.*?)$/m
288 "#{$1}..."
288 "#{$1}..."
289 else
289 else
290 string
290 string
291 end
291 end
292 end
292 end
293
293
294 def html_hours(text)
294 def html_hours(text)
295 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
295 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
296 end
296 end
297
297
298 def authoring(created, author, options={})
298 def authoring(created, author, options={})
299 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
299 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
300 end
300 end
301
301
302 def time_tag(time)
302 def time_tag(time)
303 text = distance_of_time_in_words(Time.now, time)
303 text = distance_of_time_in_words(Time.now, time)
304 if @project
304 if @project
305 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
305 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
306 else
306 else
307 content_tag('acronym', text, :title => format_time(time))
307 content_tag('acronym', text, :title => format_time(time))
308 end
308 end
309 end
309 end
310
310
311 def syntax_highlight(name, content)
311 def syntax_highlight(name, content)
312 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
312 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
313 end
313 end
314
314
315 def to_path_param(path)
315 def to_path_param(path)
316 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
316 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
317 end
317 end
318
318
319 def pagination_links_full(paginator, count=nil, options={})
319 def pagination_links_full(paginator, count=nil, options={})
320 page_param = options.delete(:page_param) || :page
320 page_param = options.delete(:page_param) || :page
321 per_page_links = options.delete(:per_page_links)
321 per_page_links = options.delete(:per_page_links)
322 url_param = params.dup
322 url_param = params.dup
323 # don't reuse query params if filters are present
323 # don't reuse query params if filters are present
324 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
324 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
325
325
326 html = ''
326 html = ''
327 if paginator.current.previous
327 if paginator.current.previous
328 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
328 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
329 end
329 end
330
330
331 html << (pagination_links_each(paginator, options) do |n|
331 html << (pagination_links_each(paginator, options) do |n|
332 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
332 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
333 end || '')
333 end || '')
334
334
335 if paginator.current.next
335 if paginator.current.next
336 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
336 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
337 end
337 end
338
338
339 unless count.nil?
339 unless count.nil?
340 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
340 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
341 if per_page_links != false && links = per_page_links(paginator.items_per_page)
341 if per_page_links != false && links = per_page_links(paginator.items_per_page)
342 html << " | #{links}"
342 html << " | #{links}"
343 end
343 end
344 end
344 end
345
345
346 html
346 html
347 end
347 end
348
348
349 def per_page_links(selected=nil)
349 def per_page_links(selected=nil)
350 url_param = params.dup
350 url_param = params.dup
351 url_param.clear if url_param.has_key?(:set_filter)
351 url_param.clear if url_param.has_key?(:set_filter)
352
352
353 links = Setting.per_page_options_array.collect do |n|
353 links = Setting.per_page_options_array.collect do |n|
354 n == selected ? n : link_to_remote(n, {:update => "content",
354 n == selected ? n : link_to_remote(n, {:update => "content",
355 :url => params.dup.merge(:per_page => n),
355 :url => params.dup.merge(:per_page => n),
356 :method => :get},
356 :method => :get},
357 {:href => url_for(url_param.merge(:per_page => n))})
357 {:href => url_for(url_param.merge(:per_page => n))})
358 end
358 end
359 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
359 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
360 end
360 end
361
361
362 def reorder_links(name, url)
362 def reorder_links(name, url)
363 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
363 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
364 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
364 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
365 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
365 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
366 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
366 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
367 end
367 end
368
368
369 def breadcrumb(*args)
369 def breadcrumb(*args)
370 elements = args.flatten
370 elements = args.flatten
371 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
371 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
372 end
372 end
373
373
374 def other_formats_links(&block)
374 def other_formats_links(&block)
375 concat('<p class="other-formats">' + l(:label_export_to))
375 concat('<p class="other-formats">' + l(:label_export_to))
376 yield Redmine::Views::OtherFormatsBuilder.new(self)
376 yield Redmine::Views::OtherFormatsBuilder.new(self)
377 concat('</p>')
377 concat('</p>')
378 end
378 end
379
379
380 def page_header_title
380 def page_header_title
381 if @project.nil? || @project.new_record?
381 if @project.nil? || @project.new_record?
382 h(Setting.app_title)
382 h(Setting.app_title)
383 else
383 else
384 b = []
384 b = []
385 ancestors = (@project.root? ? [] : @project.ancestors.visible)
385 ancestors = (@project.root? ? [] : @project.ancestors.visible)
386 if ancestors.any?
386 if ancestors.any?
387 root = ancestors.shift
387 root = ancestors.shift
388 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
388 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
389 if ancestors.size > 2
389 if ancestors.size > 2
390 b << '&#8230;'
390 b << '&#8230;'
391 ancestors = ancestors[-2, 2]
391 ancestors = ancestors[-2, 2]
392 end
392 end
393 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
393 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
394 end
394 end
395 b << h(@project)
395 b << h(@project)
396 b.join(' &#187; ')
396 b.join(' &#187; ')
397 end
397 end
398 end
398 end
399
399
400 def html_title(*args)
400 def html_title(*args)
401 if args.empty?
401 if args.empty?
402 title = []
402 title = []
403 title << @project.name if @project
403 title << @project.name if @project
404 title += @html_title if @html_title
404 title += @html_title if @html_title
405 title << Setting.app_title
405 title << Setting.app_title
406 title.select {|t| !t.blank? }.join(' - ')
406 title.select {|t| !t.blank? }.join(' - ')
407 else
407 else
408 @html_title ||= []
408 @html_title ||= []
409 @html_title += args
409 @html_title += args
410 end
410 end
411 end
411 end
412
412
413 # Returns the theme, controller name, and action as css classes for the
413 # Returns the theme, controller name, and action as css classes for the
414 # HTML body.
414 # HTML body.
415 def body_css_classes
415 def body_css_classes
416 css = []
416 css = []
417 if theme = Redmine::Themes.theme(Setting.ui_theme)
417 if theme = Redmine::Themes.theme(Setting.ui_theme)
418 css << 'theme-' + theme.name
418 css << 'theme-' + theme.name
419 end
419 end
420
420
421 css << 'controller-' + params[:controller]
421 css << 'controller-' + params[:controller]
422 css << 'action-' + params[:action]
422 css << 'action-' + params[:action]
423 css.join(' ')
423 css.join(' ')
424 end
424 end
425
425
426 def accesskey(s)
426 def accesskey(s)
427 Redmine::AccessKeys.key_for s
427 Redmine::AccessKeys.key_for s
428 end
428 end
429
429
430 # Formats text according to system settings.
430 # Formats text according to system settings.
431 # 2 ways to call this method:
431 # 2 ways to call this method:
432 # * with a String: textilizable(text, options)
432 # * with a String: textilizable(text, options)
433 # * with an object and one of its attribute: textilizable(issue, :description, options)
433 # * with an object and one of its attribute: textilizable(issue, :description, options)
434 def textilizable(*args)
434 def textilizable(*args)
435 options = args.last.is_a?(Hash) ? args.pop : {}
435 options = args.last.is_a?(Hash) ? args.pop : {}
436 case args.size
436 case args.size
437 when 1
437 when 1
438 obj = options[:object]
438 obj = options[:object]
439 text = args.shift
439 text = args.shift
440 when 2
440 when 2
441 obj = args.shift
441 obj = args.shift
442 attr = args.shift
442 attr = args.shift
443 text = obj.send(attr).to_s
443 text = obj.send(attr).to_s
444 else
444 else
445 raise ArgumentError, 'invalid arguments to textilizable'
445 raise ArgumentError, 'invalid arguments to textilizable'
446 end
446 end
447 return '' if text.blank?
447 return '' if text.blank?
448 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
448 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
449 only_path = options.delete(:only_path) == false ? false : true
449 only_path = options.delete(:only_path) == false ? false : true
450
450
451 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
451 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
452
452
453 parse_non_pre_blocks(text) do |text|
453 parse_non_pre_blocks(text) do |text|
454 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
454 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
455 send method_name, text, project, obj, attr, only_path, options
455 send method_name, text, project, obj, attr, only_path, options
456 end
456 end
457 end
457 end
458 end
458 end
459
459
460 def parse_non_pre_blocks(text)
460 def parse_non_pre_blocks(text)
461 s = StringScanner.new(text)
461 s = StringScanner.new(text)
462 tags = []
462 tags = []
463 parsed = ''
463 parsed = ''
464 while !s.eos?
464 while !s.eos?
465 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
465 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
466 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
466 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
467 if tags.empty?
467 if tags.empty?
468 yield text
468 yield text
469 end
469 end
470 parsed << text
470 parsed << text
471 if tag
471 if tag
472 if closing
472 if closing
473 if tags.last == tag.downcase
473 if tags.last == tag.downcase
474 tags.pop
474 tags.pop
475 end
475 end
476 else
476 else
477 tags << tag.downcase
477 tags << tag.downcase
478 end
478 end
479 parsed << full_tag
479 parsed << full_tag
480 end
480 end
481 end
481 end
482 # Close any non closing tags
482 # Close any non closing tags
483 while tag = tags.pop
483 while tag = tags.pop
484 parsed << "</#{tag}>"
484 parsed << "</#{tag}>"
485 end
485 end
486 parsed
486 parsed
487 end
487 end
488
488
489 def parse_inline_attachments(text, project, obj, attr, only_path, options)
489 def parse_inline_attachments(text, project, obj, attr, only_path, options)
490 # when using an image link, try to use an attachment, if possible
490 # when using an image link, try to use an attachment, if possible
491 if options[:attachments] || (obj && obj.respond_to?(:attachments))
491 if options[:attachments] || (obj && obj.respond_to?(:attachments))
492 attachments = nil
492 attachments = nil
493 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
493 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
494 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
494 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
495 attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
495 attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
496 # search for the picture in attachments
496 # search for the picture in attachments
497 if found = attachments.detect { |att| att.filename.downcase == filename }
497 if found = attachments.detect { |att| att.filename.downcase == filename }
498 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
498 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
499 desc = found.description.to_s.gsub('"', '')
499 desc = found.description.to_s.gsub('"', '')
500 if !desc.blank? && alttext.blank?
500 if !desc.blank? && alttext.blank?
501 alt = " title=\"#{desc}\" alt=\"#{desc}\""
501 alt = " title=\"#{desc}\" alt=\"#{desc}\""
502 end
502 end
503 "src=\"#{image_url}\"#{alt}"
503 "src=\"#{image_url}\"#{alt}"
504 else
504 else
505 m
505 m
506 end
506 end
507 end
507 end
508 end
508 end
509 end
509 end
510
510
511 # Wiki links
511 # Wiki links
512 #
512 #
513 # Examples:
513 # Examples:
514 # [[mypage]]
514 # [[mypage]]
515 # [[mypage|mytext]]
515 # [[mypage|mytext]]
516 # wiki links can refer other project wikis, using project name or identifier:
516 # wiki links can refer other project wikis, using project name or identifier:
517 # [[project:]] -> wiki starting page
517 # [[project:]] -> wiki starting page
518 # [[project:|mytext]]
518 # [[project:|mytext]]
519 # [[project:mypage]]
519 # [[project:mypage]]
520 # [[project:mypage|mytext]]
520 # [[project:mypage|mytext]]
521 def parse_wiki_links(text, project, obj, attr, only_path, options)
521 def parse_wiki_links(text, project, obj, attr, only_path, options)
522 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
522 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
523 link_project = project
523 link_project = project
524 esc, all, page, title = $1, $2, $3, $5
524 esc, all, page, title = $1, $2, $3, $5
525 if esc.nil?
525 if esc.nil?
526 if page =~ /^([^\:]+)\:(.*)$/
526 if page =~ /^([^\:]+)\:(.*)$/
527 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
527 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
528 page = $2
528 page = $2
529 title ||= $1 if page.blank?
529 title ||= $1 if page.blank?
530 end
530 end
531
531
532 if link_project && link_project.wiki
532 if link_project && link_project.wiki
533 # extract anchor
533 # extract anchor
534 anchor = nil
534 anchor = nil
535 if page =~ /^(.+?)\#(.+)$/
535 if page =~ /^(.+?)\#(.+)$/
536 page, anchor = $1, $2
536 page, anchor = $1, $2
537 end
537 end
538 # check if page exists
538 # check if page exists
539 wiki_page = link_project.wiki.find_page(page)
539 wiki_page = link_project.wiki.find_page(page)
540 url = case options[:wiki_links]
540 url = case options[:wiki_links]
541 when :local; "#{title}.html"
541 when :local; "#{title}.html"
542 when :anchor; "##{title}" # used for single-file wiki export
542 when :anchor; "##{title}" # used for single-file wiki export
543 else
543 else
544 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => Wiki.titleize(page), :anchor => anchor)
544 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
545 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
545 end
546 end
546 link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
547 link_to((title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
547 else
548 else
548 # project or wiki doesn't exist
549 # project or wiki doesn't exist
549 all
550 all
550 end
551 end
551 else
552 else
552 all
553 all
553 end
554 end
554 end
555 end
555 end
556 end
556
557
557 # Redmine links
558 # Redmine links
558 #
559 #
559 # Examples:
560 # Examples:
560 # Issues:
561 # Issues:
561 # #52 -> Link to issue #52
562 # #52 -> Link to issue #52
562 # Changesets:
563 # Changesets:
563 # r52 -> Link to revision 52
564 # r52 -> Link to revision 52
564 # commit:a85130f -> Link to scmid starting with a85130f
565 # commit:a85130f -> Link to scmid starting with a85130f
565 # Documents:
566 # Documents:
566 # document#17 -> Link to document with id 17
567 # document#17 -> Link to document with id 17
567 # document:Greetings -> Link to the document with title "Greetings"
568 # document:Greetings -> Link to the document with title "Greetings"
568 # document:"Some document" -> Link to the document with title "Some document"
569 # document:"Some document" -> Link to the document with title "Some document"
569 # Versions:
570 # Versions:
570 # version#3 -> Link to version with id 3
571 # version#3 -> Link to version with id 3
571 # version:1.0.0 -> Link to version named "1.0.0"
572 # version:1.0.0 -> Link to version named "1.0.0"
572 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
573 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
573 # Attachments:
574 # Attachments:
574 # attachment:file.zip -> Link to the attachment of the current object named file.zip
575 # attachment:file.zip -> Link to the attachment of the current object named file.zip
575 # Source files:
576 # Source files:
576 # source:some/file -> Link to the file located at /some/file in the project's repository
577 # source:some/file -> Link to the file located at /some/file in the project's repository
577 # source:some/file@52 -> Link to the file's revision 52
578 # source:some/file@52 -> Link to the file's revision 52
578 # source:some/file#L120 -> Link to line 120 of the file
579 # source:some/file#L120 -> Link to line 120 of the file
579 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
580 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
580 # export:some/file -> Force the download of the file
581 # export:some/file -> Force the download of the file
581 # Forum messages:
582 # Forum messages:
582 # message#1218 -> Link to message with id 1218
583 # message#1218 -> Link to message with id 1218
583 def parse_redmine_links(text, project, obj, attr, only_path, options)
584 def parse_redmine_links(text, project, obj, attr, only_path, options)
584 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
585 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
585 leading, esc, prefix, sep, identifier = $1, $2, $3, $5 || $7, $6 || $8
586 leading, esc, prefix, sep, identifier = $1, $2, $3, $5 || $7, $6 || $8
586 link = nil
587 link = nil
587 if esc.nil?
588 if esc.nil?
588 if prefix.nil? && sep == 'r'
589 if prefix.nil? && sep == 'r'
589 if project && (changeset = project.changesets.find_by_revision(identifier))
590 if project && (changeset = project.changesets.find_by_revision(identifier))
590 link = link_to("r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
591 link = link_to("r#{identifier}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
591 :class => 'changeset',
592 :class => 'changeset',
592 :title => truncate_single_line(changeset.comments, :length => 100))
593 :title => truncate_single_line(changeset.comments, :length => 100))
593 end
594 end
594 elsif sep == '#'
595 elsif sep == '#'
595 oid = identifier.to_i
596 oid = identifier.to_i
596 case prefix
597 case prefix
597 when nil
598 when nil
598 if issue = Issue.visible.find_by_id(oid, :include => :status)
599 if issue = Issue.visible.find_by_id(oid, :include => :status)
599 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
600 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
600 :class => issue.css_classes,
601 :class => issue.css_classes,
601 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
602 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
602 end
603 end
603 when 'document'
604 when 'document'
604 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
605 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
605 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
606 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
606 :class => 'document'
607 :class => 'document'
607 end
608 end
608 when 'version'
609 when 'version'
609 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
610 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
610 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
611 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
611 :class => 'version'
612 :class => 'version'
612 end
613 end
613 when 'message'
614 when 'message'
614 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
615 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
615 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
616 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
616 :controller => 'messages',
617 :controller => 'messages',
617 :action => 'show',
618 :action => 'show',
618 :board_id => message.board,
619 :board_id => message.board,
619 :id => message.root,
620 :id => message.root,
620 :anchor => (message.parent ? "message-#{message.id}" : nil)},
621 :anchor => (message.parent ? "message-#{message.id}" : nil)},
621 :class => 'message'
622 :class => 'message'
622 end
623 end
623 when 'project'
624 when 'project'
624 if p = Project.visible.find_by_id(oid)
625 if p = Project.visible.find_by_id(oid)
625 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
626 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
626 end
627 end
627 end
628 end
628 elsif sep == ':'
629 elsif sep == ':'
629 # removes the double quotes if any
630 # removes the double quotes if any
630 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
631 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
631 case prefix
632 case prefix
632 when 'document'
633 when 'document'
633 if project && document = project.documents.find_by_title(name)
634 if project && document = project.documents.find_by_title(name)
634 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
635 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
635 :class => 'document'
636 :class => 'document'
636 end
637 end
637 when 'version'
638 when 'version'
638 if project && version = project.versions.find_by_name(name)
639 if project && version = project.versions.find_by_name(name)
639 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
640 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
640 :class => 'version'
641 :class => 'version'
641 end
642 end
642 when 'commit'
643 when 'commit'
643 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
644 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
644 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
645 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
645 :class => 'changeset',
646 :class => 'changeset',
646 :title => truncate_single_line(changeset.comments, :length => 100)
647 :title => truncate_single_line(changeset.comments, :length => 100)
647 end
648 end
648 when 'source', 'export'
649 when 'source', 'export'
649 if project && project.repository
650 if project && project.repository
650 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
651 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
651 path, rev, anchor = $1, $3, $5
652 path, rev, anchor = $1, $3, $5
652 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
653 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
653 :path => to_path_param(path),
654 :path => to_path_param(path),
654 :rev => rev,
655 :rev => rev,
655 :anchor => anchor,
656 :anchor => anchor,
656 :format => (prefix == 'export' ? 'raw' : nil)},
657 :format => (prefix == 'export' ? 'raw' : nil)},
657 :class => (prefix == 'export' ? 'source download' : 'source')
658 :class => (prefix == 'export' ? 'source download' : 'source')
658 end
659 end
659 when 'attachment'
660 when 'attachment'
660 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
661 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
661 if attachments && attachment = attachments.detect {|a| a.filename == name }
662 if attachments && attachment = attachments.detect {|a| a.filename == name }
662 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
663 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
663 :class => 'attachment'
664 :class => 'attachment'
664 end
665 end
665 when 'project'
666 when 'project'
666 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
667 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
667 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
668 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
668 end
669 end
669 end
670 end
670 end
671 end
671 end
672 end
672 leading + (link || "#{prefix}#{sep}#{identifier}")
673 leading + (link || "#{prefix}#{sep}#{identifier}")
673 end
674 end
674 end
675 end
675
676
676 # Same as Rails' simple_format helper without using paragraphs
677 # Same as Rails' simple_format helper without using paragraphs
677 def simple_format_without_paragraph(text)
678 def simple_format_without_paragraph(text)
678 text.to_s.
679 text.to_s.
679 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
680 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
680 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
681 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
681 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
682 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
682 end
683 end
683
684
684 def lang_options_for_select(blank=true)
685 def lang_options_for_select(blank=true)
685 (blank ? [["(auto)", ""]] : []) +
686 (blank ? [["(auto)", ""]] : []) +
686 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
687 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
687 end
688 end
688
689
689 def label_tag_for(name, option_tags = nil, options = {})
690 def label_tag_for(name, option_tags = nil, options = {})
690 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
691 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
691 content_tag("label", label_text)
692 content_tag("label", label_text)
692 end
693 end
693
694
694 def labelled_tabular_form_for(name, object, options, &proc)
695 def labelled_tabular_form_for(name, object, options, &proc)
695 options[:html] ||= {}
696 options[:html] ||= {}
696 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
697 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
697 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
698 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
698 end
699 end
699
700
700 def back_url_hidden_field_tag
701 def back_url_hidden_field_tag
701 back_url = params[:back_url] || request.env['HTTP_REFERER']
702 back_url = params[:back_url] || request.env['HTTP_REFERER']
702 back_url = CGI.unescape(back_url.to_s)
703 back_url = CGI.unescape(back_url.to_s)
703 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
704 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
704 end
705 end
705
706
706 def check_all_links(form_name)
707 def check_all_links(form_name)
707 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
708 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
708 " | " +
709 " | " +
709 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
710 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
710 end
711 end
711
712
712 def progress_bar(pcts, options={})
713 def progress_bar(pcts, options={})
713 pcts = [pcts, pcts] unless pcts.is_a?(Array)
714 pcts = [pcts, pcts] unless pcts.is_a?(Array)
714 pcts = pcts.collect(&:round)
715 pcts = pcts.collect(&:round)
715 pcts[1] = pcts[1] - pcts[0]
716 pcts[1] = pcts[1] - pcts[0]
716 pcts << (100 - pcts[1] - pcts[0])
717 pcts << (100 - pcts[1] - pcts[0])
717 width = options[:width] || '100px;'
718 width = options[:width] || '100px;'
718 legend = options[:legend] || ''
719 legend = options[:legend] || ''
719 content_tag('table',
720 content_tag('table',
720 content_tag('tr',
721 content_tag('tr',
721 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
722 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
722 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
723 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
723 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
724 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
724 ), :class => 'progress', :style => "width: #{width};") +
725 ), :class => 'progress', :style => "width: #{width};") +
725 content_tag('p', legend, :class => 'pourcent')
726 content_tag('p', legend, :class => 'pourcent')
726 end
727 end
727
728
728 def checked_image(checked=true)
729 def checked_image(checked=true)
729 if checked
730 if checked
730 image_tag 'toggle_check.png'
731 image_tag 'toggle_check.png'
731 end
732 end
732 end
733 end
733
734
734 def context_menu(url)
735 def context_menu(url)
735 unless @context_menu_included
736 unless @context_menu_included
736 content_for :header_tags do
737 content_for :header_tags do
737 javascript_include_tag('context_menu') +
738 javascript_include_tag('context_menu') +
738 stylesheet_link_tag('context_menu')
739 stylesheet_link_tag('context_menu')
739 end
740 end
740 if l(:direction) == 'rtl'
741 if l(:direction) == 'rtl'
741 content_for :header_tags do
742 content_for :header_tags do
742 stylesheet_link_tag('context_menu_rtl')
743 stylesheet_link_tag('context_menu_rtl')
743 end
744 end
744 end
745 end
745 @context_menu_included = true
746 @context_menu_included = true
746 end
747 end
747 javascript_tag "new ContextMenu('#{ url_for(url) }')"
748 javascript_tag "new ContextMenu('#{ url_for(url) }')"
748 end
749 end
749
750
750 def context_menu_link(name, url, options={})
751 def context_menu_link(name, url, options={})
751 options[:class] ||= ''
752 options[:class] ||= ''
752 if options.delete(:selected)
753 if options.delete(:selected)
753 options[:class] << ' icon-checked disabled'
754 options[:class] << ' icon-checked disabled'
754 options[:disabled] = true
755 options[:disabled] = true
755 end
756 end
756 if options.delete(:disabled)
757 if options.delete(:disabled)
757 options.delete(:method)
758 options.delete(:method)
758 options.delete(:confirm)
759 options.delete(:confirm)
759 options.delete(:onclick)
760 options.delete(:onclick)
760 options[:class] << ' disabled'
761 options[:class] << ' disabled'
761 url = '#'
762 url = '#'
762 end
763 end
763 link_to name, url, options
764 link_to name, url, options
764 end
765 end
765
766
766 def calendar_for(field_id)
767 def calendar_for(field_id)
767 include_calendar_headers_tags
768 include_calendar_headers_tags
768 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
769 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
769 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
770 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
770 end
771 end
771
772
772 def include_calendar_headers_tags
773 def include_calendar_headers_tags
773 unless @calendar_headers_tags_included
774 unless @calendar_headers_tags_included
774 @calendar_headers_tags_included = true
775 @calendar_headers_tags_included = true
775 content_for :header_tags do
776 content_for :header_tags do
776 start_of_week = case Setting.start_of_week.to_i
777 start_of_week = case Setting.start_of_week.to_i
777 when 1
778 when 1
778 'Calendar._FD = 1;' # Monday
779 'Calendar._FD = 1;' # Monday
779 when 7
780 when 7
780 'Calendar._FD = 0;' # Sunday
781 'Calendar._FD = 0;' # Sunday
781 else
782 else
782 '' # use language
783 '' # use language
783 end
784 end
784
785
785 javascript_include_tag('calendar/calendar') +
786 javascript_include_tag('calendar/calendar') +
786 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
787 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
787 javascript_tag(start_of_week) +
788 javascript_tag(start_of_week) +
788 javascript_include_tag('calendar/calendar-setup') +
789 javascript_include_tag('calendar/calendar-setup') +
789 stylesheet_link_tag('calendar')
790 stylesheet_link_tag('calendar')
790 end
791 end
791 end
792 end
792 end
793 end
793
794
794 def content_for(name, content = nil, &block)
795 def content_for(name, content = nil, &block)
795 @has_content ||= {}
796 @has_content ||= {}
796 @has_content[name] = true
797 @has_content[name] = true
797 super(name, content, &block)
798 super(name, content, &block)
798 end
799 end
799
800
800 def has_content?(name)
801 def has_content?(name)
801 (@has_content && @has_content[name]) || false
802 (@has_content && @has_content[name]) || false
802 end
803 end
803
804
804 # Returns the avatar image tag for the given +user+ if avatars are enabled
805 # Returns the avatar image tag for the given +user+ if avatars are enabled
805 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
806 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
806 def avatar(user, options = { })
807 def avatar(user, options = { })
807 if Setting.gravatar_enabled?
808 if Setting.gravatar_enabled?
808 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
809 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
809 email = nil
810 email = nil
810 if user.respond_to?(:mail)
811 if user.respond_to?(:mail)
811 email = user.mail
812 email = user.mail
812 elsif user.to_s =~ %r{<(.+?)>}
813 elsif user.to_s =~ %r{<(.+?)>}
813 email = $1
814 email = $1
814 end
815 end
815 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
816 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
816 else
817 else
817 ''
818 ''
818 end
819 end
819 end
820 end
820
821
821 def favicon
822 def favicon
822 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
823 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />"
823 end
824 end
824
825
825 private
826 private
826
827
827 def wiki_helper
828 def wiki_helper
828 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
829 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
829 extend helper
830 extend helper
830 return self
831 return self
831 end
832 end
832
833
833 def link_to_remote_content_update(text, url_params)
834 def link_to_remote_content_update(text, url_params)
834 link_to_remote(text,
835 link_to_remote(text,
835 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
836 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
836 {:href => url_for(:params => url_params)}
837 {:href => url_for(:params => url_params)}
837 )
838 )
838 end
839 end
839
840
840 end
841 end
@@ -1,41 +1,41
1 <% if @project.shared_versions.any? %>
1 <% if @project.shared_versions.any? %>
2 <table class="list versions">
2 <table class="list versions">
3 <thead><tr>
3 <thead><tr>
4 <th><%= l(:label_version) %></th>
4 <th><%= l(:label_version) %></th>
5 <th><%= l(:field_effective_date) %></th>
5 <th><%= l(:field_effective_date) %></th>
6 <th><%= l(:field_description) %></th>
6 <th><%= l(:field_description) %></th>
7 <th><%= l(:field_status) %></th>
7 <th><%= l(:field_status) %></th>
8 <th><%= l(:field_sharing) %></th>
8 <th><%= l(:field_sharing) %></th>
9 <th><%= l(:label_wiki_page) unless @project.wiki.nil? %></th>
9 <th><%= l(:label_wiki_page) unless @project.wiki.nil? %></th>
10 <th style="width:15%"></th>
10 <th style="width:15%"></th>
11 </tr></thead>
11 </tr></thead>
12 <tbody>
12 <tbody>
13 <% for version in @project.shared_versions.sort %>
13 <% for version in @project.shared_versions.sort %>
14 <tr class="version <%= cycle 'odd', 'even' %> <%=h version.status %> <%= 'shared' if version.project != @project %>">
14 <tr class="version <%= cycle 'odd', 'even' %> <%=h version.status %> <%= 'shared' if version.project != @project %>">
15 <td class="name"><%= link_to_version version %></td>
15 <td class="name"><%= link_to_version version %></td>
16 <td class="date"><%= format_date(version.effective_date) %></td>
16 <td class="date"><%= format_date(version.effective_date) %></td>
17 <td class="description"><%=h version.description %></td>
17 <td class="description"><%=h version.description %></td>
18 <td class="status"><%= l("version_status_#{version.status}") %></td>
18 <td class="status"><%= l("version_status_#{version.status}") %></td>
19 <td class="sharing"><%=h format_version_sharing(version.sharing) %></td>
19 <td class="sharing"><%=h format_version_sharing(version.sharing) %></td>
20 <td><%= link_to(h(version.wiki_page_title), :controller => 'wiki', :id => Wiki.titleize(version.wiki_page_title)) unless version.wiki_page_title.blank? || @project.wiki.nil? %></td>
20 <td><%= link_to(h(version.wiki_page_title), :controller => 'wiki', :action => 'show', :id => Wiki.titleize(version.wiki_page_title), :project_id => @project) unless version.wiki_page_title.blank? || @project.wiki.nil? %></td>
21 <td class="buttons">
21 <td class="buttons">
22 <% if version.project == @project %>
22 <% if version.project == @project %>
23 <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %>
23 <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %>
24 <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
24 <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
25 <% end %>
25 <% end %>
26 </td>
26 </td>
27 </tr>
27 </tr>
28 <% end; reset_cycle %>
28 <% end; reset_cycle %>
29 </tbody>
29 </tbody>
30 </table>
30 </table>
31 <% else %>
31 <% else %>
32 <p class="nodata"><%= l(:label_no_data) %></p>
32 <p class="nodata"><%= l(:label_no_data) %></p>
33 <% end %>
33 <% end %>
34
34
35 <div class="contextual">
35 <div class="contextual">
36 <% if @project.versions.any? %>
36 <% if @project.versions.any? %>
37 <%= link_to l(:label_close_versions), close_completed_project_versions_path(@project), :method => :put %>
37 <%= link_to l(:label_close_versions), close_completed_project_versions_path(@project), :method => :put %>
38 <% end %>
38 <% end %>
39 </div>
39 </div>
40
40
41 <p><%= link_to_if_authorized l(:label_version_new), :controller => 'versions', :action => 'new', :project_id => @project %></p>
41 <p><%= link_to_if_authorized l(:label_version_new), :controller => 'versions', :action => 'new', :project_id => @project %></p>
@@ -1,33 +1,33
1 <div class="contextual">
1 <div class="contextual">
2 <%= watcher_tag(@wiki, User.current) %>
2 <%= watcher_tag(@wiki, User.current) %>
3 </div>
3 </div>
4
4
5 <h2><%= l(:label_index_by_date) %></h2>
5 <h2><%= l(:label_index_by_date) %></h2>
6
6
7 <% if @pages.empty? %>
7 <% if @pages.empty? %>
8 <p class="nodata"><%= l(:label_no_data) %></p>
8 <p class="nodata"><%= l(:label_no_data) %></p>
9 <% end %>
9 <% end %>
10
10
11 <% @pages_by_date.keys.sort.reverse.each do |date| %>
11 <% @pages_by_date.keys.sort.reverse.each do |date| %>
12 <h3><%= format_date(date) %></h3>
12 <h3><%= format_date(date) %></h3>
13 <ul>
13 <ul>
14 <% @pages_by_date[date].each do |page| %>
14 <% @pages_by_date[date].each do |page| %>
15 <li><%= link_to page.pretty_title, :action => 'show', :id => page.title %></li>
15 <li><%= link_to page.pretty_title, :action => 'show', :id => page.title, :project_id => page.project %></li>
16 <% end %>
16 <% end %>
17 </ul>
17 </ul>
18 <% end %>
18 <% end %>
19
19
20 <% content_for :sidebar do %>
20 <% content_for :sidebar do %>
21 <%= render :partial => 'sidebar' %>
21 <%= render :partial => 'sidebar' %>
22 <% end %>
22 <% end %>
23
23
24 <% unless @pages.empty? %>
24 <% unless @pages.empty? %>
25 <% other_formats_links do |f| %>
25 <% other_formats_links do |f| %>
26 <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'show', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %>
26 <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'show', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %>
27 <%= f.link_to('HTML', :url => {:action => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %>
27 <%= f.link_to('HTML', :url => {:action => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %>
28 <% end %>
28 <% end %>
29 <% end %>
29 <% end %>
30
30
31 <% content_for :header_tags do %>
31 <% content_for :header_tags do %>
32 <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'show', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
32 <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'show', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
33 <% end %>
33 <% end %>
@@ -1,28 +1,28
1 <h2><%=h @page.pretty_title %></h2>
1 <h2><%=h @page.pretty_title %></h2>
2
2
3 <% form_for :content, @content, :url => {:action => 'update', :id => @page.title}, :html => {:multipart => true, :id => 'wiki_form'} do |f| %>
3 <% form_for :content, @content, :url => {:action => 'update', :id => @page.title}, :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %>
4 <%= f.hidden_field :version %>
4 <%= f.hidden_field :version %>
5 <%= error_messages_for 'content' %>
5 <%= error_messages_for 'content' %>
6
6
7 <p><%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit) %></p>
7 <p><%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit) %></p>
8 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
8 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
9 <p><label><%=l(:label_attachment_plural)%></label><br /><%= render :partial => 'attachments/form' %></p>
9 <p><label><%=l(:label_attachment_plural)%></label><br /><%= render :partial => 'attachments/form' %></p>
10
10
11 <p><%= submit_tag l(:button_save) %>
11 <p><%= submit_tag l(:button_save) %>
12 <%= link_to_remote l(:label_preview),
12 <%= link_to_remote l(:label_preview),
13 { :url => { :controller => 'wiki', :action => 'preview', :project_id => @project, :id => @page.title },
13 { :url => { :controller => 'wiki', :action => 'preview', :project_id => @project, :id => @page.title },
14 :method => 'post',
14 :method => :post,
15 :update => 'preview',
15 :update => 'preview',
16 :with => "Form.serialize('wiki_form')",
16 :with => "Form.serialize('wiki_form')",
17 :complete => "Element.scrollTo('preview')"
17 :complete => "Element.scrollTo('preview')"
18 }, :accesskey => accesskey(:preview) %></p>
18 }, :accesskey => accesskey(:preview) %></p>
19 <%= wikitoolbar_for 'content_text' %>
19 <%= wikitoolbar_for 'content_text' %>
20 <% end %>
20 <% end %>
21
21
22 <div id="preview" class="wiki"></div>
22 <div id="preview" class="wiki"></div>
23
23
24 <% content_for :header_tags do %>
24 <% content_for :header_tags do %>
25 <%= stylesheet_link_tag 'scm' %>
25 <%= stylesheet_link_tag 'scm' %>
26 <% end %>
26 <% end %>
27
27
28 <% html_title @page.pretty_title %>
28 <% html_title @page.pretty_title %>
@@ -1,36 +1,36
1 <h2><%= @page.pretty_title %></h2>
1 <h2><%= @page.pretty_title %></h2>
2
2
3 <h3><%= l(:label_history) %></h3>
3 <h3><%= l(:label_history) %></h3>
4
4
5 <% form_tag({:action => "diff"}, :method => :get) do %>
5 <% form_tag({:action => "diff"}, :method => :get) do %>
6 <%= hidden_field_tag('project_id', h(@project.to_param)) %>
6 <%= hidden_field_tag('project_id', h(@project.to_param)) %>
7 <table class="list">
7 <table class="list">
8 <thead><tr>
8 <thead><tr>
9 <th>#</th>
9 <th>#</th>
10 <th></th>
10 <th></th>
11 <th></th>
11 <th></th>
12 <th><%= l(:field_updated_on) %></th>
12 <th><%= l(:field_updated_on) %></th>
13 <th><%= l(:field_author) %></th>
13 <th><%= l(:field_author) %></th>
14 <th><%= l(:field_comments) %></th>
14 <th><%= l(:field_comments) %></th>
15 <th></th>
15 <th></th>
16 </tr></thead>
16 </tr></thead>
17 <tbody>
17 <tbody>
18 <% show_diff = @versions.size > 1 %>
18 <% show_diff = @versions.size > 1 %>
19 <% line_num = 1 %>
19 <% line_num = 1 %>
20 <% @versions.each do |ver| %>
20 <% @versions.each do |ver| %>
21 <tr class="<%= cycle("odd", "even") %>">
21 <tr class="<%= cycle("odd", "even") %>">
22 <td class="id"><%= link_to ver.version, :action => 'show', :id => @page.title, :version => ver.version %></td>
22 <td class="id"><%= link_to ver.version, :action => 'show', :id => @page.title, :project_id => @page.project, :version => ver.version %></td>
23 <td class="checkbox"><%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < @versions.size) %></td>
23 <td class="checkbox"><%= radio_button_tag('version', ver.version, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < @versions.size) %></td>
24 <td class="checkbox"><%= radio_button_tag('version_from', ver.version, (line_num==2), :id => "cbto-#{line_num}") if show_diff && (line_num > 1) %></td>
24 <td class="checkbox"><%= radio_button_tag('version_from', ver.version, (line_num==2), :id => "cbto-#{line_num}") if show_diff && (line_num > 1) %></td>
25 <td align="center"><%= format_time(ver.updated_on) %></td>
25 <td align="center"><%= format_time(ver.updated_on) %></td>
26 <td><%= link_to_user ver.author %></td>
26 <td><%= link_to_user ver.author %></td>
27 <td><%=h ver.comments %></td>
27 <td><%=h ver.comments %></td>
28 <td align="center"><%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %></td>
28 <td align="center"><%= link_to l(:button_annotate), :action => 'annotate', :id => @page.title, :version => ver.version %></td>
29 </tr>
29 </tr>
30 <% line_num += 1 %>
30 <% line_num += 1 %>
31 <% end %>
31 <% end %>
32 </tbody>
32 </tbody>
33 </table>
33 </table>
34 <%= submit_tag l(:label_view_diff), :class => 'small' if show_diff %>
34 <%= submit_tag l(:label_view_diff), :class => 'small' if show_diff %>
35 <span class="pagination"><%= pagination_links_full @version_pages, @version_count, :page_param => :p %></span>
35 <span class="pagination"><%= pagination_links_full @version_pages, @version_count, :page_param => :p %></span>
36 <% end %>
36 <% end %>
@@ -1,61 +1,61
1 <div class="contextual">
1 <div class="contextual">
2 <% if @editable %>
2 <% if @editable %>
3 <%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) if @content.version == @page.content.version %>
3 <%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) if @content.version == @page.content.version %>
4 <%= watcher_tag(@page, User.current) %>
4 <%= watcher_tag(@page, User.current) %>
5 <%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %>
5 <%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %>
6 <%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %>
6 <%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %>
7 <%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :id => @page.title}, :class => 'icon icon-move') if @content.version == @page.content.version %>
7 <%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :id => @page.title}, :class => 'icon icon-move') if @content.version == @page.content.version %>
8 <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page.title}, :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
8 <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :id => @page.title}, :method => :delete, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
9 <%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :id => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %>
9 <%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :id => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %>
10 <% end %>
10 <% end %>
11 <%= link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %>
11 <%= link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %>
12 </div>
12 </div>
13
13
14 <%= breadcrumb(@page.ancestors.reverse.collect {|parent| link_to h(parent.pretty_title), {:id => parent.title}}) %>
14 <%= breadcrumb(@page.ancestors.reverse.collect {|parent| link_to h(parent.pretty_title), {:id => parent.title, :project_id => parent.project}}) %>
15
15
16 <% if @content.version != @page.content.version %>
16 <% if @content.version != @page.content.version %>
17 <p>
17 <p>
18 <%= link_to(('&#171; ' + l(:label_previous)), :action => 'show', :id => @page.title, :version => (@content.version - 1)) + " - " if @content.version > 1 %>
18 <%= link_to(('&#171; ' + l(:label_previous)), :action => 'show', :id => @page.title, :project_id => @page.project, :version => (@content.version - 1)) + " - " if @content.version > 1 %>
19 <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %>
19 <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %>
20 <%= '(' + link_to('diff', :controller => 'wiki', :action => 'diff', :id => @page.title, :version => @content.version) + ')' if @content.version > 1 %> -
20 <%= '(' + link_to('diff', :controller => 'wiki', :action => 'diff', :id => @page.title, :project_id => @page.project, :version => @content.version) + ')' if @content.version > 1 %> -
21 <%= link_to((l(:label_next) + ' &#187;'), :action => 'show', :id => @page.title, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %>
21 <%= link_to((l(:label_next) + ' &#187;'), :action => 'show', :id => @page.title, :project_id => @page.project, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %>
22 <%= link_to(l(:label_current_version), :action => 'show', :id => @page.title) %>
22 <%= link_to(l(:label_current_version), :action => 'show', :id => @page.title, :project_id => @page.project) %>
23 <br />
23 <br />
24 <em><%= @content.author ? @content.author.name : "anonyme" %>, <%= format_time(@content.updated_on) %> </em><br />
24 <em><%= @content.author ? @content.author.name : "anonyme" %>, <%= format_time(@content.updated_on) %> </em><br />
25 <%=h @content.comments %>
25 <%=h @content.comments %>
26 </p>
26 </p>
27 <hr />
27 <hr />
28 <% end %>
28 <% end %>
29
29
30 <%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
30 <%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
31
31
32 <%= link_to_attachments @page %>
32 <%= link_to_attachments @page %>
33
33
34 <% if @editable && authorize_for('wiki', 'add_attachment') %>
34 <% if @editable && authorize_for('wiki', 'add_attachment') %>
35 <div id="wiki_add_attachment">
35 <div id="wiki_add_attachment">
36 <p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
36 <p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
37 :id => 'attach_files_link' %></p>
37 :id => 'attach_files_link' %></p>
38 <% form_tag({ :controller => 'wiki', :action => 'add_attachment', :project_id => @project, :id => @page.title }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>
38 <% form_tag({ :controller => 'wiki', :action => 'add_attachment', :project_id => @project, :id => @page.title }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>
39 <div class="box">
39 <div class="box">
40 <p><%= render :partial => 'attachments/form' %></p>
40 <p><%= render :partial => 'attachments/form' %></p>
41 </div>
41 </div>
42 <%= submit_tag l(:button_add) %>
42 <%= submit_tag l(:button_add) %>
43 <%= link_to l(:button_cancel), {}, :onclick => "Element.hide('add_attachment_form'); Element.show('attach_files_link'); return false;" %>
43 <%= link_to l(:button_cancel), {}, :onclick => "Element.hide('add_attachment_form'); Element.show('attach_files_link'); return false;" %>
44 <% end %>
44 <% end %>
45 </div>
45 </div>
46 <% end %>
46 <% end %>
47
47
48 <% other_formats_links do |f| %>
48 <% other_formats_links do |f| %>
49 <%= f.link_to 'HTML', :url => {:id => @page.title, :version => @content.version} %>
49 <%= f.link_to 'HTML', :url => {:id => @page.title, :version => @content.version} %>
50 <%= f.link_to 'TXT', :url => {:id => @page.title, :version => @content.version} %>
50 <%= f.link_to 'TXT', :url => {:id => @page.title, :version => @content.version} %>
51 <% end if User.current.allowed_to?(:export_wiki_pages, @project) %>
51 <% end if User.current.allowed_to?(:export_wiki_pages, @project) %>
52
52
53 <% content_for :header_tags do %>
53 <% content_for :header_tags do %>
54 <%= stylesheet_link_tag 'scm' %>
54 <%= stylesheet_link_tag 'scm' %>
55 <% end %>
55 <% end %>
56
56
57 <% content_for :sidebar do %>
57 <% content_for :sidebar do %>
58 <%= render :partial => 'sidebar' %>
58 <%= render :partial => 'sidebar' %>
59 <% end %>
59 <% end %>
60
60
61 <% html_title @page.pretty_title %>
61 <% html_title @page.pretty_title %>
@@ -1,252 +1,245
1 ActionController::Routing::Routes.draw do |map|
1 ActionController::Routing::Routes.draw do |map|
2 # Add your own custom routes here.
2 # Add your own custom routes here.
3 # The priority is based upon order of creation: first created -> highest priority.
3 # The priority is based upon order of creation: first created -> highest priority.
4
4
5 # Here's a sample route:
5 # Here's a sample route:
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 # Keep in mind you can assign values other than :controller and :action
7 # Keep in mind you can assign values other than :controller and :action
8
8
9 map.home '', :controller => 'welcome'
9 map.home '', :controller => 'welcome'
10
10
11 map.signin 'login', :controller => 'account', :action => 'login'
11 map.signin 'login', :controller => 'account', :action => 'login'
12 map.signout 'logout', :controller => 'account', :action => 'logout'
12 map.signout 'logout', :controller => 'account', :action => 'logout'
13
13
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
16
16
17 map.connect 'projects/:project_id/time_entries/report', :controller => 'time_entry_reports', :action => 'report'
17 map.connect 'projects/:project_id/time_entries/report', :controller => 'time_entry_reports', :action => 'report'
18 map.with_options :controller => 'time_entry_reports', :action => 'report',:conditions => {:method => :get} do |time_report|
18 map.with_options :controller => 'time_entry_reports', :action => 'report',:conditions => {:method => :get} do |time_report|
19 time_report.connect 'time_entries/report'
19 time_report.connect 'time_entries/report'
20 time_report.connect 'time_entries/report.:format'
20 time_report.connect 'time_entries/report.:format'
21 time_report.connect 'projects/:project_id/time_entries/report.:format'
21 time_report.connect 'projects/:project_id/time_entries/report.:format'
22 end
22 end
23
23
24 # TODO: wasteful since this is also nested under issues, projects, and projects/issues
24 # TODO: wasteful since this is also nested under issues, projects, and projects/issues
25 map.resources :time_entries, :controller => 'timelog'
25 map.resources :time_entries, :controller => 'timelog'
26
26
27 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
27 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
28 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
28 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
29 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
29 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
30 map.with_options :controller => 'wiki' do |wiki_routes|
31 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
32 wiki_views.connect 'projects/:project_id/wiki/export', :action => 'export'
33 wiki_views.connect 'projects/:project_id/wiki/index', :action => 'index'
34 wiki_views.connect 'projects/:project_id/wiki/date_index', :action => 'date_index'
35 wiki_views.connect 'projects/:project_id/wiki/:id', :action => 'show', :id => nil
36 wiki_views.connect 'projects/:project_id/wiki/:id/edit', :action => 'edit'
37 wiki_views.connect 'projects/:project_id/wiki/:id/rename', :action => 'rename'
38 wiki_views.connect 'projects/:project_id/wiki/:id/history', :action => 'history'
39 wiki_views.connect 'projects/:project_id/wiki/:id/diff/:version/vs/:version_from', :action => 'diff'
40 wiki_views.connect 'projects/:project_id/wiki/:id/annotate/:version', :action => 'annotate'
41 end
42
43 wiki_routes.connect 'projects/:project_id/wiki/:id/:action',
44 :action => /rename|preview|protect|add_attachment/,
45 :conditions => {:method => :post}
46
47 wiki_routes.connect 'projects/:project_id/wiki/:id/edit', :action => 'update', :conditions => {:method => :post}
48
49 wiki_routes.connect 'projects/:project_id/wiki/:id', :action => 'destroy', :conditions => {:method => :delete}
50 end
51
30
52 map.with_options :controller => 'messages' do |messages_routes|
31 map.with_options :controller => 'messages' do |messages_routes|
53 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
32 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
54 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
33 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
55 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
34 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
56 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
35 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
57 end
36 end
58 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
37 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
59 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
38 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
60 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
39 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
61 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
40 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
62 end
41 end
63 end
42 end
64
43
65 map.with_options :controller => 'boards' do |board_routes|
44 map.with_options :controller => 'boards' do |board_routes|
66 board_routes.with_options :conditions => {:method => :get} do |board_views|
45 board_routes.with_options :conditions => {:method => :get} do |board_views|
67 board_views.connect 'projects/:project_id/boards', :action => 'index'
46 board_views.connect 'projects/:project_id/boards', :action => 'index'
68 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
47 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
69 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
48 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
70 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
49 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
71 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
50 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
72 end
51 end
73 board_routes.with_options :conditions => {:method => :post} do |board_actions|
52 board_routes.with_options :conditions => {:method => :post} do |board_actions|
74 board_actions.connect 'projects/:project_id/boards', :action => 'new'
53 board_actions.connect 'projects/:project_id/boards', :action => 'new'
75 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
54 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
76 end
55 end
77 end
56 end
78
57
79 map.with_options :controller => 'documents' do |document_routes|
58 map.with_options :controller => 'documents' do |document_routes|
80 document_routes.with_options :conditions => {:method => :get} do |document_views|
59 document_routes.with_options :conditions => {:method => :get} do |document_views|
81 document_views.connect 'projects/:project_id/documents', :action => 'index'
60 document_views.connect 'projects/:project_id/documents', :action => 'index'
82 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
61 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
83 document_views.connect 'documents/:id', :action => 'show'
62 document_views.connect 'documents/:id', :action => 'show'
84 document_views.connect 'documents/:id/edit', :action => 'edit'
63 document_views.connect 'documents/:id/edit', :action => 'edit'
85 end
64 end
86 document_routes.with_options :conditions => {:method => :post} do |document_actions|
65 document_routes.with_options :conditions => {:method => :post} do |document_actions|
87 document_actions.connect 'projects/:project_id/documents', :action => 'new'
66 document_actions.connect 'projects/:project_id/documents', :action => 'new'
88 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
67 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
89 end
68 end
90 end
69 end
91
70
92 map.resources :issue_moves, :only => [:new, :create], :path_prefix => '/issues', :as => 'move'
71 map.resources :issue_moves, :only => [:new, :create], :path_prefix => '/issues', :as => 'move'
93
72
94 # Misc issue routes. TODO: move into resources
73 # Misc issue routes. TODO: move into resources
95 map.auto_complete_issues '/issues/auto_complete', :controller => 'auto_completes', :action => 'issues'
74 map.auto_complete_issues '/issues/auto_complete', :controller => 'auto_completes', :action => 'issues'
96 map.preview_issue '/issues/preview/:id', :controller => 'previews', :action => 'issue' # TODO: would look nicer as /issues/:id/preview
75 map.preview_issue '/issues/preview/:id', :controller => 'previews', :action => 'issue' # TODO: would look nicer as /issues/:id/preview
97 map.issues_context_menu '/issues/context_menu', :controller => 'context_menus', :action => 'issues'
76 map.issues_context_menu '/issues/context_menu', :controller => 'context_menus', :action => 'issues'
98 map.issue_changes '/issues/changes', :controller => 'journals', :action => 'index'
77 map.issue_changes '/issues/changes', :controller => 'journals', :action => 'index'
99 map.bulk_edit_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_edit', :conditions => { :method => :get }
78 map.bulk_edit_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_edit', :conditions => { :method => :get }
100 map.bulk_update_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_update', :conditions => { :method => :post }
79 map.bulk_update_issue 'issues/bulk_edit', :controller => 'issues', :action => 'bulk_update', :conditions => { :method => :post }
101 map.quoted_issue '/issues/:id/quoted', :controller => 'journals', :action => 'new', :id => /\d+/, :conditions => { :method => :post }
80 map.quoted_issue '/issues/:id/quoted', :controller => 'journals', :action => 'new', :id => /\d+/, :conditions => { :method => :post }
102 map.connect '/issues/:id/destroy', :controller => 'issues', :action => 'destroy', :conditions => { :method => :post } # legacy
81 map.connect '/issues/:id/destroy', :controller => 'issues', :action => 'destroy', :conditions => { :method => :post } # legacy
103
82
104 map.resource :gantt, :path_prefix => '/issues', :controller => 'gantts', :only => [:show, :update]
83 map.resource :gantt, :path_prefix => '/issues', :controller => 'gantts', :only => [:show, :update]
105 map.resource :gantt, :path_prefix => '/projects/:project_id/issues', :controller => 'gantts', :only => [:show, :update]
84 map.resource :gantt, :path_prefix => '/projects/:project_id/issues', :controller => 'gantts', :only => [:show, :update]
106 map.resource :calendar, :path_prefix => '/issues', :controller => 'calendars', :only => [:show, :update]
85 map.resource :calendar, :path_prefix => '/issues', :controller => 'calendars', :only => [:show, :update]
107 map.resource :calendar, :path_prefix => '/projects/:project_id/issues', :controller => 'calendars', :only => [:show, :update]
86 map.resource :calendar, :path_prefix => '/projects/:project_id/issues', :controller => 'calendars', :only => [:show, :update]
108
87
109 map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports|
88 map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports|
110 reports.connect 'projects/:id/issues/report', :action => 'issue_report'
89 reports.connect 'projects/:id/issues/report', :action => 'issue_report'
111 reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details'
90 reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details'
112 end
91 end
113
92
114 # Following two routes conflict with the resources because #index allows POST
93 # Following two routes conflict with the resources because #index allows POST
115 map.connect '/issues', :controller => 'issues', :action => 'index', :conditions => { :method => :post }
94 map.connect '/issues', :controller => 'issues', :action => 'index', :conditions => { :method => :post }
116 map.connect '/issues/create', :controller => 'issues', :action => 'index', :conditions => { :method => :post }
95 map.connect '/issues/create', :controller => 'issues', :action => 'index', :conditions => { :method => :post }
117
96
118 map.resources :issues, :member => { :edit => :post }, :collection => {} do |issues|
97 map.resources :issues, :member => { :edit => :post }, :collection => {} do |issues|
119 issues.resources :time_entries, :controller => 'timelog'
98 issues.resources :time_entries, :controller => 'timelog'
120 end
99 end
121
100
122 map.resources :issues, :path_prefix => '/projects/:project_id', :collection => { :create => :post } do |issues|
101 map.resources :issues, :path_prefix => '/projects/:project_id', :collection => { :create => :post } do |issues|
123 issues.resources :time_entries, :controller => 'timelog'
102 issues.resources :time_entries, :controller => 'timelog'
124 end
103 end
125
104
126 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
105 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
127 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
106 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
128 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
107 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
129 end
108 end
130
109
131 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
110 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
132
111
133 map.with_options :controller => 'users' do |users|
112 map.with_options :controller => 'users' do |users|
134 users.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil, :conditions => {:method => :get}
113 users.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil, :conditions => {:method => :get}
135
114
136 users.with_options :conditions => {:method => :post} do |user_actions|
115 users.with_options :conditions => {:method => :post} do |user_actions|
137 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
116 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
138 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
117 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
139 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
118 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
140 end
119 end
141 end
120 end
142
121
143 map.resources :users, :member => {
122 map.resources :users, :member => {
144 :edit_membership => :post,
123 :edit_membership => :post,
145 :destroy_membership => :post
124 :destroy_membership => :post
146 },
125 },
147 :except => [:destroy]
126 :except => [:destroy]
148
127
149 # For nice "roadmap" in the url for the index action
128 # For nice "roadmap" in the url for the index action
150 map.connect 'projects/:project_id/roadmap', :controller => 'versions', :action => 'index'
129 map.connect 'projects/:project_id/roadmap', :controller => 'versions', :action => 'index'
151
130
152 map.all_news 'news', :controller => 'news', :action => 'index'
131 map.all_news 'news', :controller => 'news', :action => 'index'
153 map.formatted_all_news 'news.:format', :controller => 'news', :action => 'index'
132 map.formatted_all_news 'news.:format', :controller => 'news', :action => 'index'
154 map.preview_news '/news/preview', :controller => 'previews', :action => 'news'
133 map.preview_news '/news/preview', :controller => 'previews', :action => 'news'
155 map.connect 'news/:id/comments', :controller => 'comments', :action => 'create', :conditions => {:method => :post}
134 map.connect 'news/:id/comments', :controller => 'comments', :action => 'create', :conditions => {:method => :post}
156 map.connect 'news/:id/comments/:comment_id', :controller => 'comments', :action => 'destroy', :conditions => {:method => :delete}
135 map.connect 'news/:id/comments/:comment_id', :controller => 'comments', :action => 'destroy', :conditions => {:method => :delete}
157
136
158 map.resources :projects, :member => {
137 map.resources :projects, :member => {
159 :copy => [:get, :post],
138 :copy => [:get, :post],
160 :settings => :get,
139 :settings => :get,
161 :modules => :post,
140 :modules => :post,
162 :archive => :post,
141 :archive => :post,
163 :unarchive => :post
142 :unarchive => :post
164 } do |project|
143 } do |project|
165 project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
144 project.resource :project_enumerations, :as => 'enumerations', :only => [:update, :destroy]
166 project.resources :files, :only => [:index, :new, :create]
145 project.resources :files, :only => [:index, :new, :create]
167 project.resources :versions, :collection => {:close_completed => :put}, :member => {:status_by => :post}
146 project.resources :versions, :collection => {:close_completed => :put}, :member => {:status_by => :post}
168 project.resources :news, :shallow => true
147 project.resources :news, :shallow => true
169 project.resources :time_entries, :controller => 'timelog', :path_prefix => 'projects/:project_id'
148 project.resources :time_entries, :controller => 'timelog', :path_prefix => 'projects/:project_id'
170
149
171
150 project.wiki_start_page 'wiki', :controller => 'wiki', :action => 'show', :conditions => {:method => :get}
151 project.wiki_index 'wiki/index', :controller => 'wiki', :action => 'index', :conditions => {:method => :get}
152 project.wiki_diff 'wiki/:id/diff/:version/vs/:version_from', :controller => 'wiki', :action => 'diff'
153 project.wiki_annotate 'wiki/:id/annotate/:version', :controller => 'wiki', :action => 'annotate'
154 project.resources :wiki, :except => [:new, :create], :member => {
155 :rename => [:get, :post],
156 :history => :get,
157 :preview => :any,
158 :protect => :post,
159 :add_attachment => :post
160 }, :collection => {
161 :export => :get,
162 :date_index => :get
163 }
164
172 end
165 end
173
166
174 # Destroy uses a get request to prompt the user before the actual DELETE request
167 # Destroy uses a get request to prompt the user before the actual DELETE request
175 map.project_destroy_confirm 'projects/:id/destroy', :controller => 'projects', :action => 'destroy', :conditions => {:method => :get}
168 map.project_destroy_confirm 'projects/:id/destroy', :controller => 'projects', :action => 'destroy', :conditions => {:method => :get}
176
169
177 # TODO: port to be part of the resources route(s)
170 # TODO: port to be part of the resources route(s)
178 map.with_options :controller => 'projects' do |project_mapper|
171 map.with_options :controller => 'projects' do |project_mapper|
179 project_mapper.with_options :conditions => {:method => :get} do |project_views|
172 project_mapper.with_options :conditions => {:method => :get} do |project_views|
180 project_views.connect 'projects/:id/settings/:tab', :controller => 'projects', :action => 'settings'
173 project_views.connect 'projects/:id/settings/:tab', :controller => 'projects', :action => 'settings'
181 project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new'
174 project_views.connect 'projects/:project_id/issues/:copy_from/copy', :controller => 'issues', :action => 'new'
182 end
175 end
183 end
176 end
184
177
185 map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity|
178 map.with_options :controller => 'activities', :action => 'index', :conditions => {:method => :get} do |activity|
186 activity.connect 'projects/:id/activity'
179 activity.connect 'projects/:id/activity'
187 activity.connect 'projects/:id/activity.:format'
180 activity.connect 'projects/:id/activity.:format'
188 activity.connect 'activity', :id => nil
181 activity.connect 'activity', :id => nil
189 activity.connect 'activity.:format', :id => nil
182 activity.connect 'activity.:format', :id => nil
190 end
183 end
191
184
192
185
193 map.with_options :controller => 'issue_categories' do |categories|
186 map.with_options :controller => 'issue_categories' do |categories|
194 categories.connect 'projects/:project_id/issue_categories/new', :action => 'new'
187 categories.connect 'projects/:project_id/issue_categories/new', :action => 'new'
195 end
188 end
196
189
197 map.with_options :controller => 'repositories' do |repositories|
190 map.with_options :controller => 'repositories' do |repositories|
198 repositories.with_options :conditions => {:method => :get} do |repository_views|
191 repositories.with_options :conditions => {:method => :get} do |repository_views|
199 repository_views.connect 'projects/:id/repository', :action => 'show'
192 repository_views.connect 'projects/:id/repository', :action => 'show'
200 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
193 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
201 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
194 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
202 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
195 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
203 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
196 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
204 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
197 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
205 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
198 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
206 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
199 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
207 repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
200 repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
208 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
201 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
209 repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw'
202 repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw'
210 # TODO: why the following route is required?
203 # TODO: why the following route is required?
211 repository_views.connect 'projects/:id/repository/entry/*path', :action => 'entry'
204 repository_views.connect 'projects/:id/repository/entry/*path', :action => 'entry'
212 repository_views.connect 'projects/:id/repository/:action/*path'
205 repository_views.connect 'projects/:id/repository/:action/*path'
213 end
206 end
214
207
215 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
208 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
216 end
209 end
217
210
218 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
211 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
219 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
212 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
220 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
213 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
221
214
222 map.resources :groups
215 map.resources :groups
223
216
224 #left old routes at the bottom for backwards compat
217 #left old routes at the bottom for backwards compat
225 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
218 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
226 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
219 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
227 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
220 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
228 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
221 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
229 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
222 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
230 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
223 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
231 map.connect 'projects/:project_id/news/:action', :controller => 'news'
224 map.connect 'projects/:project_id/news/:action', :controller => 'news'
232 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
225 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
233 map.with_options :controller => 'repositories' do |omap|
226 map.with_options :controller => 'repositories' do |omap|
234 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
227 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
235 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
228 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
236 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
229 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
237 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
230 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
238 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
231 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
239 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
232 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
240 end
233 end
241
234
242 map.with_options :controller => 'sys' do |sys|
235 map.with_options :controller => 'sys' do |sys|
243 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
236 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
244 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
237 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
245 end
238 end
246
239
247 # Install the default route as the lowest priority.
240 # Install the default route as the lowest priority.
248 map.connect ':controller/:action/:id'
241 map.connect ':controller/:action/:id'
249 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
242 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
250 # Used for OpenID
243 # Used for OpenID
251 map.root :controller => 'account', :action => 'login'
244 map.root :controller => 'account', :action => 'login'
252 end
245 end
@@ -1,365 +1,365
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'wiki_controller'
19 require 'wiki_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class WikiController; def rescue_action(e) raise e end; end
22 class WikiController; def rescue_action(e) raise e end; end
23
23
24 class WikiControllerTest < ActionController::TestCase
24 class WikiControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments
26
26
27 def setup
27 def setup
28 @controller = WikiController.new
28 @controller = WikiController.new
29 @request = ActionController::TestRequest.new
29 @request = ActionController::TestRequest.new
30 @response = ActionController::TestResponse.new
30 @response = ActionController::TestResponse.new
31 User.current = nil
31 User.current = nil
32 end
32 end
33
33
34 def test_show_start_page
34 def test_show_start_page
35 get :show, :project_id => 'ecookbook'
35 get :show, :project_id => 'ecookbook'
36 assert_response :success
36 assert_response :success
37 assert_template 'show'
37 assert_template 'show'
38 assert_tag :tag => 'h1', :content => /CookBook documentation/
38 assert_tag :tag => 'h1', :content => /CookBook documentation/
39
39
40 # child_pages macro
40 # child_pages macro
41 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
41 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
42 :child => { :tag => 'li',
42 :child => { :tag => 'li',
43 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
43 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
44 :content => 'Page with an inline image' } }
44 :content => 'Page with an inline image' } }
45 end
45 end
46
46
47 def test_show_page_with_name
47 def test_show_page_with_name
48 get :show, :project_id => 1, :id => 'Another_page'
48 get :show, :project_id => 1, :id => 'Another_page'
49 assert_response :success
49 assert_response :success
50 assert_template 'show'
50 assert_template 'show'
51 assert_tag :tag => 'h1', :content => /Another page/
51 assert_tag :tag => 'h1', :content => /Another page/
52 # Included page with an inline image
52 # Included page with an inline image
53 assert_tag :tag => 'p', :content => /This is an inline image/
53 assert_tag :tag => 'p', :content => /This is an inline image/
54 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
54 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
55 :alt => 'This is a logo' }
55 :alt => 'This is a logo' }
56 end
56 end
57
57
58 def test_show_with_sidebar
58 def test_show_with_sidebar
59 page = Project.find(1).wiki.pages.new(:title => 'Sidebar')
59 page = Project.find(1).wiki.pages.new(:title => 'Sidebar')
60 page.content = WikiContent.new(:text => 'Side bar content for test_show_with_sidebar')
60 page.content = WikiContent.new(:text => 'Side bar content for test_show_with_sidebar')
61 page.save!
61 page.save!
62
62
63 get :show, :project_id => 1, :id => 'Another_page'
63 get :show, :project_id => 1, :id => 'Another_page'
64 assert_response :success
64 assert_response :success
65 assert_tag :tag => 'div', :attributes => {:id => 'sidebar'},
65 assert_tag :tag => 'div', :attributes => {:id => 'sidebar'},
66 :content => /Side bar content for test_show_with_sidebar/
66 :content => /Side bar content for test_show_with_sidebar/
67 end
67 end
68
68
69 def test_show_unexistent_page_without_edit_right
69 def test_show_unexistent_page_without_edit_right
70 get :show, :project_id => 1, :id => 'Unexistent page'
70 get :show, :project_id => 1, :id => 'Unexistent page'
71 assert_response 404
71 assert_response 404
72 end
72 end
73
73
74 def test_show_unexistent_page_with_edit_right
74 def test_show_unexistent_page_with_edit_right
75 @request.session[:user_id] = 2
75 @request.session[:user_id] = 2
76 get :show, :project_id => 1, :id => 'Unexistent page'
76 get :show, :project_id => 1, :id => 'Unexistent page'
77 assert_response :success
77 assert_response :success
78 assert_template 'edit'
78 assert_template 'edit'
79 end
79 end
80
80
81 def test_create_page
81 def test_create_page
82 @request.session[:user_id] = 2
82 @request.session[:user_id] = 2
83 post :update, :project_id => 1,
83 put :update, :project_id => 1,
84 :id => 'New page',
84 :id => 'New page',
85 :content => {:comments => 'Created the page',
85 :content => {:comments => 'Created the page',
86 :text => "h1. New page\n\nThis is a new page",
86 :text => "h1. New page\n\nThis is a new page",
87 :version => 0}
87 :version => 0}
88 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'New_page'
88 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'New_page'
89 page = Project.find(1).wiki.find_page('New page')
89 page = Project.find(1).wiki.find_page('New page')
90 assert !page.new_record?
90 assert !page.new_record?
91 assert_not_nil page.content
91 assert_not_nil page.content
92 assert_equal 'Created the page', page.content.comments
92 assert_equal 'Created the page', page.content.comments
93 end
93 end
94
94
95 def test_create_page_with_attachments
95 def test_create_page_with_attachments
96 @request.session[:user_id] = 2
96 @request.session[:user_id] = 2
97 assert_difference 'WikiPage.count' do
97 assert_difference 'WikiPage.count' do
98 assert_difference 'Attachment.count' do
98 assert_difference 'Attachment.count' do
99 post :update, :project_id => 1,
99 put :update, :project_id => 1,
100 :id => 'New page',
100 :id => 'New page',
101 :content => {:comments => 'Created the page',
101 :content => {:comments => 'Created the page',
102 :text => "h1. New page\n\nThis is a new page",
102 :text => "h1. New page\n\nThis is a new page",
103 :version => 0},
103 :version => 0},
104 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
104 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
105 end
105 end
106 end
106 end
107 page = Project.find(1).wiki.find_page('New page')
107 page = Project.find(1).wiki.find_page('New page')
108 assert_equal 1, page.attachments.count
108 assert_equal 1, page.attachments.count
109 assert_equal 'testfile.txt', page.attachments.first.filename
109 assert_equal 'testfile.txt', page.attachments.first.filename
110 end
110 end
111
111
112 def test_preview
112 def test_preview
113 @request.session[:user_id] = 2
113 @request.session[:user_id] = 2
114 xhr :post, :preview, :project_id => 1, :id => 'CookBook_documentation',
114 xhr :post, :preview, :project_id => 1, :id => 'CookBook_documentation',
115 :content => { :comments => '',
115 :content => { :comments => '',
116 :text => 'this is a *previewed text*',
116 :text => 'this is a *previewed text*',
117 :version => 3 }
117 :version => 3 }
118 assert_response :success
118 assert_response :success
119 assert_template 'common/_preview'
119 assert_template 'common/_preview'
120 assert_tag :tag => 'strong', :content => /previewed text/
120 assert_tag :tag => 'strong', :content => /previewed text/
121 end
121 end
122
122
123 def test_preview_new_page
123 def test_preview_new_page
124 @request.session[:user_id] = 2
124 @request.session[:user_id] = 2
125 xhr :post, :preview, :project_id => 1, :id => 'New page',
125 xhr :post, :preview, :project_id => 1, :id => 'New page',
126 :content => { :text => 'h1. New page',
126 :content => { :text => 'h1. New page',
127 :comments => '',
127 :comments => '',
128 :version => 0 }
128 :version => 0 }
129 assert_response :success
129 assert_response :success
130 assert_template 'common/_preview'
130 assert_template 'common/_preview'
131 assert_tag :tag => 'h1', :content => /New page/
131 assert_tag :tag => 'h1', :content => /New page/
132 end
132 end
133
133
134 def test_history
134 def test_history
135 get :history, :project_id => 1, :id => 'CookBook_documentation'
135 get :history, :project_id => 1, :id => 'CookBook_documentation'
136 assert_response :success
136 assert_response :success
137 assert_template 'history'
137 assert_template 'history'
138 assert_not_nil assigns(:versions)
138 assert_not_nil assigns(:versions)
139 assert_equal 3, assigns(:versions).size
139 assert_equal 3, assigns(:versions).size
140 assert_select "input[type=submit][name=commit]"
140 assert_select "input[type=submit][name=commit]"
141 end
141 end
142
142
143 def test_history_with_one_version
143 def test_history_with_one_version
144 get :history, :project_id => 1, :id => 'Another_page'
144 get :history, :project_id => 1, :id => 'Another_page'
145 assert_response :success
145 assert_response :success
146 assert_template 'history'
146 assert_template 'history'
147 assert_not_nil assigns(:versions)
147 assert_not_nil assigns(:versions)
148 assert_equal 1, assigns(:versions).size
148 assert_equal 1, assigns(:versions).size
149 assert_select "input[type=submit][name=commit]", false
149 assert_select "input[type=submit][name=commit]", false
150 end
150 end
151
151
152 def test_diff
152 def test_diff
153 get :diff, :project_id => 1, :id => 'CookBook_documentation', :version => 2, :version_from => 1
153 get :diff, :project_id => 1, :id => 'CookBook_documentation', :version => 2, :version_from => 1
154 assert_response :success
154 assert_response :success
155 assert_template 'diff'
155 assert_template 'diff'
156 assert_tag :tag => 'span', :attributes => { :class => 'diff_in'},
156 assert_tag :tag => 'span', :attributes => { :class => 'diff_in'},
157 :content => /updated/
157 :content => /updated/
158 end
158 end
159
159
160 def test_annotate
160 def test_annotate
161 get :annotate, :project_id => 1, :id => 'CookBook_documentation', :version => 2
161 get :annotate, :project_id => 1, :id => 'CookBook_documentation', :version => 2
162 assert_response :success
162 assert_response :success
163 assert_template 'annotate'
163 assert_template 'annotate'
164 # Line 1
164 # Line 1
165 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1' },
165 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1' },
166 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/ },
166 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/ },
167 :child => { :tag => 'td', :content => /h1\. CookBook documentation/ }
167 :child => { :tag => 'td', :content => /h1\. CookBook documentation/ }
168 # Line 2
168 # Line 2
169 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '2' },
169 assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '2' },
170 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/ },
170 :child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/ },
171 :child => { :tag => 'td', :content => /Some updated \[\[documentation\]\] here/ }
171 :child => { :tag => 'td', :content => /Some updated \[\[documentation\]\] here/ }
172 end
172 end
173
173
174 def test_rename_with_redirect
174 def test_rename_with_redirect
175 @request.session[:user_id] = 2
175 @request.session[:user_id] = 2
176 post :rename, :project_id => 1, :id => 'Another_page',
176 post :rename, :project_id => 1, :id => 'Another_page',
177 :wiki_page => { :title => 'Another renamed page',
177 :wiki_page => { :title => 'Another renamed page',
178 :redirect_existing_links => 1 }
178 :redirect_existing_links => 1 }
179 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
179 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
180 wiki = Project.find(1).wiki
180 wiki = Project.find(1).wiki
181 # Check redirects
181 # Check redirects
182 assert_not_nil wiki.find_page('Another page')
182 assert_not_nil wiki.find_page('Another page')
183 assert_nil wiki.find_page('Another page', :with_redirect => false)
183 assert_nil wiki.find_page('Another page', :with_redirect => false)
184 end
184 end
185
185
186 def test_rename_without_redirect
186 def test_rename_without_redirect
187 @request.session[:user_id] = 2
187 @request.session[:user_id] = 2
188 post :rename, :project_id => 1, :id => 'Another_page',
188 post :rename, :project_id => 1, :id => 'Another_page',
189 :wiki_page => { :title => 'Another renamed page',
189 :wiki_page => { :title => 'Another renamed page',
190 :redirect_existing_links => "0" }
190 :redirect_existing_links => "0" }
191 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
191 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
192 wiki = Project.find(1).wiki
192 wiki = Project.find(1).wiki
193 # Check that there's no redirects
193 # Check that there's no redirects
194 assert_nil wiki.find_page('Another page')
194 assert_nil wiki.find_page('Another page')
195 end
195 end
196
196
197 def test_destroy_child
197 def test_destroy_child
198 @request.session[:user_id] = 2
198 @request.session[:user_id] = 2
199 delete :destroy, :project_id => 1, :id => 'Child_1'
199 delete :destroy, :project_id => 1, :id => 'Child_1'
200 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
200 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
201 end
201 end
202
202
203 def test_destroy_parent
203 def test_destroy_parent
204 @request.session[:user_id] = 2
204 @request.session[:user_id] = 2
205 assert_no_difference('WikiPage.count') do
205 assert_no_difference('WikiPage.count') do
206 delete :destroy, :project_id => 1, :id => 'Another_page'
206 delete :destroy, :project_id => 1, :id => 'Another_page'
207 end
207 end
208 assert_response :success
208 assert_response :success
209 assert_template 'destroy'
209 assert_template 'destroy'
210 end
210 end
211
211
212 def test_destroy_parent_with_nullify
212 def test_destroy_parent_with_nullify
213 @request.session[:user_id] = 2
213 @request.session[:user_id] = 2
214 assert_difference('WikiPage.count', -1) do
214 assert_difference('WikiPage.count', -1) do
215 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'nullify'
215 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'nullify'
216 end
216 end
217 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
217 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
218 assert_nil WikiPage.find_by_id(2)
218 assert_nil WikiPage.find_by_id(2)
219 end
219 end
220
220
221 def test_destroy_parent_with_cascade
221 def test_destroy_parent_with_cascade
222 @request.session[:user_id] = 2
222 @request.session[:user_id] = 2
223 assert_difference('WikiPage.count', -3) do
223 assert_difference('WikiPage.count', -3) do
224 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'destroy'
224 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'destroy'
225 end
225 end
226 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
226 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
227 assert_nil WikiPage.find_by_id(2)
227 assert_nil WikiPage.find_by_id(2)
228 assert_nil WikiPage.find_by_id(5)
228 assert_nil WikiPage.find_by_id(5)
229 end
229 end
230
230
231 def test_destroy_parent_with_reassign
231 def test_destroy_parent_with_reassign
232 @request.session[:user_id] = 2
232 @request.session[:user_id] = 2
233 assert_difference('WikiPage.count', -1) do
233 assert_difference('WikiPage.count', -1) do
234 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'reassign', :reassign_to_id => 1
234 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'reassign', :reassign_to_id => 1
235 end
235 end
236 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
236 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
237 assert_nil WikiPage.find_by_id(2)
237 assert_nil WikiPage.find_by_id(2)
238 assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
238 assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
239 end
239 end
240
240
241 def test_index
241 def test_index
242 get :index, :project_id => 'ecookbook'
242 get :index, :project_id => 'ecookbook'
243 assert_response :success
243 assert_response :success
244 assert_template 'index'
244 assert_template 'index'
245 pages = assigns(:pages)
245 pages = assigns(:pages)
246 assert_not_nil pages
246 assert_not_nil pages
247 assert_equal Project.find(1).wiki.pages.size, pages.size
247 assert_equal Project.find(1).wiki.pages.size, pages.size
248
248
249 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
249 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
250 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
250 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
251 :content => 'CookBook documentation' },
251 :content => 'CookBook documentation' },
252 :child => { :tag => 'ul',
252 :child => { :tag => 'ul',
253 :child => { :tag => 'li',
253 :child => { :tag => 'li',
254 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
254 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
255 :content => 'Page with an inline image' } } } },
255 :content => 'Page with an inline image' } } } },
256 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
256 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
257 :content => 'Another page' } }
257 :content => 'Another page' } }
258 end
258 end
259
259
260 context "GET :export" do
260 context "GET :export" do
261 context "with an authorized user to export the wiki" do
261 context "with an authorized user to export the wiki" do
262 setup do
262 setup do
263 @request.session[:user_id] = 2
263 @request.session[:user_id] = 2
264 get :export, :project_id => 'ecookbook'
264 get :export, :project_id => 'ecookbook'
265 end
265 end
266
266
267 should_respond_with :success
267 should_respond_with :success
268 should_assign_to :pages
268 should_assign_to :pages
269 should_respond_with_content_type "text/html"
269 should_respond_with_content_type "text/html"
270 should "export all of the wiki pages to a single html file" do
270 should "export all of the wiki pages to a single html file" do
271 assert_select "a[name=?]", "CookBook_documentation"
271 assert_select "a[name=?]", "CookBook_documentation"
272 assert_select "a[name=?]", "Another_page"
272 assert_select "a[name=?]", "Another_page"
273 assert_select "a[name=?]", "Page_with_an_inline_image"
273 assert_select "a[name=?]", "Page_with_an_inline_image"
274 end
274 end
275
275
276 end
276 end
277
277
278 context "with an unauthorized user" do
278 context "with an unauthorized user" do
279 setup do
279 setup do
280 get :export, :project_id => 'ecookbook'
280 get :export, :project_id => 'ecookbook'
281
281
282 should_respond_with :redirect
282 should_respond_with :redirect
283 should_redirect_to('wiki index') { {:action => 'show', :project_id => @project, :id => nil} }
283 should_redirect_to('wiki index') { {:action => 'show', :project_id => @project, :id => nil} }
284 end
284 end
285 end
285 end
286 end
286 end
287
287
288 context "GET :date_index" do
288 context "GET :date_index" do
289 setup do
289 setup do
290 get :date_index, :project_id => 'ecookbook'
290 get :date_index, :project_id => 'ecookbook'
291 end
291 end
292
292
293 should_respond_with :success
293 should_respond_with :success
294 should_assign_to :pages
294 should_assign_to :pages
295 should_assign_to :pages_by_date
295 should_assign_to :pages_by_date
296 should_render_template 'wiki/date_index'
296 should_render_template 'wiki/date_index'
297
297
298 end
298 end
299
299
300 def test_not_found
300 def test_not_found
301 get :show, :project_id => 999
301 get :show, :project_id => 999
302 assert_response 404
302 assert_response 404
303 end
303 end
304
304
305 def test_protect_page
305 def test_protect_page
306 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
306 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
307 assert !page.protected?
307 assert !page.protected?
308 @request.session[:user_id] = 2
308 @request.session[:user_id] = 2
309 post :protect, :project_id => 1, :id => page.title, :protected => '1'
309 post :protect, :project_id => 1, :id => page.title, :protected => '1'
310 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_page'
310 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_page'
311 assert page.reload.protected?
311 assert page.reload.protected?
312 end
312 end
313
313
314 def test_unprotect_page
314 def test_unprotect_page
315 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
315 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
316 assert page.protected?
316 assert page.protected?
317 @request.session[:user_id] = 2
317 @request.session[:user_id] = 2
318 post :protect, :project_id => 1, :id => page.title, :protected => '0'
318 post :protect, :project_id => 1, :id => page.title, :protected => '0'
319 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'CookBook_documentation'
319 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'CookBook_documentation'
320 assert !page.reload.protected?
320 assert !page.reload.protected?
321 end
321 end
322
322
323 def test_show_page_with_edit_link
323 def test_show_page_with_edit_link
324 @request.session[:user_id] = 2
324 @request.session[:user_id] = 2
325 get :show, :project_id => 1
325 get :show, :project_id => 1
326 assert_response :success
326 assert_response :success
327 assert_template 'show'
327 assert_template 'show'
328 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
328 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
329 end
329 end
330
330
331 def test_show_page_without_edit_link
331 def test_show_page_without_edit_link
332 @request.session[:user_id] = 4
332 @request.session[:user_id] = 4
333 get :show, :project_id => 1
333 get :show, :project_id => 1
334 assert_response :success
334 assert_response :success
335 assert_template 'show'
335 assert_template 'show'
336 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
336 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
337 end
337 end
338
338
339 def test_edit_unprotected_page
339 def test_edit_unprotected_page
340 # Non members can edit unprotected wiki pages
340 # Non members can edit unprotected wiki pages
341 @request.session[:user_id] = 4
341 @request.session[:user_id] = 4
342 get :edit, :project_id => 1, :id => 'Another_page'
342 get :edit, :project_id => 1, :id => 'Another_page'
343 assert_response :success
343 assert_response :success
344 assert_template 'edit'
344 assert_template 'edit'
345 end
345 end
346
346
347 def test_edit_protected_page_by_nonmember
347 def test_edit_protected_page_by_nonmember
348 # Non members can't edit protected wiki pages
348 # Non members can't edit protected wiki pages
349 @request.session[:user_id] = 4
349 @request.session[:user_id] = 4
350 get :edit, :project_id => 1, :id => 'CookBook_documentation'
350 get :edit, :project_id => 1, :id => 'CookBook_documentation'
351 assert_response 403
351 assert_response 403
352 end
352 end
353
353
354 def test_edit_protected_page_by_member
354 def test_edit_protected_page_by_member
355 @request.session[:user_id] = 2
355 @request.session[:user_id] = 2
356 get :edit, :project_id => 1, :id => 'CookBook_documentation'
356 get :edit, :project_id => 1, :id => 'CookBook_documentation'
357 assert_response :success
357 assert_response :success
358 assert_template 'edit'
358 assert_template 'edit'
359 end
359 end
360
360
361 def test_history_of_non_existing_page_should_return_404
361 def test_history_of_non_existing_page_should_return_404
362 get :history, :project_id => 1, :id => 'Unknown_page'
362 get :history, :project_id => 1, :id => 'Unknown_page'
363 assert_response 404
363 assert_response 404
364 end
364 end
365 end
365 end
@@ -1,344 +1,345
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
2 # Copyright (C) 2006-2010 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.dirname(__FILE__)}/../test_helper"
18 require "#{File.dirname(__FILE__)}/../test_helper"
19
19
20 class RoutingTest < ActionController::IntegrationTest
20 class RoutingTest < ActionController::IntegrationTest
21 context "activities" do
21 context "activities" do
22 should_route :get, "/activity", :controller => 'activities', :action => 'index', :id => nil
22 should_route :get, "/activity", :controller => 'activities', :action => 'index', :id => nil
23 should_route :get, "/activity.atom", :controller => 'activities', :action => 'index', :id => nil, :format => 'atom'
23 should_route :get, "/activity.atom", :controller => 'activities', :action => 'index', :id => nil, :format => 'atom'
24 end
24 end
25
25
26 context "attachments" do
26 context "attachments" do
27 should_route :get, "/attachments/1", :controller => 'attachments', :action => 'show', :id => '1'
27 should_route :get, "/attachments/1", :controller => 'attachments', :action => 'show', :id => '1'
28 should_route :get, "/attachments/1/filename.ext", :controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext'
28 should_route :get, "/attachments/1/filename.ext", :controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext'
29 should_route :get, "/attachments/download/1", :controller => 'attachments', :action => 'download', :id => '1'
29 should_route :get, "/attachments/download/1", :controller => 'attachments', :action => 'download', :id => '1'
30 should_route :get, "/attachments/download/1/filename.ext", :controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext'
30 should_route :get, "/attachments/download/1/filename.ext", :controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext'
31 end
31 end
32
32
33 context "boards" do
33 context "boards" do
34 should_route :get, "/projects/world_domination/boards", :controller => 'boards', :action => 'index', :project_id => 'world_domination'
34 should_route :get, "/projects/world_domination/boards", :controller => 'boards', :action => 'index', :project_id => 'world_domination'
35 should_route :get, "/projects/world_domination/boards/new", :controller => 'boards', :action => 'new', :project_id => 'world_domination'
35 should_route :get, "/projects/world_domination/boards/new", :controller => 'boards', :action => 'new', :project_id => 'world_domination'
36 should_route :get, "/projects/world_domination/boards/44", :controller => 'boards', :action => 'show', :project_id => 'world_domination', :id => '44'
36 should_route :get, "/projects/world_domination/boards/44", :controller => 'boards', :action => 'show', :project_id => 'world_domination', :id => '44'
37 should_route :get, "/projects/world_domination/boards/44.atom", :controller => 'boards', :action => 'show', :project_id => 'world_domination', :id => '44', :format => 'atom'
37 should_route :get, "/projects/world_domination/boards/44.atom", :controller => 'boards', :action => 'show', :project_id => 'world_domination', :id => '44', :format => 'atom'
38 should_route :get, "/projects/world_domination/boards/44/edit", :controller => 'boards', :action => 'edit', :project_id => 'world_domination', :id => '44'
38 should_route :get, "/projects/world_domination/boards/44/edit", :controller => 'boards', :action => 'edit', :project_id => 'world_domination', :id => '44'
39
39
40 should_route :post, "/projects/world_domination/boards/new", :controller => 'boards', :action => 'new', :project_id => 'world_domination'
40 should_route :post, "/projects/world_domination/boards/new", :controller => 'boards', :action => 'new', :project_id => 'world_domination'
41 should_route :post, "/projects/world_domination/boards/44/edit", :controller => 'boards', :action => 'edit', :project_id => 'world_domination', :id => '44'
41 should_route :post, "/projects/world_domination/boards/44/edit", :controller => 'boards', :action => 'edit', :project_id => 'world_domination', :id => '44'
42 should_route :post, "/projects/world_domination/boards/44/destroy", :controller => 'boards', :action => 'destroy', :project_id => 'world_domination', :id => '44'
42 should_route :post, "/projects/world_domination/boards/44/destroy", :controller => 'boards', :action => 'destroy', :project_id => 'world_domination', :id => '44'
43
43
44 end
44 end
45
45
46 context "documents" do
46 context "documents" do
47 should_route :get, "/projects/567/documents", :controller => 'documents', :action => 'index', :project_id => '567'
47 should_route :get, "/projects/567/documents", :controller => 'documents', :action => 'index', :project_id => '567'
48 should_route :get, "/projects/567/documents/new", :controller => 'documents', :action => 'new', :project_id => '567'
48 should_route :get, "/projects/567/documents/new", :controller => 'documents', :action => 'new', :project_id => '567'
49 should_route :get, "/documents/22", :controller => 'documents', :action => 'show', :id => '22'
49 should_route :get, "/documents/22", :controller => 'documents', :action => 'show', :id => '22'
50 should_route :get, "/documents/22/edit", :controller => 'documents', :action => 'edit', :id => '22'
50 should_route :get, "/documents/22/edit", :controller => 'documents', :action => 'edit', :id => '22'
51
51
52 should_route :post, "/projects/567/documents/new", :controller => 'documents', :action => 'new', :project_id => '567'
52 should_route :post, "/projects/567/documents/new", :controller => 'documents', :action => 'new', :project_id => '567'
53 should_route :post, "/documents/567/edit", :controller => 'documents', :action => 'edit', :id => '567'
53 should_route :post, "/documents/567/edit", :controller => 'documents', :action => 'edit', :id => '567'
54 should_route :post, "/documents/567/destroy", :controller => 'documents', :action => 'destroy', :id => '567'
54 should_route :post, "/documents/567/destroy", :controller => 'documents', :action => 'destroy', :id => '567'
55 end
55 end
56
56
57 context "issues" do
57 context "issues" do
58 # REST actions
58 # REST actions
59 should_route :get, "/issues", :controller => 'issues', :action => 'index'
59 should_route :get, "/issues", :controller => 'issues', :action => 'index'
60 should_route :get, "/issues.pdf", :controller => 'issues', :action => 'index', :format => 'pdf'
60 should_route :get, "/issues.pdf", :controller => 'issues', :action => 'index', :format => 'pdf'
61 should_route :get, "/issues.atom", :controller => 'issues', :action => 'index', :format => 'atom'
61 should_route :get, "/issues.atom", :controller => 'issues', :action => 'index', :format => 'atom'
62 should_route :get, "/issues.xml", :controller => 'issues', :action => 'index', :format => 'xml'
62 should_route :get, "/issues.xml", :controller => 'issues', :action => 'index', :format => 'xml'
63 should_route :get, "/projects/23/issues", :controller => 'issues', :action => 'index', :project_id => '23'
63 should_route :get, "/projects/23/issues", :controller => 'issues', :action => 'index', :project_id => '23'
64 should_route :get, "/projects/23/issues.pdf", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
64 should_route :get, "/projects/23/issues.pdf", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
65 should_route :get, "/projects/23/issues.atom", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
65 should_route :get, "/projects/23/issues.atom", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
66 should_route :get, "/projects/23/issues.xml", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'xml'
66 should_route :get, "/projects/23/issues.xml", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'xml'
67 should_route :get, "/issues/64", :controller => 'issues', :action => 'show', :id => '64'
67 should_route :get, "/issues/64", :controller => 'issues', :action => 'show', :id => '64'
68 should_route :get, "/issues/64.pdf", :controller => 'issues', :action => 'show', :id => '64', :format => 'pdf'
68 should_route :get, "/issues/64.pdf", :controller => 'issues', :action => 'show', :id => '64', :format => 'pdf'
69 should_route :get, "/issues/64.atom", :controller => 'issues', :action => 'show', :id => '64', :format => 'atom'
69 should_route :get, "/issues/64.atom", :controller => 'issues', :action => 'show', :id => '64', :format => 'atom'
70 should_route :get, "/issues/64.xml", :controller => 'issues', :action => 'show', :id => '64', :format => 'xml'
70 should_route :get, "/issues/64.xml", :controller => 'issues', :action => 'show', :id => '64', :format => 'xml'
71
71
72 should_route :get, "/projects/23/issues/new", :controller => 'issues', :action => 'new', :project_id => '23'
72 should_route :get, "/projects/23/issues/new", :controller => 'issues', :action => 'new', :project_id => '23'
73 should_route :post, "/projects/23/issues", :controller => 'issues', :action => 'create', :project_id => '23'
73 should_route :post, "/projects/23/issues", :controller => 'issues', :action => 'create', :project_id => '23'
74 should_route :post, "/issues.xml", :controller => 'issues', :action => 'create', :format => 'xml'
74 should_route :post, "/issues.xml", :controller => 'issues', :action => 'create', :format => 'xml'
75
75
76 should_route :get, "/issues/64/edit", :controller => 'issues', :action => 'edit', :id => '64'
76 should_route :get, "/issues/64/edit", :controller => 'issues', :action => 'edit', :id => '64'
77 # TODO: Should use PUT
77 # TODO: Should use PUT
78 should_route :post, "/issues/64/edit", :controller => 'issues', :action => 'edit', :id => '64'
78 should_route :post, "/issues/64/edit", :controller => 'issues', :action => 'edit', :id => '64'
79 should_route :put, "/issues/1.xml", :controller => 'issues', :action => 'update', :id => '1', :format => 'xml'
79 should_route :put, "/issues/1.xml", :controller => 'issues', :action => 'update', :id => '1', :format => 'xml'
80
80
81 # TODO: Should use DELETE
81 # TODO: Should use DELETE
82 should_route :post, "/issues/64/destroy", :controller => 'issues', :action => 'destroy', :id => '64'
82 should_route :post, "/issues/64/destroy", :controller => 'issues', :action => 'destroy', :id => '64'
83 should_route :delete, "/issues/1.xml", :controller => 'issues', :action => 'destroy', :id => '1', :format => 'xml'
83 should_route :delete, "/issues/1.xml", :controller => 'issues', :action => 'destroy', :id => '1', :format => 'xml'
84
84
85 # Extra actions
85 # Extra actions
86 should_route :get, "/projects/23/issues/64/copy", :controller => 'issues', :action => 'new', :project_id => '23', :copy_from => '64'
86 should_route :get, "/projects/23/issues/64/copy", :controller => 'issues', :action => 'new', :project_id => '23', :copy_from => '64'
87
87
88 should_route :get, "/issues/move/new", :controller => 'issue_moves', :action => 'new'
88 should_route :get, "/issues/move/new", :controller => 'issue_moves', :action => 'new'
89 should_route :post, "/issues/move", :controller => 'issue_moves', :action => 'create'
89 should_route :post, "/issues/move", :controller => 'issue_moves', :action => 'create'
90
90
91 should_route :post, "/issues/1/quoted", :controller => 'journals', :action => 'new', :id => '1'
91 should_route :post, "/issues/1/quoted", :controller => 'journals', :action => 'new', :id => '1'
92
92
93 should_route :get, "/issues/calendar", :controller => 'calendars', :action => 'show'
93 should_route :get, "/issues/calendar", :controller => 'calendars', :action => 'show'
94 should_route :put, "/issues/calendar", :controller => 'calendars', :action => 'update'
94 should_route :put, "/issues/calendar", :controller => 'calendars', :action => 'update'
95 should_route :get, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'show', :project_id => 'project-name'
95 should_route :get, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'show', :project_id => 'project-name'
96 should_route :put, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'update', :project_id => 'project-name'
96 should_route :put, "/projects/project-name/issues/calendar", :controller => 'calendars', :action => 'update', :project_id => 'project-name'
97
97
98 should_route :get, "/issues/gantt", :controller => 'gantts', :action => 'show'
98 should_route :get, "/issues/gantt", :controller => 'gantts', :action => 'show'
99 should_route :put, "/issues/gantt", :controller => 'gantts', :action => 'update'
99 should_route :put, "/issues/gantt", :controller => 'gantts', :action => 'update'
100 should_route :get, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'show', :project_id => 'project-name'
100 should_route :get, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'show', :project_id => 'project-name'
101 should_route :put, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'update', :project_id => 'project-name'
101 should_route :put, "/projects/project-name/issues/gantt", :controller => 'gantts', :action => 'update', :project_id => 'project-name'
102
102
103 should_route :get, "/issues/auto_complete", :controller => 'auto_completes', :action => 'issues'
103 should_route :get, "/issues/auto_complete", :controller => 'auto_completes', :action => 'issues'
104
104
105 should_route :get, "/issues/preview/123", :controller => 'previews', :action => 'issue', :id => '123'
105 should_route :get, "/issues/preview/123", :controller => 'previews', :action => 'issue', :id => '123'
106 should_route :post, "/issues/preview/123", :controller => 'previews', :action => 'issue', :id => '123'
106 should_route :post, "/issues/preview/123", :controller => 'previews', :action => 'issue', :id => '123'
107 should_route :get, "/issues/context_menu", :controller => 'context_menus', :action => 'issues'
107 should_route :get, "/issues/context_menu", :controller => 'context_menus', :action => 'issues'
108 should_route :post, "/issues/context_menu", :controller => 'context_menus', :action => 'issues'
108 should_route :post, "/issues/context_menu", :controller => 'context_menus', :action => 'issues'
109
109
110 should_route :get, "/issues/changes", :controller => 'journals', :action => 'index'
110 should_route :get, "/issues/changes", :controller => 'journals', :action => 'index'
111
111
112 should_route :get, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_edit'
112 should_route :get, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_edit'
113 should_route :post, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_update'
113 should_route :post, "/issues/bulk_edit", :controller => 'issues', :action => 'bulk_update'
114 end
114 end
115
115
116 context "issue categories" do
116 context "issue categories" do
117 should_route :get, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test'
117 should_route :get, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test'
118
118
119 should_route :post, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test'
119 should_route :post, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test'
120 end
120 end
121
121
122 context "issue relations" do
122 context "issue relations" do
123 should_route :post, "/issues/1/relations", :controller => 'issue_relations', :action => 'new', :issue_id => '1'
123 should_route :post, "/issues/1/relations", :controller => 'issue_relations', :action => 'new', :issue_id => '1'
124 should_route :post, "/issues/1/relations/23/destroy", :controller => 'issue_relations', :action => 'destroy', :issue_id => '1', :id => '23'
124 should_route :post, "/issues/1/relations/23/destroy", :controller => 'issue_relations', :action => 'destroy', :issue_id => '1', :id => '23'
125 end
125 end
126
126
127 context "issue reports" do
127 context "issue reports" do
128 should_route :get, "/projects/567/issues/report", :controller => 'reports', :action => 'issue_report', :id => '567'
128 should_route :get, "/projects/567/issues/report", :controller => 'reports', :action => 'issue_report', :id => '567'
129 should_route :get, "/projects/567/issues/report/assigned_to", :controller => 'reports', :action => 'issue_report_details', :id => '567', :detail => 'assigned_to'
129 should_route :get, "/projects/567/issues/report/assigned_to", :controller => 'reports', :action => 'issue_report_details', :id => '567', :detail => 'assigned_to'
130 end
130 end
131
131
132 context "members" do
132 context "members" do
133 should_route :post, "/projects/5234/members/new", :controller => 'members', :action => 'new', :id => '5234'
133 should_route :post, "/projects/5234/members/new", :controller => 'members', :action => 'new', :id => '5234'
134 end
134 end
135
135
136 context "messages" do
136 context "messages" do
137 should_route :get, "/boards/22/topics/2", :controller => 'messages', :action => 'show', :id => '2', :board_id => '22'
137 should_route :get, "/boards/22/topics/2", :controller => 'messages', :action => 'show', :id => '2', :board_id => '22'
138 should_route :get, "/boards/lala/topics/new", :controller => 'messages', :action => 'new', :board_id => 'lala'
138 should_route :get, "/boards/lala/topics/new", :controller => 'messages', :action => 'new', :board_id => 'lala'
139 should_route :get, "/boards/lala/topics/22/edit", :controller => 'messages', :action => 'edit', :id => '22', :board_id => 'lala'
139 should_route :get, "/boards/lala/topics/22/edit", :controller => 'messages', :action => 'edit', :id => '22', :board_id => 'lala'
140
140
141 should_route :post, "/boards/lala/topics/new", :controller => 'messages', :action => 'new', :board_id => 'lala'
141 should_route :post, "/boards/lala/topics/new", :controller => 'messages', :action => 'new', :board_id => 'lala'
142 should_route :post, "/boards/lala/topics/22/edit", :controller => 'messages', :action => 'edit', :id => '22', :board_id => 'lala'
142 should_route :post, "/boards/lala/topics/22/edit", :controller => 'messages', :action => 'edit', :id => '22', :board_id => 'lala'
143 should_route :post, "/boards/22/topics/555/replies", :controller => 'messages', :action => 'reply', :id => '555', :board_id => '22'
143 should_route :post, "/boards/22/topics/555/replies", :controller => 'messages', :action => 'reply', :id => '555', :board_id => '22'
144 should_route :post, "/boards/22/topics/555/destroy", :controller => 'messages', :action => 'destroy', :id => '555', :board_id => '22'
144 should_route :post, "/boards/22/topics/555/destroy", :controller => 'messages', :action => 'destroy', :id => '555', :board_id => '22'
145 end
145 end
146
146
147 context "news" do
147 context "news" do
148 should_route :get, "/news", :controller => 'news', :action => 'index'
148 should_route :get, "/news", :controller => 'news', :action => 'index'
149 should_route :get, "/news.atom", :controller => 'news', :action => 'index', :format => 'atom'
149 should_route :get, "/news.atom", :controller => 'news', :action => 'index', :format => 'atom'
150 should_route :get, "/news.xml", :controller => 'news', :action => 'index', :format => 'xml'
150 should_route :get, "/news.xml", :controller => 'news', :action => 'index', :format => 'xml'
151 should_route :get, "/news.json", :controller => 'news', :action => 'index', :format => 'json'
151 should_route :get, "/news.json", :controller => 'news', :action => 'index', :format => 'json'
152 should_route :get, "/projects/567/news", :controller => 'news', :action => 'index', :project_id => '567'
152 should_route :get, "/projects/567/news", :controller => 'news', :action => 'index', :project_id => '567'
153 should_route :get, "/projects/567/news.atom", :controller => 'news', :action => 'index', :format => 'atom', :project_id => '567'
153 should_route :get, "/projects/567/news.atom", :controller => 'news', :action => 'index', :format => 'atom', :project_id => '567'
154 should_route :get, "/projects/567/news.xml", :controller => 'news', :action => 'index', :format => 'xml', :project_id => '567'
154 should_route :get, "/projects/567/news.xml", :controller => 'news', :action => 'index', :format => 'xml', :project_id => '567'
155 should_route :get, "/projects/567/news.json", :controller => 'news', :action => 'index', :format => 'json', :project_id => '567'
155 should_route :get, "/projects/567/news.json", :controller => 'news', :action => 'index', :format => 'json', :project_id => '567'
156 should_route :get, "/news/2", :controller => 'news', :action => 'show', :id => '2'
156 should_route :get, "/news/2", :controller => 'news', :action => 'show', :id => '2'
157 should_route :get, "/projects/567/news/new", :controller => 'news', :action => 'new', :project_id => '567'
157 should_route :get, "/projects/567/news/new", :controller => 'news', :action => 'new', :project_id => '567'
158 should_route :get, "/news/234", :controller => 'news', :action => 'show', :id => '234'
158 should_route :get, "/news/234", :controller => 'news', :action => 'show', :id => '234'
159 should_route :get, "/news/567/edit", :controller => 'news', :action => 'edit', :id => '567'
159 should_route :get, "/news/567/edit", :controller => 'news', :action => 'edit', :id => '567'
160 should_route :get, "/news/preview", :controller => 'previews', :action => 'news'
160 should_route :get, "/news/preview", :controller => 'previews', :action => 'news'
161
161
162 should_route :post, "/projects/567/news", :controller => 'news', :action => 'create', :project_id => '567'
162 should_route :post, "/projects/567/news", :controller => 'news', :action => 'create', :project_id => '567'
163 should_route :post, "/news/567/comments", :controller => 'comments', :action => 'create', :id => '567'
163 should_route :post, "/news/567/comments", :controller => 'comments', :action => 'create', :id => '567'
164
164
165 should_route :put, "/news/567", :controller => 'news', :action => 'update', :id => '567'
165 should_route :put, "/news/567", :controller => 'news', :action => 'update', :id => '567'
166
166
167 should_route :delete, "/news/567", :controller => 'news', :action => 'destroy', :id => '567'
167 should_route :delete, "/news/567", :controller => 'news', :action => 'destroy', :id => '567'
168 should_route :delete, "/news/567/comments/15", :controller => 'comments', :action => 'destroy', :id => '567', :comment_id => '15'
168 should_route :delete, "/news/567/comments/15", :controller => 'comments', :action => 'destroy', :id => '567', :comment_id => '15'
169 end
169 end
170
170
171 context "projects" do
171 context "projects" do
172 should_route :get, "/projects", :controller => 'projects', :action => 'index'
172 should_route :get, "/projects", :controller => 'projects', :action => 'index'
173 should_route :get, "/projects.atom", :controller => 'projects', :action => 'index', :format => 'atom'
173 should_route :get, "/projects.atom", :controller => 'projects', :action => 'index', :format => 'atom'
174 should_route :get, "/projects.xml", :controller => 'projects', :action => 'index', :format => 'xml'
174 should_route :get, "/projects.xml", :controller => 'projects', :action => 'index', :format => 'xml'
175 should_route :get, "/projects/new", :controller => 'projects', :action => 'new'
175 should_route :get, "/projects/new", :controller => 'projects', :action => 'new'
176 should_route :get, "/projects/test", :controller => 'projects', :action => 'show', :id => 'test'
176 should_route :get, "/projects/test", :controller => 'projects', :action => 'show', :id => 'test'
177 should_route :get, "/projects/1.xml", :controller => 'projects', :action => 'show', :id => '1', :format => 'xml'
177 should_route :get, "/projects/1.xml", :controller => 'projects', :action => 'show', :id => '1', :format => 'xml'
178 should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223'
178 should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223'
179 should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members'
179 should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members'
180 should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :project_id => '33'
180 should_route :get, "/projects/33/files", :controller => 'files', :action => 'index', :project_id => '33'
181 should_route :get, "/projects/33/files/new", :controller => 'files', :action => 'new', :project_id => '33'
181 should_route :get, "/projects/33/files/new", :controller => 'files', :action => 'new', :project_id => '33'
182 should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33'
182 should_route :get, "/projects/33/roadmap", :controller => 'versions', :action => 'index', :project_id => '33'
183 should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33'
183 should_route :get, "/projects/33/activity", :controller => 'activities', :action => 'index', :id => '33'
184 should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom'
184 should_route :get, "/projects/33/activity.atom", :controller => 'activities', :action => 'index', :id => '33', :format => 'atom'
185
185
186 should_route :post, "/projects", :controller => 'projects', :action => 'create'
186 should_route :post, "/projects", :controller => 'projects', :action => 'create'
187 should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml'
187 should_route :post, "/projects.xml", :controller => 'projects', :action => 'create', :format => 'xml'
188 should_route :post, "/projects/33/files", :controller => 'files', :action => 'create', :project_id => '33'
188 should_route :post, "/projects/33/files", :controller => 'files', :action => 'create', :project_id => '33'
189 should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64'
189 should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64'
190 should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64'
190 should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64'
191
191
192 should_route :put, "/projects/64/enumerations", :controller => 'project_enumerations', :action => 'update', :project_id => '64'
192 should_route :put, "/projects/64/enumerations", :controller => 'project_enumerations', :action => 'update', :project_id => '64'
193 should_route :put, "/projects/4223", :controller => 'projects', :action => 'update', :id => '4223'
193 should_route :put, "/projects/4223", :controller => 'projects', :action => 'update', :id => '4223'
194 should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'update', :id => '1', :format => 'xml'
194 should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'update', :id => '1', :format => 'xml'
195
195
196 should_route :delete, "/projects/64", :controller => 'projects', :action => 'destroy', :id => '64'
196 should_route :delete, "/projects/64", :controller => 'projects', :action => 'destroy', :id => '64'
197 should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml'
197 should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml'
198 should_route :delete, "/projects/64/enumerations", :controller => 'project_enumerations', :action => 'destroy', :project_id => '64'
198 should_route :delete, "/projects/64/enumerations", :controller => 'project_enumerations', :action => 'destroy', :project_id => '64'
199 end
199 end
200
200
201 context "repositories" do
201 context "repositories" do
202 should_route :get, "/projects/redmine/repository", :controller => 'repositories', :action => 'show', :id => 'redmine'
202 should_route :get, "/projects/redmine/repository", :controller => 'repositories', :action => 'show', :id => 'redmine'
203 should_route :get, "/projects/redmine/repository/edit", :controller => 'repositories', :action => 'edit', :id => 'redmine'
203 should_route :get, "/projects/redmine/repository/edit", :controller => 'repositories', :action => 'edit', :id => 'redmine'
204 should_route :get, "/projects/redmine/repository/revisions", :controller => 'repositories', :action => 'revisions', :id => 'redmine'
204 should_route :get, "/projects/redmine/repository/revisions", :controller => 'repositories', :action => 'revisions', :id => 'redmine'
205 should_route :get, "/projects/redmine/repository/revisions.atom", :controller => 'repositories', :action => 'revisions', :id => 'redmine', :format => 'atom'
205 should_route :get, "/projects/redmine/repository/revisions.atom", :controller => 'repositories', :action => 'revisions', :id => 'redmine', :format => 'atom'
206 should_route :get, "/projects/redmine/repository/revisions/2457", :controller => 'repositories', :action => 'revision', :id => 'redmine', :rev => '2457'
206 should_route :get, "/projects/redmine/repository/revisions/2457", :controller => 'repositories', :action => 'revision', :id => 'redmine', :rev => '2457'
207 should_route :get, "/projects/redmine/repository/revisions/2457/diff", :controller => 'repositories', :action => 'diff', :id => 'redmine', :rev => '2457'
207 should_route :get, "/projects/redmine/repository/revisions/2457/diff", :controller => 'repositories', :action => 'diff', :id => 'redmine', :rev => '2457'
208 should_route :get, "/projects/redmine/repository/revisions/2457/diff.diff", :controller => 'repositories', :action => 'diff', :id => 'redmine', :rev => '2457', :format => 'diff'
208 should_route :get, "/projects/redmine/repository/revisions/2457/diff.diff", :controller => 'repositories', :action => 'diff', :id => 'redmine', :rev => '2457', :format => 'diff'
209 should_route :get, "/projects/redmine/repository/diff/path/to/file.c", :controller => 'repositories', :action => 'diff', :id => 'redmine', :path => %w[path to file.c]
209 should_route :get, "/projects/redmine/repository/diff/path/to/file.c", :controller => 'repositories', :action => 'diff', :id => 'redmine', :path => %w[path to file.c]
210 should_route :get, "/projects/redmine/repository/revisions/2/diff/path/to/file.c", :controller => 'repositories', :action => 'diff', :id => 'redmine', :path => %w[path to file.c], :rev => '2'
210 should_route :get, "/projects/redmine/repository/revisions/2/diff/path/to/file.c", :controller => 'repositories', :action => 'diff', :id => 'redmine', :path => %w[path to file.c], :rev => '2'
211 should_route :get, "/projects/redmine/repository/browse/path/to/file.c", :controller => 'repositories', :action => 'browse', :id => 'redmine', :path => %w[path to file.c]
211 should_route :get, "/projects/redmine/repository/browse/path/to/file.c", :controller => 'repositories', :action => 'browse', :id => 'redmine', :path => %w[path to file.c]
212 should_route :get, "/projects/redmine/repository/entry/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c]
212 should_route :get, "/projects/redmine/repository/entry/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c]
213 should_route :get, "/projects/redmine/repository/revisions/2/entry/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :rev => '2'
213 should_route :get, "/projects/redmine/repository/revisions/2/entry/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :rev => '2'
214 should_route :get, "/projects/redmine/repository/raw/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :format => 'raw'
214 should_route :get, "/projects/redmine/repository/raw/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :format => 'raw'
215 should_route :get, "/projects/redmine/repository/revisions/2/raw/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :rev => '2', :format => 'raw'
215 should_route :get, "/projects/redmine/repository/revisions/2/raw/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :rev => '2', :format => 'raw'
216 should_route :get, "/projects/redmine/repository/annotate/path/to/file.c", :controller => 'repositories', :action => 'annotate', :id => 'redmine', :path => %w[path to file.c]
216 should_route :get, "/projects/redmine/repository/annotate/path/to/file.c", :controller => 'repositories', :action => 'annotate', :id => 'redmine', :path => %w[path to file.c]
217 should_route :get, "/projects/redmine/repository/changes/path/to/file.c", :controller => 'repositories', :action => 'changes', :id => 'redmine', :path => %w[path to file.c]
217 should_route :get, "/projects/redmine/repository/changes/path/to/file.c", :controller => 'repositories', :action => 'changes', :id => 'redmine', :path => %w[path to file.c]
218 should_route :get, "/projects/redmine/repository/statistics", :controller => 'repositories', :action => 'stats', :id => 'redmine'
218 should_route :get, "/projects/redmine/repository/statistics", :controller => 'repositories', :action => 'stats', :id => 'redmine'
219
219
220
220
221 should_route :post, "/projects/redmine/repository/edit", :controller => 'repositories', :action => 'edit', :id => 'redmine'
221 should_route :post, "/projects/redmine/repository/edit", :controller => 'repositories', :action => 'edit', :id => 'redmine'
222 end
222 end
223
223
224 context "timelogs (global)" do
224 context "timelogs (global)" do
225 should_route :get, "/time_entries", :controller => 'timelog', :action => 'index'
225 should_route :get, "/time_entries", :controller => 'timelog', :action => 'index'
226 should_route :get, "/time_entries.csv", :controller => 'timelog', :action => 'index', :format => 'csv'
226 should_route :get, "/time_entries.csv", :controller => 'timelog', :action => 'index', :format => 'csv'
227 should_route :get, "/time_entries.atom", :controller => 'timelog', :action => 'index', :format => 'atom'
227 should_route :get, "/time_entries.atom", :controller => 'timelog', :action => 'index', :format => 'atom'
228 should_route :get, "/time_entries/new", :controller => 'timelog', :action => 'new'
228 should_route :get, "/time_entries/new", :controller => 'timelog', :action => 'new'
229 should_route :get, "/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22'
229 should_route :get, "/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22'
230
230
231 should_route :post, "/time_entries", :controller => 'timelog', :action => 'create'
231 should_route :post, "/time_entries", :controller => 'timelog', :action => 'create'
232
232
233 should_route :put, "/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22'
233 should_route :put, "/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22'
234
234
235 should_route :delete, "/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55'
235 should_route :delete, "/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55'
236 end
236 end
237
237
238 context "timelogs (scoped under project)" do
238 context "timelogs (scoped under project)" do
239 should_route :get, "/projects/567/time_entries", :controller => 'timelog', :action => 'index', :project_id => '567'
239 should_route :get, "/projects/567/time_entries", :controller => 'timelog', :action => 'index', :project_id => '567'
240 should_route :get, "/projects/567/time_entries.csv", :controller => 'timelog', :action => 'index', :project_id => '567', :format => 'csv'
240 should_route :get, "/projects/567/time_entries.csv", :controller => 'timelog', :action => 'index', :project_id => '567', :format => 'csv'
241 should_route :get, "/projects/567/time_entries.atom", :controller => 'timelog', :action => 'index', :project_id => '567', :format => 'atom'
241 should_route :get, "/projects/567/time_entries.atom", :controller => 'timelog', :action => 'index', :project_id => '567', :format => 'atom'
242 should_route :get, "/projects/567/time_entries/new", :controller => 'timelog', :action => 'new', :project_id => '567'
242 should_route :get, "/projects/567/time_entries/new", :controller => 'timelog', :action => 'new', :project_id => '567'
243 should_route :get, "/projects/567/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22', :project_id => '567'
243 should_route :get, "/projects/567/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22', :project_id => '567'
244
244
245 should_route :post, "/projects/567/time_entries", :controller => 'timelog', :action => 'create', :project_id => '567'
245 should_route :post, "/projects/567/time_entries", :controller => 'timelog', :action => 'create', :project_id => '567'
246
246
247 should_route :put, "/projects/567/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22', :project_id => '567'
247 should_route :put, "/projects/567/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22', :project_id => '567'
248
248
249 should_route :delete, "/projects/567/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55', :project_id => '567'
249 should_route :delete, "/projects/567/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55', :project_id => '567'
250 end
250 end
251
251
252 context "timelogs (scoped under issues)" do
252 context "timelogs (scoped under issues)" do
253 should_route :get, "/issues/234/time_entries", :controller => 'timelog', :action => 'index', :issue_id => '234'
253 should_route :get, "/issues/234/time_entries", :controller => 'timelog', :action => 'index', :issue_id => '234'
254 should_route :get, "/issues/234/time_entries.csv", :controller => 'timelog', :action => 'index', :issue_id => '234', :format => 'csv'
254 should_route :get, "/issues/234/time_entries.csv", :controller => 'timelog', :action => 'index', :issue_id => '234', :format => 'csv'
255 should_route :get, "/issues/234/time_entries.atom", :controller => 'timelog', :action => 'index', :issue_id => '234', :format => 'atom'
255 should_route :get, "/issues/234/time_entries.atom", :controller => 'timelog', :action => 'index', :issue_id => '234', :format => 'atom'
256 should_route :get, "/issues/234/time_entries/new", :controller => 'timelog', :action => 'new', :issue_id => '234'
256 should_route :get, "/issues/234/time_entries/new", :controller => 'timelog', :action => 'new', :issue_id => '234'
257 should_route :get, "/issues/234/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22', :issue_id => '234'
257 should_route :get, "/issues/234/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22', :issue_id => '234'
258
258
259 should_route :post, "/issues/234/time_entries", :controller => 'timelog', :action => 'create', :issue_id => '234'
259 should_route :post, "/issues/234/time_entries", :controller => 'timelog', :action => 'create', :issue_id => '234'
260
260
261 should_route :put, "/issues/234/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22', :issue_id => '234'
261 should_route :put, "/issues/234/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22', :issue_id => '234'
262
262
263 should_route :delete, "/issues/234/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55', :issue_id => '234'
263 should_route :delete, "/issues/234/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55', :issue_id => '234'
264 end
264 end
265
265
266 context "timelogs (scoped under project and issues)" do
266 context "timelogs (scoped under project and issues)" do
267 should_route :get, "/projects/ecookbook/issues/234/time_entries", :controller => 'timelog', :action => 'index', :issue_id => '234', :project_id => 'ecookbook'
267 should_route :get, "/projects/ecookbook/issues/234/time_entries", :controller => 'timelog', :action => 'index', :issue_id => '234', :project_id => 'ecookbook'
268 should_route :get, "/projects/ecookbook/issues/234/time_entries.csv", :controller => 'timelog', :action => 'index', :issue_id => '234', :project_id => 'ecookbook', :format => 'csv'
268 should_route :get, "/projects/ecookbook/issues/234/time_entries.csv", :controller => 'timelog', :action => 'index', :issue_id => '234', :project_id => 'ecookbook', :format => 'csv'
269 should_route :get, "/projects/ecookbook/issues/234/time_entries.atom", :controller => 'timelog', :action => 'index', :issue_id => '234', :project_id => 'ecookbook', :format => 'atom'
269 should_route :get, "/projects/ecookbook/issues/234/time_entries.atom", :controller => 'timelog', :action => 'index', :issue_id => '234', :project_id => 'ecookbook', :format => 'atom'
270 should_route :get, "/projects/ecookbook/issues/234/time_entries/new", :controller => 'timelog', :action => 'new', :issue_id => '234', :project_id => 'ecookbook'
270 should_route :get, "/projects/ecookbook/issues/234/time_entries/new", :controller => 'timelog', :action => 'new', :issue_id => '234', :project_id => 'ecookbook'
271 should_route :get, "/projects/ecookbook/issues/234/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22', :issue_id => '234', :project_id => 'ecookbook'
271 should_route :get, "/projects/ecookbook/issues/234/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22', :issue_id => '234', :project_id => 'ecookbook'
272
272
273 should_route :post, "/projects/ecookbook/issues/234/time_entries", :controller => 'timelog', :action => 'create', :issue_id => '234', :project_id => 'ecookbook'
273 should_route :post, "/projects/ecookbook/issues/234/time_entries", :controller => 'timelog', :action => 'create', :issue_id => '234', :project_id => 'ecookbook'
274
274
275 should_route :put, "/projects/ecookbook/issues/234/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22', :issue_id => '234', :project_id => 'ecookbook'
275 should_route :put, "/projects/ecookbook/issues/234/time_entries/22", :controller => 'timelog', :action => 'update', :id => '22', :issue_id => '234', :project_id => 'ecookbook'
276
276
277 should_route :delete, "/projects/ecookbook/issues/234/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55', :issue_id => '234', :project_id => 'ecookbook'
277 should_route :delete, "/projects/ecookbook/issues/234/time_entries/55", :controller => 'timelog', :action => 'destroy', :id => '55', :issue_id => '234', :project_id => 'ecookbook'
278 end
278 end
279
279
280 context "time_entry_reports" do
280 context "time_entry_reports" do
281 should_route :get, "/time_entries/report", :controller => 'time_entry_reports', :action => 'report'
281 should_route :get, "/time_entries/report", :controller => 'time_entry_reports', :action => 'report'
282 should_route :get, "/projects/567/time_entries/report", :controller => 'time_entry_reports', :action => 'report', :project_id => '567'
282 should_route :get, "/projects/567/time_entries/report", :controller => 'time_entry_reports', :action => 'report', :project_id => '567'
283 should_route :get, "/projects/567/time_entries/report.csv", :controller => 'time_entry_reports', :action => 'report', :project_id => '567', :format => 'csv'
283 should_route :get, "/projects/567/time_entries/report.csv", :controller => 'time_entry_reports', :action => 'report', :project_id => '567', :format => 'csv'
284 end
284 end
285
285
286 context "users" do
286 context "users" do
287 should_route :get, "/users", :controller => 'users', :action => 'index'
287 should_route :get, "/users", :controller => 'users', :action => 'index'
288 should_route :get, "/users/44", :controller => 'users', :action => 'show', :id => '44'
288 should_route :get, "/users/44", :controller => 'users', :action => 'show', :id => '44'
289 should_route :get, "/users/new", :controller => 'users', :action => 'new'
289 should_route :get, "/users/new", :controller => 'users', :action => 'new'
290 should_route :get, "/users/444/edit", :controller => 'users', :action => 'edit', :id => '444'
290 should_route :get, "/users/444/edit", :controller => 'users', :action => 'edit', :id => '444'
291 should_route :get, "/users/222/edit/membership", :controller => 'users', :action => 'edit', :id => '222', :tab => 'membership'
291 should_route :get, "/users/222/edit/membership", :controller => 'users', :action => 'edit', :id => '222', :tab => 'membership'
292
292
293 should_route :post, "/users", :controller => 'users', :action => 'create'
293 should_route :post, "/users", :controller => 'users', :action => 'create'
294 should_route :post, "/users/123/memberships", :controller => 'users', :action => 'edit_membership', :id => '123'
294 should_route :post, "/users/123/memberships", :controller => 'users', :action => 'edit_membership', :id => '123'
295 should_route :post, "/users/123/memberships/55", :controller => 'users', :action => 'edit_membership', :id => '123', :membership_id => '55'
295 should_route :post, "/users/123/memberships/55", :controller => 'users', :action => 'edit_membership', :id => '123', :membership_id => '55'
296 should_route :post, "/users/567/memberships/12/destroy", :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12'
296 should_route :post, "/users/567/memberships/12/destroy", :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12'
297
297
298 should_route :put, "/users/444", :controller => 'users', :action => 'update', :id => '444'
298 should_route :put, "/users/444", :controller => 'users', :action => 'update', :id => '444'
299 end
299 end
300
300
301 # TODO: should they all be scoped under /projects/:project_id ?
301 # TODO: should they all be scoped under /projects/:project_id ?
302 context "versions" do
302 context "versions" do
303 should_route :get, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo'
303 should_route :get, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo'
304 should_route :get, "/versions/show/1", :controller => 'versions', :action => 'show', :id => '1'
304 should_route :get, "/versions/show/1", :controller => 'versions', :action => 'show', :id => '1'
305 should_route :get, "/versions/edit/1", :controller => 'versions', :action => 'edit', :id => '1'
305 should_route :get, "/versions/edit/1", :controller => 'versions', :action => 'edit', :id => '1'
306
306
307 should_route :post, "/projects/foo/versions", :controller => 'versions', :action => 'create', :project_id => 'foo'
307 should_route :post, "/projects/foo/versions", :controller => 'versions', :action => 'create', :project_id => 'foo'
308 should_route :post, "/versions/update/1", :controller => 'versions', :action => 'update', :id => '1'
308 should_route :post, "/versions/update/1", :controller => 'versions', :action => 'update', :id => '1'
309
309
310 should_route :delete, "/versions/destroy/1", :controller => 'versions', :action => 'destroy', :id => '1'
310 should_route :delete, "/versions/destroy/1", :controller => 'versions', :action => 'destroy', :id => '1'
311 end
311 end
312
312
313 context "wiki (singular, project's pages)" do
313 context "wiki (singular, project's pages)" do
314 should_route :get, "/projects/567/wiki", :controller => 'wiki', :action => 'show', :project_id => '567'
314 should_route :get, "/projects/567/wiki", :controller => 'wiki', :action => 'show', :project_id => '567'
315 should_route :get, "/projects/567/wiki/lalala", :controller => 'wiki', :action => 'show', :project_id => '567', :id => 'lalala'
315 should_route :get, "/projects/567/wiki/lalala", :controller => 'wiki', :action => 'show', :project_id => '567', :id => 'lalala'
316 should_route :get, "/projects/567/wiki/my_page/edit", :controller => 'wiki', :action => 'edit', :project_id => '567', :id => 'my_page'
316 should_route :get, "/projects/567/wiki/my_page/edit", :controller => 'wiki', :action => 'edit', :project_id => '567', :id => 'my_page'
317 should_route :get, "/projects/1/wiki/CookBook_documentation/history", :controller => 'wiki', :action => 'history', :project_id => '1', :id => 'CookBook_documentation'
317 should_route :get, "/projects/1/wiki/CookBook_documentation/history", :controller => 'wiki', :action => 'history', :project_id => '1', :id => 'CookBook_documentation'
318 should_route :get, "/projects/1/wiki/CookBook_documentation/diff/2/vs/1", :controller => 'wiki', :action => 'diff', :project_id => '1', :id => 'CookBook_documentation', :version => '2', :version_from => '1'
318 should_route :get, "/projects/1/wiki/CookBook_documentation/diff/2/vs/1", :controller => 'wiki', :action => 'diff', :project_id => '1', :id => 'CookBook_documentation', :version => '2', :version_from => '1'
319 should_route :get, "/projects/1/wiki/CookBook_documentation/annotate/2", :controller => 'wiki', :action => 'annotate', :project_id => '1', :id => 'CookBook_documentation', :version => '2'
319 should_route :get, "/projects/1/wiki/CookBook_documentation/annotate/2", :controller => 'wiki', :action => 'annotate', :project_id => '1', :id => 'CookBook_documentation', :version => '2'
320 should_route :get, "/projects/22/wiki/ladida/rename", :controller => 'wiki', :action => 'rename', :project_id => '22', :id => 'ladida'
320 should_route :get, "/projects/22/wiki/ladida/rename", :controller => 'wiki', :action => 'rename', :project_id => '22', :id => 'ladida'
321 should_route :get, "/projects/567/wiki/index", :controller => 'wiki', :action => 'index', :project_id => '567'
321 should_route :get, "/projects/567/wiki/index", :controller => 'wiki', :action => 'index', :project_id => '567'
322 should_route :get, "/projects/567/wiki/date_index", :controller => 'wiki', :action => 'date_index', :project_id => '567'
322 should_route :get, "/projects/567/wiki/date_index", :controller => 'wiki', :action => 'date_index', :project_id => '567'
323 should_route :get, "/projects/567/wiki/export", :controller => 'wiki', :action => 'export', :project_id => '567'
323 should_route :get, "/projects/567/wiki/export", :controller => 'wiki', :action => 'export', :project_id => '567'
324
324
325 should_route :post, "/projects/567/wiki/my_page/edit", :controller => 'wiki', :action => 'update', :project_id => '567', :id => 'my_page'
326 should_route :post, "/projects/567/wiki/CookBook_documentation/preview", :controller => 'wiki', :action => 'preview', :project_id => '567', :id => 'CookBook_documentation'
325 should_route :post, "/projects/567/wiki/CookBook_documentation/preview", :controller => 'wiki', :action => 'preview', :project_id => '567', :id => 'CookBook_documentation'
327 should_route :post, "/projects/22/wiki/ladida/rename", :controller => 'wiki', :action => 'rename', :project_id => '22', :id => 'ladida'
326 should_route :post, "/projects/22/wiki/ladida/rename", :controller => 'wiki', :action => 'rename', :project_id => '22', :id => 'ladida'
328 should_route :post, "/projects/22/wiki/ladida/protect", :controller => 'wiki', :action => 'protect', :project_id => '22', :id => 'ladida'
327 should_route :post, "/projects/22/wiki/ladida/protect", :controller => 'wiki', :action => 'protect', :project_id => '22', :id => 'ladida'
329 should_route :post, "/projects/22/wiki/ladida/add_attachment", :controller => 'wiki', :action => 'add_attachment', :project_id => '22', :id => 'ladida'
328 should_route :post, "/projects/22/wiki/ladida/add_attachment", :controller => 'wiki', :action => 'add_attachment', :project_id => '22', :id => 'ladida'
330
329
330 should_route :put, "/projects/567/wiki/my_page", :controller => 'wiki', :action => 'update', :project_id => '567', :id => 'my_page'
331
331 should_route :delete, "/projects/22/wiki/ladida", :controller => 'wiki', :action => 'destroy', :project_id => '22', :id => 'ladida'
332 should_route :delete, "/projects/22/wiki/ladida", :controller => 'wiki', :action => 'destroy', :project_id => '22', :id => 'ladida'
332 end
333 end
333
334
334 context "wikis (plural, admin setup)" do
335 context "wikis (plural, admin setup)" do
335 should_route :get, "/projects/ladida/wiki/destroy", :controller => 'wikis', :action => 'destroy', :id => 'ladida'
336 should_route :get, "/projects/ladida/wiki/destroy", :controller => 'wikis', :action => 'destroy', :id => 'ladida'
336
337
337 should_route :post, "/projects/ladida/wiki", :controller => 'wikis', :action => 'edit', :id => 'ladida'
338 should_route :post, "/projects/ladida/wiki", :controller => 'wikis', :action => 'edit', :id => 'ladida'
338 should_route :post, "/projects/ladida/wiki/destroy", :controller => 'wikis', :action => 'destroy', :id => 'ladida'
339 should_route :post, "/projects/ladida/wiki/destroy", :controller => 'wikis', :action => 'destroy', :id => 'ladida'
339 end
340 end
340
341
341 context "administration panel" do
342 context "administration panel" do
342 should_route :get, "/admin/projects", :controller => 'admin', :action => 'projects'
343 should_route :get, "/admin/projects", :controller => 'admin', :action => 'projects'
343 end
344 end
344 end
345 end
@@ -1,631 +1,631
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 File.dirname(__FILE__) + '/../../test_helper'
18 require File.dirname(__FILE__) + '/../../test_helper'
19
19
20 class ApplicationHelperTest < ActionView::TestCase
20 class ApplicationHelperTest < ActionView::TestCase
21
21
22 fixtures :projects, :roles, :enabled_modules, :users,
22 fixtures :projects, :roles, :enabled_modules, :users,
23 :repositories, :changesets,
23 :repositories, :changesets,
24 :trackers, :issue_statuses, :issues, :versions, :documents,
24 :trackers, :issue_statuses, :issues, :versions, :documents,
25 :wikis, :wiki_pages, :wiki_contents,
25 :wikis, :wiki_pages, :wiki_contents,
26 :boards, :messages,
26 :boards, :messages,
27 :attachments,
27 :attachments,
28 :enumerations
28 :enumerations
29
29
30 def setup
30 def setup
31 super
31 super
32 end
32 end
33
33
34 context "#link_to_if_authorized" do
34 context "#link_to_if_authorized" do
35 context "authorized user" do
35 context "authorized user" do
36 should "be tested"
36 should "be tested"
37 end
37 end
38
38
39 context "unauthorized user" do
39 context "unauthorized user" do
40 should "be tested"
40 should "be tested"
41 end
41 end
42
42
43 should "allow using the :controller and :action for the target link" do
43 should "allow using the :controller and :action for the target link" do
44 User.current = User.find_by_login('admin')
44 User.current = User.find_by_login('admin')
45
45
46 @project = Issue.first.project # Used by helper
46 @project = Issue.first.project # Used by helper
47 response = link_to_if_authorized("By controller/action",
47 response = link_to_if_authorized("By controller/action",
48 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
48 {:controller => 'issues', :action => 'edit', :id => Issue.first.id})
49 assert_match /href/, response
49 assert_match /href/, response
50 end
50 end
51
51
52 end
52 end
53
53
54 def test_auto_links
54 def test_auto_links
55 to_test = {
55 to_test = {
56 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
56 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
57 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
57 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
58 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
58 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
59 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
59 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.',
60 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
60 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
61 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
61 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
62 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
62 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
63 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
63 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
64 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
64 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
65 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
65 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
66 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
66 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
67 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
67 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
68 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
68 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
69 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
69 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
70 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
70 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
71 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
71 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
72 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
72 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
73 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
73 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
74 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
74 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
75 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
75 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
76 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
76 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>',
77 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
77 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>',
78 # two exclamation marks
78 # two exclamation marks
79 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
79 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>',
80 # escaping
80 # escaping
81 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo"bar</a>',
81 'http://foo"bar' => '<a class="external" href="http://foo&quot;bar">http://foo"bar</a>',
82 # wrap in angle brackets
82 # wrap in angle brackets
83 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;'
83 '<http://foo.bar>' => '&lt;<a class="external" href="http://foo.bar">http://foo.bar</a>&gt;'
84 }
84 }
85 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
85 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
86 end
86 end
87
87
88 def test_auto_mailto
88 def test_auto_mailto
89 assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
89 assert_equal '<p><a class="email" href="mailto:test@foo.bar">test@foo.bar</a></p>',
90 textilizable('test@foo.bar')
90 textilizable('test@foo.bar')
91 end
91 end
92
92
93 def test_inline_images
93 def test_inline_images
94 to_test = {
94 to_test = {
95 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
95 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
96 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
96 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
97 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
97 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
98 # inline styles should be stripped
98 # inline styles should be stripped
99 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
99 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" alt="" />',
100 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
100 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />',
101 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
101 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted &quot;title&quot;" alt="This is a double-quoted &quot;title&quot;" />',
102 }
102 }
103 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
103 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
104 end
104 end
105
105
106 def test_inline_images_inside_tags
106 def test_inline_images_inside_tags
107 raw = <<-RAW
107 raw = <<-RAW
108 h1. !foo.png! Heading
108 h1. !foo.png! Heading
109
109
110 Centered image:
110 Centered image:
111
111
112 p=. !bar.gif!
112 p=. !bar.gif!
113 RAW
113 RAW
114
114
115 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
115 assert textilizable(raw).include?('<img src="foo.png" alt="" />')
116 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
116 assert textilizable(raw).include?('<img src="bar.gif" alt="" />')
117 end
117 end
118
118
119 def test_acronyms
119 def test_acronyms
120 to_test = {
120 to_test = {
121 'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
121 'this is an acronym: GPL(General Public License)' => 'this is an acronym: <acronym title="General Public License">GPL</acronym>',
122 'GPL(This is a double-quoted "title")' => '<acronym title="This is a double-quoted &quot;title&quot;">GPL</acronym>',
122 'GPL(This is a double-quoted "title")' => '<acronym title="This is a double-quoted &quot;title&quot;">GPL</acronym>',
123 }
123 }
124 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
124 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
125
125
126 end
126 end
127
127
128 def test_attached_images
128 def test_attached_images
129 to_test = {
129 to_test = {
130 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
130 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
131 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
131 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
132 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
132 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />',
133 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
133 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />',
134 # link image
134 # link image
135 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3" title="This is a logo" alt="This is a logo" /></a>',
135 '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3" title="This is a logo" alt="This is a logo" /></a>',
136 }
136 }
137 attachments = Attachment.find(:all)
137 attachments = Attachment.find(:all)
138 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
138 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
139 end
139 end
140
140
141 def test_textile_external_links
141 def test_textile_external_links
142 to_test = {
142 to_test = {
143 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
143 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
144 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
144 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
145 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
145 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
146 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
146 '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with &quot;double-quotes&quot;" class="external">link</a>',
147 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
147 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
148 # no multiline link text
148 # no multiline link text
149 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test",
149 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test",
150 # mailto link
150 # mailto link
151 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
151 "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>",
152 # two exclamation marks
152 # two exclamation marks
153 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
153 '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>',
154 # escaping
154 # escaping
155 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
155 '"test":http://foo"bar' => '<a href="http://foo&quot;bar" class="external">test</a>',
156 }
156 }
157 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
157 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
158 end
158 end
159
159
160 def test_redmine_links
160 def test_redmine_links
161 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
161 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
162 :class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')
162 :class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')
163
163
164 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
164 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
165 :class => 'changeset', :title => 'My very first commit')
165 :class => 'changeset', :title => 'My very first commit')
166 changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
166 changeset_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
167 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
167 :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
168
168
169 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
169 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
170 :class => 'document')
170 :class => 'document')
171
171
172 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
172 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
173 :class => 'version')
173 :class => 'version')
174
174
175 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
175 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
176
176
177 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
177 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
178
178
179 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
179 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
180 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
180 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
181
181
182 to_test = {
182 to_test = {
183 # tickets
183 # tickets
184 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
184 '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.",
185 # changesets
185 # changesets
186 'r1' => changeset_link,
186 'r1' => changeset_link,
187 'r1.' => "#{changeset_link}.",
187 'r1.' => "#{changeset_link}.",
188 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
188 'r1, r2' => "#{changeset_link}, #{changeset_link2}",
189 'r1,r2' => "#{changeset_link},#{changeset_link2}",
189 'r1,r2' => "#{changeset_link},#{changeset_link2}",
190 # documents
190 # documents
191 'document#1' => document_link,
191 'document#1' => document_link,
192 'document:"Test document"' => document_link,
192 'document:"Test document"' => document_link,
193 # versions
193 # versions
194 'version#2' => version_link,
194 'version#2' => version_link,
195 'version:1.0' => version_link,
195 'version:1.0' => version_link,
196 'version:"1.0"' => version_link,
196 'version:"1.0"' => version_link,
197 # source
197 # source
198 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
198 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
199 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
199 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
200 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
200 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
201 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
201 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
202 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
202 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
203 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
203 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
204 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
204 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
205 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
205 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
206 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
206 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
207 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
207 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
208 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
208 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
209 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
209 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
210 # message
210 # message
211 'message#4' => link_to('Post 2', message_url, :class => 'message'),
211 'message#4' => link_to('Post 2', message_url, :class => 'message'),
212 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
212 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
213 # project
213 # project
214 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
214 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
215 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
215 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
216 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
216 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
217 # escaping
217 # escaping
218 '!#3.' => '#3.',
218 '!#3.' => '#3.',
219 '!r1' => 'r1',
219 '!r1' => 'r1',
220 '!document#1' => 'document#1',
220 '!document#1' => 'document#1',
221 '!document:"Test document"' => 'document:"Test document"',
221 '!document:"Test document"' => 'document:"Test document"',
222 '!version#2' => 'version#2',
222 '!version#2' => 'version#2',
223 '!version:1.0' => 'version:1.0',
223 '!version:1.0' => 'version:1.0',
224 '!version:"1.0"' => 'version:"1.0"',
224 '!version:"1.0"' => 'version:"1.0"',
225 '!source:/some/file' => 'source:/some/file',
225 '!source:/some/file' => 'source:/some/file',
226 # not found
226 # not found
227 '#0123456789' => '#0123456789',
227 '#0123456789' => '#0123456789',
228 # invalid expressions
228 # invalid expressions
229 'source:' => 'source:',
229 'source:' => 'source:',
230 # url hash
230 # url hash
231 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
231 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
232 }
232 }
233 @project = Project.find(1)
233 @project = Project.find(1)
234 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
234 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
235 end
235 end
236
236
237 def test_attachment_links
237 def test_attachment_links
238 attachment_link = link_to('error281.txt', {:controller => 'attachments', :action => 'download', :id => '1'}, :class => 'attachment')
238 attachment_link = link_to('error281.txt', {:controller => 'attachments', :action => 'download', :id => '1'}, :class => 'attachment')
239 to_test = {
239 to_test = {
240 'attachment:error281.txt' => attachment_link
240 'attachment:error281.txt' => attachment_link
241 }
241 }
242 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
242 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
243 end
243 end
244
244
245 def test_wiki_links
245 def test_wiki_links
246 to_test = {
246 to_test = {
247 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
247 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
248 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
248 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
249 # link with anchor
249 # link with anchor
250 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
250 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
251 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
251 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
252 # page that doesn't exist
252 # page that doesn't exist
253 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
253 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
254 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
254 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
255 # link to another project wiki
255 # link to another project wiki
256 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">onlinestore</a>',
256 '[[onlinestore:]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">onlinestore</a>',
257 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki/" class="wiki-page">Wiki</a>',
257 '[[onlinestore:|Wiki]]' => '<a href="/projects/onlinestore/wiki" class="wiki-page">Wiki</a>',
258 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
258 '[[onlinestore:Start page]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Start page</a>',
259 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
259 '[[onlinestore:Start page|Text]]' => '<a href="/projects/onlinestore/wiki/Start_page" class="wiki-page">Text</a>',
260 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
260 '[[onlinestore:Unknown page]]' => '<a href="/projects/onlinestore/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
261 # striked through link
261 # striked through link
262 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
262 '-[[Another page|Page]]-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a></del>',
263 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
263 '-[[Another page|Page]] link-' => '<del><a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a> link</del>',
264 # escaping
264 # escaping
265 '![[Another page|Page]]' => '[[Another page|Page]]',
265 '![[Another page|Page]]' => '[[Another page|Page]]',
266 # project does not exist
266 # project does not exist
267 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
267 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
268 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
268 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
269 }
269 }
270 @project = Project.find(1)
270 @project = Project.find(1)
271 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
271 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
272 end
272 end
273
273
274 def test_html_tags
274 def test_html_tags
275 to_test = {
275 to_test = {
276 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
276 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
277 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
277 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
278 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
278 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
279 # do not escape pre/code tags
279 # do not escape pre/code tags
280 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
280 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
281 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
281 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
282 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
282 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
283 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
283 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
284 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
284 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
285 # remove attributes except class
285 # remove attributes except class
286 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
286 "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>",
287 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
287 "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>",
288 }
288 }
289 to_test.each { |text, result| assert_equal result, textilizable(text) }
289 to_test.each { |text, result| assert_equal result, textilizable(text) }
290 end
290 end
291
291
292 def test_allowed_html_tags
292 def test_allowed_html_tags
293 to_test = {
293 to_test = {
294 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
294 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
295 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
295 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
296 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
296 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
297 }
297 }
298 to_test.each { |text, result| assert_equal result, textilizable(text) }
298 to_test.each { |text, result| assert_equal result, textilizable(text) }
299 end
299 end
300
300
301 def test_pre_tags
301 def test_pre_tags
302 raw = <<-RAW
302 raw = <<-RAW
303 Before
303 Before
304
304
305 <pre>
305 <pre>
306 <prepared-statement-cache-size>32</prepared-statement-cache-size>
306 <prepared-statement-cache-size>32</prepared-statement-cache-size>
307 </pre>
307 </pre>
308
308
309 After
309 After
310 RAW
310 RAW
311
311
312 expected = <<-EXPECTED
312 expected = <<-EXPECTED
313 <p>Before</p>
313 <p>Before</p>
314 <pre>
314 <pre>
315 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
315 &lt;prepared-statement-cache-size&gt;32&lt;/prepared-statement-cache-size&gt;
316 </pre>
316 </pre>
317 <p>After</p>
317 <p>After</p>
318 EXPECTED
318 EXPECTED
319
319
320 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
320 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
321 end
321 end
322
322
323 def test_pre_content_should_not_parse_wiki_and_redmine_links
323 def test_pre_content_should_not_parse_wiki_and_redmine_links
324 raw = <<-RAW
324 raw = <<-RAW
325 [[CookBook documentation]]
325 [[CookBook documentation]]
326
326
327 #1
327 #1
328
328
329 <pre>
329 <pre>
330 [[CookBook documentation]]
330 [[CookBook documentation]]
331
331
332 #1
332 #1
333 </pre>
333 </pre>
334 RAW
334 RAW
335
335
336 expected = <<-EXPECTED
336 expected = <<-EXPECTED
337 <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
337 <p><a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a></p>
338 <p><a href="/issues/1" class="issue status-1 priority-1" title="Can't print recipes (New)">#1</a></p>
338 <p><a href="/issues/1" class="issue status-1 priority-1" title="Can't print recipes (New)">#1</a></p>
339 <pre>
339 <pre>
340 [[CookBook documentation]]
340 [[CookBook documentation]]
341
341
342 #1
342 #1
343 </pre>
343 </pre>
344 EXPECTED
344 EXPECTED
345
345
346 @project = Project.find(1)
346 @project = Project.find(1)
347 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
347 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
348 end
348 end
349
349
350 def test_non_closing_pre_blocks_should_be_closed
350 def test_non_closing_pre_blocks_should_be_closed
351 raw = <<-RAW
351 raw = <<-RAW
352 <pre><code>
352 <pre><code>
353 RAW
353 RAW
354
354
355 expected = <<-EXPECTED
355 expected = <<-EXPECTED
356 <pre><code>
356 <pre><code>
357 </code></pre>
357 </code></pre>
358 EXPECTED
358 EXPECTED
359
359
360 @project = Project.find(1)
360 @project = Project.find(1)
361 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
361 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
362 end
362 end
363
363
364 def test_syntax_highlight
364 def test_syntax_highlight
365 raw = <<-RAW
365 raw = <<-RAW
366 <pre><code class="ruby">
366 <pre><code class="ruby">
367 # Some ruby code here
367 # Some ruby code here
368 </code></pre>
368 </code></pre>
369 RAW
369 RAW
370
370
371 expected = <<-EXPECTED
371 expected = <<-EXPECTED
372 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="no">1</span> <span class="c"># Some ruby code here</span></span>
372 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="no">1</span> <span class="c"># Some ruby code here</span></span>
373 </code></pre>
373 </code></pre>
374 EXPECTED
374 EXPECTED
375
375
376 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
376 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
377 end
377 end
378
378
379 def test_wiki_links_in_tables
379 def test_wiki_links_in_tables
380 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
380 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
381 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
381 '<tr><td><a href="/projects/ecookbook/wiki/Page" class="wiki-page new">Link title</a></td>' +
382 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
382 '<td><a href="/projects/ecookbook/wiki/Other_Page" class="wiki-page new">Other title</a></td>' +
383 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
383 '</tr><tr><td>Cell 21</td><td><a href="/projects/ecookbook/wiki/Last_page" class="wiki-page new">Last page</a></td></tr>'
384 }
384 }
385 @project = Project.find(1)
385 @project = Project.find(1)
386 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
386 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
387 end
387 end
388
388
389 def test_text_formatting
389 def test_text_formatting
390 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
390 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
391 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
391 '(_text within parentheses_)' => '(<em>text within parentheses</em>)',
392 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
392 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator',
393 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
393 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>',
394 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
394 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator',
395 }
395 }
396 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
396 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
397 end
397 end
398
398
399 def test_wiki_horizontal_rule
399 def test_wiki_horizontal_rule
400 assert_equal '<hr />', textilizable('---')
400 assert_equal '<hr />', textilizable('---')
401 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
401 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
402 end
402 end
403
403
404 def test_acronym
404 def test_acronym
405 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
405 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
406 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
406 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
407 end
407 end
408
408
409 def test_footnotes
409 def test_footnotes
410 raw = <<-RAW
410 raw = <<-RAW
411 This is some text[1].
411 This is some text[1].
412
412
413 fn1. This is the foot note
413 fn1. This is the foot note
414 RAW
414 RAW
415
415
416 expected = <<-EXPECTED
416 expected = <<-EXPECTED
417 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
417 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
418 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
418 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
419 EXPECTED
419 EXPECTED
420
420
421 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
421 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
422 end
422 end
423
423
424 def test_table_of_content
424 def test_table_of_content
425 raw = <<-RAW
425 raw = <<-RAW
426 {{toc}}
426 {{toc}}
427
427
428 h1. Title
428 h1. Title
429
429
430 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
430 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
431
431
432 h2. Subtitle with a [[Wiki]] link
432 h2. Subtitle with a [[Wiki]] link
433
433
434 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
434 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
435
435
436 h2. Subtitle with [[Wiki|another Wiki]] link
436 h2. Subtitle with [[Wiki|another Wiki]] link
437
437
438 h2. Subtitle with %{color:red}red text%
438 h2. Subtitle with %{color:red}red text%
439
439
440 h1. Another title
440 h1. Another title
441
441
442 h2. An "Internet link":http://www.redmine.org/ inside subtitle
442 h2. An "Internet link":http://www.redmine.org/ inside subtitle
443
443
444 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
444 h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues
445
445
446 RAW
446 RAW
447
447
448 expected = '<ul class="toc">' +
448 expected = '<ul class="toc">' +
449 '<li class="heading1"><a href="#Title">Title</a></li>' +
449 '<li class="heading1"><a href="#Title">Title</a></li>' +
450 '<li class="heading2"><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
450 '<li class="heading2"><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' +
451 '<li class="heading2"><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
451 '<li class="heading2"><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' +
452 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
452 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
453 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
453 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
454 '<li class="heading2"><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
454 '<li class="heading2"><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' +
455 '<li class="heading2"><a href="#Project-Name">Project Name</a></li>' +
455 '<li class="heading2"><a href="#Project-Name">Project Name</a></li>' +
456 '</ul>'
456 '</ul>'
457
457
458 assert textilizable(raw).gsub("\n", "").include?(expected)
458 assert textilizable(raw).gsub("\n", "").include?(expected)
459 end
459 end
460
460
461 def test_blockquote
461 def test_blockquote
462 # orig raw text
462 # orig raw text
463 raw = <<-RAW
463 raw = <<-RAW
464 John said:
464 John said:
465 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
465 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
466 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
466 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
467 > * Donec odio lorem,
467 > * Donec odio lorem,
468 > * sagittis ac,
468 > * sagittis ac,
469 > * malesuada in,
469 > * malesuada in,
470 > * adipiscing eu, dolor.
470 > * adipiscing eu, dolor.
471 >
471 >
472 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
472 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
473 > Proin a tellus. Nam vel neque.
473 > Proin a tellus. Nam vel neque.
474
474
475 He's right.
475 He's right.
476 RAW
476 RAW
477
477
478 # expected html
478 # expected html
479 expected = <<-EXPECTED
479 expected = <<-EXPECTED
480 <p>John said:</p>
480 <p>John said:</p>
481 <blockquote>
481 <blockquote>
482 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
482 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
483 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
483 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
484 <ul>
484 <ul>
485 <li>Donec odio lorem,</li>
485 <li>Donec odio lorem,</li>
486 <li>sagittis ac,</li>
486 <li>sagittis ac,</li>
487 <li>malesuada in,</li>
487 <li>malesuada in,</li>
488 <li>adipiscing eu, dolor.</li>
488 <li>adipiscing eu, dolor.</li>
489 </ul>
489 </ul>
490 <blockquote>
490 <blockquote>
491 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
491 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
492 </blockquote>
492 </blockquote>
493 <p>Proin a tellus. Nam vel neque.</p>
493 <p>Proin a tellus. Nam vel neque.</p>
494 </blockquote>
494 </blockquote>
495 <p>He's right.</p>
495 <p>He's right.</p>
496 EXPECTED
496 EXPECTED
497
497
498 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
498 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
499 end
499 end
500
500
501 def test_table
501 def test_table
502 raw = <<-RAW
502 raw = <<-RAW
503 This is a table with empty cells:
503 This is a table with empty cells:
504
504
505 |cell11|cell12||
505 |cell11|cell12||
506 |cell21||cell23|
506 |cell21||cell23|
507 |cell31|cell32|cell33|
507 |cell31|cell32|cell33|
508 RAW
508 RAW
509
509
510 expected = <<-EXPECTED
510 expected = <<-EXPECTED
511 <p>This is a table with empty cells:</p>
511 <p>This is a table with empty cells:</p>
512
512
513 <table>
513 <table>
514 <tr><td>cell11</td><td>cell12</td><td></td></tr>
514 <tr><td>cell11</td><td>cell12</td><td></td></tr>
515 <tr><td>cell21</td><td></td><td>cell23</td></tr>
515 <tr><td>cell21</td><td></td><td>cell23</td></tr>
516 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
516 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
517 </table>
517 </table>
518 EXPECTED
518 EXPECTED
519
519
520 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
520 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
521 end
521 end
522
522
523 def test_table_with_line_breaks
523 def test_table_with_line_breaks
524 raw = <<-RAW
524 raw = <<-RAW
525 This is a table with line breaks:
525 This is a table with line breaks:
526
526
527 |cell11
527 |cell11
528 continued|cell12||
528 continued|cell12||
529 |-cell21-||cell23
529 |-cell21-||cell23
530 cell23 line2
530 cell23 line2
531 cell23 *line3*|
531 cell23 *line3*|
532 |cell31|cell32
532 |cell31|cell32
533 cell32 line2|cell33|
533 cell32 line2|cell33|
534
534
535 RAW
535 RAW
536
536
537 expected = <<-EXPECTED
537 expected = <<-EXPECTED
538 <p>This is a table with line breaks:</p>
538 <p>This is a table with line breaks:</p>
539
539
540 <table>
540 <table>
541 <tr>
541 <tr>
542 <td>cell11<br />continued</td>
542 <td>cell11<br />continued</td>
543 <td>cell12</td>
543 <td>cell12</td>
544 <td></td>
544 <td></td>
545 </tr>
545 </tr>
546 <tr>
546 <tr>
547 <td><del>cell21</del></td>
547 <td><del>cell21</del></td>
548 <td></td>
548 <td></td>
549 <td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
549 <td>cell23<br/>cell23 line2<br/>cell23 <strong>line3</strong></td>
550 </tr>
550 </tr>
551 <tr>
551 <tr>
552 <td>cell31</td>
552 <td>cell31</td>
553 <td>cell32<br/>cell32 line2</td>
553 <td>cell32<br/>cell32 line2</td>
554 <td>cell33</td>
554 <td>cell33</td>
555 </tr>
555 </tr>
556 </table>
556 </table>
557 EXPECTED
557 EXPECTED
558
558
559 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
559 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
560 end
560 end
561
561
562 def test_textile_should_not_mangle_brackets
562 def test_textile_should_not_mangle_brackets
563 assert_equal '<p>[msg1][msg2]</p>', textilizable('[msg1][msg2]')
563 assert_equal '<p>[msg1][msg2]</p>', textilizable('[msg1][msg2]')
564 end
564 end
565
565
566 def test_default_formatter
566 def test_default_formatter
567 Setting.text_formatting = 'unknown'
567 Setting.text_formatting = 'unknown'
568 text = 'a *link*: http://www.example.net/'
568 text = 'a *link*: http://www.example.net/'
569 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
569 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
570 Setting.text_formatting = 'textile'
570 Setting.text_formatting = 'textile'
571 end
571 end
572
572
573 def test_due_date_distance_in_words
573 def test_due_date_distance_in_words
574 to_test = { Date.today => 'Due in 0 days',
574 to_test = { Date.today => 'Due in 0 days',
575 Date.today + 1 => 'Due in 1 day',
575 Date.today + 1 => 'Due in 1 day',
576 Date.today + 100 => 'Due in about 3 months',
576 Date.today + 100 => 'Due in about 3 months',
577 Date.today + 20000 => 'Due in over 54 years',
577 Date.today + 20000 => 'Due in over 54 years',
578 Date.today - 1 => '1 day late',
578 Date.today - 1 => '1 day late',
579 Date.today - 100 => 'about 3 months late',
579 Date.today - 100 => 'about 3 months late',
580 Date.today - 20000 => 'over 54 years late',
580 Date.today - 20000 => 'over 54 years late',
581 }
581 }
582 to_test.each do |date, expected|
582 to_test.each do |date, expected|
583 assert_equal expected, due_date_distance_in_words(date)
583 assert_equal expected, due_date_distance_in_words(date)
584 end
584 end
585 end
585 end
586
586
587 def test_avatar
587 def test_avatar
588 # turn on avatars
588 # turn on avatars
589 Setting.gravatar_enabled = '1'
589 Setting.gravatar_enabled = '1'
590 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
590 assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
591 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
591 assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo'))
592 assert_nil avatar('jsmith')
592 assert_nil avatar('jsmith')
593 assert_nil avatar(nil)
593 assert_nil avatar(nil)
594
594
595 # turn off avatars
595 # turn off avatars
596 Setting.gravatar_enabled = '0'
596 Setting.gravatar_enabled = '0'
597 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
597 assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
598 end
598 end
599
599
600 def test_link_to_user
600 def test_link_to_user
601 user = User.find(2)
601 user = User.find(2)
602 t = link_to_user(user)
602 t = link_to_user(user)
603 assert_equal "<a href=\"/users/2\">#{ user.name }</a>", t
603 assert_equal "<a href=\"/users/2\">#{ user.name }</a>", t
604 end
604 end
605
605
606 def test_link_to_user_should_not_link_to_locked_user
606 def test_link_to_user_should_not_link_to_locked_user
607 user = User.find(5)
607 user = User.find(5)
608 assert user.locked?
608 assert user.locked?
609 t = link_to_user(user)
609 t = link_to_user(user)
610 assert_equal user.name, t
610 assert_equal user.name, t
611 end
611 end
612
612
613 def test_link_to_user_should_not_link_to_anonymous
613 def test_link_to_user_should_not_link_to_anonymous
614 user = User.anonymous
614 user = User.anonymous
615 assert user.anonymous?
615 assert user.anonymous?
616 t = link_to_user(user)
616 t = link_to_user(user)
617 assert_equal ::I18n.t(:label_user_anonymous), t
617 assert_equal ::I18n.t(:label_user_anonymous), t
618 end
618 end
619
619
620 def test_link_to_project
620 def test_link_to_project
621 project = Project.find(1)
621 project = Project.find(1)
622 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
622 assert_equal %(<a href="/projects/ecookbook">eCookbook</a>),
623 link_to_project(project)
623 link_to_project(project)
624 assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
624 assert_equal %(<a href="/projects/ecookbook/settings">eCookbook</a>),
625 link_to_project(project, :action => 'settings')
625 link_to_project(project, :action => 'settings')
626 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
626 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
627 link_to_project(project, {:only_path => false, :jump => 'blah'})
627 link_to_project(project, {:only_path => false, :jump => 'blah'})
628 assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
628 assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
629 link_to_project(project, {:action => 'settings'}, :class => "project")
629 link_to_project(project, {:action => 'settings'}, :class => "project")
630 end
630 end
631 end
631 end
General Comments 0
You need to be logged in to leave comments. Login now