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