##// END OF EJS Templates
Makes issue update link work without javascript (#1337)....
Jean-Philippe Lang -
r1591:47064c02f144
parent child
Show More
@@ -1,525 +1,525
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
20
21 module ApplicationHelper
21 module ApplicationHelper
22 include Redmine::WikiFormatting::Macros::Definitions
22 include Redmine::WikiFormatting::Macros::Definitions
23
23
24 def current_role
24 def current_role
25 @current_role ||= User.current.role_for_project(@project)
25 @current_role ||= User.current.role_for_project(@project)
26 end
26 end
27
27
28 # Return true if user is authorized for controller/action, otherwise false
28 # Return true if user is authorized for controller/action, otherwise false
29 def authorize_for(controller, action)
29 def authorize_for(controller, action)
30 User.current.allowed_to?({:controller => controller, :action => action}, @project)
30 User.current.allowed_to?({:controller => controller, :action => action}, @project)
31 end
31 end
32
32
33 # Display a link if user is authorized
33 # Display a link if user is authorized
34 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
34 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
35 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
35 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
36 end
36 end
37
37
38 # Display a link to user's account page
38 # Display a link to user's account page
39 def link_to_user(user)
39 def link_to_user(user)
40 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
40 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
41 end
41 end
42
42
43 def link_to_issue(issue, options={})
43 def link_to_issue(issue, options={})
44 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
44 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
45 end
45 end
46
46
47 def toggle_link(name, id, options={})
47 def toggle_link(name, id, options={})
48 onclick = "Element.toggle('#{id}'); "
48 onclick = "Element.toggle('#{id}'); "
49 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
49 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
50 onclick << "return false;"
50 onclick << "return false;"
51 link_to(name, "#", :onclick => onclick)
51 link_to(name, "#", :onclick => onclick)
52 end
52 end
53
53
54 def show_and_goto_link(name, id, options={})
54 def show_and_goto_js(id, options={})
55 onclick = "Element.show('#{id}'); "
55 onclick = "Element.show('#{id}'); "
56 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
56 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
57 onclick << "Element.scrollTo('#{id}'); "
57 onclick << "Element.scrollTo('#{id}'); "
58 onclick << "return false;"
58 onclick << "return false;"
59 link_to(name, "#", options.merge(:onclick => onclick))
59 link_to(name, "#", options.merge(:onclick => onclick))
60 end
60 end
61
61
62 def image_to_function(name, function, html_options = {})
62 def image_to_function(name, function, html_options = {})
63 html_options.symbolize_keys!
63 html_options.symbolize_keys!
64 tag(:input, html_options.merge({
64 tag(:input, html_options.merge({
65 :type => "image", :src => image_path(name),
65 :type => "image", :src => image_path(name),
66 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
66 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
67 }))
67 }))
68 end
68 end
69
69
70 def prompt_to_remote(name, text, param, url, html_options = {})
70 def prompt_to_remote(name, text, param, url, html_options = {})
71 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
71 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
72 link_to name, {}, html_options
72 link_to name, {}, html_options
73 end
73 end
74
74
75 def format_date(date)
75 def format_date(date)
76 return nil unless date
76 return nil unless date
77 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
77 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
78 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
78 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
79 date.strftime(@date_format)
79 date.strftime(@date_format)
80 end
80 end
81
81
82 def format_time(time, include_date = true)
82 def format_time(time, include_date = true)
83 return nil unless time
83 return nil unless time
84 time = time.to_time if time.is_a?(String)
84 time = time.to_time if time.is_a?(String)
85 zone = User.current.time_zone
85 zone = User.current.time_zone
86 if time.utc?
86 if time.utc?
87 local = zone ? zone.adjust(time) : time.getlocal
87 local = zone ? zone.adjust(time) : time.getlocal
88 else
88 else
89 local = zone ? zone.adjust(time.getutc) : time
89 local = zone ? zone.adjust(time.getutc) : time
90 end
90 end
91 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
91 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
92 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
92 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
93 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
93 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
94 end
94 end
95
95
96 # Truncates and returns the string as a single line
96 # Truncates and returns the string as a single line
97 def truncate_single_line(string, *args)
97 def truncate_single_line(string, *args)
98 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
98 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
99 end
99 end
100
100
101 def html_hours(text)
101 def html_hours(text)
102 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
102 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
103 end
103 end
104
104
105 def authoring(created, author)
105 def authoring(created, author)
106 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
106 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
107 l(:label_added_time_by, author || 'Anonymous', time_tag)
107 l(:label_added_time_by, author || 'Anonymous', time_tag)
108 end
108 end
109
109
110 def l_or_humanize(s)
110 def l_or_humanize(s)
111 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
111 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
112 end
112 end
113
113
114 def day_name(day)
114 def day_name(day)
115 l(:general_day_names).split(',')[day-1]
115 l(:general_day_names).split(',')[day-1]
116 end
116 end
117
117
118 def month_name(month)
118 def month_name(month)
119 l(:actionview_datehelper_select_month_names).split(',')[month-1]
119 l(:actionview_datehelper_select_month_names).split(',')[month-1]
120 end
120 end
121
121
122 def syntax_highlight(name, content)
122 def syntax_highlight(name, content)
123 type = CodeRay::FileType[name]
123 type = CodeRay::FileType[name]
124 type ? CodeRay.scan(content, type).html : h(content)
124 type ? CodeRay.scan(content, type).html : h(content)
125 end
125 end
126
126
127 def pagination_links_full(paginator, count=nil, options={})
127 def pagination_links_full(paginator, count=nil, options={})
128 page_param = options.delete(:page_param) || :page
128 page_param = options.delete(:page_param) || :page
129 url_param = params.dup
129 url_param = params.dup
130 # don't reuse params if filters are present
130 # don't reuse params if filters are present
131 url_param.clear if url_param.has_key?(:set_filter)
131 url_param.clear if url_param.has_key?(:set_filter)
132
132
133 html = ''
133 html = ''
134 html << link_to_remote(('&#171; ' + l(:label_previous)),
134 html << link_to_remote(('&#171; ' + l(:label_previous)),
135 {:update => 'content',
135 {:update => 'content',
136 :url => url_param.merge(page_param => paginator.current.previous),
136 :url => url_param.merge(page_param => paginator.current.previous),
137 :complete => 'window.scrollTo(0,0)'},
137 :complete => 'window.scrollTo(0,0)'},
138 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
138 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
139
139
140 html << (pagination_links_each(paginator, options) do |n|
140 html << (pagination_links_each(paginator, options) do |n|
141 link_to_remote(n.to_s,
141 link_to_remote(n.to_s,
142 {:url => {:params => url_param.merge(page_param => n)},
142 {:url => {:params => url_param.merge(page_param => n)},
143 :update => 'content',
143 :update => 'content',
144 :complete => 'window.scrollTo(0,0)'},
144 :complete => 'window.scrollTo(0,0)'},
145 {:href => url_for(:params => url_param.merge(page_param => n))})
145 {:href => url_for(:params => url_param.merge(page_param => n))})
146 end || '')
146 end || '')
147
147
148 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
148 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
149 {:update => 'content',
149 {:update => 'content',
150 :url => url_param.merge(page_param => paginator.current.next),
150 :url => url_param.merge(page_param => paginator.current.next),
151 :complete => 'window.scrollTo(0,0)'},
151 :complete => 'window.scrollTo(0,0)'},
152 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
152 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
153
153
154 unless count.nil?
154 unless count.nil?
155 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
155 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
156 end
156 end
157
157
158 html
158 html
159 end
159 end
160
160
161 def per_page_links(selected=nil)
161 def per_page_links(selected=nil)
162 url_param = params.dup
162 url_param = params.dup
163 url_param.clear if url_param.has_key?(:set_filter)
163 url_param.clear if url_param.has_key?(:set_filter)
164
164
165 links = Setting.per_page_options_array.collect do |n|
165 links = Setting.per_page_options_array.collect do |n|
166 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
166 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
167 {:href => url_for(url_param.merge(:per_page => n))})
167 {:href => url_for(url_param.merge(:per_page => n))})
168 end
168 end
169 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
169 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
170 end
170 end
171
171
172 def breadcrumb(*args)
172 def breadcrumb(*args)
173 content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb')
173 content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb')
174 end
174 end
175
175
176 def html_title(*args)
176 def html_title(*args)
177 if args.empty?
177 if args.empty?
178 title = []
178 title = []
179 title << @project.name if @project
179 title << @project.name if @project
180 title += @html_title if @html_title
180 title += @html_title if @html_title
181 title << Setting.app_title
181 title << Setting.app_title
182 title.compact.join(' - ')
182 title.compact.join(' - ')
183 else
183 else
184 @html_title ||= []
184 @html_title ||= []
185 @html_title += args
185 @html_title += args
186 end
186 end
187 end
187 end
188
188
189 def accesskey(s)
189 def accesskey(s)
190 Redmine::AccessKeys.key_for s
190 Redmine::AccessKeys.key_for s
191 end
191 end
192
192
193 # Formats text according to system settings.
193 # Formats text according to system settings.
194 # 2 ways to call this method:
194 # 2 ways to call this method:
195 # * with a String: textilizable(text, options)
195 # * with a String: textilizable(text, options)
196 # * with an object and one of its attribute: textilizable(issue, :description, options)
196 # * with an object and one of its attribute: textilizable(issue, :description, options)
197 def textilizable(*args)
197 def textilizable(*args)
198 options = args.last.is_a?(Hash) ? args.pop : {}
198 options = args.last.is_a?(Hash) ? args.pop : {}
199 case args.size
199 case args.size
200 when 1
200 when 1
201 obj = nil
201 obj = nil
202 text = args.shift
202 text = args.shift
203 when 2
203 when 2
204 obj = args.shift
204 obj = args.shift
205 text = obj.send(args.shift).to_s
205 text = obj.send(args.shift).to_s
206 else
206 else
207 raise ArgumentError, 'invalid arguments to textilizable'
207 raise ArgumentError, 'invalid arguments to textilizable'
208 end
208 end
209 return '' if text.blank?
209 return '' if text.blank?
210
210
211 only_path = options.delete(:only_path) == false ? false : true
211 only_path = options.delete(:only_path) == false ? false : true
212
212
213 # when using an image link, try to use an attachment, if possible
213 # when using an image link, try to use an attachment, if possible
214 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
214 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
215
215
216 if attachments
216 if attachments
217 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
217 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
218 style = $1
218 style = $1
219 filename = $6
219 filename = $6
220 rf = Regexp.new(filename, Regexp::IGNORECASE)
220 rf = Regexp.new(filename, Regexp::IGNORECASE)
221 # search for the picture in attachments
221 # search for the picture in attachments
222 if found = attachments.detect { |att| att.filename =~ rf }
222 if found = attachments.detect { |att| att.filename =~ rf }
223 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
223 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
224 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
224 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
225 alt = desc.blank? ? nil : "(#{desc})"
225 alt = desc.blank? ? nil : "(#{desc})"
226 "!#{style}#{image_url}#{alt}!"
226 "!#{style}#{image_url}#{alt}!"
227 else
227 else
228 "!#{style}#{filename}!"
228 "!#{style}#{filename}!"
229 end
229 end
230 end
230 end
231 end
231 end
232
232
233 text = (Setting.text_formatting == 'textile') ?
233 text = (Setting.text_formatting == 'textile') ?
234 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
234 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
235 simple_format(auto_link(h(text)))
235 simple_format(auto_link(h(text)))
236
236
237 # different methods for formatting wiki links
237 # different methods for formatting wiki links
238 case options[:wiki_links]
238 case options[:wiki_links]
239 when :local
239 when :local
240 # used for local links to html files
240 # used for local links to html files
241 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
241 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
242 when :anchor
242 when :anchor
243 # used for single-file wiki export
243 # used for single-file wiki export
244 format_wiki_link = Proc.new {|project, title| "##{title}" }
244 format_wiki_link = Proc.new {|project, title| "##{title}" }
245 else
245 else
246 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
246 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
247 end
247 end
248
248
249 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
249 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
250
250
251 # Wiki links
251 # Wiki links
252 #
252 #
253 # Examples:
253 # Examples:
254 # [[mypage]]
254 # [[mypage]]
255 # [[mypage|mytext]]
255 # [[mypage|mytext]]
256 # wiki links can refer other project wikis, using project name or identifier:
256 # wiki links can refer other project wikis, using project name or identifier:
257 # [[project:]] -> wiki starting page
257 # [[project:]] -> wiki starting page
258 # [[project:|mytext]]
258 # [[project:|mytext]]
259 # [[project:mypage]]
259 # [[project:mypage]]
260 # [[project:mypage|mytext]]
260 # [[project:mypage|mytext]]
261 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
261 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
262 link_project = project
262 link_project = project
263 esc, all, page, title = $1, $2, $3, $5
263 esc, all, page, title = $1, $2, $3, $5
264 if esc.nil?
264 if esc.nil?
265 if page =~ /^([^\:]+)\:(.*)$/
265 if page =~ /^([^\:]+)\:(.*)$/
266 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
266 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
267 page = $2
267 page = $2
268 title ||= $1 if page.blank?
268 title ||= $1 if page.blank?
269 end
269 end
270
270
271 if link_project && link_project.wiki
271 if link_project && link_project.wiki
272 # check if page exists
272 # check if page exists
273 wiki_page = link_project.wiki.find_page(page)
273 wiki_page = link_project.wiki.find_page(page)
274 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
274 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
275 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
275 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
276 else
276 else
277 # project or wiki doesn't exist
277 # project or wiki doesn't exist
278 title || page
278 title || page
279 end
279 end
280 else
280 else
281 all
281 all
282 end
282 end
283 end
283 end
284
284
285 # Redmine links
285 # Redmine links
286 #
286 #
287 # Examples:
287 # Examples:
288 # Issues:
288 # Issues:
289 # #52 -> Link to issue #52
289 # #52 -> Link to issue #52
290 # Changesets:
290 # Changesets:
291 # r52 -> Link to revision 52
291 # r52 -> Link to revision 52
292 # commit:a85130f -> Link to scmid starting with a85130f
292 # commit:a85130f -> Link to scmid starting with a85130f
293 # Documents:
293 # Documents:
294 # document#17 -> Link to document with id 17
294 # document#17 -> Link to document with id 17
295 # document:Greetings -> Link to the document with title "Greetings"
295 # document:Greetings -> Link to the document with title "Greetings"
296 # document:"Some document" -> Link to the document with title "Some document"
296 # document:"Some document" -> Link to the document with title "Some document"
297 # Versions:
297 # Versions:
298 # version#3 -> Link to version with id 3
298 # version#3 -> Link to version with id 3
299 # version:1.0.0 -> Link to version named "1.0.0"
299 # version:1.0.0 -> Link to version named "1.0.0"
300 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
300 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
301 # Attachments:
301 # Attachments:
302 # attachment:file.zip -> Link to the attachment of the current object named file.zip
302 # attachment:file.zip -> Link to the attachment of the current object named file.zip
303 # Source files:
303 # Source files:
304 # source:some/file -> Link to the file located at /some/file in the project's repository
304 # source:some/file -> Link to the file located at /some/file in the project's repository
305 # source:some/file@52 -> Link to the file's revision 52
305 # source:some/file@52 -> Link to the file's revision 52
306 # source:some/file#L120 -> Link to line 120 of the file
306 # source:some/file#L120 -> Link to line 120 of the file
307 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
307 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
308 # export:some/file -> Force the download of the file
308 # export:some/file -> Force the download of the file
309 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
309 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
310 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
310 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
311 link = nil
311 link = nil
312 if esc.nil?
312 if esc.nil?
313 if prefix.nil? && sep == 'r'
313 if prefix.nil? && sep == 'r'
314 if project && (changeset = project.changesets.find_by_revision(oid))
314 if project && (changeset = project.changesets.find_by_revision(oid))
315 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
315 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
316 :class => 'changeset',
316 :class => 'changeset',
317 :title => truncate_single_line(changeset.comments, 100))
317 :title => truncate_single_line(changeset.comments, 100))
318 end
318 end
319 elsif sep == '#'
319 elsif sep == '#'
320 oid = oid.to_i
320 oid = oid.to_i
321 case prefix
321 case prefix
322 when nil
322 when nil
323 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
323 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
324 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
324 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
325 :class => (issue.closed? ? 'issue closed' : 'issue'),
325 :class => (issue.closed? ? 'issue closed' : 'issue'),
326 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
326 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
327 link = content_tag('del', link) if issue.closed?
327 link = content_tag('del', link) if issue.closed?
328 end
328 end
329 when 'document'
329 when 'document'
330 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
330 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
331 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
331 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
332 :class => 'document'
332 :class => 'document'
333 end
333 end
334 when 'version'
334 when 'version'
335 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
335 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
336 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
336 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
337 :class => 'version'
337 :class => 'version'
338 end
338 end
339 end
339 end
340 elsif sep == ':'
340 elsif sep == ':'
341 # removes the double quotes if any
341 # removes the double quotes if any
342 name = oid.gsub(%r{^"(.*)"$}, "\\1")
342 name = oid.gsub(%r{^"(.*)"$}, "\\1")
343 case prefix
343 case prefix
344 when 'document'
344 when 'document'
345 if project && document = project.documents.find_by_title(name)
345 if project && document = project.documents.find_by_title(name)
346 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
346 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
347 :class => 'document'
347 :class => 'document'
348 end
348 end
349 when 'version'
349 when 'version'
350 if project && version = project.versions.find_by_name(name)
350 if project && version = project.versions.find_by_name(name)
351 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
351 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
352 :class => 'version'
352 :class => 'version'
353 end
353 end
354 when 'commit'
354 when 'commit'
355 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
355 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
356 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
356 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
357 :class => 'changeset',
357 :class => 'changeset',
358 :title => truncate_single_line(changeset.comments, 100)
358 :title => truncate_single_line(changeset.comments, 100)
359 end
359 end
360 when 'source', 'export'
360 when 'source', 'export'
361 if project && project.repository
361 if project && project.repository
362 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
362 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
363 path, rev, anchor = $1, $3, $5
363 path, rev, anchor = $1, $3, $5
364 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :path => path,
364 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :path => path,
365 :rev => rev,
365 :rev => rev,
366 :anchor => anchor,
366 :anchor => anchor,
367 :format => (prefix == 'export' ? 'raw' : nil)},
367 :format => (prefix == 'export' ? 'raw' : nil)},
368 :class => (prefix == 'export' ? 'source download' : 'source')
368 :class => (prefix == 'export' ? 'source download' : 'source')
369 end
369 end
370 when 'attachment'
370 when 'attachment'
371 if attachments && attachment = attachments.detect {|a| a.filename == name }
371 if attachments && attachment = attachments.detect {|a| a.filename == name }
372 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
372 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
373 :class => 'attachment'
373 :class => 'attachment'
374 end
374 end
375 end
375 end
376 end
376 end
377 end
377 end
378 leading + (link || "#{prefix}#{sep}#{oid}")
378 leading + (link || "#{prefix}#{sep}#{oid}")
379 end
379 end
380
380
381 text
381 text
382 end
382 end
383
383
384 # Same as Rails' simple_format helper without using paragraphs
384 # Same as Rails' simple_format helper without using paragraphs
385 def simple_format_without_paragraph(text)
385 def simple_format_without_paragraph(text)
386 text.to_s.
386 text.to_s.
387 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
387 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
388 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
388 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
389 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
389 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
390 end
390 end
391
391
392 def error_messages_for(object_name, options = {})
392 def error_messages_for(object_name, options = {})
393 options = options.symbolize_keys
393 options = options.symbolize_keys
394 object = instance_variable_get("@#{object_name}")
394 object = instance_variable_get("@#{object_name}")
395 if object && !object.errors.empty?
395 if object && !object.errors.empty?
396 # build full_messages here with controller current language
396 # build full_messages here with controller current language
397 full_messages = []
397 full_messages = []
398 object.errors.each do |attr, msg|
398 object.errors.each do |attr, msg|
399 next if msg.nil?
399 next if msg.nil?
400 msg = msg.first if msg.is_a? Array
400 msg = msg.first if msg.is_a? Array
401 if attr == "base"
401 if attr == "base"
402 full_messages << l(msg)
402 full_messages << l(msg)
403 else
403 else
404 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
404 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
405 end
405 end
406 end
406 end
407 # retrieve custom values error messages
407 # retrieve custom values error messages
408 if object.errors[:custom_values]
408 if object.errors[:custom_values]
409 object.custom_values.each do |v|
409 object.custom_values.each do |v|
410 v.errors.each do |attr, msg|
410 v.errors.each do |attr, msg|
411 next if msg.nil?
411 next if msg.nil?
412 msg = msg.first if msg.is_a? Array
412 msg = msg.first if msg.is_a? Array
413 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
413 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
414 end
414 end
415 end
415 end
416 end
416 end
417 content_tag("div",
417 content_tag("div",
418 content_tag(
418 content_tag(
419 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
419 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
420 ) +
420 ) +
421 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
421 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
422 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
422 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
423 )
423 )
424 else
424 else
425 ""
425 ""
426 end
426 end
427 end
427 end
428
428
429 def lang_options_for_select(blank=true)
429 def lang_options_for_select(blank=true)
430 (blank ? [["(auto)", ""]] : []) +
430 (blank ? [["(auto)", ""]] : []) +
431 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
431 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
432 end
432 end
433
433
434 def label_tag_for(name, option_tags = nil, options = {})
434 def label_tag_for(name, option_tags = nil, options = {})
435 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
435 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
436 content_tag("label", label_text)
436 content_tag("label", label_text)
437 end
437 end
438
438
439 def labelled_tabular_form_for(name, object, options, &proc)
439 def labelled_tabular_form_for(name, object, options, &proc)
440 options[:html] ||= {}
440 options[:html] ||= {}
441 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
441 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
442 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
442 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
443 end
443 end
444
444
445 def back_url_hidden_field_tag
445 def back_url_hidden_field_tag
446 hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
446 hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
447 end
447 end
448
448
449 def check_all_links(form_name)
449 def check_all_links(form_name)
450 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
450 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
451 " | " +
451 " | " +
452 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
452 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
453 end
453 end
454
454
455 def progress_bar(pcts, options={})
455 def progress_bar(pcts, options={})
456 pcts = [pcts, pcts] unless pcts.is_a?(Array)
456 pcts = [pcts, pcts] unless pcts.is_a?(Array)
457 pcts[1] = pcts[1] - pcts[0]
457 pcts[1] = pcts[1] - pcts[0]
458 pcts << (100 - pcts[1] - pcts[0])
458 pcts << (100 - pcts[1] - pcts[0])
459 width = options[:width] || '100px;'
459 width = options[:width] || '100px;'
460 legend = options[:legend] || ''
460 legend = options[:legend] || ''
461 content_tag('table',
461 content_tag('table',
462 content_tag('tr',
462 content_tag('tr',
463 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
463 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
464 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
464 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
465 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
465 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
466 ), :class => 'progress', :style => "width: #{width};") +
466 ), :class => 'progress', :style => "width: #{width};") +
467 content_tag('p', legend, :class => 'pourcent')
467 content_tag('p', legend, :class => 'pourcent')
468 end
468 end
469
469
470 def context_menu_link(name, url, options={})
470 def context_menu_link(name, url, options={})
471 options[:class] ||= ''
471 options[:class] ||= ''
472 if options.delete(:selected)
472 if options.delete(:selected)
473 options[:class] << ' icon-checked disabled'
473 options[:class] << ' icon-checked disabled'
474 options[:disabled] = true
474 options[:disabled] = true
475 end
475 end
476 if options.delete(:disabled)
476 if options.delete(:disabled)
477 options.delete(:method)
477 options.delete(:method)
478 options.delete(:confirm)
478 options.delete(:confirm)
479 options.delete(:onclick)
479 options.delete(:onclick)
480 options[:class] << ' disabled'
480 options[:class] << ' disabled'
481 url = '#'
481 url = '#'
482 end
482 end
483 link_to name, url, options
483 link_to name, url, options
484 end
484 end
485
485
486 def calendar_for(field_id)
486 def calendar_for(field_id)
487 include_calendar_headers_tags
487 include_calendar_headers_tags
488 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
488 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
489 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
489 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
490 end
490 end
491
491
492 def include_calendar_headers_tags
492 def include_calendar_headers_tags
493 unless @calendar_headers_tags_included
493 unless @calendar_headers_tags_included
494 @calendar_headers_tags_included = true
494 @calendar_headers_tags_included = true
495 content_for :header_tags do
495 content_for :header_tags do
496 javascript_include_tag('calendar/calendar') +
496 javascript_include_tag('calendar/calendar') +
497 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
497 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
498 javascript_include_tag('calendar/calendar-setup') +
498 javascript_include_tag('calendar/calendar-setup') +
499 stylesheet_link_tag('calendar')
499 stylesheet_link_tag('calendar')
500 end
500 end
501 end
501 end
502 end
502 end
503
503
504 def wikitoolbar_for(field_id)
504 def wikitoolbar_for(field_id)
505 return '' unless Setting.text_formatting == 'textile'
505 return '' unless Setting.text_formatting == 'textile'
506
506
507 help_link = l(:setting_text_formatting) + ': ' +
507 help_link = l(:setting_text_formatting) + ': ' +
508 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
508 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
509 :onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
509 :onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
510
510
511 javascript_include_tag('jstoolbar/jstoolbar') +
511 javascript_include_tag('jstoolbar/jstoolbar') +
512 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
512 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
513 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
513 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
514 end
514 end
515
515
516 def content_for(name, content = nil, &block)
516 def content_for(name, content = nil, &block)
517 @has_content ||= {}
517 @has_content ||= {}
518 @has_content[name] = true
518 @has_content[name] = true
519 super(name, content, &block)
519 super(name, content, &block)
520 end
520 end
521
521
522 def has_content?(name)
522 def has_content?(name)
523 (@has_content && @has_content[name]) || false
523 (@has_content && @has_content[name]) || false
524 end
524 end
525 end
525 end
@@ -1,120 +1,120
1 <div class="contextual">
1 <div class="contextual">
2 <%= show_and_goto_link(l(:button_update), 'update', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) if authorize_for('issues', 'edit') %>
2 <%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
3 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
3 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
4 <%= watcher_tag(@issue, User.current) %>
4 <%= watcher_tag(@issue, User.current) %>
5 <%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-copy' %>
5 <%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-copy' %>
6 <%= link_to_if_authorized l(:button_move), {:controller => 'issues', :action => 'move', :id => @issue }, :class => 'icon icon-move' %>
6 <%= link_to_if_authorized l(:button_move), {:controller => 'issues', :action => 'move', :id => @issue }, :class => 'icon icon-move' %>
7 <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
7 <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
8 </div>
8 </div>
9
9
10 <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
10 <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
11
11
12 <div class="issue <%= "status-#{@issue.status.position} priority-#{@issue.priority.position}" %>">
12 <div class="issue <%= "status-#{@issue.status.position} priority-#{@issue.priority.position}" %>">
13 <h3><%=h @issue.subject %></h3>
13 <h3><%=h @issue.subject %></h3>
14 <p class="author">
14 <p class="author">
15 <%= authoring @issue.created_on, @issue.author %>.
15 <%= authoring @issue.created_on, @issue.author %>.
16 <%= l(:label_updated_time, distance_of_time_in_words(Time.now, @issue.updated_on)) + '.' if @issue.created_on != @issue.updated_on %>
16 <%= l(:label_updated_time, distance_of_time_in_words(Time.now, @issue.updated_on)) + '.' if @issue.created_on != @issue.updated_on %>
17 </p>
17 </p>
18
18
19 <table width="100%">
19 <table width="100%">
20 <tr>
20 <tr>
21 <td style="width:15%"><b><%=l(:field_status)%>:</b></td><td style="width:35%"><%= @issue.status.name %></td>
21 <td style="width:15%"><b><%=l(:field_status)%>:</b></td><td style="width:35%"><%= @issue.status.name %></td>
22 <td style="width:15%"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
22 <td style="width:15%"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
23 </tr>
23 </tr>
24 <tr>
24 <tr>
25 <td><b><%=l(:field_priority)%>:</b></td><td><%= @issue.priority.name %></td>
25 <td><b><%=l(:field_priority)%>:</b></td><td><%= @issue.priority.name %></td>
26 <td><b><%=l(:field_due_date)%>:</b></td><td><%= format_date(@issue.due_date) %></td>
26 <td><b><%=l(:field_due_date)%>:</b></td><td><%= format_date(@issue.due_date) %></td>
27 </tr>
27 </tr>
28 <tr>
28 <tr>
29 <td><b><%=l(:field_assigned_to)%>:</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
29 <td><b><%=l(:field_assigned_to)%>:</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
30 <td><b><%=l(:field_done_ratio)%>:</b></td><td><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
30 <td><b><%=l(:field_done_ratio)%>:</b></td><td><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
31 </tr>
31 </tr>
32 <tr>
32 <tr>
33 <td><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
33 <td><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
34 <% if User.current.allowed_to?(:view_time_entries, @project) %>
34 <% if User.current.allowed_to?(:view_time_entries, @project) %>
35 <td><b><%=l(:label_spent_time)%>:</b></td>
35 <td><b><%=l(:label_spent_time)%>:</b></td>
36 <td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
36 <td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
37 <% end %>
37 <% end %>
38 </tr>
38 </tr>
39 <tr>
39 <tr>
40 <td><b><%=l(:field_fixed_version)%>:</b></td><td><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
40 <td><b><%=l(:field_fixed_version)%>:</b></td><td><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
41 <% if @issue.estimated_hours %>
41 <% if @issue.estimated_hours %>
42 <td><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
42 <td><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
43 <% end %>
43 <% end %>
44 </tr>
44 </tr>
45 <tr>
45 <tr>
46 <% n = 0 -%>
46 <% n = 0 -%>
47 <% @issue.custom_values.each do |value| -%>
47 <% @issue.custom_values.each do |value| -%>
48 <td valign="top"><b><%=h value.custom_field.name %>:</b></td><td valign="top"><%= simple_format(h(show_value(value))) %></td>
48 <td valign="top"><b><%=h value.custom_field.name %>:</b></td><td valign="top"><%= simple_format(h(show_value(value))) %></td>
49 <% n = n + 1
49 <% n = n + 1
50 if (n > 1)
50 if (n > 1)
51 n = 0 %>
51 n = 0 %>
52 </tr><tr>
52 </tr><tr>
53 <%end
53 <%end
54 end %>
54 end %>
55 </tr>
55 </tr>
56 </table>
56 </table>
57 <hr />
57 <hr />
58
58
59 <div class="contextual">
59 <div class="contextual">
60 <%= link_to_remote(image_tag('comment.png'),
60 <%= link_to_remote(image_tag('comment.png'),
61 { :url => {:controller => 'issues', :action => 'reply', :id => @issue} },
61 { :url => {:controller => 'issues', :action => 'reply', :id => @issue} },
62 :title => l(:button_reply)) if authorize_for('issues', 'edit') %>
62 :title => l(:button_reply)) if authorize_for('issues', 'edit') %>
63 </div>
63 </div>
64
64
65 <p><strong><%=l(:field_description)%></strong></p>
65 <p><strong><%=l(:field_description)%></strong></p>
66 <div class="wiki">
66 <div class="wiki">
67 <%= textilizable @issue, :description, :attachments => @issue.attachments %>
67 <%= textilizable @issue, :description, :attachments => @issue.attachments %>
68 </div>
68 </div>
69
69
70 <% if @issue.attachments.any? %>
70 <% if @issue.attachments.any? %>
71 <%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %>
71 <%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %>
72 <% end %>
72 <% end %>
73
73
74 <% if authorize_for('issue_relations', 'new') || @issue.relations.any? %>
74 <% if authorize_for('issue_relations', 'new') || @issue.relations.any? %>
75 <hr />
75 <hr />
76 <div id="relations">
76 <div id="relations">
77 <%= render :partial => 'relations' %>
77 <%= render :partial => 'relations' %>
78 </div>
78 </div>
79 <% end %>
79 <% end %>
80
80
81 </div>
81 </div>
82
82
83 <% if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @project) %>
83 <% if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @project) %>
84 <div id="issue-changesets">
84 <div id="issue-changesets">
85 <h3><%=l(:label_associated_revisions)%></h3>
85 <h3><%=l(:label_associated_revisions)%></h3>
86 <%= render :partial => 'changesets', :locals => { :changesets => @issue.changesets} %>
86 <%= render :partial => 'changesets', :locals => { :changesets => @issue.changesets} %>
87 </div>
87 </div>
88 <% end %>
88 <% end %>
89
89
90 <% if @journals.any? %>
90 <% if @journals.any? %>
91 <div id="history">
91 <div id="history">
92 <h3><%=l(:label_history)%></h3>
92 <h3><%=l(:label_history)%></h3>
93 <%= render :partial => 'history', :locals => { :journals => @journals } %>
93 <%= render :partial => 'history', :locals => { :journals => @journals } %>
94 </div>
94 </div>
95 <% end %>
95 <% end %>
96 <div style="clear: both;"></div>
96 <div style="clear: both;"></div>
97
97
98 <% if authorize_for('issues', 'edit') %>
98 <% if authorize_for('issues', 'edit') %>
99 <div id="update" style="display:none;">
99 <div id="update" style="display:none;">
100 <h3><%= l(:button_update) %></h3>
100 <h3><%= l(:button_update) %></h3>
101 <%= render :partial => 'edit' %>
101 <%= render :partial => 'edit' %>
102 </div>
102 </div>
103 <% end %>
103 <% end %>
104
104
105 <p class="other-formats">
105 <p class="other-formats">
106 <%= l(:label_export_to) %>
106 <%= l(:label_export_to) %>
107 <span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
107 <span><%= link_to 'Atom', {:format => 'atom', :key => User.current.rss_key}, :class => 'feed' %></span>
108 <span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
108 <span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
109 </p>
109 </p>
110
110
111 <% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
111 <% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
112
112
113 <% content_for :sidebar do %>
113 <% content_for :sidebar do %>
114 <%= render :partial => 'issues/sidebar' %>
114 <%= render :partial => 'issues/sidebar' %>
115 <% end %>
115 <% end %>
116
116
117 <% content_for :header_tags do %>
117 <% content_for :header_tags do %>
118 <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
118 <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
119 <%= stylesheet_link_tag 'scm' %>
119 <%= stylesheet_link_tag 'scm' %>
120 <% end %>
120 <% end %>
@@ -1,131 +1,137
1 /* redMine - project management software
1 /* redMine - project management software
2 Copyright (C) 2006-2008 Jean-Philippe Lang */
2 Copyright (C) 2006-2008 Jean-Philippe Lang */
3
3
4 function checkAll (id, checked) {
4 function checkAll (id, checked) {
5 var els = Element.descendants(id);
5 var els = Element.descendants(id);
6 for (var i = 0; i < els.length; i++) {
6 for (var i = 0; i < els.length; i++) {
7 if (els[i].disabled==false) {
7 if (els[i].disabled==false) {
8 els[i].checked = checked;
8 els[i].checked = checked;
9 }
9 }
10 }
10 }
11 }
11 }
12
12
13 function showAndScrollTo(id, focus) {
14 Element.show(id);
15 if (focus!=null) { Form.Element.focus(focus); }
16 Element.scrollTo(id);
17 }
18
13 var fileFieldCount = 1;
19 var fileFieldCount = 1;
14
20
15 function addFileField() {
21 function addFileField() {
16 if (fileFieldCount >= 10) return false
22 if (fileFieldCount >= 10) return false
17 fileFieldCount++;
23 fileFieldCount++;
18 var f = document.createElement("input");
24 var f = document.createElement("input");
19 f.type = "file";
25 f.type = "file";
20 f.name = "attachments[" + fileFieldCount + "][file]";
26 f.name = "attachments[" + fileFieldCount + "][file]";
21 f.size = 30;
27 f.size = 30;
22 var d = document.createElement("input");
28 var d = document.createElement("input");
23 d.type = "text";
29 d.type = "text";
24 d.name = "attachments[" + fileFieldCount + "][description]";
30 d.name = "attachments[" + fileFieldCount + "][description]";
25 d.size = 60;
31 d.size = 60;
26
32
27 p = document.getElementById("attachments_fields");
33 p = document.getElementById("attachments_fields");
28 p.appendChild(document.createElement("br"));
34 p.appendChild(document.createElement("br"));
29 p.appendChild(f);
35 p.appendChild(f);
30 p.appendChild(d);
36 p.appendChild(d);
31 }
37 }
32
38
33 function showTab(name) {
39 function showTab(name) {
34 var f = $$('div#content .tab-content');
40 var f = $$('div#content .tab-content');
35 for(var i=0; i<f.length; i++){
41 for(var i=0; i<f.length; i++){
36 Element.hide(f[i]);
42 Element.hide(f[i]);
37 }
43 }
38 var f = $$('div.tabs a');
44 var f = $$('div.tabs a');
39 for(var i=0; i<f.length; i++){
45 for(var i=0; i<f.length; i++){
40 Element.removeClassName(f[i], "selected");
46 Element.removeClassName(f[i], "selected");
41 }
47 }
42 Element.show('tab-content-' + name);
48 Element.show('tab-content-' + name);
43 Element.addClassName('tab-' + name, "selected");
49 Element.addClassName('tab-' + name, "selected");
44 return false;
50 return false;
45 }
51 }
46
52
47 function setPredecessorFieldsVisibility() {
53 function setPredecessorFieldsVisibility() {
48 relationType = $('relation_relation_type');
54 relationType = $('relation_relation_type');
49 if (relationType && relationType.value == "precedes") {
55 if (relationType && relationType.value == "precedes") {
50 Element.show('predecessor_fields');
56 Element.show('predecessor_fields');
51 } else {
57 } else {
52 Element.hide('predecessor_fields');
58 Element.hide('predecessor_fields');
53 }
59 }
54 }
60 }
55
61
56 function promptToRemote(text, param, url) {
62 function promptToRemote(text, param, url) {
57 value = prompt(text + ':');
63 value = prompt(text + ':');
58 if (value) {
64 if (value) {
59 new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
65 new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
60 return false;
66 return false;
61 }
67 }
62 }
68 }
63
69
64 function collapseScmEntry(id) {
70 function collapseScmEntry(id) {
65 var els = document.getElementsByClassName(id, 'browser');
71 var els = document.getElementsByClassName(id, 'browser');
66 for (var i = 0; i < els.length; i++) {
72 for (var i = 0; i < els.length; i++) {
67 if (els[i].hasClassName('open')) {
73 if (els[i].hasClassName('open')) {
68 collapseScmEntry(els[i].id);
74 collapseScmEntry(els[i].id);
69 }
75 }
70 Element.hide(els[i]);
76 Element.hide(els[i]);
71 }
77 }
72 $(id).removeClassName('open');
78 $(id).removeClassName('open');
73 }
79 }
74
80
75 function expandScmEntry(id) {
81 function expandScmEntry(id) {
76 var els = document.getElementsByClassName(id, 'browser');
82 var els = document.getElementsByClassName(id, 'browser');
77 for (var i = 0; i < els.length; i++) {
83 for (var i = 0; i < els.length; i++) {
78 Element.show(els[i]);
84 Element.show(els[i]);
79 if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
85 if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
80 expandScmEntry(els[i].id);
86 expandScmEntry(els[i].id);
81 }
87 }
82 }
88 }
83 $(id).addClassName('open');
89 $(id).addClassName('open');
84 }
90 }
85
91
86 function scmEntryClick(id) {
92 function scmEntryClick(id) {
87 el = $(id);
93 el = $(id);
88 if (el.hasClassName('open')) {
94 if (el.hasClassName('open')) {
89 collapseScmEntry(id);
95 collapseScmEntry(id);
90 el.addClassName('collapsed');
96 el.addClassName('collapsed');
91 return false;
97 return false;
92 } else if (el.hasClassName('loaded')) {
98 } else if (el.hasClassName('loaded')) {
93 expandScmEntry(id);
99 expandScmEntry(id);
94 el.removeClassName('collapsed');
100 el.removeClassName('collapsed');
95 return false;
101 return false;
96 }
102 }
97 if (el.hasClassName('loading')) {
103 if (el.hasClassName('loading')) {
98 return false;
104 return false;
99 }
105 }
100 el.addClassName('loading');
106 el.addClassName('loading');
101 return true;
107 return true;
102 }
108 }
103
109
104 function scmEntryLoaded(id) {
110 function scmEntryLoaded(id) {
105 Element.addClassName(id, 'open');
111 Element.addClassName(id, 'open');
106 Element.addClassName(id, 'loaded');
112 Element.addClassName(id, 'loaded');
107 Element.removeClassName(id, 'loading');
113 Element.removeClassName(id, 'loading');
108 }
114 }
109
115
110 function randomKey(size) {
116 function randomKey(size) {
111 var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
117 var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
112 var key = '';
118 var key = '';
113 for (i = 0; i < size; i++) {
119 for (i = 0; i < size; i++) {
114 key += chars[Math.floor(Math.random() * chars.length)];
120 key += chars[Math.floor(Math.random() * chars.length)];
115 }
121 }
116 return key;
122 return key;
117 }
123 }
118
124
119 /* shows and hides ajax indicator */
125 /* shows and hides ajax indicator */
120 Ajax.Responders.register({
126 Ajax.Responders.register({
121 onCreate: function(){
127 onCreate: function(){
122 if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
128 if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
123 Element.show('ajax-indicator');
129 Element.show('ajax-indicator');
124 }
130 }
125 },
131 },
126 onComplete: function(){
132 onComplete: function(){
127 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
133 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
128 Element.hide('ajax-indicator');
134 Element.hide('ajax-indicator');
129 }
135 }
130 }
136 }
131 });
137 });
General Comments 0
You need to be logged in to leave comments. Login now