##// END OF EJS Templates
Sort the list of users to add to a group or project (#4150)....
Jean-Philippe Lang -
r2890:b2a55bda0cc2
parent child
Show More
@@ -1,675 +1,675
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 'coderay'
18 require 'coderay'
19 require 'coderay/helpers/file_type'
19 require 'coderay/helpers/file_type'
20 require 'forwardable'
20 require 'forwardable'
21 require 'cgi'
21 require 'cgi'
22
22
23 module ApplicationHelper
23 module ApplicationHelper
24 include Redmine::WikiFormatting::Macros::Definitions
24 include Redmine::WikiFormatting::Macros::Definitions
25 include Redmine::I18n
25 include Redmine::I18n
26 include GravatarHelper::PublicMethods
26 include GravatarHelper::PublicMethods
27
27
28 extend Forwardable
28 extend Forwardable
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
30
30
31 # Return true if user is authorized for controller/action, otherwise false
31 # Return true if user is authorized for controller/action, otherwise false
32 def authorize_for(controller, action)
32 def authorize_for(controller, action)
33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
34 end
34 end
35
35
36 # Display a link if user is authorized
36 # Display a link if user is authorized
37 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
37 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
38 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
38 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
39 end
39 end
40
40
41 # Display a link to remote if user is authorized
41 # Display a link to remote if user is authorized
42 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
42 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
43 url = options[:url] || {}
43 url = options[:url] || {}
44 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
44 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
45 end
45 end
46
46
47 # Display a link to user's account page
47 # Display a link to user's account page
48 def link_to_user(user, options={})
48 def link_to_user(user, options={})
49 if user.is_a?(User)
49 if user.is_a?(User)
50 !user.anonymous? ? link_to(user.name(options[:format]), :controller => 'users', :action => 'show', :id => user) : 'Anonymous'
50 !user.anonymous? ? link_to(user.name(options[:format]), :controller => 'users', :action => 'show', :id => user) : 'Anonymous'
51 else
51 else
52 user.to_s
52 user.to_s
53 end
53 end
54 end
54 end
55
55
56 def link_to_issue(issue, options={})
56 def link_to_issue(issue, options={})
57 options[:class] ||= issue.css_classes
57 options[:class] ||= issue.css_classes
58 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
58 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
59 end
59 end
60
60
61 # Generates a link to an attachment.
61 # Generates a link to an attachment.
62 # Options:
62 # Options:
63 # * :text - Link text (default to attachment filename)
63 # * :text - Link text (default to attachment filename)
64 # * :download - Force download (default: false)
64 # * :download - Force download (default: false)
65 def link_to_attachment(attachment, options={})
65 def link_to_attachment(attachment, options={})
66 text = options.delete(:text) || attachment.filename
66 text = options.delete(:text) || attachment.filename
67 action = options.delete(:download) ? 'download' : 'show'
67 action = options.delete(:download) ? 'download' : 'show'
68
68
69 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
69 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
70 end
70 end
71
71
72 def toggle_link(name, id, options={})
72 def toggle_link(name, id, options={})
73 onclick = "Element.toggle('#{id}'); "
73 onclick = "Element.toggle('#{id}'); "
74 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
74 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
75 onclick << "return false;"
75 onclick << "return false;"
76 link_to(name, "#", :onclick => onclick)
76 link_to(name, "#", :onclick => onclick)
77 end
77 end
78
78
79 def image_to_function(name, function, html_options = {})
79 def image_to_function(name, function, html_options = {})
80 html_options.symbolize_keys!
80 html_options.symbolize_keys!
81 tag(:input, html_options.merge({
81 tag(:input, html_options.merge({
82 :type => "image", :src => image_path(name),
82 :type => "image", :src => image_path(name),
83 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
83 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
84 }))
84 }))
85 end
85 end
86
86
87 def prompt_to_remote(name, text, param, url, html_options = {})
87 def prompt_to_remote(name, text, param, url, html_options = {})
88 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
88 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
89 link_to name, {}, html_options
89 link_to name, {}, html_options
90 end
90 end
91
91
92 def format_activity_title(text)
92 def format_activity_title(text)
93 h(truncate_single_line(text, :length => 100))
93 h(truncate_single_line(text, :length => 100))
94 end
94 end
95
95
96 def format_activity_day(date)
96 def format_activity_day(date)
97 date == Date.today ? l(:label_today).titleize : format_date(date)
97 date == Date.today ? l(:label_today).titleize : format_date(date)
98 end
98 end
99
99
100 def format_activity_description(text)
100 def format_activity_description(text)
101 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
101 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
102 end
102 end
103
103
104 def due_date_distance_in_words(date)
104 def due_date_distance_in_words(date)
105 if date
105 if date
106 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
106 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
107 end
107 end
108 end
108 end
109
109
110 def render_page_hierarchy(pages, node=nil)
110 def render_page_hierarchy(pages, node=nil)
111 content = ''
111 content = ''
112 if pages[node]
112 if pages[node]
113 content << "<ul class=\"pages-hierarchy\">\n"
113 content << "<ul class=\"pages-hierarchy\">\n"
114 pages[node].each do |page|
114 pages[node].each do |page|
115 content << "<li>"
115 content << "<li>"
116 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
116 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
117 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
117 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
118 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
118 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
119 content << "</li>\n"
119 content << "</li>\n"
120 end
120 end
121 content << "</ul>\n"
121 content << "</ul>\n"
122 end
122 end
123 content
123 content
124 end
124 end
125
125
126 # Renders flash messages
126 # Renders flash messages
127 def render_flash_messages
127 def render_flash_messages
128 s = ''
128 s = ''
129 flash.each do |k,v|
129 flash.each do |k,v|
130 s << content_tag('div', v, :class => "flash #{k}")
130 s << content_tag('div', v, :class => "flash #{k}")
131 end
131 end
132 s
132 s
133 end
133 end
134
134
135 # Renders tabs and their content
135 # Renders tabs and their content
136 def render_tabs(tabs)
136 def render_tabs(tabs)
137 if tabs.any?
137 if tabs.any?
138 render :partial => 'common/tabs', :locals => {:tabs => tabs}
138 render :partial => 'common/tabs', :locals => {:tabs => tabs}
139 else
139 else
140 content_tag 'p', l(:label_no_data), :class => "nodata"
140 content_tag 'p', l(:label_no_data), :class => "nodata"
141 end
141 end
142 end
142 end
143
143
144 # Renders the project quick-jump box
144 # Renders the project quick-jump box
145 def render_project_jump_box
145 def render_project_jump_box
146 # Retrieve them now to avoid a COUNT query
146 # Retrieve them now to avoid a COUNT query
147 projects = User.current.projects.all
147 projects = User.current.projects.all
148 if projects.any?
148 if projects.any?
149 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
149 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
150 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
150 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
151 '<option value="" disabled="disabled">---</option>'
151 '<option value="" disabled="disabled">---</option>'
152 s << project_tree_options_for_select(projects, :selected => @project) do |p|
152 s << project_tree_options_for_select(projects, :selected => @project) do |p|
153 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
153 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
154 end
154 end
155 s << '</select>'
155 s << '</select>'
156 s
156 s
157 end
157 end
158 end
158 end
159
159
160 def project_tree_options_for_select(projects, options = {})
160 def project_tree_options_for_select(projects, options = {})
161 s = ''
161 s = ''
162 project_tree(projects) do |project, level|
162 project_tree(projects) do |project, level|
163 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
163 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
164 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
164 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
165 tag_options.merge!(yield(project)) if block_given?
165 tag_options.merge!(yield(project)) if block_given?
166 s << content_tag('option', name_prefix + h(project), tag_options)
166 s << content_tag('option', name_prefix + h(project), tag_options)
167 end
167 end
168 s
168 s
169 end
169 end
170
170
171 # Yields the given block for each project with its level in the tree
171 # Yields the given block for each project with its level in the tree
172 def project_tree(projects, &block)
172 def project_tree(projects, &block)
173 ancestors = []
173 ancestors = []
174 projects.sort_by(&:lft).each do |project|
174 projects.sort_by(&:lft).each do |project|
175 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
175 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
176 ancestors.pop
176 ancestors.pop
177 end
177 end
178 yield project, ancestors.size
178 yield project, ancestors.size
179 ancestors << project
179 ancestors << project
180 end
180 end
181 end
181 end
182
182
183 def project_nested_ul(projects, &block)
183 def project_nested_ul(projects, &block)
184 s = ''
184 s = ''
185 if projects.any?
185 if projects.any?
186 ancestors = []
186 ancestors = []
187 projects.sort_by(&:lft).each do |project|
187 projects.sort_by(&:lft).each do |project|
188 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
188 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
189 s << "<ul>\n"
189 s << "<ul>\n"
190 else
190 else
191 ancestors.pop
191 ancestors.pop
192 s << "</li>"
192 s << "</li>"
193 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
193 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
194 ancestors.pop
194 ancestors.pop
195 s << "</ul></li>\n"
195 s << "</ul></li>\n"
196 end
196 end
197 end
197 end
198 s << "<li>"
198 s << "<li>"
199 s << yield(project).to_s
199 s << yield(project).to_s
200 ancestors << project
200 ancestors << project
201 end
201 end
202 s << ("</li></ul>\n" * ancestors.size)
202 s << ("</li></ul>\n" * ancestors.size)
203 end
203 end
204 s
204 s
205 end
205 end
206
206
207 def principals_check_box_tags(name, principals)
207 def principals_check_box_tags(name, principals)
208 s = ''
208 s = ''
209 principals.each do |principal|
209 principals.sort.each do |principal|
210 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
210 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
211 end
211 end
212 s
212 s
213 end
213 end
214
214
215 # Truncates and returns the string as a single line
215 # Truncates and returns the string as a single line
216 def truncate_single_line(string, *args)
216 def truncate_single_line(string, *args)
217 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
217 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
218 end
218 end
219
219
220 def html_hours(text)
220 def html_hours(text)
221 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
221 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
222 end
222 end
223
223
224 def authoring(created, author, options={})
224 def authoring(created, author, options={})
225 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
225 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
226 end
226 end
227
227
228 def time_tag(time)
228 def time_tag(time)
229 text = distance_of_time_in_words(Time.now, time)
229 text = distance_of_time_in_words(Time.now, time)
230 if @project
230 if @project
231 link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
231 link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
232 else
232 else
233 content_tag('acronym', text, :title => format_time(time))
233 content_tag('acronym', text, :title => format_time(time))
234 end
234 end
235 end
235 end
236
236
237 def syntax_highlight(name, content)
237 def syntax_highlight(name, content)
238 type = CodeRay::FileType[name]
238 type = CodeRay::FileType[name]
239 type ? CodeRay.scan(content, type).html : h(content)
239 type ? CodeRay.scan(content, type).html : h(content)
240 end
240 end
241
241
242 def to_path_param(path)
242 def to_path_param(path)
243 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
243 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
244 end
244 end
245
245
246 def pagination_links_full(paginator, count=nil, options={})
246 def pagination_links_full(paginator, count=nil, options={})
247 page_param = options.delete(:page_param) || :page
247 page_param = options.delete(:page_param) || :page
248 url_param = params.dup
248 url_param = params.dup
249 # don't reuse query params if filters are present
249 # don't reuse query params if filters are present
250 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
250 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
251
251
252 html = ''
252 html = ''
253 if paginator.current.previous
253 if paginator.current.previous
254 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
254 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
255 end
255 end
256
256
257 html << (pagination_links_each(paginator, options) do |n|
257 html << (pagination_links_each(paginator, options) do |n|
258 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
258 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
259 end || '')
259 end || '')
260
260
261 if paginator.current.next
261 if paginator.current.next
262 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
262 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
263 end
263 end
264
264
265 unless count.nil?
265 unless count.nil?
266 html << [
266 html << [
267 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
267 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
268 per_page_links(paginator.items_per_page)
268 per_page_links(paginator.items_per_page)
269 ].compact.join(' | ')
269 ].compact.join(' | ')
270 end
270 end
271
271
272 html
272 html
273 end
273 end
274
274
275 def per_page_links(selected=nil)
275 def per_page_links(selected=nil)
276 url_param = params.dup
276 url_param = params.dup
277 url_param.clear if url_param.has_key?(:set_filter)
277 url_param.clear if url_param.has_key?(:set_filter)
278
278
279 links = Setting.per_page_options_array.collect do |n|
279 links = Setting.per_page_options_array.collect do |n|
280 n == selected ? n : link_to_remote(n, {:update => "content",
280 n == selected ? n : link_to_remote(n, {:update => "content",
281 :url => params.dup.merge(:per_page => n),
281 :url => params.dup.merge(:per_page => n),
282 :method => :get},
282 :method => :get},
283 {:href => url_for(url_param.merge(:per_page => n))})
283 {:href => url_for(url_param.merge(:per_page => n))})
284 end
284 end
285 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
285 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
286 end
286 end
287
287
288 def reorder_links(name, url)
288 def reorder_links(name, url)
289 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
289 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
290 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
290 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
291 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
291 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
292 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
292 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
293 end
293 end
294
294
295 def breadcrumb(*args)
295 def breadcrumb(*args)
296 elements = args.flatten
296 elements = args.flatten
297 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
297 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
298 end
298 end
299
299
300 def other_formats_links(&block)
300 def other_formats_links(&block)
301 concat('<p class="other-formats">' + l(:label_export_to))
301 concat('<p class="other-formats">' + l(:label_export_to))
302 yield Redmine::Views::OtherFormatsBuilder.new(self)
302 yield Redmine::Views::OtherFormatsBuilder.new(self)
303 concat('</p>')
303 concat('</p>')
304 end
304 end
305
305
306 def page_header_title
306 def page_header_title
307 if @project.nil? || @project.new_record?
307 if @project.nil? || @project.new_record?
308 h(Setting.app_title)
308 h(Setting.app_title)
309 else
309 else
310 b = []
310 b = []
311 ancestors = (@project.root? ? [] : @project.ancestors.visible)
311 ancestors = (@project.root? ? [] : @project.ancestors.visible)
312 if ancestors.any?
312 if ancestors.any?
313 root = ancestors.shift
313 root = ancestors.shift
314 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
314 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
315 if ancestors.size > 2
315 if ancestors.size > 2
316 b << '&#8230;'
316 b << '&#8230;'
317 ancestors = ancestors[-2, 2]
317 ancestors = ancestors[-2, 2]
318 end
318 end
319 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
319 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
320 end
320 end
321 b << h(@project)
321 b << h(@project)
322 b.join(' &#187; ')
322 b.join(' &#187; ')
323 end
323 end
324 end
324 end
325
325
326 def html_title(*args)
326 def html_title(*args)
327 if args.empty?
327 if args.empty?
328 title = []
328 title = []
329 title << @project.name if @project
329 title << @project.name if @project
330 title += @html_title if @html_title
330 title += @html_title if @html_title
331 title << Setting.app_title
331 title << Setting.app_title
332 title.select {|t| !t.blank? }.join(' - ')
332 title.select {|t| !t.blank? }.join(' - ')
333 else
333 else
334 @html_title ||= []
334 @html_title ||= []
335 @html_title += args
335 @html_title += args
336 end
336 end
337 end
337 end
338
338
339 def accesskey(s)
339 def accesskey(s)
340 Redmine::AccessKeys.key_for s
340 Redmine::AccessKeys.key_for s
341 end
341 end
342
342
343 # Formats text according to system settings.
343 # Formats text according to system settings.
344 # 2 ways to call this method:
344 # 2 ways to call this method:
345 # * with a String: textilizable(text, options)
345 # * with a String: textilizable(text, options)
346 # * with an object and one of its attribute: textilizable(issue, :description, options)
346 # * with an object and one of its attribute: textilizable(issue, :description, options)
347 def textilizable(*args)
347 def textilizable(*args)
348 options = args.last.is_a?(Hash) ? args.pop : {}
348 options = args.last.is_a?(Hash) ? args.pop : {}
349 case args.size
349 case args.size
350 when 1
350 when 1
351 obj = options[:object]
351 obj = options[:object]
352 text = args.shift
352 text = args.shift
353 when 2
353 when 2
354 obj = args.shift
354 obj = args.shift
355 text = obj.send(args.shift).to_s
355 text = obj.send(args.shift).to_s
356 else
356 else
357 raise ArgumentError, 'invalid arguments to textilizable'
357 raise ArgumentError, 'invalid arguments to textilizable'
358 end
358 end
359 return '' if text.blank?
359 return '' if text.blank?
360
360
361 only_path = options.delete(:only_path) == false ? false : true
361 only_path = options.delete(:only_path) == false ? false : true
362
362
363 # when using an image link, try to use an attachment, if possible
363 # when using an image link, try to use an attachment, if possible
364 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
364 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
365
365
366 if attachments
366 if attachments
367 attachments = attachments.sort_by(&:created_on).reverse
367 attachments = attachments.sort_by(&:created_on).reverse
368 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
368 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
369 style = $1
369 style = $1
370 filename = $6.downcase
370 filename = $6.downcase
371 # search for the picture in attachments
371 # search for the picture in attachments
372 if found = attachments.detect { |att| att.filename.downcase == filename }
372 if found = attachments.detect { |att| att.filename.downcase == filename }
373 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
373 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
374 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
374 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
375 alt = desc.blank? ? nil : "(#{desc})"
375 alt = desc.blank? ? nil : "(#{desc})"
376 "!#{style}#{image_url}#{alt}!"
376 "!#{style}#{image_url}#{alt}!"
377 else
377 else
378 m
378 m
379 end
379 end
380 end
380 end
381 end
381 end
382
382
383 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
383 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
384
384
385 # different methods for formatting wiki links
385 # different methods for formatting wiki links
386 case options[:wiki_links]
386 case options[:wiki_links]
387 when :local
387 when :local
388 # used for local links to html files
388 # used for local links to html files
389 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
389 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
390 when :anchor
390 when :anchor
391 # used for single-file wiki export
391 # used for single-file wiki export
392 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
392 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
393 else
393 else
394 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
394 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
395 end
395 end
396
396
397 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
397 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
398
398
399 # Wiki links
399 # Wiki links
400 #
400 #
401 # Examples:
401 # Examples:
402 # [[mypage]]
402 # [[mypage]]
403 # [[mypage|mytext]]
403 # [[mypage|mytext]]
404 # wiki links can refer other project wikis, using project name or identifier:
404 # wiki links can refer other project wikis, using project name or identifier:
405 # [[project:]] -> wiki starting page
405 # [[project:]] -> wiki starting page
406 # [[project:|mytext]]
406 # [[project:|mytext]]
407 # [[project:mypage]]
407 # [[project:mypage]]
408 # [[project:mypage|mytext]]
408 # [[project:mypage|mytext]]
409 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
409 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
410 link_project = project
410 link_project = project
411 esc, all, page, title = $1, $2, $3, $5
411 esc, all, page, title = $1, $2, $3, $5
412 if esc.nil?
412 if esc.nil?
413 if page =~ /^([^\:]+)\:(.*)$/
413 if page =~ /^([^\:]+)\:(.*)$/
414 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
414 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
415 page = $2
415 page = $2
416 title ||= $1 if page.blank?
416 title ||= $1 if page.blank?
417 end
417 end
418
418
419 if link_project && link_project.wiki
419 if link_project && link_project.wiki
420 # extract anchor
420 # extract anchor
421 anchor = nil
421 anchor = nil
422 if page =~ /^(.+?)\#(.+)$/
422 if page =~ /^(.+?)\#(.+)$/
423 page, anchor = $1, $2
423 page, anchor = $1, $2
424 end
424 end
425 # check if page exists
425 # check if page exists
426 wiki_page = link_project.wiki.find_page(page)
426 wiki_page = link_project.wiki.find_page(page)
427 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
427 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
428 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
428 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
429 else
429 else
430 # project or wiki doesn't exist
430 # project or wiki doesn't exist
431 all
431 all
432 end
432 end
433 else
433 else
434 all
434 all
435 end
435 end
436 end
436 end
437
437
438 # Redmine links
438 # Redmine links
439 #
439 #
440 # Examples:
440 # Examples:
441 # Issues:
441 # Issues:
442 # #52 -> Link to issue #52
442 # #52 -> Link to issue #52
443 # Changesets:
443 # Changesets:
444 # r52 -> Link to revision 52
444 # r52 -> Link to revision 52
445 # commit:a85130f -> Link to scmid starting with a85130f
445 # commit:a85130f -> Link to scmid starting with a85130f
446 # Documents:
446 # Documents:
447 # document#17 -> Link to document with id 17
447 # document#17 -> Link to document with id 17
448 # document:Greetings -> Link to the document with title "Greetings"
448 # document:Greetings -> Link to the document with title "Greetings"
449 # document:"Some document" -> Link to the document with title "Some document"
449 # document:"Some document" -> Link to the document with title "Some document"
450 # Versions:
450 # Versions:
451 # version#3 -> Link to version with id 3
451 # version#3 -> Link to version with id 3
452 # version:1.0.0 -> Link to version named "1.0.0"
452 # version:1.0.0 -> Link to version named "1.0.0"
453 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
453 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
454 # Attachments:
454 # Attachments:
455 # attachment:file.zip -> Link to the attachment of the current object named file.zip
455 # attachment:file.zip -> Link to the attachment of the current object named file.zip
456 # Source files:
456 # Source files:
457 # source:some/file -> Link to the file located at /some/file in the project's repository
457 # source:some/file -> Link to the file located at /some/file in the project's repository
458 # source:some/file@52 -> Link to the file's revision 52
458 # source:some/file@52 -> Link to the file's revision 52
459 # source:some/file#L120 -> Link to line 120 of the file
459 # source:some/file#L120 -> Link to line 120 of the file
460 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
460 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
461 # export:some/file -> Force the download of the file
461 # export:some/file -> Force the download of the file
462 # Forum messages:
462 # Forum messages:
463 # message#1218 -> Link to message with id 1218
463 # message#1218 -> Link to message with id 1218
464 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
464 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
465 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
465 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
466 link = nil
466 link = nil
467 if esc.nil?
467 if esc.nil?
468 if prefix.nil? && sep == 'r'
468 if prefix.nil? && sep == 'r'
469 if project && (changeset = project.changesets.find_by_revision(oid))
469 if project && (changeset = project.changesets.find_by_revision(oid))
470 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
470 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
471 :class => 'changeset',
471 :class => 'changeset',
472 :title => truncate_single_line(changeset.comments, :length => 100))
472 :title => truncate_single_line(changeset.comments, :length => 100))
473 end
473 end
474 elsif sep == '#'
474 elsif sep == '#'
475 oid = oid.to_i
475 oid = oid.to_i
476 case prefix
476 case prefix
477 when nil
477 when nil
478 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
478 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
479 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
479 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
480 :class => (issue.closed? ? 'issue closed' : 'issue'),
480 :class => (issue.closed? ? 'issue closed' : 'issue'),
481 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
481 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
482 link = content_tag('del', link) if issue.closed?
482 link = content_tag('del', link) if issue.closed?
483 end
483 end
484 when 'document'
484 when 'document'
485 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
485 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
486 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
486 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
487 :class => 'document'
487 :class => 'document'
488 end
488 end
489 when 'version'
489 when 'version'
490 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
490 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
491 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
491 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
492 :class => 'version'
492 :class => 'version'
493 end
493 end
494 when 'message'
494 when 'message'
495 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
495 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
496 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
496 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
497 :controller => 'messages',
497 :controller => 'messages',
498 :action => 'show',
498 :action => 'show',
499 :board_id => message.board,
499 :board_id => message.board,
500 :id => message.root,
500 :id => message.root,
501 :anchor => (message.parent ? "message-#{message.id}" : nil)},
501 :anchor => (message.parent ? "message-#{message.id}" : nil)},
502 :class => 'message'
502 :class => 'message'
503 end
503 end
504 end
504 end
505 elsif sep == ':'
505 elsif sep == ':'
506 # removes the double quotes if any
506 # removes the double quotes if any
507 name = oid.gsub(%r{^"(.*)"$}, "\\1")
507 name = oid.gsub(%r{^"(.*)"$}, "\\1")
508 case prefix
508 case prefix
509 when 'document'
509 when 'document'
510 if project && document = project.documents.find_by_title(name)
510 if project && document = project.documents.find_by_title(name)
511 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
511 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
512 :class => 'document'
512 :class => 'document'
513 end
513 end
514 when 'version'
514 when 'version'
515 if project && version = project.versions.find_by_name(name)
515 if project && version = project.versions.find_by_name(name)
516 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
516 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
517 :class => 'version'
517 :class => 'version'
518 end
518 end
519 when 'commit'
519 when 'commit'
520 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
520 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
521 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
521 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
522 :class => 'changeset',
522 :class => 'changeset',
523 :title => truncate_single_line(changeset.comments, :length => 100)
523 :title => truncate_single_line(changeset.comments, :length => 100)
524 end
524 end
525 when 'source', 'export'
525 when 'source', 'export'
526 if project && project.repository
526 if project && project.repository
527 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
527 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
528 path, rev, anchor = $1, $3, $5
528 path, rev, anchor = $1, $3, $5
529 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
529 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
530 :path => to_path_param(path),
530 :path => to_path_param(path),
531 :rev => rev,
531 :rev => rev,
532 :anchor => anchor,
532 :anchor => anchor,
533 :format => (prefix == 'export' ? 'raw' : nil)},
533 :format => (prefix == 'export' ? 'raw' : nil)},
534 :class => (prefix == 'export' ? 'source download' : 'source')
534 :class => (prefix == 'export' ? 'source download' : 'source')
535 end
535 end
536 when 'attachment'
536 when 'attachment'
537 if attachments && attachment = attachments.detect {|a| a.filename == name }
537 if attachments && attachment = attachments.detect {|a| a.filename == name }
538 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
538 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
539 :class => 'attachment'
539 :class => 'attachment'
540 end
540 end
541 end
541 end
542 end
542 end
543 end
543 end
544 leading + (link || "#{prefix}#{sep}#{oid}")
544 leading + (link || "#{prefix}#{sep}#{oid}")
545 end
545 end
546
546
547 text
547 text
548 end
548 end
549
549
550 # Same as Rails' simple_format helper without using paragraphs
550 # Same as Rails' simple_format helper without using paragraphs
551 def simple_format_without_paragraph(text)
551 def simple_format_without_paragraph(text)
552 text.to_s.
552 text.to_s.
553 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
553 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
554 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
554 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
555 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
555 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
556 end
556 end
557
557
558 def lang_options_for_select(blank=true)
558 def lang_options_for_select(blank=true)
559 (blank ? [["(auto)", ""]] : []) +
559 (blank ? [["(auto)", ""]] : []) +
560 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
560 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
561 end
561 end
562
562
563 def label_tag_for(name, option_tags = nil, options = {})
563 def label_tag_for(name, option_tags = nil, options = {})
564 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
564 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
565 content_tag("label", label_text)
565 content_tag("label", label_text)
566 end
566 end
567
567
568 def labelled_tabular_form_for(name, object, options, &proc)
568 def labelled_tabular_form_for(name, object, options, &proc)
569 options[:html] ||= {}
569 options[:html] ||= {}
570 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
570 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
571 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
571 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
572 end
572 end
573
573
574 def back_url_hidden_field_tag
574 def back_url_hidden_field_tag
575 back_url = params[:back_url] || request.env['HTTP_REFERER']
575 back_url = params[:back_url] || request.env['HTTP_REFERER']
576 back_url = CGI.unescape(back_url.to_s)
576 back_url = CGI.unescape(back_url.to_s)
577 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
577 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
578 end
578 end
579
579
580 def check_all_links(form_name)
580 def check_all_links(form_name)
581 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
581 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
582 " | " +
582 " | " +
583 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
583 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
584 end
584 end
585
585
586 def progress_bar(pcts, options={})
586 def progress_bar(pcts, options={})
587 pcts = [pcts, pcts] unless pcts.is_a?(Array)
587 pcts = [pcts, pcts] unless pcts.is_a?(Array)
588 pcts[1] = pcts[1] - pcts[0]
588 pcts[1] = pcts[1] - pcts[0]
589 pcts << (100 - pcts[1] - pcts[0])
589 pcts << (100 - pcts[1] - pcts[0])
590 width = options[:width] || '100px;'
590 width = options[:width] || '100px;'
591 legend = options[:legend] || ''
591 legend = options[:legend] || ''
592 content_tag('table',
592 content_tag('table',
593 content_tag('tr',
593 content_tag('tr',
594 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
594 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
595 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
595 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
596 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
596 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
597 ), :class => 'progress', :style => "width: #{width};") +
597 ), :class => 'progress', :style => "width: #{width};") +
598 content_tag('p', legend, :class => 'pourcent')
598 content_tag('p', legend, :class => 'pourcent')
599 end
599 end
600
600
601 def context_menu_link(name, url, options={})
601 def context_menu_link(name, url, options={})
602 options[:class] ||= ''
602 options[:class] ||= ''
603 if options.delete(:selected)
603 if options.delete(:selected)
604 options[:class] << ' icon-checked disabled'
604 options[:class] << ' icon-checked disabled'
605 options[:disabled] = true
605 options[:disabled] = true
606 end
606 end
607 if options.delete(:disabled)
607 if options.delete(:disabled)
608 options.delete(:method)
608 options.delete(:method)
609 options.delete(:confirm)
609 options.delete(:confirm)
610 options.delete(:onclick)
610 options.delete(:onclick)
611 options[:class] << ' disabled'
611 options[:class] << ' disabled'
612 url = '#'
612 url = '#'
613 end
613 end
614 link_to name, url, options
614 link_to name, url, options
615 end
615 end
616
616
617 def calendar_for(field_id)
617 def calendar_for(field_id)
618 include_calendar_headers_tags
618 include_calendar_headers_tags
619 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
619 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
620 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
620 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
621 end
621 end
622
622
623 def include_calendar_headers_tags
623 def include_calendar_headers_tags
624 unless @calendar_headers_tags_included
624 unless @calendar_headers_tags_included
625 @calendar_headers_tags_included = true
625 @calendar_headers_tags_included = true
626 content_for :header_tags do
626 content_for :header_tags do
627 javascript_include_tag('calendar/calendar') +
627 javascript_include_tag('calendar/calendar') +
628 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
628 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
629 javascript_include_tag('calendar/calendar-setup') +
629 javascript_include_tag('calendar/calendar-setup') +
630 stylesheet_link_tag('calendar')
630 stylesheet_link_tag('calendar')
631 end
631 end
632 end
632 end
633 end
633 end
634
634
635 def content_for(name, content = nil, &block)
635 def content_for(name, content = nil, &block)
636 @has_content ||= {}
636 @has_content ||= {}
637 @has_content[name] = true
637 @has_content[name] = true
638 super(name, content, &block)
638 super(name, content, &block)
639 end
639 end
640
640
641 def has_content?(name)
641 def has_content?(name)
642 (@has_content && @has_content[name]) || false
642 (@has_content && @has_content[name]) || false
643 end
643 end
644
644
645 # Returns the avatar image tag for the given +user+ if avatars are enabled
645 # Returns the avatar image tag for the given +user+ if avatars are enabled
646 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
646 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
647 def avatar(user, options = { })
647 def avatar(user, options = { })
648 if Setting.gravatar_enabled?
648 if Setting.gravatar_enabled?
649 options.merge!({:ssl => Setting.protocol == 'https'})
649 options.merge!({:ssl => Setting.protocol == 'https'})
650 email = nil
650 email = nil
651 if user.respond_to?(:mail)
651 if user.respond_to?(:mail)
652 email = user.mail
652 email = user.mail
653 elsif user.to_s =~ %r{<(.+?)>}
653 elsif user.to_s =~ %r{<(.+?)>}
654 email = $1
654 email = $1
655 end
655 end
656 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
656 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
657 end
657 end
658 end
658 end
659
659
660 private
660 private
661
661
662 def wiki_helper
662 def wiki_helper
663 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
663 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
664 extend helper
664 extend helper
665 return self
665 return self
666 end
666 end
667
667
668 def link_to_remote_content_update(text, url_params)
668 def link_to_remote_content_update(text, url_params)
669 link_to_remote(text,
669 link_to_remote(text,
670 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
670 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
671 {:href => url_for(:params => url_params)}
671 {:href => url_for(:params => url_params)}
672 )
672 )
673 end
673 end
674
674
675 end
675 end
General Comments 0
You need to be logged in to leave comments. Login now