##// END OF EJS Templates
Adds a helper for preview links....
Jean-Philippe Lang -
r9848:ff68fff80ed1
parent child
Show More
@@ -1,1224 +1,1232
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
4 # Copyright (C) 2006-2012 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
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 #
37 #
38 # @param [String] name Anchor text (passed to link_to)
38 # @param [String] name Anchor text (passed to link_to)
39 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
39 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
40 # @param [optional, Hash] html_options Options passed to link_to
40 # @param [optional, Hash] html_options Options passed to link_to
41 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
41 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
42 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
42 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
43 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
43 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
44 end
44 end
45
45
46 # Display a link to remote if user is authorized
46 # Display a link to remote if user is authorized
47 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
47 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
48 url = options[:url] || {}
48 url = options[:url] || {}
49 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
49 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
50 end
50 end
51
51
52 # Displays a link to user's account page if active
52 # Displays a link to user's account page if active
53 def link_to_user(user, options={})
53 def link_to_user(user, options={})
54 if user.is_a?(User)
54 if user.is_a?(User)
55 name = h(user.name(options[:format]))
55 name = h(user.name(options[:format]))
56 if user.active?
56 if user.active?
57 link_to name, :controller => 'users', :action => 'show', :id => user
57 link_to name, :controller => 'users', :action => 'show', :id => user
58 else
58 else
59 name
59 name
60 end
60 end
61 else
61 else
62 h(user.to_s)
62 h(user.to_s)
63 end
63 end
64 end
64 end
65
65
66 # Displays a link to +issue+ with its subject.
66 # Displays a link to +issue+ with its subject.
67 # Examples:
67 # Examples:
68 #
68 #
69 # link_to_issue(issue) # => Defect #6: This is the subject
69 # link_to_issue(issue) # => Defect #6: This is the subject
70 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
70 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
71 # link_to_issue(issue, :subject => false) # => Defect #6
71 # link_to_issue(issue, :subject => false) # => Defect #6
72 # link_to_issue(issue, :project => true) # => Foo - Defect #6
72 # link_to_issue(issue, :project => true) # => Foo - Defect #6
73 #
73 #
74 def link_to_issue(issue, options={})
74 def link_to_issue(issue, options={})
75 title = nil
75 title = nil
76 subject = nil
76 subject = nil
77 if options[:subject] == false
77 if options[:subject] == false
78 title = truncate(issue.subject, :length => 60)
78 title = truncate(issue.subject, :length => 60)
79 else
79 else
80 subject = issue.subject
80 subject = issue.subject
81 if options[:truncate]
81 if options[:truncate]
82 subject = truncate(subject, :length => options[:truncate])
82 subject = truncate(subject, :length => options[:truncate])
83 end
83 end
84 end
84 end
85 s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
85 s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
86 :class => issue.css_classes,
86 :class => issue.css_classes,
87 :title => title
87 :title => title
88 s << h(": #{subject}") if subject
88 s << h(": #{subject}") if subject
89 s = h("#{issue.project} - ") + s if options[:project]
89 s = h("#{issue.project} - ") + s if options[:project]
90 s
90 s
91 end
91 end
92
92
93 # Generates a link to an attachment.
93 # Generates a link to an attachment.
94 # Options:
94 # Options:
95 # * :text - Link text (default to attachment filename)
95 # * :text - Link text (default to attachment filename)
96 # * :download - Force download (default: false)
96 # * :download - Force download (default: false)
97 def link_to_attachment(attachment, options={})
97 def link_to_attachment(attachment, options={})
98 text = options.delete(:text) || attachment.filename
98 text = options.delete(:text) || attachment.filename
99 action = options.delete(:download) ? 'download' : 'show'
99 action = options.delete(:download) ? 'download' : 'show'
100 opt_only_path = {}
100 opt_only_path = {}
101 opt_only_path[:only_path] = (options[:only_path] == false ? false : true)
101 opt_only_path[:only_path] = (options[:only_path] == false ? false : true)
102 options.delete(:only_path)
102 options.delete(:only_path)
103 link_to(h(text),
103 link_to(h(text),
104 {:controller => 'attachments', :action => action,
104 {:controller => 'attachments', :action => action,
105 :id => attachment, :filename => attachment.filename}.merge(opt_only_path),
105 :id => attachment, :filename => attachment.filename}.merge(opt_only_path),
106 options)
106 options)
107 end
107 end
108
108
109 # Generates a link to a SCM revision
109 # Generates a link to a SCM revision
110 # Options:
110 # Options:
111 # * :text - Link text (default to the formatted revision)
111 # * :text - Link text (default to the formatted revision)
112 def link_to_revision(revision, repository, options={})
112 def link_to_revision(revision, repository, options={})
113 if repository.is_a?(Project)
113 if repository.is_a?(Project)
114 repository = repository.repository
114 repository = repository.repository
115 end
115 end
116 text = options.delete(:text) || format_revision(revision)
116 text = options.delete(:text) || format_revision(revision)
117 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
117 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
118 link_to(
118 link_to(
119 h(text),
119 h(text),
120 {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
120 {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
121 :title => l(:label_revision_id, format_revision(revision))
121 :title => l(:label_revision_id, format_revision(revision))
122 )
122 )
123 end
123 end
124
124
125 # Generates a link to a message
125 # Generates a link to a message
126 def link_to_message(message, options={}, html_options = nil)
126 def link_to_message(message, options={}, html_options = nil)
127 link_to(
127 link_to(
128 h(truncate(message.subject, :length => 60)),
128 h(truncate(message.subject, :length => 60)),
129 { :controller => 'messages', :action => 'show',
129 { :controller => 'messages', :action => 'show',
130 :board_id => message.board_id,
130 :board_id => message.board_id,
131 :id => (message.parent_id || message.id),
131 :id => (message.parent_id || message.id),
132 :r => (message.parent_id && message.id),
132 :r => (message.parent_id && message.id),
133 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
133 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
134 }.merge(options),
134 }.merge(options),
135 html_options
135 html_options
136 )
136 )
137 end
137 end
138
138
139 # Generates a link to a project if active
139 # Generates a link to a project if active
140 # Examples:
140 # Examples:
141 #
141 #
142 # link_to_project(project) # => link to the specified project overview
142 # link_to_project(project) # => link to the specified project overview
143 # link_to_project(project, :action=>'settings') # => link to project settings
143 # link_to_project(project, :action=>'settings') # => link to project settings
144 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
144 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
145 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
145 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
146 #
146 #
147 def link_to_project(project, options={}, html_options = nil)
147 def link_to_project(project, options={}, html_options = nil)
148 if project.archived?
148 if project.archived?
149 h(project)
149 h(project)
150 else
150 else
151 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
151 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
152 link_to(h(project), url, html_options)
152 link_to(h(project), url, html_options)
153 end
153 end
154 end
154 end
155
155
156 def thumbnail_tag(attachment)
156 def thumbnail_tag(attachment)
157 link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
157 link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
158 {:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
158 {:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
159 :title => attachment.filename
159 :title => attachment.filename
160 end
160 end
161
161
162 def toggle_link(name, id, options={})
162 def toggle_link(name, id, options={})
163 onclick = "Element.toggle('#{id}'); "
163 onclick = "Element.toggle('#{id}'); "
164 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
164 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
165 onclick << "return false;"
165 onclick << "return false;"
166 link_to(name, "#", :onclick => onclick)
166 link_to(name, "#", :onclick => onclick)
167 end
167 end
168
168
169 def image_to_function(name, function, html_options = {})
169 def image_to_function(name, function, html_options = {})
170 html_options.symbolize_keys!
170 html_options.symbolize_keys!
171 tag(:input, html_options.merge({
171 tag(:input, html_options.merge({
172 :type => "image", :src => image_path(name),
172 :type => "image", :src => image_path(name),
173 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
173 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
174 }))
174 }))
175 end
175 end
176
176
177 def prompt_to_remote(name, text, param, url, html_options = {})
177 def prompt_to_remote(name, text, param, url, html_options = {})
178 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
178 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
179 link_to name, {}, html_options
179 link_to name, {}, html_options
180 end
180 end
181
181
182 def format_activity_title(text)
182 def format_activity_title(text)
183 h(truncate_single_line(text, :length => 100))
183 h(truncate_single_line(text, :length => 100))
184 end
184 end
185
185
186 def format_activity_day(date)
186 def format_activity_day(date)
187 date == User.current.today ? l(:label_today).titleize : format_date(date)
187 date == User.current.today ? l(:label_today).titleize : format_date(date)
188 end
188 end
189
189
190 def format_activity_description(text)
190 def format_activity_description(text)
191 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
191 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
192 ).gsub(/[\r\n]+/, "<br />").html_safe
192 ).gsub(/[\r\n]+/, "<br />").html_safe
193 end
193 end
194
194
195 def format_version_name(version)
195 def format_version_name(version)
196 if version.project == @project
196 if version.project == @project
197 h(version)
197 h(version)
198 else
198 else
199 h("#{version.project} - #{version}")
199 h("#{version.project} - #{version}")
200 end
200 end
201 end
201 end
202
202
203 def due_date_distance_in_words(date)
203 def due_date_distance_in_words(date)
204 if date
204 if date
205 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
205 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
206 end
206 end
207 end
207 end
208
208
209 def render_page_hierarchy(pages, node=nil, options={})
209 def render_page_hierarchy(pages, node=nil, options={})
210 content = ''
210 content = ''
211 if pages[node]
211 if pages[node]
212 content << "<ul class=\"pages-hierarchy\">\n"
212 content << "<ul class=\"pages-hierarchy\">\n"
213 pages[node].each do |page|
213 pages[node].each do |page|
214 content << "<li>"
214 content << "<li>"
215 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
215 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
216 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
216 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
217 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
217 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
218 content << "</li>\n"
218 content << "</li>\n"
219 end
219 end
220 content << "</ul>\n"
220 content << "</ul>\n"
221 end
221 end
222 content.html_safe
222 content.html_safe
223 end
223 end
224
224
225 # Renders flash messages
225 # Renders flash messages
226 def render_flash_messages
226 def render_flash_messages
227 s = ''
227 s = ''
228 flash.each do |k,v|
228 flash.each do |k,v|
229 s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
229 s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
230 end
230 end
231 s.html_safe
231 s.html_safe
232 end
232 end
233
233
234 # Renders tabs and their content
234 # Renders tabs and their content
235 def render_tabs(tabs)
235 def render_tabs(tabs)
236 if tabs.any?
236 if tabs.any?
237 render :partial => 'common/tabs', :locals => {:tabs => tabs}
237 render :partial => 'common/tabs', :locals => {:tabs => tabs}
238 else
238 else
239 content_tag 'p', l(:label_no_data), :class => "nodata"
239 content_tag 'p', l(:label_no_data), :class => "nodata"
240 end
240 end
241 end
241 end
242
242
243 # Renders the project quick-jump box
243 # Renders the project quick-jump box
244 def render_project_jump_box
244 def render_project_jump_box
245 return unless User.current.logged?
245 return unless User.current.logged?
246 projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
246 projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
247 if projects.any?
247 if projects.any?
248 options =
248 options =
249 ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
249 ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
250 '<option value="" disabled="disabled">---</option>').html_safe
250 '<option value="" disabled="disabled">---</option>').html_safe
251
251
252 options << project_tree_options_for_select(projects, :selected => @project) do |p|
252 options << project_tree_options_for_select(projects, :selected => @project) do |p|
253 { :value => project_path(:id => p, :jump => current_menu_item) }
253 { :value => project_path(:id => p, :jump => current_menu_item) }
254 end
254 end
255
255
256 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
256 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
257 end
257 end
258 end
258 end
259
259
260 def project_tree_options_for_select(projects, options = {})
260 def project_tree_options_for_select(projects, options = {})
261 s = ''
261 s = ''
262 project_tree(projects) do |project, level|
262 project_tree(projects) do |project, level|
263 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
263 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
264 tag_options = {:value => project.id}
264 tag_options = {:value => project.id}
265 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
265 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
266 tag_options[:selected] = 'selected'
266 tag_options[:selected] = 'selected'
267 else
267 else
268 tag_options[:selected] = nil
268 tag_options[:selected] = nil
269 end
269 end
270 tag_options.merge!(yield(project)) if block_given?
270 tag_options.merge!(yield(project)) if block_given?
271 s << content_tag('option', name_prefix + h(project), tag_options)
271 s << content_tag('option', name_prefix + h(project), tag_options)
272 end
272 end
273 s.html_safe
273 s.html_safe
274 end
274 end
275
275
276 # Yields the given block for each project with its level in the tree
276 # Yields the given block for each project with its level in the tree
277 #
277 #
278 # Wrapper for Project#project_tree
278 # Wrapper for Project#project_tree
279 def project_tree(projects, &block)
279 def project_tree(projects, &block)
280 Project.project_tree(projects, &block)
280 Project.project_tree(projects, &block)
281 end
281 end
282
282
283 def project_nested_ul(projects, &block)
283 def project_nested_ul(projects, &block)
284 s = ''
284 s = ''
285 if projects.any?
285 if projects.any?
286 ancestors = []
286 ancestors = []
287 projects.sort_by(&:lft).each do |project|
287 projects.sort_by(&:lft).each do |project|
288 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
288 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
289 s << "<ul>\n"
289 s << "<ul>\n"
290 else
290 else
291 ancestors.pop
291 ancestors.pop
292 s << "</li>"
292 s << "</li>"
293 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
293 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
294 ancestors.pop
294 ancestors.pop
295 s << "</ul></li>\n"
295 s << "</ul></li>\n"
296 end
296 end
297 end
297 end
298 s << "<li>"
298 s << "<li>"
299 s << yield(project).to_s
299 s << yield(project).to_s
300 ancestors << project
300 ancestors << project
301 end
301 end
302 s << ("</li></ul>\n" * ancestors.size)
302 s << ("</li></ul>\n" * ancestors.size)
303 end
303 end
304 s.html_safe
304 s.html_safe
305 end
305 end
306
306
307 def principals_check_box_tags(name, principals)
307 def principals_check_box_tags(name, principals)
308 s = ''
308 s = ''
309 principals.sort.each do |principal|
309 principals.sort.each do |principal|
310 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
310 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
311 end
311 end
312 s.html_safe
312 s.html_safe
313 end
313 end
314
314
315 # Returns a string for users/groups option tags
315 # Returns a string for users/groups option tags
316 def principals_options_for_select(collection, selected=nil)
316 def principals_options_for_select(collection, selected=nil)
317 s = ''
317 s = ''
318 if collection.include?(User.current)
318 if collection.include?(User.current)
319 s << content_tag('option', "<< #{l(:label_me)} >>".html_safe, :value => User.current.id)
319 s << content_tag('option', "<< #{l(:label_me)} >>".html_safe, :value => User.current.id)
320 end
320 end
321 groups = ''
321 groups = ''
322 collection.sort.each do |element|
322 collection.sort.each do |element|
323 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
323 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
324 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
324 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
325 end
325 end
326 unless groups.empty?
326 unless groups.empty?
327 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
327 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
328 end
328 end
329 s.html_safe
329 s.html_safe
330 end
330 end
331
331
332 # Truncates and returns the string as a single line
332 # Truncates and returns the string as a single line
333 def truncate_single_line(string, *args)
333 def truncate_single_line(string, *args)
334 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
334 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
335 end
335 end
336
336
337 # Truncates at line break after 250 characters or options[:length]
337 # Truncates at line break after 250 characters or options[:length]
338 def truncate_lines(string, options={})
338 def truncate_lines(string, options={})
339 length = options[:length] || 250
339 length = options[:length] || 250
340 if string.to_s =~ /\A(.{#{length}}.*?)$/m
340 if string.to_s =~ /\A(.{#{length}}.*?)$/m
341 "#{$1}..."
341 "#{$1}..."
342 else
342 else
343 string
343 string
344 end
344 end
345 end
345 end
346
346
347 def anchor(text)
347 def anchor(text)
348 text.to_s.gsub(' ', '_')
348 text.to_s.gsub(' ', '_')
349 end
349 end
350
350
351 def html_hours(text)
351 def html_hours(text)
352 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
352 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
353 end
353 end
354
354
355 def authoring(created, author, options={})
355 def authoring(created, author, options={})
356 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
356 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
357 end
357 end
358
358
359 def time_tag(time)
359 def time_tag(time)
360 text = distance_of_time_in_words(Time.now, time)
360 text = distance_of_time_in_words(Time.now, time)
361 if @project
361 if @project
362 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
362 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
363 else
363 else
364 content_tag('acronym', text, :title => format_time(time))
364 content_tag('acronym', text, :title => format_time(time))
365 end
365 end
366 end
366 end
367
367
368 def syntax_highlight_lines(name, content)
368 def syntax_highlight_lines(name, content)
369 lines = []
369 lines = []
370 syntax_highlight(name, content).each_line { |line| lines << line }
370 syntax_highlight(name, content).each_line { |line| lines << line }
371 lines
371 lines
372 end
372 end
373
373
374 def syntax_highlight(name, content)
374 def syntax_highlight(name, content)
375 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
375 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
376 end
376 end
377
377
378 def to_path_param(path)
378 def to_path_param(path)
379 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
379 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
380 str.blank? ? nil : str
380 str.blank? ? nil : str
381 end
381 end
382
382
383 def pagination_links_full(paginator, count=nil, options={})
383 def pagination_links_full(paginator, count=nil, options={})
384 page_param = options.delete(:page_param) || :page
384 page_param = options.delete(:page_param) || :page
385 per_page_links = options.delete(:per_page_links)
385 per_page_links = options.delete(:per_page_links)
386 url_param = params.dup
386 url_param = params.dup
387
387
388 html = ''
388 html = ''
389 if paginator.current.previous
389 if paginator.current.previous
390 # \xc2\xab(utf-8) = &#171;
390 # \xc2\xab(utf-8) = &#171;
391 html << link_to_content_update(
391 html << link_to_content_update(
392 "\xc2\xab " + l(:label_previous),
392 "\xc2\xab " + l(:label_previous),
393 url_param.merge(page_param => paginator.current.previous)) + ' '
393 url_param.merge(page_param => paginator.current.previous)) + ' '
394 end
394 end
395
395
396 html << (pagination_links_each(paginator, options) do |n|
396 html << (pagination_links_each(paginator, options) do |n|
397 link_to_content_update(n.to_s, url_param.merge(page_param => n))
397 link_to_content_update(n.to_s, url_param.merge(page_param => n))
398 end || '')
398 end || '')
399
399
400 if paginator.current.next
400 if paginator.current.next
401 # \xc2\xbb(utf-8) = &#187;
401 # \xc2\xbb(utf-8) = &#187;
402 html << ' ' + link_to_content_update(
402 html << ' ' + link_to_content_update(
403 (l(:label_next) + " \xc2\xbb"),
403 (l(:label_next) + " \xc2\xbb"),
404 url_param.merge(page_param => paginator.current.next))
404 url_param.merge(page_param => paginator.current.next))
405 end
405 end
406
406
407 unless count.nil?
407 unless count.nil?
408 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
408 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
409 if per_page_links != false && links = per_page_links(paginator.items_per_page, count)
409 if per_page_links != false && links = per_page_links(paginator.items_per_page, count)
410 html << " | #{links}"
410 html << " | #{links}"
411 end
411 end
412 end
412 end
413
413
414 html.html_safe
414 html.html_safe
415 end
415 end
416
416
417 def per_page_links(selected=nil, item_count=nil)
417 def per_page_links(selected=nil, item_count=nil)
418 values = Setting.per_page_options_array
418 values = Setting.per_page_options_array
419 if item_count && values.any?
419 if item_count && values.any?
420 if item_count > values.first
420 if item_count > values.first
421 max = values.detect {|value| value >= item_count} || item_count
421 max = values.detect {|value| value >= item_count} || item_count
422 else
422 else
423 max = item_count
423 max = item_count
424 end
424 end
425 values = values.select {|value| value <= max || value == selected}
425 values = values.select {|value| value <= max || value == selected}
426 end
426 end
427 if values.empty? || (values.size == 1 && values.first == selected)
427 if values.empty? || (values.size == 1 && values.first == selected)
428 return nil
428 return nil
429 end
429 end
430 links = values.collect do |n|
430 links = values.collect do |n|
431 n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
431 n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
432 end
432 end
433 l(:label_display_per_page, links.join(', '))
433 l(:label_display_per_page, links.join(', '))
434 end
434 end
435
435
436 def reorder_links(name, url, method = :post)
436 def reorder_links(name, url, method = :post)
437 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
437 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
438 url.merge({"#{name}[move_to]" => 'highest'}),
438 url.merge({"#{name}[move_to]" => 'highest'}),
439 :method => method, :title => l(:label_sort_highest)) +
439 :method => method, :title => l(:label_sort_highest)) +
440 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
440 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
441 url.merge({"#{name}[move_to]" => 'higher'}),
441 url.merge({"#{name}[move_to]" => 'higher'}),
442 :method => method, :title => l(:label_sort_higher)) +
442 :method => method, :title => l(:label_sort_higher)) +
443 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
443 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
444 url.merge({"#{name}[move_to]" => 'lower'}),
444 url.merge({"#{name}[move_to]" => 'lower'}),
445 :method => method, :title => l(:label_sort_lower)) +
445 :method => method, :title => l(:label_sort_lower)) +
446 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
446 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
447 url.merge({"#{name}[move_to]" => 'lowest'}),
447 url.merge({"#{name}[move_to]" => 'lowest'}),
448 :method => method, :title => l(:label_sort_lowest))
448 :method => method, :title => l(:label_sort_lowest))
449 end
449 end
450
450
451 def breadcrumb(*args)
451 def breadcrumb(*args)
452 elements = args.flatten
452 elements = args.flatten
453 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
453 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
454 end
454 end
455
455
456 def other_formats_links(&block)
456 def other_formats_links(&block)
457 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
457 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
458 yield Redmine::Views::OtherFormatsBuilder.new(self)
458 yield Redmine::Views::OtherFormatsBuilder.new(self)
459 concat('</p>'.html_safe)
459 concat('</p>'.html_safe)
460 end
460 end
461
461
462 def page_header_title
462 def page_header_title
463 if @project.nil? || @project.new_record?
463 if @project.nil? || @project.new_record?
464 h(Setting.app_title)
464 h(Setting.app_title)
465 else
465 else
466 b = []
466 b = []
467 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
467 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
468 if ancestors.any?
468 if ancestors.any?
469 root = ancestors.shift
469 root = ancestors.shift
470 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
470 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
471 if ancestors.size > 2
471 if ancestors.size > 2
472 b << "\xe2\x80\xa6"
472 b << "\xe2\x80\xa6"
473 ancestors = ancestors[-2, 2]
473 ancestors = ancestors[-2, 2]
474 end
474 end
475 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
475 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
476 end
476 end
477 b << h(@project)
477 b << h(@project)
478 b.join(" \xc2\xbb ").html_safe
478 b.join(" \xc2\xbb ").html_safe
479 end
479 end
480 end
480 end
481
481
482 def html_title(*args)
482 def html_title(*args)
483 if args.empty?
483 if args.empty?
484 title = @html_title || []
484 title = @html_title || []
485 title << @project.name if @project
485 title << @project.name if @project
486 title << Setting.app_title unless Setting.app_title == title.last
486 title << Setting.app_title unless Setting.app_title == title.last
487 title.select {|t| !t.blank? }.join(' - ')
487 title.select {|t| !t.blank? }.join(' - ')
488 else
488 else
489 @html_title ||= []
489 @html_title ||= []
490 @html_title += args
490 @html_title += args
491 end
491 end
492 end
492 end
493
493
494 # Returns the theme, controller name, and action as css classes for the
494 # Returns the theme, controller name, and action as css classes for the
495 # HTML body.
495 # HTML body.
496 def body_css_classes
496 def body_css_classes
497 css = []
497 css = []
498 if theme = Redmine::Themes.theme(Setting.ui_theme)
498 if theme = Redmine::Themes.theme(Setting.ui_theme)
499 css << 'theme-' + theme.name
499 css << 'theme-' + theme.name
500 end
500 end
501
501
502 css << 'controller-' + controller_name
502 css << 'controller-' + controller_name
503 css << 'action-' + action_name
503 css << 'action-' + action_name
504 css.join(' ')
504 css.join(' ')
505 end
505 end
506
506
507 def accesskey(s)
507 def accesskey(s)
508 Redmine::AccessKeys.key_for s
508 Redmine::AccessKeys.key_for s
509 end
509 end
510
510
511 # Formats text according to system settings.
511 # Formats text according to system settings.
512 # 2 ways to call this method:
512 # 2 ways to call this method:
513 # * with a String: textilizable(text, options)
513 # * with a String: textilizable(text, options)
514 # * with an object and one of its attribute: textilizable(issue, :description, options)
514 # * with an object and one of its attribute: textilizable(issue, :description, options)
515 def textilizable(*args)
515 def textilizable(*args)
516 options = args.last.is_a?(Hash) ? args.pop : {}
516 options = args.last.is_a?(Hash) ? args.pop : {}
517 case args.size
517 case args.size
518 when 1
518 when 1
519 obj = options[:object]
519 obj = options[:object]
520 text = args.shift
520 text = args.shift
521 when 2
521 when 2
522 obj = args.shift
522 obj = args.shift
523 attr = args.shift
523 attr = args.shift
524 text = obj.send(attr).to_s
524 text = obj.send(attr).to_s
525 else
525 else
526 raise ArgumentError, 'invalid arguments to textilizable'
526 raise ArgumentError, 'invalid arguments to textilizable'
527 end
527 end
528 return '' if text.blank?
528 return '' if text.blank?
529 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
529 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
530 only_path = options.delete(:only_path) == false ? false : true
530 only_path = options.delete(:only_path) == false ? false : true
531
531
532 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
532 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
533
533
534 @parsed_headings = []
534 @parsed_headings = []
535 @heading_anchors = {}
535 @heading_anchors = {}
536 @current_section = 0 if options[:edit_section_links]
536 @current_section = 0 if options[:edit_section_links]
537
537
538 parse_sections(text, project, obj, attr, only_path, options)
538 parse_sections(text, project, obj, attr, only_path, options)
539 text = parse_non_pre_blocks(text) do |text|
539 text = parse_non_pre_blocks(text) do |text|
540 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
540 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros].each do |method_name|
541 send method_name, text, project, obj, attr, only_path, options
541 send method_name, text, project, obj, attr, only_path, options
542 end
542 end
543 end
543 end
544 parse_headings(text, project, obj, attr, only_path, options)
544 parse_headings(text, project, obj, attr, only_path, options)
545
545
546 if @parsed_headings.any?
546 if @parsed_headings.any?
547 replace_toc(text, @parsed_headings)
547 replace_toc(text, @parsed_headings)
548 end
548 end
549
549
550 text.html_safe
550 text.html_safe
551 end
551 end
552
552
553 def parse_non_pre_blocks(text)
553 def parse_non_pre_blocks(text)
554 s = StringScanner.new(text)
554 s = StringScanner.new(text)
555 tags = []
555 tags = []
556 parsed = ''
556 parsed = ''
557 while !s.eos?
557 while !s.eos?
558 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
558 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
559 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
559 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
560 if tags.empty?
560 if tags.empty?
561 yield text
561 yield text
562 end
562 end
563 parsed << text
563 parsed << text
564 if tag
564 if tag
565 if closing
565 if closing
566 if tags.last == tag.downcase
566 if tags.last == tag.downcase
567 tags.pop
567 tags.pop
568 end
568 end
569 else
569 else
570 tags << tag.downcase
570 tags << tag.downcase
571 end
571 end
572 parsed << full_tag
572 parsed << full_tag
573 end
573 end
574 end
574 end
575 # Close any non closing tags
575 # Close any non closing tags
576 while tag = tags.pop
576 while tag = tags.pop
577 parsed << "</#{tag}>"
577 parsed << "</#{tag}>"
578 end
578 end
579 parsed
579 parsed
580 end
580 end
581
581
582 def parse_inline_attachments(text, project, obj, attr, only_path, options)
582 def parse_inline_attachments(text, project, obj, attr, only_path, options)
583 # when using an image link, try to use an attachment, if possible
583 # when using an image link, try to use an attachment, if possible
584 if options[:attachments] || (obj && obj.respond_to?(:attachments))
584 if options[:attachments] || (obj && obj.respond_to?(:attachments))
585 attachments = options[:attachments] || obj.attachments
585 attachments = options[:attachments] || obj.attachments
586 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
586 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
587 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
587 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
588 # search for the picture in attachments
588 # search for the picture in attachments
589 if found = Attachment.latest_attach(attachments, filename)
589 if found = Attachment.latest_attach(attachments, filename)
590 image_url = url_for :only_path => only_path, :controller => 'attachments',
590 image_url = url_for :only_path => only_path, :controller => 'attachments',
591 :action => 'download', :id => found
591 :action => 'download', :id => found
592 desc = found.description.to_s.gsub('"', '')
592 desc = found.description.to_s.gsub('"', '')
593 if !desc.blank? && alttext.blank?
593 if !desc.blank? && alttext.blank?
594 alt = " title=\"#{desc}\" alt=\"#{desc}\""
594 alt = " title=\"#{desc}\" alt=\"#{desc}\""
595 end
595 end
596 "src=\"#{image_url}\"#{alt}"
596 "src=\"#{image_url}\"#{alt}"
597 else
597 else
598 m
598 m
599 end
599 end
600 end
600 end
601 end
601 end
602 end
602 end
603
603
604 # Wiki links
604 # Wiki links
605 #
605 #
606 # Examples:
606 # Examples:
607 # [[mypage]]
607 # [[mypage]]
608 # [[mypage|mytext]]
608 # [[mypage|mytext]]
609 # wiki links can refer other project wikis, using project name or identifier:
609 # wiki links can refer other project wikis, using project name or identifier:
610 # [[project:]] -> wiki starting page
610 # [[project:]] -> wiki starting page
611 # [[project:|mytext]]
611 # [[project:|mytext]]
612 # [[project:mypage]]
612 # [[project:mypage]]
613 # [[project:mypage|mytext]]
613 # [[project:mypage|mytext]]
614 def parse_wiki_links(text, project, obj, attr, only_path, options)
614 def parse_wiki_links(text, project, obj, attr, only_path, options)
615 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
615 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
616 link_project = project
616 link_project = project
617 esc, all, page, title = $1, $2, $3, $5
617 esc, all, page, title = $1, $2, $3, $5
618 if esc.nil?
618 if esc.nil?
619 if page =~ /^([^\:]+)\:(.*)$/
619 if page =~ /^([^\:]+)\:(.*)$/
620 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
620 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
621 page = $2
621 page = $2
622 title ||= $1 if page.blank?
622 title ||= $1 if page.blank?
623 end
623 end
624
624
625 if link_project && link_project.wiki
625 if link_project && link_project.wiki
626 # extract anchor
626 # extract anchor
627 anchor = nil
627 anchor = nil
628 if page =~ /^(.+?)\#(.+)$/
628 if page =~ /^(.+?)\#(.+)$/
629 page, anchor = $1, $2
629 page, anchor = $1, $2
630 end
630 end
631 anchor = sanitize_anchor_name(anchor) if anchor.present?
631 anchor = sanitize_anchor_name(anchor) if anchor.present?
632 # check if page exists
632 # check if page exists
633 wiki_page = link_project.wiki.find_page(page)
633 wiki_page = link_project.wiki.find_page(page)
634 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
634 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
635 "##{anchor}"
635 "##{anchor}"
636 else
636 else
637 case options[:wiki_links]
637 case options[:wiki_links]
638 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
638 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
639 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
639 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
640 else
640 else
641 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
641 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
642 parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
642 parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
643 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
643 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
644 :id => wiki_page_id, :anchor => anchor, :parent => parent)
644 :id => wiki_page_id, :anchor => anchor, :parent => parent)
645 end
645 end
646 end
646 end
647 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
647 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
648 else
648 else
649 # project or wiki doesn't exist
649 # project or wiki doesn't exist
650 all
650 all
651 end
651 end
652 else
652 else
653 all
653 all
654 end
654 end
655 end
655 end
656 end
656 end
657
657
658 # Redmine links
658 # Redmine links
659 #
659 #
660 # Examples:
660 # Examples:
661 # Issues:
661 # Issues:
662 # #52 -> Link to issue #52
662 # #52 -> Link to issue #52
663 # Changesets:
663 # Changesets:
664 # r52 -> Link to revision 52
664 # r52 -> Link to revision 52
665 # commit:a85130f -> Link to scmid starting with a85130f
665 # commit:a85130f -> Link to scmid starting with a85130f
666 # Documents:
666 # Documents:
667 # document#17 -> Link to document with id 17
667 # document#17 -> Link to document with id 17
668 # document:Greetings -> Link to the document with title "Greetings"
668 # document:Greetings -> Link to the document with title "Greetings"
669 # document:"Some document" -> Link to the document with title "Some document"
669 # document:"Some document" -> Link to the document with title "Some document"
670 # Versions:
670 # Versions:
671 # version#3 -> Link to version with id 3
671 # version#3 -> Link to version with id 3
672 # version:1.0.0 -> Link to version named "1.0.0"
672 # version:1.0.0 -> Link to version named "1.0.0"
673 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
673 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
674 # Attachments:
674 # Attachments:
675 # attachment:file.zip -> Link to the attachment of the current object named file.zip
675 # attachment:file.zip -> Link to the attachment of the current object named file.zip
676 # Source files:
676 # Source files:
677 # source:some/file -> Link to the file located at /some/file in the project's repository
677 # source:some/file -> Link to the file located at /some/file in the project's repository
678 # source:some/file@52 -> Link to the file's revision 52
678 # source:some/file@52 -> Link to the file's revision 52
679 # source:some/file#L120 -> Link to line 120 of the file
679 # source:some/file#L120 -> Link to line 120 of the file
680 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
680 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
681 # export:some/file -> Force the download of the file
681 # export:some/file -> Force the download of the file
682 # Forum messages:
682 # Forum messages:
683 # message#1218 -> Link to message with id 1218
683 # message#1218 -> Link to message with id 1218
684 #
684 #
685 # Links can refer other objects from other projects, using project identifier:
685 # Links can refer other objects from other projects, using project identifier:
686 # identifier:r52
686 # identifier:r52
687 # identifier:document:"Some document"
687 # identifier:document:"Some document"
688 # identifier:version:1.0.0
688 # identifier:version:1.0.0
689 # identifier:source:some/file
689 # identifier:source:some/file
690 def parse_redmine_links(text, project, obj, attr, only_path, options)
690 def parse_redmine_links(text, project, obj, attr, only_path, options)
691 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
691 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
692 leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
692 leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
693 link = nil
693 link = nil
694 if project_identifier
694 if project_identifier
695 project = Project.visible.find_by_identifier(project_identifier)
695 project = Project.visible.find_by_identifier(project_identifier)
696 end
696 end
697 if esc.nil?
697 if esc.nil?
698 if prefix.nil? && sep == 'r'
698 if prefix.nil? && sep == 'r'
699 if project
699 if project
700 repository = nil
700 repository = nil
701 if repo_identifier
701 if repo_identifier
702 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
702 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
703 else
703 else
704 repository = project.repository
704 repository = project.repository
705 end
705 end
706 # project.changesets.visible raises an SQL error because of a double join on repositories
706 # project.changesets.visible raises an SQL error because of a double join on repositories
707 if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
707 if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
708 link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.revision},
708 link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.revision},
709 :class => 'changeset',
709 :class => 'changeset',
710 :title => truncate_single_line(changeset.comments, :length => 100))
710 :title => truncate_single_line(changeset.comments, :length => 100))
711 end
711 end
712 end
712 end
713 elsif sep == '#'
713 elsif sep == '#'
714 oid = identifier.to_i
714 oid = identifier.to_i
715 case prefix
715 case prefix
716 when nil
716 when nil
717 if issue = Issue.visible.find_by_id(oid, :include => :status)
717 if issue = Issue.visible.find_by_id(oid, :include => :status)
718 anchor = comment_id ? "note-#{comment_id}" : nil
718 anchor = comment_id ? "note-#{comment_id}" : nil
719 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
719 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
720 :class => issue.css_classes,
720 :class => issue.css_classes,
721 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
721 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
722 end
722 end
723 when 'document'
723 when 'document'
724 if document = Document.visible.find_by_id(oid)
724 if document = Document.visible.find_by_id(oid)
725 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
725 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
726 :class => 'document'
726 :class => 'document'
727 end
727 end
728 when 'version'
728 when 'version'
729 if version = Version.visible.find_by_id(oid)
729 if version = Version.visible.find_by_id(oid)
730 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
730 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
731 :class => 'version'
731 :class => 'version'
732 end
732 end
733 when 'message'
733 when 'message'
734 if message = Message.visible.find_by_id(oid, :include => :parent)
734 if message = Message.visible.find_by_id(oid, :include => :parent)
735 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
735 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
736 end
736 end
737 when 'forum'
737 when 'forum'
738 if board = Board.visible.find_by_id(oid)
738 if board = Board.visible.find_by_id(oid)
739 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
739 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
740 :class => 'board'
740 :class => 'board'
741 end
741 end
742 when 'news'
742 when 'news'
743 if news = News.visible.find_by_id(oid)
743 if news = News.visible.find_by_id(oid)
744 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
744 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
745 :class => 'news'
745 :class => 'news'
746 end
746 end
747 when 'project'
747 when 'project'
748 if p = Project.visible.find_by_id(oid)
748 if p = Project.visible.find_by_id(oid)
749 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
749 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
750 end
750 end
751 end
751 end
752 elsif sep == ':'
752 elsif sep == ':'
753 # removes the double quotes if any
753 # removes the double quotes if any
754 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
754 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
755 case prefix
755 case prefix
756 when 'document'
756 when 'document'
757 if project && document = project.documents.visible.find_by_title(name)
757 if project && document = project.documents.visible.find_by_title(name)
758 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
758 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
759 :class => 'document'
759 :class => 'document'
760 end
760 end
761 when 'version'
761 when 'version'
762 if project && version = project.versions.visible.find_by_name(name)
762 if project && version = project.versions.visible.find_by_name(name)
763 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
763 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
764 :class => 'version'
764 :class => 'version'
765 end
765 end
766 when 'forum'
766 when 'forum'
767 if project && board = project.boards.visible.find_by_name(name)
767 if project && board = project.boards.visible.find_by_name(name)
768 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
768 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
769 :class => 'board'
769 :class => 'board'
770 end
770 end
771 when 'news'
771 when 'news'
772 if project && news = project.news.visible.find_by_title(name)
772 if project && news = project.news.visible.find_by_title(name)
773 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
773 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
774 :class => 'news'
774 :class => 'news'
775 end
775 end
776 when 'commit', 'source', 'export'
776 when 'commit', 'source', 'export'
777 if project
777 if project
778 repository = nil
778 repository = nil
779 if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
779 if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
780 repo_prefix, repo_identifier, name = $1, $2, $3
780 repo_prefix, repo_identifier, name = $1, $2, $3
781 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
781 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
782 else
782 else
783 repository = project.repository
783 repository = project.repository
784 end
784 end
785 if prefix == 'commit'
785 if prefix == 'commit'
786 if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"]))
786 if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"]))
787 link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
787 link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
788 :class => 'changeset',
788 :class => 'changeset',
789 :title => truncate_single_line(h(changeset.comments), :length => 100)
789 :title => truncate_single_line(h(changeset.comments), :length => 100)
790 end
790 end
791 else
791 else
792 if repository && User.current.allowed_to?(:browse_repository, project)
792 if repository && User.current.allowed_to?(:browse_repository, project)
793 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
793 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
794 path, rev, anchor = $1, $3, $5
794 path, rev, anchor = $1, $3, $5
795 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param,
795 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param,
796 :path => to_path_param(path),
796 :path => to_path_param(path),
797 :rev => rev,
797 :rev => rev,
798 :anchor => anchor,
798 :anchor => anchor,
799 :format => (prefix == 'export' ? 'raw' : nil)},
799 :format => (prefix == 'export' ? 'raw' : nil)},
800 :class => (prefix == 'export' ? 'source download' : 'source')
800 :class => (prefix == 'export' ? 'source download' : 'source')
801 end
801 end
802 end
802 end
803 repo_prefix = nil
803 repo_prefix = nil
804 end
804 end
805 when 'attachment'
805 when 'attachment'
806 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
806 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
807 if attachments && attachment = attachments.detect {|a| a.filename == name }
807 if attachments && attachment = attachments.detect {|a| a.filename == name }
808 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
808 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
809 :class => 'attachment'
809 :class => 'attachment'
810 end
810 end
811 when 'project'
811 when 'project'
812 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
812 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
813 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
813 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
814 end
814 end
815 end
815 end
816 end
816 end
817 end
817 end
818 (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
818 (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
819 end
819 end
820 end
820 end
821
821
822 HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
822 HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
823
823
824 def parse_sections(text, project, obj, attr, only_path, options)
824 def parse_sections(text, project, obj, attr, only_path, options)
825 return unless options[:edit_section_links]
825 return unless options[:edit_section_links]
826 text.gsub!(HEADING_RE) do
826 text.gsub!(HEADING_RE) do
827 heading = $1
827 heading = $1
828 @current_section += 1
828 @current_section += 1
829 if @current_section > 1
829 if @current_section > 1
830 content_tag('div',
830 content_tag('div',
831 link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
831 link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
832 :class => 'contextual',
832 :class => 'contextual',
833 :title => l(:button_edit_section)) + heading.html_safe
833 :title => l(:button_edit_section)) + heading.html_safe
834 else
834 else
835 heading
835 heading
836 end
836 end
837 end
837 end
838 end
838 end
839
839
840 # Headings and TOC
840 # Headings and TOC
841 # Adds ids and links to headings unless options[:headings] is set to false
841 # Adds ids and links to headings unless options[:headings] is set to false
842 def parse_headings(text, project, obj, attr, only_path, options)
842 def parse_headings(text, project, obj, attr, only_path, options)
843 return if options[:headings] == false
843 return if options[:headings] == false
844
844
845 text.gsub!(HEADING_RE) do
845 text.gsub!(HEADING_RE) do
846 level, attrs, content = $2.to_i, $3, $4
846 level, attrs, content = $2.to_i, $3, $4
847 item = strip_tags(content).strip
847 item = strip_tags(content).strip
848 anchor = sanitize_anchor_name(item)
848 anchor = sanitize_anchor_name(item)
849 # used for single-file wiki export
849 # used for single-file wiki export
850 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
850 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
851 @heading_anchors[anchor] ||= 0
851 @heading_anchors[anchor] ||= 0
852 idx = (@heading_anchors[anchor] += 1)
852 idx = (@heading_anchors[anchor] += 1)
853 if idx > 1
853 if idx > 1
854 anchor = "#{anchor}-#{idx}"
854 anchor = "#{anchor}-#{idx}"
855 end
855 end
856 @parsed_headings << [level, anchor, item]
856 @parsed_headings << [level, anchor, item]
857 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
857 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
858 end
858 end
859 end
859 end
860
860
861 MACROS_RE = /
861 MACROS_RE = /
862 (!)? # escaping
862 (!)? # escaping
863 (
863 (
864 \{\{ # opening tag
864 \{\{ # opening tag
865 ([\w]+) # macro name
865 ([\w]+) # macro name
866 (\(([^\}]*)\))? # optional arguments
866 (\(([^\}]*)\))? # optional arguments
867 \}\} # closing tag
867 \}\} # closing tag
868 )
868 )
869 /x unless const_defined?(:MACROS_RE)
869 /x unless const_defined?(:MACROS_RE)
870
870
871 # Macros substitution
871 # Macros substitution
872 def parse_macros(text, project, obj, attr, only_path, options)
872 def parse_macros(text, project, obj, attr, only_path, options)
873 text.gsub!(MACROS_RE) do
873 text.gsub!(MACROS_RE) do
874 esc, all, macro = $1, $2, $3.downcase
874 esc, all, macro = $1, $2, $3.downcase
875 args = ($5 || '').split(',').each(&:strip)
875 args = ($5 || '').split(',').each(&:strip)
876 if esc.nil?
876 if esc.nil?
877 begin
877 begin
878 exec_macro(macro, obj, args)
878 exec_macro(macro, obj, args)
879 rescue => e
879 rescue => e
880 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
880 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
881 end || all
881 end || all
882 else
882 else
883 all
883 all
884 end
884 end
885 end
885 end
886 end
886 end
887
887
888 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
888 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
889
889
890 # Renders the TOC with given headings
890 # Renders the TOC with given headings
891 def replace_toc(text, headings)
891 def replace_toc(text, headings)
892 text.gsub!(TOC_RE) do
892 text.gsub!(TOC_RE) do
893 if headings.empty?
893 if headings.empty?
894 ''
894 ''
895 else
895 else
896 div_class = 'toc'
896 div_class = 'toc'
897 div_class << ' right' if $1 == '>'
897 div_class << ' right' if $1 == '>'
898 div_class << ' left' if $1 == '<'
898 div_class << ' left' if $1 == '<'
899 out = "<ul class=\"#{div_class}\"><li>"
899 out = "<ul class=\"#{div_class}\"><li>"
900 root = headings.map(&:first).min
900 root = headings.map(&:first).min
901 current = root
901 current = root
902 started = false
902 started = false
903 headings.each do |level, anchor, item|
903 headings.each do |level, anchor, item|
904 if level > current
904 if level > current
905 out << '<ul><li>' * (level - current)
905 out << '<ul><li>' * (level - current)
906 elsif level < current
906 elsif level < current
907 out << "</li></ul>\n" * (current - level) + "</li><li>"
907 out << "</li></ul>\n" * (current - level) + "</li><li>"
908 elsif started
908 elsif started
909 out << '</li><li>'
909 out << '</li><li>'
910 end
910 end
911 out << "<a href=\"##{anchor}\">#{item}</a>"
911 out << "<a href=\"##{anchor}\">#{item}</a>"
912 current = level
912 current = level
913 started = true
913 started = true
914 end
914 end
915 out << '</li></ul>' * (current - root)
915 out << '</li></ul>' * (current - root)
916 out << '</li></ul>'
916 out << '</li></ul>'
917 end
917 end
918 end
918 end
919 end
919 end
920
920
921 # Same as Rails' simple_format helper without using paragraphs
921 # Same as Rails' simple_format helper without using paragraphs
922 def simple_format_without_paragraph(text)
922 def simple_format_without_paragraph(text)
923 text.to_s.
923 text.to_s.
924 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
924 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
925 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
925 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
926 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
926 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
927 html_safe
927 html_safe
928 end
928 end
929
929
930 def lang_options_for_select(blank=true)
930 def lang_options_for_select(blank=true)
931 (blank ? [["(auto)", ""]] : []) +
931 (blank ? [["(auto)", ""]] : []) +
932 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
932 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
933 end
933 end
934
934
935 def label_tag_for(name, option_tags = nil, options = {})
935 def label_tag_for(name, option_tags = nil, options = {})
936 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
936 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
937 content_tag("label", label_text)
937 content_tag("label", label_text)
938 end
938 end
939
939
940 def labelled_tabular_form_for(*args, &proc)
940 def labelled_tabular_form_for(*args, &proc)
941 ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_tabular_form_for is deprecated and will be removed in Redmine 1.5. Use #labelled_form_for instead."
941 ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_tabular_form_for is deprecated and will be removed in Redmine 1.5. Use #labelled_form_for instead."
942 args << {} unless args.last.is_a?(Hash)
942 args << {} unless args.last.is_a?(Hash)
943 options = args.last
943 options = args.last
944 options[:html] ||= {}
944 options[:html] ||= {}
945 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
945 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
946 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
946 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
947 form_for(*args, &proc)
947 form_for(*args, &proc)
948 end
948 end
949
949
950 def labelled_form_for(*args, &proc)
950 def labelled_form_for(*args, &proc)
951 args << {} unless args.last.is_a?(Hash)
951 args << {} unless args.last.is_a?(Hash)
952 options = args.last
952 options = args.last
953 if args.first.is_a?(Symbol)
953 if args.first.is_a?(Symbol)
954 options.merge!(:as => args.shift)
954 options.merge!(:as => args.shift)
955 end
955 end
956 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
956 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
957 form_for(*args, &proc)
957 form_for(*args, &proc)
958 end
958 end
959
959
960 def labelled_fields_for(*args, &proc)
960 def labelled_fields_for(*args, &proc)
961 args << {} unless args.last.is_a?(Hash)
961 args << {} unless args.last.is_a?(Hash)
962 options = args.last
962 options = args.last
963 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
963 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
964 fields_for(*args, &proc)
964 fields_for(*args, &proc)
965 end
965 end
966
966
967 def labelled_remote_form_for(*args, &proc)
967 def labelled_remote_form_for(*args, &proc)
968 args << {} unless args.last.is_a?(Hash)
968 args << {} unless args.last.is_a?(Hash)
969 options = args.last
969 options = args.last
970 options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
970 options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
971 form_for(*args, &proc)
971 form_for(*args, &proc)
972 end
972 end
973
973
974 def error_messages_for(*objects)
974 def error_messages_for(*objects)
975 html = ""
975 html = ""
976 objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
976 objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
977 errors = objects.map {|o| o.errors.full_messages}.flatten
977 errors = objects.map {|o| o.errors.full_messages}.flatten
978 if errors.any?
978 if errors.any?
979 html << "<div id='errorExplanation'><ul>\n"
979 html << "<div id='errorExplanation'><ul>\n"
980 errors.each do |error|
980 errors.each do |error|
981 html << "<li>#{h error}</li>\n"
981 html << "<li>#{h error}</li>\n"
982 end
982 end
983 html << "</ul></div>\n"
983 html << "</ul></div>\n"
984 end
984 end
985 html.html_safe
985 html.html_safe
986 end
986 end
987
987
988 def delete_link(url, options={})
988 def delete_link(url, options={})
989 options = {
989 options = {
990 :method => :delete,
990 :method => :delete,
991 :data => {:confirm => l(:text_are_you_sure)},
991 :data => {:confirm => l(:text_are_you_sure)},
992 :class => 'icon icon-del'
992 :class => 'icon icon-del'
993 }.merge(options)
993 }.merge(options)
994
994
995 link_to l(:button_delete), url, options
995 link_to l(:button_delete), url, options
996 end
996 end
997
997
998 def preview_link(url, form, target='preview', options={})
999 content_tag 'a', l(:label_preview), {
1000 :href => "#",
1001 :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
1002 :accesskey => accesskey(:preview)
1003 }.merge(options)
1004 end
1005
998 def back_url_hidden_field_tag
1006 def back_url_hidden_field_tag
999 back_url = params[:back_url] || request.env['HTTP_REFERER']
1007 back_url = params[:back_url] || request.env['HTTP_REFERER']
1000 back_url = CGI.unescape(back_url.to_s)
1008 back_url = CGI.unescape(back_url.to_s)
1001 hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
1009 hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
1002 end
1010 end
1003
1011
1004 def check_all_links(form_name)
1012 def check_all_links(form_name)
1005 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
1013 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
1006 " | ".html_safe +
1014 " | ".html_safe +
1007 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
1015 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
1008 end
1016 end
1009
1017
1010 def progress_bar(pcts, options={})
1018 def progress_bar(pcts, options={})
1011 pcts = [pcts, pcts] unless pcts.is_a?(Array)
1019 pcts = [pcts, pcts] unless pcts.is_a?(Array)
1012 pcts = pcts.collect(&:round)
1020 pcts = pcts.collect(&:round)
1013 pcts[1] = pcts[1] - pcts[0]
1021 pcts[1] = pcts[1] - pcts[0]
1014 pcts << (100 - pcts[1] - pcts[0])
1022 pcts << (100 - pcts[1] - pcts[0])
1015 width = options[:width] || '100px;'
1023 width = options[:width] || '100px;'
1016 legend = options[:legend] || ''
1024 legend = options[:legend] || ''
1017 content_tag('table',
1025 content_tag('table',
1018 content_tag('tr',
1026 content_tag('tr',
1019 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
1027 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
1020 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
1028 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
1021 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
1029 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
1022 ), :class => 'progress', :style => "width: #{width};").html_safe +
1030 ), :class => 'progress', :style => "width: #{width};").html_safe +
1023 content_tag('p', legend, :class => 'pourcent').html_safe
1031 content_tag('p', legend, :class => 'pourcent').html_safe
1024 end
1032 end
1025
1033
1026 def checked_image(checked=true)
1034 def checked_image(checked=true)
1027 if checked
1035 if checked
1028 image_tag 'toggle_check.png'
1036 image_tag 'toggle_check.png'
1029 end
1037 end
1030 end
1038 end
1031
1039
1032 def context_menu(url)
1040 def context_menu(url)
1033 unless @context_menu_included
1041 unless @context_menu_included
1034 content_for :header_tags do
1042 content_for :header_tags do
1035 javascript_include_tag('context_menu') +
1043 javascript_include_tag('context_menu') +
1036 stylesheet_link_tag('context_menu')
1044 stylesheet_link_tag('context_menu')
1037 end
1045 end
1038 if l(:direction) == 'rtl'
1046 if l(:direction) == 'rtl'
1039 content_for :header_tags do
1047 content_for :header_tags do
1040 stylesheet_link_tag('context_menu_rtl')
1048 stylesheet_link_tag('context_menu_rtl')
1041 end
1049 end
1042 end
1050 end
1043 @context_menu_included = true
1051 @context_menu_included = true
1044 end
1052 end
1045 javascript_tag "new ContextMenu('#{ url_for(url) }')"
1053 javascript_tag "new ContextMenu('#{ url_for(url) }')"
1046 end
1054 end
1047
1055
1048 def calendar_for(field_id)
1056 def calendar_for(field_id)
1049 include_calendar_headers_tags
1057 include_calendar_headers_tags
1050 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
1058 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
1051 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
1059 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
1052 end
1060 end
1053
1061
1054 def include_calendar_headers_tags
1062 def include_calendar_headers_tags
1055 unless @calendar_headers_tags_included
1063 unless @calendar_headers_tags_included
1056 @calendar_headers_tags_included = true
1064 @calendar_headers_tags_included = true
1057 content_for :header_tags do
1065 content_for :header_tags do
1058 start_of_week = case Setting.start_of_week.to_i
1066 start_of_week = case Setting.start_of_week.to_i
1059 when 1
1067 when 1
1060 'Calendar._FD = 1;' # Monday
1068 'Calendar._FD = 1;' # Monday
1061 when 7
1069 when 7
1062 'Calendar._FD = 0;' # Sunday
1070 'Calendar._FD = 0;' # Sunday
1063 when 6
1071 when 6
1064 'Calendar._FD = 6;' # Saturday
1072 'Calendar._FD = 6;' # Saturday
1065 else
1073 else
1066 '' # use language
1074 '' # use language
1067 end
1075 end
1068
1076
1069 javascript_include_tag('calendar/calendar') +
1077 javascript_include_tag('calendar/calendar') +
1070 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
1078 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
1071 javascript_tag(start_of_week) +
1079 javascript_tag(start_of_week) +
1072 javascript_include_tag('calendar/calendar-setup') +
1080 javascript_include_tag('calendar/calendar-setup') +
1073 stylesheet_link_tag('calendar')
1081 stylesheet_link_tag('calendar')
1074 end
1082 end
1075 end
1083 end
1076 end
1084 end
1077
1085
1078 # Overrides Rails' stylesheet_link_tag with themes and plugins support.
1086 # Overrides Rails' stylesheet_link_tag with themes and plugins support.
1079 # Examples:
1087 # Examples:
1080 # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
1088 # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
1081 # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
1089 # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
1082 #
1090 #
1083 def stylesheet_link_tag(*sources)
1091 def stylesheet_link_tag(*sources)
1084 options = sources.last.is_a?(Hash) ? sources.pop : {}
1092 options = sources.last.is_a?(Hash) ? sources.pop : {}
1085 plugin = options.delete(:plugin)
1093 plugin = options.delete(:plugin)
1086 sources = sources.map do |source|
1094 sources = sources.map do |source|
1087 if plugin
1095 if plugin
1088 "/plugin_assets/#{plugin}/stylesheets/#{source}"
1096 "/plugin_assets/#{plugin}/stylesheets/#{source}"
1089 elsif current_theme && current_theme.stylesheets.include?(source)
1097 elsif current_theme && current_theme.stylesheets.include?(source)
1090 current_theme.stylesheet_path(source)
1098 current_theme.stylesheet_path(source)
1091 else
1099 else
1092 source
1100 source
1093 end
1101 end
1094 end
1102 end
1095 super sources, options
1103 super sources, options
1096 end
1104 end
1097
1105
1098 # Overrides Rails' image_tag with themes and plugins support.
1106 # Overrides Rails' image_tag with themes and plugins support.
1099 # Examples:
1107 # Examples:
1100 # image_tag('image.png') # => picks image.png from the current theme or defaults
1108 # image_tag('image.png') # => picks image.png from the current theme or defaults
1101 # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
1109 # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
1102 #
1110 #
1103 def image_tag(source, options={})
1111 def image_tag(source, options={})
1104 if plugin = options.delete(:plugin)
1112 if plugin = options.delete(:plugin)
1105 source = "/plugin_assets/#{plugin}/images/#{source}"
1113 source = "/plugin_assets/#{plugin}/images/#{source}"
1106 elsif current_theme && current_theme.images.include?(source)
1114 elsif current_theme && current_theme.images.include?(source)
1107 source = current_theme.image_path(source)
1115 source = current_theme.image_path(source)
1108 end
1116 end
1109 super source, options
1117 super source, options
1110 end
1118 end
1111
1119
1112 # Overrides Rails' javascript_include_tag with plugins support
1120 # Overrides Rails' javascript_include_tag with plugins support
1113 # Examples:
1121 # Examples:
1114 # javascript_include_tag('scripts') # => picks scripts.js from defaults
1122 # javascript_include_tag('scripts') # => picks scripts.js from defaults
1115 # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
1123 # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
1116 #
1124 #
1117 def javascript_include_tag(*sources)
1125 def javascript_include_tag(*sources)
1118 options = sources.last.is_a?(Hash) ? sources.pop : {}
1126 options = sources.last.is_a?(Hash) ? sources.pop : {}
1119 if plugin = options.delete(:plugin)
1127 if plugin = options.delete(:plugin)
1120 sources = sources.map do |source|
1128 sources = sources.map do |source|
1121 if plugin
1129 if plugin
1122 "/plugin_assets/#{plugin}/javascripts/#{source}"
1130 "/plugin_assets/#{plugin}/javascripts/#{source}"
1123 else
1131 else
1124 source
1132 source
1125 end
1133 end
1126 end
1134 end
1127 end
1135 end
1128 super sources, options
1136 super sources, options
1129 end
1137 end
1130
1138
1131 def content_for(name, content = nil, &block)
1139 def content_for(name, content = nil, &block)
1132 @has_content ||= {}
1140 @has_content ||= {}
1133 @has_content[name] = true
1141 @has_content[name] = true
1134 super(name, content, &block)
1142 super(name, content, &block)
1135 end
1143 end
1136
1144
1137 def has_content?(name)
1145 def has_content?(name)
1138 (@has_content && @has_content[name]) || false
1146 (@has_content && @has_content[name]) || false
1139 end
1147 end
1140
1148
1141 def sidebar_content?
1149 def sidebar_content?
1142 has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1150 has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1143 end
1151 end
1144
1152
1145 def view_layouts_base_sidebar_hook_response
1153 def view_layouts_base_sidebar_hook_response
1146 @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1154 @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1147 end
1155 end
1148
1156
1149 def email_delivery_enabled?
1157 def email_delivery_enabled?
1150 !!ActionMailer::Base.perform_deliveries
1158 !!ActionMailer::Base.perform_deliveries
1151 end
1159 end
1152
1160
1153 # Returns the avatar image tag for the given +user+ if avatars are enabled
1161 # Returns the avatar image tag for the given +user+ if avatars are enabled
1154 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
1162 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
1155 def avatar(user, options = { })
1163 def avatar(user, options = { })
1156 if Setting.gravatar_enabled?
1164 if Setting.gravatar_enabled?
1157 options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default})
1165 options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default})
1158 email = nil
1166 email = nil
1159 if user.respond_to?(:mail)
1167 if user.respond_to?(:mail)
1160 email = user.mail
1168 email = user.mail
1161 elsif user.to_s =~ %r{<(.+?)>}
1169 elsif user.to_s =~ %r{<(.+?)>}
1162 email = $1
1170 email = $1
1163 end
1171 end
1164 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1172 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1165 else
1173 else
1166 ''
1174 ''
1167 end
1175 end
1168 end
1176 end
1169
1177
1170 def sanitize_anchor_name(anchor)
1178 def sanitize_anchor_name(anchor)
1171 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1179 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1172 end
1180 end
1173
1181
1174 # Returns the javascript tags that are included in the html layout head
1182 # Returns the javascript tags that are included in the html layout head
1175 def javascript_heads
1183 def javascript_heads
1176 tags = javascript_include_tag('prototype', 'effects', 'dragdrop', 'controls', 'rails', 'application')
1184 tags = javascript_include_tag('prototype', 'effects', 'dragdrop', 'controls', 'rails', 'application')
1177 unless User.current.pref.warn_on_leaving_unsaved == '0'
1185 unless User.current.pref.warn_on_leaving_unsaved == '0'
1178 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
1186 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
1179 end
1187 end
1180 tags
1188 tags
1181 end
1189 end
1182
1190
1183 def favicon
1191 def favicon
1184 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
1192 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
1185 end
1193 end
1186
1194
1187 def robot_exclusion_tag
1195 def robot_exclusion_tag
1188 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1196 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1189 end
1197 end
1190
1198
1191 # Returns true if arg is expected in the API response
1199 # Returns true if arg is expected in the API response
1192 def include_in_api_response?(arg)
1200 def include_in_api_response?(arg)
1193 unless @included_in_api_response
1201 unless @included_in_api_response
1194 param = params[:include]
1202 param = params[:include]
1195 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1203 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1196 @included_in_api_response.collect!(&:strip)
1204 @included_in_api_response.collect!(&:strip)
1197 end
1205 end
1198 @included_in_api_response.include?(arg.to_s)
1206 @included_in_api_response.include?(arg.to_s)
1199 end
1207 end
1200
1208
1201 # Returns options or nil if nometa param or X-Redmine-Nometa header
1209 # Returns options or nil if nometa param or X-Redmine-Nometa header
1202 # was set in the request
1210 # was set in the request
1203 def api_meta(options)
1211 def api_meta(options)
1204 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1212 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1205 # compatibility mode for activeresource clients that raise
1213 # compatibility mode for activeresource clients that raise
1206 # an error when unserializing an array with attributes
1214 # an error when unserializing an array with attributes
1207 nil
1215 nil
1208 else
1216 else
1209 options
1217 options
1210 end
1218 end
1211 end
1219 end
1212
1220
1213 private
1221 private
1214
1222
1215 def wiki_helper
1223 def wiki_helper
1216 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1224 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1217 extend helper
1225 extend helper
1218 return self
1226 return self
1219 end
1227 end
1220
1228
1221 def link_to_content_update(text, url_params = {}, html_options = {})
1229 def link_to_content_update(text, url_params = {}, html_options = {})
1222 link_to(text, url_params, html_options)
1230 link_to(text, url_params, html_options)
1223 end
1231 end
1224 end
1232 end
@@ -1,73 +1,67
1 <%= breadcrumb link_to(l(:label_board_plural), project_boards_path(@project)) %>
1 <%= breadcrumb link_to(l(:label_board_plural), project_boards_path(@project)) %>
2
2
3 <div class="contextual">
3 <div class="contextual">
4 <%= link_to_if_authorized l(:label_message_new),
4 <%= link_to_if_authorized l(:label_message_new),
5 {:controller => 'messages', :action => 'new', :board_id => @board},
5 {:controller => 'messages', :action => 'new', :board_id => @board},
6 :class => 'icon icon-add',
6 :class => 'icon icon-add',
7 :onclick => 'Element.show("add-message"); Form.Element.focus("message_subject"); return false;' %>
7 :onclick => 'Element.show("add-message"); Form.Element.focus("message_subject"); return false;' %>
8 <%= watcher_tag(@board, User.current) %>
8 <%= watcher_tag(@board, User.current) %>
9 </div>
9 </div>
10
10
11 <div id="add-message" style="display:none;">
11 <div id="add-message" style="display:none;">
12 <% if authorize_for('messages', 'new') %>
12 <% if authorize_for('messages', 'new') %>
13 <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%= l(:label_message_new) %></h2>
13 <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%= l(:label_message_new) %></h2>
14 <%= form_for @message, :url => {:controller => 'messages', :action => 'new', :board_id => @board}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
14 <%= form_for @message, :url => {:controller => 'messages', :action => 'new', :board_id => @board}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
15 <%= render :partial => 'messages/form', :locals => {:f => f} %>
15 <%= render :partial => 'messages/form', :locals => {:f => f} %>
16 <p><%= submit_tag l(:button_create) %>
16 <p><%= submit_tag l(:button_create) %>
17 <%= link_to_remote l(:label_preview),
17 <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %> |
18 { :url => { :controller => 'messages', :action => 'preview', :board_id => @board },
19 :method => 'post',
20 :update => 'preview',
21 :with => "Form.serialize('message-form')",
22 :complete => "Element.scrollTo('preview')"
23 }, :accesskey => accesskey(:preview) %> |
24 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-message")' %></p>
18 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-message")' %></p>
25 <% end %>
19 <% end %>
26 <div id="preview" class="wiki"></div>
20 <div id="preview" class="wiki"></div>
27 <% end %>
21 <% end %>
28 </div>
22 </div>
29
23
30 <h2><%=h @board.name %></h2>
24 <h2><%=h @board.name %></h2>
31 <p class="subtitle"><%=h @board.description %></p>
25 <p class="subtitle"><%=h @board.description %></p>
32
26
33 <% if @topics.any? %>
27 <% if @topics.any? %>
34 <table class="list messages">
28 <table class="list messages">
35 <thead><tr>
29 <thead><tr>
36 <th><%= l(:field_subject) %></th>
30 <th><%= l(:field_subject) %></th>
37 <th><%= l(:field_author) %></th>
31 <th><%= l(:field_author) %></th>
38 <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %>
32 <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %>
39 <%= sort_header_tag('replies', :caption => l(:label_reply_plural)) %>
33 <%= sort_header_tag('replies', :caption => l(:label_reply_plural)) %>
40 <%= sort_header_tag('updated_on', :caption => l(:label_message_last)) %>
34 <%= sort_header_tag('updated_on', :caption => l(:label_message_last)) %>
41 </tr></thead>
35 </tr></thead>
42 <tbody>
36 <tbody>
43 <% @topics.each do |topic| %>
37 <% @topics.each do |topic| %>
44 <tr class="message <%= cycle 'odd', 'even' %> <%= topic.sticky? ? 'sticky' : '' %> <%= topic.locked? ? 'locked' : '' %>">
38 <tr class="message <%= cycle 'odd', 'even' %> <%= topic.sticky? ? 'sticky' : '' %> <%= topic.locked? ? 'locked' : '' %>">
45 <td class="subject"><%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic } %></td>
39 <td class="subject"><%= link_to h(topic.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => topic } %></td>
46 <td class="author" align="center"><%= link_to_user(topic.author) %></td>
40 <td class="author" align="center"><%= link_to_user(topic.author) %></td>
47 <td class="created_on" align="center"><%= format_time(topic.created_on) %></td>
41 <td class="created_on" align="center"><%= format_time(topic.created_on) %></td>
48 <td class="replies" align="center"><%= topic.replies_count %></td>
42 <td class="replies" align="center"><%= topic.replies_count %></td>
49 <td class="last_message">
43 <td class="last_message">
50 <% if topic.last_reply %>
44 <% if topic.last_reply %>
51 <%= authoring topic.last_reply.created_on, topic.last_reply.author %><br />
45 <%= authoring topic.last_reply.created_on, topic.last_reply.author %><br />
52 <%= link_to_message topic.last_reply %>
46 <%= link_to_message topic.last_reply %>
53 <% end %>
47 <% end %>
54 </td>
48 </td>
55 </tr>
49 </tr>
56 <% end %>
50 <% end %>
57 </tbody>
51 </tbody>
58 </table>
52 </table>
59 <p class="pagination"><%= pagination_links_full @topic_pages, @topic_count %></p>
53 <p class="pagination"><%= pagination_links_full @topic_pages, @topic_count %></p>
60 <% else %>
54 <% else %>
61 <p class="nodata"><%= l(:label_no_data) %></p>
55 <p class="nodata"><%= l(:label_no_data) %></p>
62 <% end %>
56 <% end %>
63
57
64 <% other_formats_links do |f| %>
58 <% other_formats_links do |f| %>
65 <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
59 <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
66 <% end %>
60 <% end %>
67
61
68 <% html_title @board.name %>
62 <% html_title @board.name %>
69
63
70 <% content_for :header_tags do %>
64 <% content_for :header_tags do %>
71 <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
65 <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
72 <%= stylesheet_link_tag 'scm' %>
66 <%= stylesheet_link_tag 'scm' %>
73 <% end %>
67 <% end %>
@@ -1,50 +1,44
1 <%= labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %>
1 <%= labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %>
2 <%= error_messages_for 'issue', 'time_entry' %>
2 <%= error_messages_for 'issue', 'time_entry' %>
3 <%= render :partial => 'conflict' if @conflict %>
3 <%= render :partial => 'conflict' if @conflict %>
4 <div class="box">
4 <div class="box">
5 <% if @edit_allowed || !@allowed_statuses.empty? %>
5 <% if @edit_allowed || !@allowed_statuses.empty? %>
6 <fieldset class="tabular"><legend><%= l(:label_change_properties) %></legend>
6 <fieldset class="tabular"><legend><%= l(:label_change_properties) %></legend>
7 <div id="all_attributes">
7 <div id="all_attributes">
8 <%= render :partial => 'form', :locals => {:f => f} %>
8 <%= render :partial => 'form', :locals => {:f => f} %>
9 </div>
9 </div>
10 </fieldset>
10 </fieldset>
11 <% end %>
11 <% end %>
12 <% if User.current.allowed_to?(:log_time, @project) %>
12 <% if User.current.allowed_to?(:log_time, @project) %>
13 <fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
13 <fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
14 <%= labelled_fields_for :time_entry, @time_entry do |time_entry| %>
14 <%= labelled_fields_for :time_entry, @time_entry do |time_entry| %>
15 <div class="splitcontentleft">
15 <div class="splitcontentleft">
16 <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p>
16 <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p>
17 </div>
17 </div>
18 <div class="splitcontentright">
18 <div class="splitcontentright">
19 <p><%= time_entry.select :activity_id, activity_collection_for_select_options %></p>
19 <p><%= time_entry.select :activity_id, activity_collection_for_select_options %></p>
20 </div>
20 </div>
21 <p><%= time_entry.text_field :comments, :size => 60 %></p>
21 <p><%= time_entry.text_field :comments, :size => 60 %></p>
22 <% @time_entry.custom_field_values.each do |value| %>
22 <% @time_entry.custom_field_values.each do |value| %>
23 <p><%= custom_field_tag_with_label :time_entry, value %></p>
23 <p><%= custom_field_tag_with_label :time_entry, value %></p>
24 <% end %>
24 <% end %>
25 <% end %>
25 <% end %>
26 </fieldset>
26 </fieldset>
27 <% end %>
27 <% end %>
28
28
29 <fieldset><legend><%= l(:field_notes) %></legend>
29 <fieldset><legend><%= l(:field_notes) %></legend>
30 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
30 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
31 <%= wikitoolbar_for 'notes' %>
31 <%= wikitoolbar_for 'notes' %>
32 <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
32 <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
33
33
34 <p><%=l(:label_attachment_plural)%><br /><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
34 <p><%=l(:label_attachment_plural)%><br /><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
35 </fieldset>
35 </fieldset>
36 </div>
36 </div>
37
37
38 <%= f.hidden_field :lock_version %>
38 <%= f.hidden_field :lock_version %>
39 <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %>
39 <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %>
40 <%= submit_tag l(:button_submit) %>
40 <%= submit_tag l(:button_submit) %>
41 <%= link_to_remote l(:label_preview),
41 <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %>
42 { :url => preview_edit_issue_path(:project_id => @project, :id => @issue),
43 :method => 'post',
44 :update => 'preview',
45 :with => 'Form.serialize("issue-form")',
46 :complete => "Element.scrollTo('preview')"
47 }, :accesskey => accesskey(:preview) %>
48 <% end %>
42 <% end %>
49
43
50 <div id="preview" class="wiki"></div>
44 <div id="preview" class="wiki"></div>
@@ -1,55 +1,49
1 <h2><%=l(:label_issue_new)%></h2>
1 <h2><%=l(:label_issue_new)%></h2>
2
2
3 <%= call_hook(:view_issues_new_top, {:issue => @issue}) %>
3 <%= call_hook(:view_issues_new_top, {:issue => @issue}) %>
4
4
5 <%= labelled_form_for @issue, :url => project_issues_path(@project),
5 <%= labelled_form_for @issue, :url => project_issues_path(@project),
6 :html => {:id => 'issue-form', :multipart => true} do |f| %>
6 :html => {:id => 'issue-form', :multipart => true} do |f| %>
7 <%= error_messages_for 'issue' %>
7 <%= error_messages_for 'issue' %>
8 <%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
8 <%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
9 <div class="box tabular">
9 <div class="box tabular">
10 <div id="all_attributes">
10 <div id="all_attributes">
11 <%= render :partial => 'issues/form', :locals => {:f => f} %>
11 <%= render :partial => 'issues/form', :locals => {:f => f} %>
12 </div>
12 </div>
13
13
14 <% if @copy_from && @copy_from.attachments.any? %>
14 <% if @copy_from && @copy_from.attachments.any? %>
15 <p>
15 <p>
16 <label for="copy_attachments"><%= l(:label_copy_attachments) %></label>
16 <label for="copy_attachments"><%= l(:label_copy_attachments) %></label>
17 <%= check_box_tag 'copy_attachments', '1', @copy_attachments %>
17 <%= check_box_tag 'copy_attachments', '1', @copy_attachments %>
18 </p>
18 </p>
19 <% end %>
19 <% end %>
20
20
21 <p id="attachments_form"><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
21 <p id="attachments_form"><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
22
22
23 <% if @issue.safe_attribute? 'watcher_user_ids' -%>
23 <% if @issue.safe_attribute? 'watcher_user_ids' -%>
24 <p id="watchers_form"><label><%= l(:label_issue_watchers) %></label>
24 <p id="watchers_form"><label><%= l(:label_issue_watchers) %></label>
25 <span id="watchers_inputs">
25 <span id="watchers_inputs">
26 <%= watchers_checkboxes(@issue, @available_watchers) %>
26 <%= watchers_checkboxes(@issue, @available_watchers) %>
27 </span>
27 </span>
28 <span class="search_for_watchers">
28 <span class="search_for_watchers">
29 <%= link_to_remote l(:label_search_for_watchers),
29 <%= link_to_remote l(:label_search_for_watchers),
30 :url => {:controller => 'watchers', :action => 'new', :project_id => @issue.project},
30 :url => {:controller => 'watchers', :action => 'new', :project_id => @issue.project},
31 :method => 'get' %>
31 :method => 'get' %>
32 </span>
32 </span>
33 </p>
33 </p>
34 <% end %>
34 <% end %>
35 </div>
35 </div>
36
36
37 <%= submit_tag l(:button_create) %>
37 <%= submit_tag l(:button_create) %>
38 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
38 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
39 <%= link_to_remote l(:label_preview),
39 <%= preview_link preview_new_issue_path(:project_id => @project), 'issue-form' %>
40 { :url => preview_new_issue_path(:project_id => @project),
41 :method => 'post',
42 :update => 'preview',
43 :with => "Form.serialize('issue-form')",
44 :complete => "Element.scrollTo('preview')"
45 }, :accesskey => accesskey(:preview) %>
46
40
47 <%= javascript_tag "Form.Element.focus('issue_subject');" %>
41 <%= javascript_tag "Form.Element.focus('issue_subject');" %>
48 <% end %>
42 <% end %>
49
43
50 <div id="preview" class="wiki"></div>
44 <div id="preview" class="wiki"></div>
51
45
52 <% content_for :header_tags do %>
46 <% content_for :header_tags do %>
53 <%= stylesheet_link_tag 'scm' %>
47 <%= stylesheet_link_tag 'scm' %>
54 <%= robot_exclusion_tag %>
48 <%= robot_exclusion_tag %>
55 <% end %>
49 <% end %>
@@ -1,22 +1,17
1 <%= form_remote_tag(:url => {}, :html => { :id => "journal-#{@journal.id}-form" }) do %>
1 <%= form_remote_tag(:url => {}, :html => { :id => "journal-#{@journal.id}-form" }) do %>
2 <%= label_tag "notes", l(:description_notes), :class => "hidden-for-sighted" %>
2 <%= label_tag "notes", l(:description_notes), :class => "hidden-for-sighted" %>
3 <%= text_area_tag :notes, @journal.notes,
3 <%= text_area_tag :notes, @journal.notes,
4 :id => "journal_#{@journal.id}_notes",
4 :id => "journal_#{@journal.id}_notes",
5 :class => 'wiki-edit',
5 :class => 'wiki-edit',
6 :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
6 :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min) %>
7 <%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
7 <%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
8 <p><%= submit_tag l(:button_save) %>
8 <p><%= submit_tag l(:button_save) %>
9 <%= link_to_remote l(:label_preview),
9 <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue),
10 { :url => preview_edit_issue_path(:project_id => @project, :id => @journal.issue),
10 "journal-#{@journal.id}-form",
11 :method => 'post',
11 "journal_#{@journal.id}_preview" %> |
12 :update => "journal_#{@journal.id}_preview",
13 :with => "Form.serialize('journal-#{@journal.id}-form')",
14 :complete => "Element.scrollTo('journal_#{@journal.id}_preview')"
15 }, :accesskey => accesskey(:preview) %>
16 |
17 <%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +
12 <%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +
18 "Element.show('journal-#{@journal.id}-notes'); return false;" %></p>
13 "Element.show('journal-#{@journal.id}-notes'); return false;" %></p>
19
14
20 <div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
15 <div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
21 <% end %>
16 <% end %>
22 <%= wikitoolbar_for "journal_#{@journal.id}_notes" %>
17 <%= wikitoolbar_for "journal_#{@journal.id}_notes" %>
@@ -1,28 +1,21
1 <h2><%= link_to h(@board.name), :controller => 'boards',
1 <h2><%= link_to h(@board.name), :controller => 'boards',
2 :action => 'show', :project_id => @project,
2 :action => 'show', :project_id => @project,
3 :id => @board %> &#187; <%= h @message.subject %></h2>
3 :id => @board %> &#187; <%= h @message.subject %></h2>
4
4
5 <%= form_for @message, {
5 <%= form_for @message, {
6 :as => :message,
6 :as => :message,
7 :url => {:action => 'edit'},
7 :url => {:action => 'edit'},
8 :html => {:multipart => true,
8 :html => {:multipart => true,
9 :id => 'message-form',
9 :id => 'message-form',
10 :method => :post}
10 :method => :post}
11 } do |f| %>
11 } do |f| %>
12 <%= render :partial => 'form',
12 <%= render :partial => 'form',
13 :locals => {:f => f, :replying => !@message.parent.nil?} %>
13 :locals => {:f => f, :replying => !@message.parent.nil?} %>
14 <%= submit_tag l(:button_save) %>
14 <%= submit_tag l(:button_save) %>
15 <%= link_to_remote l(:label_preview),
15 <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board, :id => @message}, 'message-form') %>
16 { :url => { :controller => 'messages',
17 :action => 'preview', :board_id => @board, :id => @message },
18 :method => 'post',
19 :update => 'preview',
20 :with => "Form.serialize('message-form')",
21 :complete => "Element.scrollTo('preview')"
22 }, :accesskey => accesskey(:preview) %>
23 <% end %>
16 <% end %>
24 <div id="preview" class="wiki"></div>
17 <div id="preview" class="wiki"></div>
25
18
26 <% content_for :header_tags do %>
19 <% content_for :header_tags do %>
27 <%= stylesheet_link_tag 'scm' %>
20 <%= stylesheet_link_tag 'scm' %>
28 <% end %>
21 <% end %>
@@ -1,15 +1,9
1 <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%= l(:label_message_new) %></h2>
1 <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%= l(:label_message_new) %></h2>
2
2
3 <%= form_for @message, :url => {:action => 'new'}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
3 <%= form_for @message, :url => {:action => 'new'}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
5 <%= submit_tag l(:button_create) %>
5 <%= submit_tag l(:button_create) %>
6 <%= link_to_remote l(:label_preview),
6 <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %>
7 { :url => { :controller => 'messages', :action => 'preview', :board_id => @board },
8 :method => 'post',
9 :update => 'preview',
10 :with => "Form.serialize('message-form')",
11 :complete => "Element.scrollTo('preview')"
12 }, :accesskey => accesskey(:preview) %>
13 <% end %>
7 <% end %>
14
8
15 <div id="preview" class="wiki"></div>
9 <div id="preview" class="wiki"></div>
@@ -1,94 +1,88
1 <%= breadcrumb link_to(l(:label_board_plural), project_boards_path(@project)),
1 <%= breadcrumb link_to(l(:label_board_plural), project_boards_path(@project)),
2 link_to(h(@board.name), project_board_path(@project, @board)) %>
2 link_to(h(@board.name), project_board_path(@project, @board)) %>
3
3
4 <div class="contextual">
4 <div class="contextual">
5 <%= watcher_tag(@topic, User.current) %>
5 <%= watcher_tag(@topic, User.current) %>
6 <%= link_to_remote_if_authorized(
6 <%= link_to_remote_if_authorized(
7 l(:button_quote),
7 l(:button_quote),
8 { :url => {:action => 'quote', :id => @topic} },
8 { :url => {:action => 'quote', :id => @topic} },
9 :class => 'icon icon-comment'
9 :class => 'icon icon-comment'
10 ) unless @topic.locked? %>
10 ) unless @topic.locked? %>
11 <%= link_to(
11 <%= link_to(
12 l(:button_edit),
12 l(:button_edit),
13 {:action => 'edit', :id => @topic},
13 {:action => 'edit', :id => @topic},
14 :class => 'icon icon-edit'
14 :class => 'icon icon-edit'
15 ) if @message.editable_by?(User.current) %>
15 ) if @message.editable_by?(User.current) %>
16 <%= link_to(
16 <%= link_to(
17 l(:button_delete),
17 l(:button_delete),
18 {:action => 'destroy', :id => @topic},
18 {:action => 'destroy', :id => @topic},
19 :method => :post,
19 :method => :post,
20 :data => {:confirm => l(:text_are_you_sure)},
20 :data => {:confirm => l(:text_are_you_sure)},
21 :class => 'icon icon-del'
21 :class => 'icon icon-del'
22 ) if @message.destroyable_by?(User.current) %>
22 ) if @message.destroyable_by?(User.current) %>
23 </div>
23 </div>
24
24
25 <h2><%= avatar(@topic.author, :size => "24") %><%=h @topic.subject %></h2>
25 <h2><%= avatar(@topic.author, :size => "24") %><%=h @topic.subject %></h2>
26
26
27 <div class="message">
27 <div class="message">
28 <p><span class="author"><%= authoring @topic.created_on, @topic.author %></span></p>
28 <p><span class="author"><%= authoring @topic.created_on, @topic.author %></span></p>
29 <div class="wiki">
29 <div class="wiki">
30 <%= textilizable(@topic, :content) %>
30 <%= textilizable(@topic, :content) %>
31 </div>
31 </div>
32 <%= link_to_attachments @topic, :author => false %>
32 <%= link_to_attachments @topic, :author => false %>
33 </div>
33 </div>
34 <br />
34 <br />
35
35
36 <% unless @replies.empty? %>
36 <% unless @replies.empty? %>
37 <h3 class="comments"><%= l(:label_reply_plural) %> (<%= @reply_count %>)</h3>
37 <h3 class="comments"><%= l(:label_reply_plural) %> (<%= @reply_count %>)</h3>
38 <% @replies.each do |message| %>
38 <% @replies.each do |message| %>
39 <div class="message reply" id="<%= "message-#{message.id}" %>">
39 <div class="message reply" id="<%= "message-#{message.id}" %>">
40 <div class="contextual">
40 <div class="contextual">
41 <%= link_to_remote_if_authorized(
41 <%= link_to_remote_if_authorized(
42 image_tag('comment.png'),
42 image_tag('comment.png'),
43 { :url => {:action => 'quote', :id => message} },
43 { :url => {:action => 'quote', :id => message} },
44 :title => l(:button_quote)
44 :title => l(:button_quote)
45 ) unless @topic.locked? %>
45 ) unless @topic.locked? %>
46 <%= link_to(
46 <%= link_to(
47 image_tag('edit.png'),
47 image_tag('edit.png'),
48 {:action => 'edit', :id => message},
48 {:action => 'edit', :id => message},
49 :title => l(:button_edit)
49 :title => l(:button_edit)
50 ) if message.editable_by?(User.current) %>
50 ) if message.editable_by?(User.current) %>
51 <%= link_to(
51 <%= link_to(
52 image_tag('delete.png'),
52 image_tag('delete.png'),
53 {:action => 'destroy', :id => message},
53 {:action => 'destroy', :id => message},
54 :method => :post,
54 :method => :post,
55 :data => {:confirm => l(:text_are_you_sure)},
55 :data => {:confirm => l(:text_are_you_sure)},
56 :title => l(:button_delete)
56 :title => l(:button_delete)
57 ) if message.destroyable_by?(User.current) %>
57 ) if message.destroyable_by?(User.current) %>
58 </div>
58 </div>
59 <h4>
59 <h4>
60 <%= avatar(message.author, :size => "24") %>
60 <%= avatar(message.author, :size => "24") %>
61 <%= link_to h(message.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => @topic, :r => message, :anchor => "message-#{message.id}" } %>
61 <%= link_to h(message.subject), { :controller => 'messages', :action => 'show', :board_id => @board, :id => @topic, :r => message, :anchor => "message-#{message.id}" } %>
62 -
62 -
63 <%= authoring message.created_on, message.author %>
63 <%= authoring message.created_on, message.author %>
64 </h4>
64 </h4>
65 <div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
65 <div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
66 <%= link_to_attachments message, :author => false %>
66 <%= link_to_attachments message, :author => false %>
67 </div>
67 </div>
68 <% end %>
68 <% end %>
69 <p class="pagination"><%= pagination_links_full @reply_pages, @reply_count, :per_page_links => false %></p>
69 <p class="pagination"><%= pagination_links_full @reply_pages, @reply_count, :per_page_links => false %></p>
70 <% end %>
70 <% end %>
71
71
72 <% if !@topic.locked? && authorize_for('messages', 'reply') %>
72 <% if !@topic.locked? && authorize_for('messages', 'reply') %>
73 <p><%= toggle_link l(:button_reply), "reply", :focus => 'message_content' %></p>
73 <p><%= toggle_link l(:button_reply), "reply", :focus => 'message_content' %></p>
74 <div id="reply" style="display:none;">
74 <div id="reply" style="display:none;">
75 <%= form_for @reply, :as => :reply, :url => {:action => 'reply', :id => @topic}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
75 <%= form_for @reply, :as => :reply, :url => {:action => 'reply', :id => @topic}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
76 <%= render :partial => 'form', :locals => {:f => f, :replying => true} %>
76 <%= render :partial => 'form', :locals => {:f => f, :replying => true} %>
77 <%= submit_tag l(:button_submit) %>
77 <%= submit_tag l(:button_submit) %>
78 <%= link_to_remote l(:label_preview),
78 <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %>
79 { :url => { :controller => 'messages', :action => 'preview', :board_id => @board },
80 :method => 'post',
81 :update => 'preview',
82 :with => "Form.serialize('message-form')",
83 :complete => "Element.scrollTo('preview')"
84 }, :accesskey => accesskey(:preview) %>
85 <% end %>
79 <% end %>
86 <div id="preview" class="wiki"></div>
80 <div id="preview" class="wiki"></div>
87 </div>
81 </div>
88 <% end %>
82 <% end %>
89
83
90 <% content_for :header_tags do %>
84 <% content_for :header_tags do %>
91 <%= stylesheet_link_tag 'scm' %>
85 <%= stylesheet_link_tag 'scm' %>
92 <% end %>
86 <% end %>
93
87
94 <% html_title @topic.subject %>
88 <% html_title @topic.subject %>
@@ -1,17 +1,12
1 <h2><%=l(:label_news)%></h2>
1 <h2><%=l(:label_news)%></h2>
2
2
3 <%= labelled_form_for @news, :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
3 <%= labelled_form_for @news, :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
4 <%= render :partial => 'form', :locals => { :f => f } %>
4 <%= render :partial => 'form', :locals => { :f => f } %>
5 <%= submit_tag l(:button_save) %>
5 <%= submit_tag l(:button_save) %>
6 <%= link_to_remote l(:label_preview),
6 <%= preview_link preview_news_path(:project_id => @project), 'news-form' %>
7 { :url => preview_news_path(:project_id => @project),
8 :method => 'get',
9 :update => 'preview',
10 :with => "Form.serialize('news-form')"
11 }, :accesskey => accesskey(:preview) %>
12 <% end %>
7 <% end %>
13 <div id="preview" class="wiki"></div>
8 <div id="preview" class="wiki"></div>
14
9
15 <% content_for :header_tags do %>
10 <% content_for :header_tags do %>
16 <%= stylesheet_link_tag 'scm' %>
11 <%= stylesheet_link_tag 'scm' %>
17 <% end %>
12 <% end %>
@@ -1,51 +1,46
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to(l(:label_news_new),
2 <%= link_to(l(:label_news_new),
3 new_project_news_path(@project),
3 new_project_news_path(@project),
4 :class => 'icon icon-add',
4 :class => 'icon icon-add',
5 :onclick => 'Element.show("add-news"); Form.Element.focus("news_title"); return false;') if @project && User.current.allowed_to?(:manage_news, @project) %>
5 :onclick => 'Element.show("add-news"); Form.Element.focus("news_title"); return false;') if @project && User.current.allowed_to?(:manage_news, @project) %>
6 </div>
6 </div>
7
7
8 <div id="add-news" style="display:none;">
8 <div id="add-news" style="display:none;">
9 <h2><%=l(:label_news_new)%></h2>
9 <h2><%=l(:label_news_new)%></h2>
10 <%= labelled_form_for @news, :url => project_news_index_path(@project),
10 <%= labelled_form_for @news, :url => project_news_index_path(@project),
11 :html => { :id => 'news-form', :multipart => true } do |f| %>
11 :html => { :id => 'news-form', :multipart => true } do |f| %>
12 <%= render :partial => 'news/form', :locals => { :f => f } %>
12 <%= render :partial => 'news/form', :locals => { :f => f } %>
13 <%= submit_tag l(:button_create) %>
13 <%= submit_tag l(:button_create) %>
14 <%= link_to_remote l(:label_preview),
14 <%= preview_link preview_news_path(:project_id => @project), 'news-form' %> |
15 { :url => preview_news_path(:project_id => @project),
16 :method => 'get',
17 :update => 'preview',
18 :with => "Form.serialize('news-form')"
19 }, :accesskey => accesskey(:preview) %> |
20 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-news")' %>
15 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("add-news")' %>
21 <% end if @project %>
16 <% end if @project %>
22 <div id="preview" class="wiki"></div>
17 <div id="preview" class="wiki"></div>
23 </div>
18 </div>
24
19
25 <h2><%=l(:label_news_plural)%></h2>
20 <h2><%=l(:label_news_plural)%></h2>
26
21
27 <% if @newss.empty? %>
22 <% if @newss.empty? %>
28 <p class="nodata"><%= l(:label_no_data) %></p>
23 <p class="nodata"><%= l(:label_no_data) %></p>
29 <% else %>
24 <% else %>
30 <% @newss.each do |news| %>
25 <% @newss.each do |news| %>
31 <h3><%= avatar(news.author, :size => "24") %><%= link_to_project(news.project) + ': ' unless news.project == @project %>
26 <h3><%= avatar(news.author, :size => "24") %><%= link_to_project(news.project) + ': ' unless news.project == @project %>
32 <%= link_to h(news.title), news_path(news) %>
27 <%= link_to h(news.title), news_path(news) %>
33 <%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %></h3>
28 <%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %></h3>
34 <p class="author"><%= authoring news.created_on, news.author %></p>
29 <p class="author"><%= authoring news.created_on, news.author %></p>
35 <div class="wiki">
30 <div class="wiki">
36 <%= textilizable(news, :description) %>
31 <%= textilizable(news, :description) %>
37 </div>
32 </div>
38 <% end %>
33 <% end %>
39 <% end %>
34 <% end %>
40 <p class="pagination"><%= pagination_links_full @news_pages %></p>
35 <p class="pagination"><%= pagination_links_full @news_pages %></p>
41
36
42 <% other_formats_links do |f| %>
37 <% other_formats_links do |f| %>
43 <%= f.link_to 'Atom', :url => {:project_id => @project, :key => User.current.rss_key} %>
38 <%= f.link_to 'Atom', :url => {:project_id => @project, :key => User.current.rss_key} %>
44 <% end %>
39 <% end %>
45
40
46 <% content_for :header_tags do %>
41 <% content_for :header_tags do %>
47 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
42 <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :page => nil, :key => User.current.rss_key})) %>
48 <%= stylesheet_link_tag 'scm' %>
43 <%= stylesheet_link_tag 'scm' %>
49 <% end %>
44 <% end %>
50
45
51 <% html_title(l(:label_news_plural)) -%>
46 <% html_title(l(:label_news_plural)) -%>
@@ -1,14 +1,9
1 <h2><%=l(:label_news_new)%></h2>
1 <h2><%=l(:label_news_new)%></h2>
2
2
3 <%= labelled_form_for @news, :url => project_news_index_path(@project),
3 <%= labelled_form_for @news, :url => project_news_index_path(@project),
4 :html => { :id => 'news-form', :multipart => true } do |f| %>
4 :html => { :id => 'news-form', :multipart => true } do |f| %>
5 <%= render :partial => 'news/form', :locals => { :f => f } %>
5 <%= render :partial => 'news/form', :locals => { :f => f } %>
6 <%= submit_tag l(:button_create) %>
6 <%= submit_tag l(:button_create) %>
7 <%= link_to_remote l(:label_preview),
7 <%= preview_link preview_news_path(:project_id => @project), 'news-form' %>
8 { :url => preview_news_path(:project_id => @project),
9 :method => 'get',
10 :update => 'preview',
11 :with => "Form.serialize('news-form')"
12 }, :accesskey => accesskey(:preview) %>
13 <% end %>
8 <% end %>
14 <div id="preview" class="wiki"></div>
9 <div id="preview" class="wiki"></div>
@@ -1,67 +1,62
1 <div class="contextual">
1 <div class="contextual">
2 <%= watcher_tag(@news, User.current) %>
2 <%= watcher_tag(@news, User.current) %>
3 <%= link_to(l(:button_edit),
3 <%= link_to(l(:button_edit),
4 edit_news_path(@news),
4 edit_news_path(@news),
5 :class => 'icon icon-edit',
5 :class => 'icon icon-edit',
6 :accesskey => accesskey(:edit),
6 :accesskey => accesskey(:edit),
7 :onclick => 'Element.show("edit-news"); return false;') if User.current.allowed_to?(:manage_news, @project) %>
7 :onclick => 'Element.show("edit-news"); return false;') if User.current.allowed_to?(:manage_news, @project) %>
8 <%= delete_link news_path(@news) if User.current.allowed_to?(:manage_news, @project) %>
8 <%= delete_link news_path(@news) if User.current.allowed_to?(:manage_news, @project) %>
9 </div>
9 </div>
10
10
11 <h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2>
11 <h2><%= avatar(@news.author, :size => "24") %><%=h @news.title %></h2>
12
12
13 <% if authorize_for('news', 'edit') %>
13 <% if authorize_for('news', 'edit') %>
14 <div id="edit-news" style="display:none;">
14 <div id="edit-news" style="display:none;">
15 <%= labelled_form_for :news, @news, :url => news_path(@news),
15 <%= labelled_form_for :news, @news, :url => news_path(@news),
16 :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
16 :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
17 <%= render :partial => 'form', :locals => { :f => f } %>
17 <%= render :partial => 'form', :locals => { :f => f } %>
18 <%= submit_tag l(:button_save) %>
18 <%= submit_tag l(:button_save) %>
19 <%= link_to_remote l(:label_preview),
19 <%= preview_link preview_news_path(:project_id => @project), 'news-form' %> |
20 { :url => preview_news_path(:project_id => @project),
21 :method => 'get',
22 :update => 'preview',
23 :with => "Form.serialize('news-form')"
24 }, :accesskey => accesskey(:preview) %> |
25 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("edit-news"); return false;' %>
20 <%= link_to l(:button_cancel), "#", :onclick => 'Element.hide("edit-news"); return false;' %>
26 <% end %>
21 <% end %>
27 <div id="preview" class="wiki"></div>
22 <div id="preview" class="wiki"></div>
28 </div>
23 </div>
29 <% end %>
24 <% end %>
30
25
31 <p><% unless @news.summary.blank? %><em><%=h @news.summary %></em><br /><% end %>
26 <p><% unless @news.summary.blank? %><em><%=h @news.summary %></em><br /><% end %>
32 <span class="author"><%= authoring @news.created_on, @news.author %></span></p>
27 <span class="author"><%= authoring @news.created_on, @news.author %></span></p>
33 <div class="wiki">
28 <div class="wiki">
34 <%= textilizable(@news, :description) %>
29 <%= textilizable(@news, :description) %>
35 </div>
30 </div>
36 <%= link_to_attachments @news %>
31 <%= link_to_attachments @news %>
37 <br />
32 <br />
38
33
39 <div id="comments" style="margin-bottom:16px;">
34 <div id="comments" style="margin-bottom:16px;">
40 <h3 class="comments"><%= l(:label_comment_plural) %></h3>
35 <h3 class="comments"><%= l(:label_comment_plural) %></h3>
41 <% @comments.each do |comment| %>
36 <% @comments.each do |comment| %>
42 <% next if comment.new_record? %>
37 <% next if comment.new_record? %>
43 <div class="contextual">
38 <div class="contextual">
44 <%= link_to_if_authorized image_tag('delete.png'), {:controller => 'comments', :action => 'destroy', :id => @news, :comment_id => comment},
39 <%= link_to_if_authorized image_tag('delete.png'), {:controller => 'comments', :action => 'destroy', :id => @news, :comment_id => comment},
45 :data => {:confirm => l(:text_are_you_sure)}, :method => :delete, :title => l(:button_delete) %>
40 :data => {:confirm => l(:text_are_you_sure)}, :method => :delete, :title => l(:button_delete) %>
46 </div>
41 </div>
47 <h4><%= avatar(comment.author, :size => "24") %><%= authoring comment.created_on, comment.author %></h4>
42 <h4><%= avatar(comment.author, :size => "24") %><%= authoring comment.created_on, comment.author %></h4>
48 <%= textilizable(comment.comments) %>
43 <%= textilizable(comment.comments) %>
49 <% end if @comments.any? %>
44 <% end if @comments.any? %>
50 </div>
45 </div>
51
46
52 <% if @news.commentable? %>
47 <% if @news.commentable? %>
53 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
48 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
54 <%= form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
49 <%= form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
55 <div class="box">
50 <div class="box">
56 <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
51 <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
57 <%= wikitoolbar_for 'comment_comments' %>
52 <%= wikitoolbar_for 'comment_comments' %>
58 </div>
53 </div>
59 <p><%= submit_tag l(:button_add) %></p>
54 <p><%= submit_tag l(:button_add) %></p>
60 <% end %>
55 <% end %>
61 <% end %>
56 <% end %>
62
57
63 <% html_title @news.title -%>
58 <% html_title @news.title -%>
64
59
65 <% content_for :header_tags do %>
60 <% content_for :header_tags do %>
66 <%= stylesheet_link_tag 'scm' %>
61 <%= stylesheet_link_tag 'scm' %>
67 <% end %>
62 <% end %>
@@ -1,54 +1,47
1 <%= wiki_page_breadcrumb(@page) %>
1 <%= wiki_page_breadcrumb(@page) %>
2
2
3 <h2><%= h @page.pretty_title %></h2>
3 <h2><%= h @page.pretty_title %></h2>
4
4
5 <%= form_for @content, :as => :content,
5 <%= form_for @content, :as => :content,
6 :url => {:action => 'update', :id => @page.title},
6 :url => {:action => 'update', :id => @page.title},
7 :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %>
7 :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %>
8 <%= f.hidden_field :version %>
8 <%= f.hidden_field :version %>
9 <% if @section %>
9 <% if @section %>
10 <%= hidden_field_tag 'section', @section %>
10 <%= hidden_field_tag 'section', @section %>
11 <%= hidden_field_tag 'section_hash', @section_hash %>
11 <%= hidden_field_tag 'section_hash', @section_hash %>
12 <% end %>
12 <% end %>
13 <%= error_messages_for 'content' %>
13 <%= error_messages_for 'content' %>
14
14
15 <div class="box tabular">
15 <div class="box tabular">
16 <%= text_area_tag 'content[text]', @text, :cols => 100, :rows => 25,
16 <%= text_area_tag 'content[text]', @text, :cols => 100, :rows => 25,
17 :class => 'wiki-edit', :accesskey => accesskey(:edit) %>
17 :class => 'wiki-edit', :accesskey => accesskey(:edit) %>
18
18
19 <% if @page.safe_attribute_names.include?('parent_id') && @wiki.pages.any? %>
19 <% if @page.safe_attribute_names.include?('parent_id') && @wiki.pages.any? %>
20 <%= fields_for @page do |fp| %>
20 <%= fields_for @page do |fp| %>
21 <p>
21 <p>
22 <label><%= l(:field_parent_title) %></label>
22 <label><%= l(:field_parent_title) %></label>
23 <%= fp.select :parent_id,
23 <%= fp.select :parent_id,
24 content_tag('option', '', :value => '') +
24 content_tag('option', '', :value => '') +
25 wiki_page_options_for_select(@wiki.pages.all(:include => :parent) -
25 wiki_page_options_for_select(@wiki.pages.all(:include => :parent) -
26 @page.self_and_descendants, @page.parent) %>
26 @page.self_and_descendants, @page.parent) %>
27 </p>
27 </p>
28 <% end %>
28 <% end %>
29 <% end %>
29 <% end %>
30
30
31 <p><label><%= l(:field_comments) %></label><%= f.text_field :comments, :size => 120 %></p>
31 <p><label><%= l(:field_comments) %></label><%= f.text_field :comments, :size => 120 %></p>
32 <p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
32 <p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
33 </div>
33 </div>
34
34
35 <p><%= submit_tag l(:button_save) %>
35 <p><%= submit_tag l(:button_save) %>
36 <%= link_to_remote l(:label_preview),
36 <%= preview_link({:controller => 'wiki', :action => 'preview', :project_id => @project, :id => @page.title }, 'wiki_form') %></p>
37 { :url => { :controller => 'wiki', :action => 'preview',
38 :project_id => @project, :id => @page.title },
39 :method => :post,
40 :update => 'preview',
41 :with => "Form.serialize('wiki_form')",
42 :complete => "Element.scrollTo('preview')"
43 }, :accesskey => accesskey(:preview) %></p>
44 <%= wikitoolbar_for 'content_text' %>
37 <%= wikitoolbar_for 'content_text' %>
45 <% end %>
38 <% end %>
46
39
47 <div id="preview" class="wiki"></div>
40 <div id="preview" class="wiki"></div>
48
41
49 <% content_for :header_tags do %>
42 <% content_for :header_tags do %>
50 <%= stylesheet_link_tag 'scm' %>
43 <%= stylesheet_link_tag 'scm' %>
51 <%= robot_exclusion_tag %>
44 <%= robot_exclusion_tag %>
52 <% end %>
45 <% end %>
53
46
54 <% html_title @page.pretty_title %>
47 <% html_title @page.pretty_title %>
@@ -1,538 +1,548
1 /* Redmine - project management software
1 /* Redmine - project management software
2 Copyright (C) 2006-2012 Jean-Philippe Lang */
2 Copyright (C) 2006-2012 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 toggleCheckboxesBySelector(selector) {
13 function toggleCheckboxesBySelector(selector) {
14 boxes = $$(selector);
14 boxes = $$(selector);
15 var all_checked = true;
15 var all_checked = true;
16 for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } }
16 for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } }
17 for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
17 for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
18 }
18 }
19
19
20 function setCheckboxesBySelector(checked, selector) {
20 function setCheckboxesBySelector(checked, selector) {
21 var boxes = $$(selector);
21 var boxes = $$(selector);
22 boxes.each(function(ele) {
22 boxes.each(function(ele) {
23 ele.checked = checked;
23 ele.checked = checked;
24 });
24 });
25 }
25 }
26
26
27 function showAndScrollTo(id, focus) {
27 function showAndScrollTo(id, focus) {
28 Element.show(id);
28 Element.show(id);
29 if (focus!=null) { Form.Element.focus(focus); }
29 if (focus!=null) { Form.Element.focus(focus); }
30 Element.scrollTo(id);
30 Element.scrollTo(id);
31 }
31 }
32
32
33 function toggleRowGroup(el) {
33 function toggleRowGroup(el) {
34 var tr = Element.up(el, 'tr');
34 var tr = Element.up(el, 'tr');
35 var n = Element.next(tr);
35 var n = Element.next(tr);
36 tr.toggleClassName('open');
36 tr.toggleClassName('open');
37 while (n != undefined && !n.hasClassName('group')) {
37 while (n != undefined && !n.hasClassName('group')) {
38 Element.toggle(n);
38 Element.toggle(n);
39 n = Element.next(n);
39 n = Element.next(n);
40 }
40 }
41 }
41 }
42
42
43 function collapseAllRowGroups(el) {
43 function collapseAllRowGroups(el) {
44 var tbody = Element.up(el, 'tbody');
44 var tbody = Element.up(el, 'tbody');
45 tbody.childElements('tr').each(function(tr) {
45 tbody.childElements('tr').each(function(tr) {
46 if (tr.hasClassName('group')) {
46 if (tr.hasClassName('group')) {
47 tr.removeClassName('open');
47 tr.removeClassName('open');
48 } else {
48 } else {
49 tr.hide();
49 tr.hide();
50 }
50 }
51 })
51 })
52 }
52 }
53
53
54 function expandAllRowGroups(el) {
54 function expandAllRowGroups(el) {
55 var tbody = Element.up(el, 'tbody');
55 var tbody = Element.up(el, 'tbody');
56 tbody.childElements('tr').each(function(tr) {
56 tbody.childElements('tr').each(function(tr) {
57 if (tr.hasClassName('group')) {
57 if (tr.hasClassName('group')) {
58 tr.addClassName('open');
58 tr.addClassName('open');
59 } else {
59 } else {
60 tr.show();
60 tr.show();
61 }
61 }
62 })
62 })
63 }
63 }
64
64
65 function toggleAllRowGroups(el) {
65 function toggleAllRowGroups(el) {
66 var tr = Element.up(el, 'tr');
66 var tr = Element.up(el, 'tr');
67 if (tr.hasClassName('open')) {
67 if (tr.hasClassName('open')) {
68 collapseAllRowGroups(el);
68 collapseAllRowGroups(el);
69 } else {
69 } else {
70 expandAllRowGroups(el);
70 expandAllRowGroups(el);
71 }
71 }
72 }
72 }
73
73
74 function toggleFieldset(el) {
74 function toggleFieldset(el) {
75 var fieldset = Element.up(el, 'fieldset');
75 var fieldset = Element.up(el, 'fieldset');
76 fieldset.toggleClassName('collapsed');
76 fieldset.toggleClassName('collapsed');
77 Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
77 Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
78 }
78 }
79
79
80 function hideFieldset(el) {
80 function hideFieldset(el) {
81 var fieldset = Element.up(el, 'fieldset');
81 var fieldset = Element.up(el, 'fieldset');
82 fieldset.toggleClassName('collapsed');
82 fieldset.toggleClassName('collapsed');
83 fieldset.down('div').hide();
83 fieldset.down('div').hide();
84 }
84 }
85
85
86 function add_filter() {
86 function add_filter() {
87 select = $('add_filter_select');
87 select = $('add_filter_select');
88 field = select.value
88 field = select.value
89 Element.show('tr_' + field);
89 Element.show('tr_' + field);
90 check_box = $('cb_' + field);
90 check_box = $('cb_' + field);
91 check_box.checked = true;
91 check_box.checked = true;
92 toggle_filter(field);
92 toggle_filter(field);
93 select.selectedIndex = 0;
93 select.selectedIndex = 0;
94
94
95 for (i=0; i<select.options.length; i++) {
95 for (i=0; i<select.options.length; i++) {
96 if (select.options[i].value == field) {
96 if (select.options[i].value == field) {
97 select.options[i].disabled = true;
97 select.options[i].disabled = true;
98 }
98 }
99 }
99 }
100 }
100 }
101
101
102 function toggle_filter(field) {
102 function toggle_filter(field) {
103 check_box = $('cb_' + field);
103 check_box = $('cb_' + field);
104 if (check_box.checked) {
104 if (check_box.checked) {
105 Element.show("operators_" + field);
105 Element.show("operators_" + field);
106 Form.Element.enable("operators_" + field);
106 Form.Element.enable("operators_" + field);
107 toggle_operator(field);
107 toggle_operator(field);
108 } else {
108 } else {
109 Element.hide("operators_" + field);
109 Element.hide("operators_" + field);
110 Form.Element.disable("operators_" + field);
110 Form.Element.disable("operators_" + field);
111 enableValues(field, []);
111 enableValues(field, []);
112 }
112 }
113 }
113 }
114
114
115 function enableValues(field, indexes) {
115 function enableValues(field, indexes) {
116 var f = $$(".values_" + field);
116 var f = $$(".values_" + field);
117 for(var i=0;i<f.length;i++) {
117 for(var i=0;i<f.length;i++) {
118 if (indexes.include(i)) {
118 if (indexes.include(i)) {
119 Form.Element.enable(f[i]);
119 Form.Element.enable(f[i]);
120 f[i].up('span').show();
120 f[i].up('span').show();
121 } else {
121 } else {
122 f[i].value = '';
122 f[i].value = '';
123 Form.Element.disable(f[i]);
123 Form.Element.disable(f[i]);
124 f[i].up('span').hide();
124 f[i].up('span').hide();
125 }
125 }
126 }
126 }
127 if (indexes.length > 0) {
127 if (indexes.length > 0) {
128 Element.show("div_values_" + field);
128 Element.show("div_values_" + field);
129 } else {
129 } else {
130 Element.hide("div_values_" + field);
130 Element.hide("div_values_" + field);
131 }
131 }
132 }
132 }
133
133
134 function toggle_operator(field) {
134 function toggle_operator(field) {
135 operator = $("operators_" + field);
135 operator = $("operators_" + field);
136 switch (operator.value) {
136 switch (operator.value) {
137 case "!*":
137 case "!*":
138 case "*":
138 case "*":
139 case "t":
139 case "t":
140 case "w":
140 case "w":
141 case "o":
141 case "o":
142 case "c":
142 case "c":
143 enableValues(field, []);
143 enableValues(field, []);
144 break;
144 break;
145 case "><":
145 case "><":
146 enableValues(field, [0,1]);
146 enableValues(field, [0,1]);
147 break;
147 break;
148 case "<t+":
148 case "<t+":
149 case ">t+":
149 case ">t+":
150 case "t+":
150 case "t+":
151 case ">t-":
151 case ">t-":
152 case "<t-":
152 case "<t-":
153 case "t-":
153 case "t-":
154 enableValues(field, [2]);
154 enableValues(field, [2]);
155 break;
155 break;
156 default:
156 default:
157 enableValues(field, [0]);
157 enableValues(field, [0]);
158 break;
158 break;
159 }
159 }
160 }
160 }
161
161
162 function toggle_multi_select(el) {
162 function toggle_multi_select(el) {
163 var select = $(el);
163 var select = $(el);
164 if (select.multiple == true) {
164 if (select.multiple == true) {
165 select.multiple = false;
165 select.multiple = false;
166 } else {
166 } else {
167 select.multiple = true;
167 select.multiple = true;
168 }
168 }
169 }
169 }
170
170
171 function submit_query_form(id) {
171 function submit_query_form(id) {
172 selectAllOptions("selected_columns");
172 selectAllOptions("selected_columns");
173 $(id).submit();
173 $(id).submit();
174 }
174 }
175
175
176 function apply_filters_observer() {
176 function apply_filters_observer() {
177 $$("#query_form input[type=text]").invoke("observe", "keypress", function(e){
177 $$("#query_form input[type=text]").invoke("observe", "keypress", function(e){
178 if(e.keyCode == Event.KEY_RETURN) {
178 if(e.keyCode == Event.KEY_RETURN) {
179 submit_query_form("query_form");
179 submit_query_form("query_form");
180 }
180 }
181 });
181 });
182 }
182 }
183
183
184 var fileFieldCount = 1;
184 var fileFieldCount = 1;
185
185
186 function addFileField() {
186 function addFileField() {
187 var fields = $('attachments_fields');
187 var fields = $('attachments_fields');
188 if (fields.childElements().length >= 10) return false;
188 if (fields.childElements().length >= 10) return false;
189 fileFieldCount++;
189 fileFieldCount++;
190 var s = new Element('span');
190 var s = new Element('span');
191 s.update(fields.down('span').innerHTML);
191 s.update(fields.down('span').innerHTML);
192 s.down('input.file').name = "attachments[" + fileFieldCount + "][file]";
192 s.down('input.file').name = "attachments[" + fileFieldCount + "][file]";
193 s.down('input.description').name = "attachments[" + fileFieldCount + "][description]";
193 s.down('input.description').name = "attachments[" + fileFieldCount + "][description]";
194 fields.appendChild(s);
194 fields.appendChild(s);
195 }
195 }
196
196
197 function removeFileField(el) {
197 function removeFileField(el) {
198 var fields = $('attachments_fields');
198 var fields = $('attachments_fields');
199 var s = Element.up(el, 'span');
199 var s = Element.up(el, 'span');
200 if (fields.childElements().length > 1) {
200 if (fields.childElements().length > 1) {
201 s.remove();
201 s.remove();
202 } else {
202 } else {
203 s.update(s.innerHTML);
203 s.update(s.innerHTML);
204 }
204 }
205 }
205 }
206
206
207 function checkFileSize(el, maxSize, message) {
207 function checkFileSize(el, maxSize, message) {
208 var files = el.files;
208 var files = el.files;
209 if (files) {
209 if (files) {
210 for (var i=0; i<files.length; i++) {
210 for (var i=0; i<files.length; i++) {
211 if (files[i].size > maxSize) {
211 if (files[i].size > maxSize) {
212 alert(message);
212 alert(message);
213 el.value = "";
213 el.value = "";
214 }
214 }
215 }
215 }
216 }
216 }
217 }
217 }
218
218
219 function showTab(name) {
219 function showTab(name) {
220 var f = $$('div#content .tab-content');
220 var f = $$('div#content .tab-content');
221 for(var i=0; i<f.length; i++){
221 for(var i=0; i<f.length; i++){
222 Element.hide(f[i]);
222 Element.hide(f[i]);
223 }
223 }
224 var f = $$('div.tabs a');
224 var f = $$('div.tabs a');
225 for(var i=0; i<f.length; i++){
225 for(var i=0; i<f.length; i++){
226 Element.removeClassName(f[i], "selected");
226 Element.removeClassName(f[i], "selected");
227 }
227 }
228 Element.show('tab-content-' + name);
228 Element.show('tab-content-' + name);
229 Element.addClassName('tab-' + name, "selected");
229 Element.addClassName('tab-' + name, "selected");
230 return false;
230 return false;
231 }
231 }
232
232
233 function moveTabRight(el) {
233 function moveTabRight(el) {
234 var lis = Element.up(el, 'div.tabs').down('ul').childElements();
234 var lis = Element.up(el, 'div.tabs').down('ul').childElements();
235 var tabsWidth = 0;
235 var tabsWidth = 0;
236 var i;
236 var i;
237 for (i=0; i<lis.length; i++) {
237 for (i=0; i<lis.length; i++) {
238 if (lis[i].visible()) {
238 if (lis[i].visible()) {
239 tabsWidth += lis[i].getWidth() + 6;
239 tabsWidth += lis[i].getWidth() + 6;
240 }
240 }
241 }
241 }
242 if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) {
242 if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) {
243 return;
243 return;
244 }
244 }
245 i=0;
245 i=0;
246 while (i<lis.length && !lis[i].visible()) {
246 while (i<lis.length && !lis[i].visible()) {
247 i++;
247 i++;
248 }
248 }
249 lis[i].hide();
249 lis[i].hide();
250 }
250 }
251
251
252 function moveTabLeft(el) {
252 function moveTabLeft(el) {
253 var lis = Element.up(el, 'div.tabs').down('ul').childElements();
253 var lis = Element.up(el, 'div.tabs').down('ul').childElements();
254 var i = 0;
254 var i = 0;
255 while (i<lis.length && !lis[i].visible()) {
255 while (i<lis.length && !lis[i].visible()) {
256 i++;
256 i++;
257 }
257 }
258 if (i>0) {
258 if (i>0) {
259 lis[i-1].show();
259 lis[i-1].show();
260 }
260 }
261 }
261 }
262
262
263 function displayTabsButtons() {
263 function displayTabsButtons() {
264 var lis;
264 var lis;
265 var tabsWidth = 0;
265 var tabsWidth = 0;
266 var i;
266 var i;
267 $$('div.tabs').each(function(el) {
267 $$('div.tabs').each(function(el) {
268 lis = el.down('ul').childElements();
268 lis = el.down('ul').childElements();
269 for (i=0; i<lis.length; i++) {
269 for (i=0; i<lis.length; i++) {
270 if (lis[i].visible()) {
270 if (lis[i].visible()) {
271 tabsWidth += lis[i].getWidth() + 6;
271 tabsWidth += lis[i].getWidth() + 6;
272 }
272 }
273 }
273 }
274 if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) {
274 if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) {
275 el.down('div.tabs-buttons').hide();
275 el.down('div.tabs-buttons').hide();
276 } else {
276 } else {
277 el.down('div.tabs-buttons').show();
277 el.down('div.tabs-buttons').show();
278 }
278 }
279 });
279 });
280 }
280 }
281
281
282 function setPredecessorFieldsVisibility() {
282 function setPredecessorFieldsVisibility() {
283 relationType = $('relation_relation_type');
283 relationType = $('relation_relation_type');
284 if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) {
284 if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) {
285 Element.show('predecessor_fields');
285 Element.show('predecessor_fields');
286 } else {
286 } else {
287 Element.hide('predecessor_fields');
287 Element.hide('predecessor_fields');
288 }
288 }
289 }
289 }
290
290
291 function promptToRemote(text, param, url) {
291 function promptToRemote(text, param, url) {
292 value = prompt(text + ':');
292 value = prompt(text + ':');
293 if (value) {
293 if (value) {
294 new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
294 new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
295 return false;
295 return false;
296 }
296 }
297 }
297 }
298
298
299 function showModal(id, width) {
299 function showModal(id, width) {
300 el = $(id);
300 el = $(id);
301 if (el == undefined || el.visible()) {return;}
301 if (el == undefined || el.visible()) {return;}
302 var h = $$('body')[0].getHeight();
302 var h = $$('body')[0].getHeight();
303 var d = document.createElement("div");
303 var d = document.createElement("div");
304 d.id = 'modalbg';
304 d.id = 'modalbg';
305 $('main').appendChild(d);
305 $('main').appendChild(d);
306 $('modalbg').setStyle({ width: '100%', height: h + 'px' });
306 $('modalbg').setStyle({ width: '100%', height: h + 'px' });
307 $('modalbg').show();
307 $('modalbg').show();
308
308
309 var pageWidth = document.viewport.getWidth();
309 var pageWidth = document.viewport.getWidth();
310 if (width) {
310 if (width) {
311 el.setStyle({'width': width});
311 el.setStyle({'width': width});
312 }
312 }
313 el.setStyle({'left': (((pageWidth - el.getWidth())/2 *100) / pageWidth) + '%'});
313 el.setStyle({'left': (((pageWidth - el.getWidth())/2 *100) / pageWidth) + '%'});
314 el.addClassName('modal');
314 el.addClassName('modal');
315 el.show();
315 el.show();
316
316
317 if (el.down("input[type=text]")) {
317 if (el.down("input[type=text]")) {
318 el.down("input[type=text]").focus();
318 el.down("input[type=text]").focus();
319 } else if (el.down("input[type=submit]")) {
319 } else if (el.down("input[type=submit]")) {
320 el.down("input[type=submit]").focus();
320 el.down("input[type=submit]").focus();
321 }
321 }
322 }
322 }
323
323
324 function hideModal(el) {
324 function hideModal(el) {
325 var modal;
325 var modal;
326 if (el) {
326 if (el) {
327 modal = Element.up(el, 'div.modal');
327 modal = Element.up(el, 'div.modal');
328 } else {
328 } else {
329 modal = $('ajax-modal');
329 modal = $('ajax-modal');
330 }
330 }
331 if (modal) {
331 if (modal) {
332 modal.hide();
332 modal.hide();
333 }
333 }
334 var bg = $('modalbg');
334 var bg = $('modalbg');
335 if (bg) {
335 if (bg) {
336 bg.remove();
336 bg.remove();
337 }
337 }
338 }
338 }
339
339
340 function submitPreview(url, form, target) {
341 new Ajax.Updater(target, url, {
342 asynchronous:true,
343 evalScripts:true,
344 method:'post',
345 onComplete:function(request){Element.scrollTo(target)},
346 parameters:Form.serialize(form)
347 });
348 }
349
340 function collapseScmEntry(id) {
350 function collapseScmEntry(id) {
341 var els = document.getElementsByClassName(id, 'browser');
351 var els = document.getElementsByClassName(id, 'browser');
342 for (var i = 0; i < els.length; i++) {
352 for (var i = 0; i < els.length; i++) {
343 if (els[i].hasClassName('open')) {
353 if (els[i].hasClassName('open')) {
344 collapseScmEntry(els[i].id);
354 collapseScmEntry(els[i].id);
345 }
355 }
346 Element.hide(els[i]);
356 Element.hide(els[i]);
347 }
357 }
348 $(id).removeClassName('open');
358 $(id).removeClassName('open');
349 }
359 }
350
360
351 function expandScmEntry(id) {
361 function expandScmEntry(id) {
352 var els = document.getElementsByClassName(id, 'browser');
362 var els = document.getElementsByClassName(id, 'browser');
353 for (var i = 0; i < els.length; i++) {
363 for (var i = 0; i < els.length; i++) {
354 Element.show(els[i]);
364 Element.show(els[i]);
355 if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
365 if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
356 expandScmEntry(els[i].id);
366 expandScmEntry(els[i].id);
357 }
367 }
358 }
368 }
359 $(id).addClassName('open');
369 $(id).addClassName('open');
360 }
370 }
361
371
362 function scmEntryClick(id) {
372 function scmEntryClick(id) {
363 el = $(id);
373 el = $(id);
364 if (el.hasClassName('open')) {
374 if (el.hasClassName('open')) {
365 collapseScmEntry(id);
375 collapseScmEntry(id);
366 el.addClassName('collapsed');
376 el.addClassName('collapsed');
367 return false;
377 return false;
368 } else if (el.hasClassName('loaded')) {
378 } else if (el.hasClassName('loaded')) {
369 expandScmEntry(id);
379 expandScmEntry(id);
370 el.removeClassName('collapsed');
380 el.removeClassName('collapsed');
371 return false;
381 return false;
372 }
382 }
373 if (el.hasClassName('loading')) {
383 if (el.hasClassName('loading')) {
374 return false;
384 return false;
375 }
385 }
376 el.addClassName('loading');
386 el.addClassName('loading');
377 return true;
387 return true;
378 }
388 }
379
389
380 function scmEntryLoaded(id) {
390 function scmEntryLoaded(id) {
381 Element.addClassName(id, 'open');
391 Element.addClassName(id, 'open');
382 Element.addClassName(id, 'loaded');
392 Element.addClassName(id, 'loaded');
383 Element.removeClassName(id, 'loading');
393 Element.removeClassName(id, 'loading');
384 }
394 }
385
395
386 function randomKey(size) {
396 function randomKey(size) {
387 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');
397 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');
388 var key = '';
398 var key = '';
389 for (i = 0; i < size; i++) {
399 for (i = 0; i < size; i++) {
390 key += chars[Math.floor(Math.random() * chars.length)];
400 key += chars[Math.floor(Math.random() * chars.length)];
391 }
401 }
392 return key;
402 return key;
393 }
403 }
394
404
395 function observeParentIssueField(url) {
405 function observeParentIssueField(url) {
396 new Ajax.Autocompleter('issue_parent_issue_id',
406 new Ajax.Autocompleter('issue_parent_issue_id',
397 'parent_issue_candidates',
407 'parent_issue_candidates',
398 url,
408 url,
399 { minChars: 3,
409 { minChars: 3,
400 frequency: 0.5,
410 frequency: 0.5,
401 paramName: 'q',
411 paramName: 'q',
402 method: 'get',
412 method: 'get',
403 updateElement: function(value) {
413 updateElement: function(value) {
404 document.getElementById('issue_parent_issue_id').value = value.id;
414 document.getElementById('issue_parent_issue_id').value = value.id;
405 }});
415 }});
406 }
416 }
407
417
408 function observeRelatedIssueField(url) {
418 function observeRelatedIssueField(url) {
409 new Ajax.Autocompleter('relation_issue_to_id',
419 new Ajax.Autocompleter('relation_issue_to_id',
410 'related_issue_candidates',
420 'related_issue_candidates',
411 url,
421 url,
412 { minChars: 3,
422 { minChars: 3,
413 frequency: 0.5,
423 frequency: 0.5,
414 paramName: 'q',
424 paramName: 'q',
415 method: 'get',
425 method: 'get',
416 updateElement: function(value) {
426 updateElement: function(value) {
417 document.getElementById('relation_issue_to_id').value = value.id;
427 document.getElementById('relation_issue_to_id').value = value.id;
418 },
428 },
419 parameters: 'scope=all'
429 parameters: 'scope=all'
420 });
430 });
421 }
431 }
422
432
423 function setVisible(id, visible) {
433 function setVisible(id, visible) {
424 var el = $(id);
434 var el = $(id);
425 if (el) {if (visible) {el.show();} else {el.hide();}}
435 if (el) {if (visible) {el.show();} else {el.hide();}}
426 }
436 }
427
437
428 function observeProjectModules() {
438 function observeProjectModules() {
429 var f = function() {
439 var f = function() {
430 /* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */
440 /* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */
431 var c = ($('project_enabled_module_names_issue_tracking').checked == true);
441 var c = ($('project_enabled_module_names_issue_tracking').checked == true);
432 setVisible('project_trackers', c);
442 setVisible('project_trackers', c);
433 setVisible('project_issue_custom_fields', c);
443 setVisible('project_issue_custom_fields', c);
434 };
444 };
435
445
436 Event.observe(window, 'load', f);
446 Event.observe(window, 'load', f);
437 Event.observe('project_enabled_module_names_issue_tracking', 'change', f);
447 Event.observe('project_enabled_module_names_issue_tracking', 'change', f);
438 }
448 }
439
449
440 /*
450 /*
441 * Class used to warn user when leaving a page with unsaved textarea
451 * Class used to warn user when leaving a page with unsaved textarea
442 * Author: mathias.fischer@berlinonline.de
452 * Author: mathias.fischer@berlinonline.de
443 */
453 */
444
454
445 var WarnLeavingUnsaved = Class.create({
455 var WarnLeavingUnsaved = Class.create({
446 observedForms: false,
456 observedForms: false,
447 observedElements: false,
457 observedElements: false,
448 changedForms: false,
458 changedForms: false,
449 message: null,
459 message: null,
450
460
451 initialize: function(message){
461 initialize: function(message){
452 this.observedForms = $$('form');
462 this.observedForms = $$('form');
453 this.observedElements = $$('textarea');
463 this.observedElements = $$('textarea');
454 this.message = message;
464 this.message = message;
455
465
456 this.observedElements.each(this.observeChange.bind(this));
466 this.observedElements.each(this.observeChange.bind(this));
457 this.observedForms.each(this.submitAction.bind(this));
467 this.observedForms.each(this.submitAction.bind(this));
458
468
459 window.onbeforeunload = this.unload.bind(this);
469 window.onbeforeunload = this.unload.bind(this);
460 },
470 },
461
471
462 unload: function(){
472 unload: function(){
463 this.observedElements.each(function(el) {el.blur();})
473 this.observedElements.each(function(el) {el.blur();})
464 if(this.changedForms)
474 if(this.changedForms)
465 return this.message;
475 return this.message;
466 },
476 },
467
477
468 setChanged: function(){
478 setChanged: function(){
469 this.changedForms = true;
479 this.changedForms = true;
470 },
480 },
471
481
472 setUnchanged: function(){
482 setUnchanged: function(){
473 this.changedForms = false;
483 this.changedForms = false;
474 },
484 },
475
485
476 observeChange: function(element){
486 observeChange: function(element){
477 element.observe('change',this.setChanged.bindAsEventListener(this));
487 element.observe('change',this.setChanged.bindAsEventListener(this));
478 },
488 },
479
489
480 submitAction: function(element){
490 submitAction: function(element){
481 element.observe('submit',this.setUnchanged.bindAsEventListener(this));
491 element.observe('submit',this.setUnchanged.bindAsEventListener(this));
482 }
492 }
483 });
493 });
484
494
485 /*
495 /*
486 * 1 - registers a callback which copies the csrf token into the
496 * 1 - registers a callback which copies the csrf token into the
487 * X-CSRF-Token header with each ajax request. Necessary to
497 * X-CSRF-Token header with each ajax request. Necessary to
488 * work with rails applications which have fixed
498 * work with rails applications which have fixed
489 * CVE-2011-0447
499 * CVE-2011-0447
490 * 2 - shows and hides ajax indicator
500 * 2 - shows and hides ajax indicator
491 */
501 */
492 Ajax.Responders.register({
502 Ajax.Responders.register({
493 onCreate: function(request){
503 onCreate: function(request){
494 var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
504 var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
495
505
496 if (csrf_meta_tag) {
506 if (csrf_meta_tag) {
497 var header = 'X-CSRF-Token',
507 var header = 'X-CSRF-Token',
498 token = csrf_meta_tag.readAttribute('content');
508 token = csrf_meta_tag.readAttribute('content');
499
509
500 if (!request.options.requestHeaders) {
510 if (!request.options.requestHeaders) {
501 request.options.requestHeaders = {};
511 request.options.requestHeaders = {};
502 }
512 }
503 request.options.requestHeaders[header] = token;
513 request.options.requestHeaders[header] = token;
504 }
514 }
505
515
506 if ($('ajax-indicator') && Ajax.activeRequestCount > 0 && $$('input.ajax-loading').size() == 0) {
516 if ($('ajax-indicator') && Ajax.activeRequestCount > 0 && $$('input.ajax-loading').size() == 0) {
507 Element.show('ajax-indicator');
517 Element.show('ajax-indicator');
508 }
518 }
509 },
519 },
510 onComplete: function(){
520 onComplete: function(){
511 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
521 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
512 Element.hide('ajax-indicator');
522 Element.hide('ajax-indicator');
513 }
523 }
514 }
524 }
515 });
525 });
516
526
517 function hideOnLoad() {
527 function hideOnLoad() {
518 $$('.hol').each(function(el) {
528 $$('.hol').each(function(el) {
519 el.hide();
529 el.hide();
520 });
530 });
521 }
531 }
522
532
523 function addFormObserversForDoubleSubmit() {
533 function addFormObserversForDoubleSubmit() {
524 $$('form[method=post]').each(function(form) {
534 $$('form[method=post]').each(function(form) {
525 if (!form.hasClassName('multiple-submit')) {
535 if (!form.hasClassName('multiple-submit')) {
526 form.on('submit', function(form_submission) {
536 form.on('submit', function(form_submission) {
527 if (form.getStorage().get('submitted')) {
537 if (form.getStorage().get('submitted')) {
528 form_submission.stop();
538 form_submission.stop();
529 } else {
539 } else {
530 form.getStorage().set('submitted', true);
540 form.getStorage().set('submitted', true);
531 }
541 }
532 });
542 });
533 }
543 }
534 });
544 });
535 }
545 }
536
546
537 Event.observe(window, 'load', hideOnLoad);
547 Event.observe(window, 'load', hideOnLoad);
538 Event.observe(window, 'load', addFormObserversForDoubleSubmit);
548 Event.observe(window, 'load', addFormObserversForDoubleSubmit);
General Comments 0
You need to be logged in to leave comments. Login now