##// END OF EJS Templates
Upgraded JQuery to 1.8.3 and JQuery UI to 1.9.2....
Jean-Philippe Lang -
r10851:7606353ff991
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,1234 +1,1234
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 include Redmine::Pagination::Helper
27 include Redmine::Pagination::Helper
28
28
29 extend Forwardable
29 extend Forwardable
30 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
30 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
31
31
32 # Return true if user is authorized for controller/action, otherwise false
32 # Return true if user is authorized for controller/action, otherwise false
33 def authorize_for(controller, action)
33 def authorize_for(controller, action)
34 User.current.allowed_to?({:controller => controller, :action => action}, @project)
34 User.current.allowed_to?({:controller => controller, :action => action}, @project)
35 end
35 end
36
36
37 # Display a link if user is authorized
37 # Display a link if user is authorized
38 #
38 #
39 # @param [String] name Anchor text (passed to link_to)
39 # @param [String] name Anchor text (passed to link_to)
40 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
40 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
41 # @param [optional, Hash] html_options Options passed to link_to
41 # @param [optional, Hash] html_options Options passed to link_to
42 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
42 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
43 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
43 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
44 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
44 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
45 end
45 end
46
46
47 # Displays a link to user's account page if active
47 # Displays a link to user's account page if active
48 def link_to_user(user, options={})
48 def link_to_user(user, options={})
49 if user.is_a?(User)
49 if user.is_a?(User)
50 name = h(user.name(options[:format]))
50 name = h(user.name(options[:format]))
51 if user.active? || (User.current.admin? && user.logged?)
51 if user.active? || (User.current.admin? && user.logged?)
52 link_to name, user_path(user), :class => user.css_classes
52 link_to name, user_path(user), :class => user.css_classes
53 else
53 else
54 name
54 name
55 end
55 end
56 else
56 else
57 h(user.to_s)
57 h(user.to_s)
58 end
58 end
59 end
59 end
60
60
61 # Displays a link to +issue+ with its subject.
61 # Displays a link to +issue+ with its subject.
62 # Examples:
62 # Examples:
63 #
63 #
64 # link_to_issue(issue) # => Defect #6: This is the subject
64 # link_to_issue(issue) # => Defect #6: This is the subject
65 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
65 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
66 # link_to_issue(issue, :subject => false) # => Defect #6
66 # link_to_issue(issue, :subject => false) # => Defect #6
67 # link_to_issue(issue, :project => true) # => Foo - Defect #6
67 # link_to_issue(issue, :project => true) # => Foo - Defect #6
68 # link_to_issue(issue, :subject => false, :tracker => false) # => #6
68 # link_to_issue(issue, :subject => false, :tracker => false) # => #6
69 #
69 #
70 def link_to_issue(issue, options={})
70 def link_to_issue(issue, options={})
71 title = nil
71 title = nil
72 subject = nil
72 subject = nil
73 text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
73 text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
74 if options[:subject] == false
74 if options[:subject] == false
75 title = truncate(issue.subject, :length => 60)
75 title = truncate(issue.subject, :length => 60)
76 else
76 else
77 subject = issue.subject
77 subject = issue.subject
78 if options[:truncate]
78 if options[:truncate]
79 subject = truncate(subject, :length => options[:truncate])
79 subject = truncate(subject, :length => options[:truncate])
80 end
80 end
81 end
81 end
82 s = link_to text, issue_path(issue), :class => issue.css_classes, :title => title
82 s = link_to text, issue_path(issue), :class => issue.css_classes, :title => title
83 s << h(": #{subject}") if subject
83 s << h(": #{subject}") if subject
84 s = h("#{issue.project} - ") + s if options[:project]
84 s = h("#{issue.project} - ") + s if options[:project]
85 s
85 s
86 end
86 end
87
87
88 # Generates a link to an attachment.
88 # Generates a link to an attachment.
89 # Options:
89 # Options:
90 # * :text - Link text (default to attachment filename)
90 # * :text - Link text (default to attachment filename)
91 # * :download - Force download (default: false)
91 # * :download - Force download (default: false)
92 def link_to_attachment(attachment, options={})
92 def link_to_attachment(attachment, options={})
93 text = options.delete(:text) || attachment.filename
93 text = options.delete(:text) || attachment.filename
94 action = options.delete(:download) ? 'download' : 'show'
94 action = options.delete(:download) ? 'download' : 'show'
95 opt_only_path = {}
95 opt_only_path = {}
96 opt_only_path[:only_path] = (options[:only_path] == false ? false : true)
96 opt_only_path[:only_path] = (options[:only_path] == false ? false : true)
97 options.delete(:only_path)
97 options.delete(:only_path)
98 link_to(h(text),
98 link_to(h(text),
99 {:controller => 'attachments', :action => action,
99 {:controller => 'attachments', :action => action,
100 :id => attachment, :filename => attachment.filename}.merge(opt_only_path),
100 :id => attachment, :filename => attachment.filename}.merge(opt_only_path),
101 options)
101 options)
102 end
102 end
103
103
104 # Generates a link to a SCM revision
104 # Generates a link to a SCM revision
105 # Options:
105 # Options:
106 # * :text - Link text (default to the formatted revision)
106 # * :text - Link text (default to the formatted revision)
107 def link_to_revision(revision, repository, options={})
107 def link_to_revision(revision, repository, options={})
108 if repository.is_a?(Project)
108 if repository.is_a?(Project)
109 repository = repository.repository
109 repository = repository.repository
110 end
110 end
111 text = options.delete(:text) || format_revision(revision)
111 text = options.delete(:text) || format_revision(revision)
112 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
112 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
113 link_to(
113 link_to(
114 h(text),
114 h(text),
115 {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
115 {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
116 :title => l(:label_revision_id, format_revision(revision))
116 :title => l(:label_revision_id, format_revision(revision))
117 )
117 )
118 end
118 end
119
119
120 # Generates a link to a message
120 # Generates a link to a message
121 def link_to_message(message, options={}, html_options = nil)
121 def link_to_message(message, options={}, html_options = nil)
122 link_to(
122 link_to(
123 h(truncate(message.subject, :length => 60)),
123 h(truncate(message.subject, :length => 60)),
124 { :controller => 'messages', :action => 'show',
124 { :controller => 'messages', :action => 'show',
125 :board_id => message.board_id,
125 :board_id => message.board_id,
126 :id => (message.parent_id || message.id),
126 :id => (message.parent_id || message.id),
127 :r => (message.parent_id && message.id),
127 :r => (message.parent_id && message.id),
128 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
128 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
129 }.merge(options),
129 }.merge(options),
130 html_options
130 html_options
131 )
131 )
132 end
132 end
133
133
134 # Generates a link to a project if active
134 # Generates a link to a project if active
135 # Examples:
135 # Examples:
136 #
136 #
137 # link_to_project(project) # => link to the specified project overview
137 # link_to_project(project) # => link to the specified project overview
138 # link_to_project(project, :action=>'settings') # => link to project settings
138 # link_to_project(project, :action=>'settings') # => link to project settings
139 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
139 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
140 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
140 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
141 #
141 #
142 def link_to_project(project, options={}, html_options = nil)
142 def link_to_project(project, options={}, html_options = nil)
143 if project.archived?
143 if project.archived?
144 h(project)
144 h(project)
145 else
145 else
146 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
146 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
147 link_to(h(project), url, html_options)
147 link_to(h(project), url, html_options)
148 end
148 end
149 end
149 end
150
150
151 def wiki_page_path(page, options={})
151 def wiki_page_path(page, options={})
152 url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
152 url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
153 end
153 end
154
154
155 def thumbnail_tag(attachment)
155 def thumbnail_tag(attachment)
156 link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
156 link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)),
157 {:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
157 {:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename},
158 :title => attachment.filename
158 :title => attachment.filename
159 end
159 end
160
160
161 def toggle_link(name, id, options={})
161 def toggle_link(name, id, options={})
162 onclick = "$('##{id}').toggle(); "
162 onclick = "$('##{id}').toggle(); "
163 onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
163 onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
164 onclick << "return false;"
164 onclick << "return false;"
165 link_to(name, "#", :onclick => onclick)
165 link_to(name, "#", :onclick => onclick)
166 end
166 end
167
167
168 def image_to_function(name, function, html_options = {})
168 def image_to_function(name, function, html_options = {})
169 html_options.symbolize_keys!
169 html_options.symbolize_keys!
170 tag(:input, html_options.merge({
170 tag(:input, html_options.merge({
171 :type => "image", :src => image_path(name),
171 :type => "image", :src => image_path(name),
172 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
172 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
173 }))
173 }))
174 end
174 end
175
175
176 def format_activity_title(text)
176 def format_activity_title(text)
177 h(truncate_single_line(text, :length => 100))
177 h(truncate_single_line(text, :length => 100))
178 end
178 end
179
179
180 def format_activity_day(date)
180 def format_activity_day(date)
181 date == User.current.today ? l(:label_today).titleize : format_date(date)
181 date == User.current.today ? l(:label_today).titleize : format_date(date)
182 end
182 end
183
183
184 def format_activity_description(text)
184 def format_activity_description(text)
185 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
185 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
186 ).gsub(/[\r\n]+/, "<br />").html_safe
186 ).gsub(/[\r\n]+/, "<br />").html_safe
187 end
187 end
188
188
189 def format_version_name(version)
189 def format_version_name(version)
190 if version.project == @project
190 if version.project == @project
191 h(version)
191 h(version)
192 else
192 else
193 h("#{version.project} - #{version}")
193 h("#{version.project} - #{version}")
194 end
194 end
195 end
195 end
196
196
197 def due_date_distance_in_words(date)
197 def due_date_distance_in_words(date)
198 if date
198 if date
199 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
199 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
200 end
200 end
201 end
201 end
202
202
203 # Renders a tree of projects as a nested set of unordered lists
203 # Renders a tree of projects as a nested set of unordered lists
204 # The given collection may be a subset of the whole project tree
204 # The given collection may be a subset of the whole project tree
205 # (eg. some intermediate nodes are private and can not be seen)
205 # (eg. some intermediate nodes are private and can not be seen)
206 def render_project_nested_lists(projects)
206 def render_project_nested_lists(projects)
207 s = ''
207 s = ''
208 if projects.any?
208 if projects.any?
209 ancestors = []
209 ancestors = []
210 original_project = @project
210 original_project = @project
211 projects.sort_by(&:lft).each do |project|
211 projects.sort_by(&:lft).each do |project|
212 # set the project environment to please macros.
212 # set the project environment to please macros.
213 @project = project
213 @project = project
214 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
214 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
215 s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
215 s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
216 else
216 else
217 ancestors.pop
217 ancestors.pop
218 s << "</li>"
218 s << "</li>"
219 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
219 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
220 ancestors.pop
220 ancestors.pop
221 s << "</ul></li>\n"
221 s << "</ul></li>\n"
222 end
222 end
223 end
223 end
224 classes = (ancestors.empty? ? 'root' : 'child')
224 classes = (ancestors.empty? ? 'root' : 'child')
225 s << "<li class='#{classes}'><div class='#{classes}'>"
225 s << "<li class='#{classes}'><div class='#{classes}'>"
226 s << h(block_given? ? yield(project) : project.name)
226 s << h(block_given? ? yield(project) : project.name)
227 s << "</div>\n"
227 s << "</div>\n"
228 ancestors << project
228 ancestors << project
229 end
229 end
230 s << ("</li></ul>\n" * ancestors.size)
230 s << ("</li></ul>\n" * ancestors.size)
231 @project = original_project
231 @project = original_project
232 end
232 end
233 s.html_safe
233 s.html_safe
234 end
234 end
235
235
236 def render_page_hierarchy(pages, node=nil, options={})
236 def render_page_hierarchy(pages, node=nil, options={})
237 content = ''
237 content = ''
238 if pages[node]
238 if pages[node]
239 content << "<ul class=\"pages-hierarchy\">\n"
239 content << "<ul class=\"pages-hierarchy\">\n"
240 pages[node].each do |page|
240 pages[node].each do |page|
241 content << "<li>"
241 content << "<li>"
242 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil},
242 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil},
243 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
243 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
244 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
244 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
245 content << "</li>\n"
245 content << "</li>\n"
246 end
246 end
247 content << "</ul>\n"
247 content << "</ul>\n"
248 end
248 end
249 content.html_safe
249 content.html_safe
250 end
250 end
251
251
252 # Renders flash messages
252 # Renders flash messages
253 def render_flash_messages
253 def render_flash_messages
254 s = ''
254 s = ''
255 flash.each do |k,v|
255 flash.each do |k,v|
256 s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
256 s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
257 end
257 end
258 s.html_safe
258 s.html_safe
259 end
259 end
260
260
261 # Renders tabs and their content
261 # Renders tabs and their content
262 def render_tabs(tabs)
262 def render_tabs(tabs)
263 if tabs.any?
263 if tabs.any?
264 render :partial => 'common/tabs', :locals => {:tabs => tabs}
264 render :partial => 'common/tabs', :locals => {:tabs => tabs}
265 else
265 else
266 content_tag 'p', l(:label_no_data), :class => "nodata"
266 content_tag 'p', l(:label_no_data), :class => "nodata"
267 end
267 end
268 end
268 end
269
269
270 # Renders the project quick-jump box
270 # Renders the project quick-jump box
271 def render_project_jump_box
271 def render_project_jump_box
272 return unless User.current.logged?
272 return unless User.current.logged?
273 projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
273 projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
274 if projects.any?
274 if projects.any?
275 options =
275 options =
276 ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
276 ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
277 '<option value="" disabled="disabled">---</option>').html_safe
277 '<option value="" disabled="disabled">---</option>').html_safe
278
278
279 options << project_tree_options_for_select(projects, :selected => @project) do |p|
279 options << project_tree_options_for_select(projects, :selected => @project) do |p|
280 { :value => project_path(:id => p, :jump => current_menu_item) }
280 { :value => project_path(:id => p, :jump => current_menu_item) }
281 end
281 end
282
282
283 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
283 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
284 end
284 end
285 end
285 end
286
286
287 def project_tree_options_for_select(projects, options = {})
287 def project_tree_options_for_select(projects, options = {})
288 s = ''
288 s = ''
289 project_tree(projects) do |project, level|
289 project_tree(projects) do |project, level|
290 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
290 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
291 tag_options = {:value => project.id}
291 tag_options = {:value => project.id}
292 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
292 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
293 tag_options[:selected] = 'selected'
293 tag_options[:selected] = 'selected'
294 else
294 else
295 tag_options[:selected] = nil
295 tag_options[:selected] = nil
296 end
296 end
297 tag_options.merge!(yield(project)) if block_given?
297 tag_options.merge!(yield(project)) if block_given?
298 s << content_tag('option', name_prefix + h(project), tag_options)
298 s << content_tag('option', name_prefix + h(project), tag_options)
299 end
299 end
300 s.html_safe
300 s.html_safe
301 end
301 end
302
302
303 # Yields the given block for each project with its level in the tree
303 # Yields the given block for each project with its level in the tree
304 #
304 #
305 # Wrapper for Project#project_tree
305 # Wrapper for Project#project_tree
306 def project_tree(projects, &block)
306 def project_tree(projects, &block)
307 Project.project_tree(projects, &block)
307 Project.project_tree(projects, &block)
308 end
308 end
309
309
310 def principals_check_box_tags(name, principals)
310 def principals_check_box_tags(name, principals)
311 s = ''
311 s = ''
312 principals.sort.each do |principal|
312 principals.sort.each do |principal|
313 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
313 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
314 end
314 end
315 s.html_safe
315 s.html_safe
316 end
316 end
317
317
318 # Returns a string for users/groups option tags
318 # Returns a string for users/groups option tags
319 def principals_options_for_select(collection, selected=nil)
319 def principals_options_for_select(collection, selected=nil)
320 s = ''
320 s = ''
321 if collection.include?(User.current)
321 if collection.include?(User.current)
322 s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
322 s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
323 end
323 end
324 groups = ''
324 groups = ''
325 collection.sort.each do |element|
325 collection.sort.each do |element|
326 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
326 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
327 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
327 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
328 end
328 end
329 unless groups.empty?
329 unless groups.empty?
330 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
330 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
331 end
331 end
332 s.html_safe
332 s.html_safe
333 end
333 end
334
334
335 # Options for the new membership projects combo-box
335 # Options for the new membership projects combo-box
336 def options_for_membership_project_select(principal, projects)
336 def options_for_membership_project_select(principal, projects)
337 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
337 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
338 options << project_tree_options_for_select(projects) do |p|
338 options << project_tree_options_for_select(projects) do |p|
339 {:disabled => principal.projects.include?(p)}
339 {:disabled => principal.projects.include?(p)}
340 end
340 end
341 options
341 options
342 end
342 end
343
343
344 # Truncates and returns the string as a single line
344 # Truncates and returns the string as a single line
345 def truncate_single_line(string, *args)
345 def truncate_single_line(string, *args)
346 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
346 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
347 end
347 end
348
348
349 # Truncates at line break after 250 characters or options[:length]
349 # Truncates at line break after 250 characters or options[:length]
350 def truncate_lines(string, options={})
350 def truncate_lines(string, options={})
351 length = options[:length] || 250
351 length = options[:length] || 250
352 if string.to_s =~ /\A(.{#{length}}.*?)$/m
352 if string.to_s =~ /\A(.{#{length}}.*?)$/m
353 "#{$1}..."
353 "#{$1}..."
354 else
354 else
355 string
355 string
356 end
356 end
357 end
357 end
358
358
359 def anchor(text)
359 def anchor(text)
360 text.to_s.gsub(' ', '_')
360 text.to_s.gsub(' ', '_')
361 end
361 end
362
362
363 def html_hours(text)
363 def html_hours(text)
364 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
364 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
365 end
365 end
366
366
367 def authoring(created, author, options={})
367 def authoring(created, author, options={})
368 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
368 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
369 end
369 end
370
370
371 def time_tag(time)
371 def time_tag(time)
372 text = distance_of_time_in_words(Time.now, time)
372 text = distance_of_time_in_words(Time.now, time)
373 if @project
373 if @project
374 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
374 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => User.current.time_to_date(time)}, :title => format_time(time))
375 else
375 else
376 content_tag('acronym', text, :title => format_time(time))
376 content_tag('acronym', text, :title => format_time(time))
377 end
377 end
378 end
378 end
379
379
380 def syntax_highlight_lines(name, content)
380 def syntax_highlight_lines(name, content)
381 lines = []
381 lines = []
382 syntax_highlight(name, content).each_line { |line| lines << line }
382 syntax_highlight(name, content).each_line { |line| lines << line }
383 lines
383 lines
384 end
384 end
385
385
386 def syntax_highlight(name, content)
386 def syntax_highlight(name, content)
387 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
387 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
388 end
388 end
389
389
390 def to_path_param(path)
390 def to_path_param(path)
391 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
391 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
392 str.blank? ? nil : str
392 str.blank? ? nil : str
393 end
393 end
394
394
395 def reorder_links(name, url, method = :post)
395 def reorder_links(name, url, method = :post)
396 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
396 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
397 url.merge({"#{name}[move_to]" => 'highest'}),
397 url.merge({"#{name}[move_to]" => 'highest'}),
398 :method => method, :title => l(:label_sort_highest)) +
398 :method => method, :title => l(:label_sort_highest)) +
399 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
399 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
400 url.merge({"#{name}[move_to]" => 'higher'}),
400 url.merge({"#{name}[move_to]" => 'higher'}),
401 :method => method, :title => l(:label_sort_higher)) +
401 :method => method, :title => l(:label_sort_higher)) +
402 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
402 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
403 url.merge({"#{name}[move_to]" => 'lower'}),
403 url.merge({"#{name}[move_to]" => 'lower'}),
404 :method => method, :title => l(:label_sort_lower)) +
404 :method => method, :title => l(:label_sort_lower)) +
405 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
405 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
406 url.merge({"#{name}[move_to]" => 'lowest'}),
406 url.merge({"#{name}[move_to]" => 'lowest'}),
407 :method => method, :title => l(:label_sort_lowest))
407 :method => method, :title => l(:label_sort_lowest))
408 end
408 end
409
409
410 def breadcrumb(*args)
410 def breadcrumb(*args)
411 elements = args.flatten
411 elements = args.flatten
412 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
412 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
413 end
413 end
414
414
415 def other_formats_links(&block)
415 def other_formats_links(&block)
416 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
416 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
417 yield Redmine::Views::OtherFormatsBuilder.new(self)
417 yield Redmine::Views::OtherFormatsBuilder.new(self)
418 concat('</p>'.html_safe)
418 concat('</p>'.html_safe)
419 end
419 end
420
420
421 def page_header_title
421 def page_header_title
422 if @project.nil? || @project.new_record?
422 if @project.nil? || @project.new_record?
423 h(Setting.app_title)
423 h(Setting.app_title)
424 else
424 else
425 b = []
425 b = []
426 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
426 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
427 if ancestors.any?
427 if ancestors.any?
428 root = ancestors.shift
428 root = ancestors.shift
429 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
429 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
430 if ancestors.size > 2
430 if ancestors.size > 2
431 b << "\xe2\x80\xa6"
431 b << "\xe2\x80\xa6"
432 ancestors = ancestors[-2, 2]
432 ancestors = ancestors[-2, 2]
433 end
433 end
434 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
434 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
435 end
435 end
436 b << h(@project)
436 b << h(@project)
437 b.join(" \xc2\xbb ").html_safe
437 b.join(" \xc2\xbb ").html_safe
438 end
438 end
439 end
439 end
440
440
441 def html_title(*args)
441 def html_title(*args)
442 if args.empty?
442 if args.empty?
443 title = @html_title || []
443 title = @html_title || []
444 title << @project.name if @project
444 title << @project.name if @project
445 title << Setting.app_title unless Setting.app_title == title.last
445 title << Setting.app_title unless Setting.app_title == title.last
446 title.select {|t| !t.blank? }.join(' - ')
446 title.select {|t| !t.blank? }.join(' - ')
447 else
447 else
448 @html_title ||= []
448 @html_title ||= []
449 @html_title += args
449 @html_title += args
450 end
450 end
451 end
451 end
452
452
453 # Returns the theme, controller name, and action as css classes for the
453 # Returns the theme, controller name, and action as css classes for the
454 # HTML body.
454 # HTML body.
455 def body_css_classes
455 def body_css_classes
456 css = []
456 css = []
457 if theme = Redmine::Themes.theme(Setting.ui_theme)
457 if theme = Redmine::Themes.theme(Setting.ui_theme)
458 css << 'theme-' + theme.name
458 css << 'theme-' + theme.name
459 end
459 end
460
460
461 css << 'controller-' + controller_name
461 css << 'controller-' + controller_name
462 css << 'action-' + action_name
462 css << 'action-' + action_name
463 css.join(' ')
463 css.join(' ')
464 end
464 end
465
465
466 def accesskey(s)
466 def accesskey(s)
467 Redmine::AccessKeys.key_for s
467 Redmine::AccessKeys.key_for s
468 end
468 end
469
469
470 # Formats text according to system settings.
470 # Formats text according to system settings.
471 # 2 ways to call this method:
471 # 2 ways to call this method:
472 # * with a String: textilizable(text, options)
472 # * with a String: textilizable(text, options)
473 # * with an object and one of its attribute: textilizable(issue, :description, options)
473 # * with an object and one of its attribute: textilizable(issue, :description, options)
474 def textilizable(*args)
474 def textilizable(*args)
475 options = args.last.is_a?(Hash) ? args.pop : {}
475 options = args.last.is_a?(Hash) ? args.pop : {}
476 case args.size
476 case args.size
477 when 1
477 when 1
478 obj = options[:object]
478 obj = options[:object]
479 text = args.shift
479 text = args.shift
480 when 2
480 when 2
481 obj = args.shift
481 obj = args.shift
482 attr = args.shift
482 attr = args.shift
483 text = obj.send(attr).to_s
483 text = obj.send(attr).to_s
484 else
484 else
485 raise ArgumentError, 'invalid arguments to textilizable'
485 raise ArgumentError, 'invalid arguments to textilizable'
486 end
486 end
487 return '' if text.blank?
487 return '' if text.blank?
488 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
488 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
489 only_path = options.delete(:only_path) == false ? false : true
489 only_path = options.delete(:only_path) == false ? false : true
490
490
491 text = text.dup
491 text = text.dup
492 macros = catch_macros(text)
492 macros = catch_macros(text)
493 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
493 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
494
494
495 @parsed_headings = []
495 @parsed_headings = []
496 @heading_anchors = {}
496 @heading_anchors = {}
497 @current_section = 0 if options[:edit_section_links]
497 @current_section = 0 if options[:edit_section_links]
498
498
499 parse_sections(text, project, obj, attr, only_path, options)
499 parse_sections(text, project, obj, attr, only_path, options)
500 text = parse_non_pre_blocks(text, obj, macros) do |text|
500 text = parse_non_pre_blocks(text, obj, macros) do |text|
501 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
501 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
502 send method_name, text, project, obj, attr, only_path, options
502 send method_name, text, project, obj, attr, only_path, options
503 end
503 end
504 end
504 end
505 parse_headings(text, project, obj, attr, only_path, options)
505 parse_headings(text, project, obj, attr, only_path, options)
506
506
507 if @parsed_headings.any?
507 if @parsed_headings.any?
508 replace_toc(text, @parsed_headings)
508 replace_toc(text, @parsed_headings)
509 end
509 end
510
510
511 text.html_safe
511 text.html_safe
512 end
512 end
513
513
514 def parse_non_pre_blocks(text, obj, macros)
514 def parse_non_pre_blocks(text, obj, macros)
515 s = StringScanner.new(text)
515 s = StringScanner.new(text)
516 tags = []
516 tags = []
517 parsed = ''
517 parsed = ''
518 while !s.eos?
518 while !s.eos?
519 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
519 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
520 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
520 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
521 if tags.empty?
521 if tags.empty?
522 yield text
522 yield text
523 inject_macros(text, obj, macros) if macros.any?
523 inject_macros(text, obj, macros) if macros.any?
524 else
524 else
525 inject_macros(text, obj, macros, false) if macros.any?
525 inject_macros(text, obj, macros, false) if macros.any?
526 end
526 end
527 parsed << text
527 parsed << text
528 if tag
528 if tag
529 if closing
529 if closing
530 if tags.last == tag.downcase
530 if tags.last == tag.downcase
531 tags.pop
531 tags.pop
532 end
532 end
533 else
533 else
534 tags << tag.downcase
534 tags << tag.downcase
535 end
535 end
536 parsed << full_tag
536 parsed << full_tag
537 end
537 end
538 end
538 end
539 # Close any non closing tags
539 # Close any non closing tags
540 while tag = tags.pop
540 while tag = tags.pop
541 parsed << "</#{tag}>"
541 parsed << "</#{tag}>"
542 end
542 end
543 parsed
543 parsed
544 end
544 end
545
545
546 def parse_inline_attachments(text, project, obj, attr, only_path, options)
546 def parse_inline_attachments(text, project, obj, attr, only_path, options)
547 # when using an image link, try to use an attachment, if possible
547 # when using an image link, try to use an attachment, if possible
548 if options[:attachments].present? || (obj && obj.respond_to?(:attachments))
548 if options[:attachments].present? || (obj && obj.respond_to?(:attachments))
549 attachments = options[:attachments] || []
549 attachments = options[:attachments] || []
550 attachments += obj.attachments if obj
550 attachments += obj.attachments if obj
551 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
551 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
552 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
552 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
553 # search for the picture in attachments
553 # search for the picture in attachments
554 if found = Attachment.latest_attach(attachments, filename)
554 if found = Attachment.latest_attach(attachments, filename)
555 image_url = url_for :only_path => only_path, :controller => 'attachments',
555 image_url = url_for :only_path => only_path, :controller => 'attachments',
556 :action => 'download', :id => found
556 :action => 'download', :id => found
557 desc = found.description.to_s.gsub('"', '')
557 desc = found.description.to_s.gsub('"', '')
558 if !desc.blank? && alttext.blank?
558 if !desc.blank? && alttext.blank?
559 alt = " title=\"#{desc}\" alt=\"#{desc}\""
559 alt = " title=\"#{desc}\" alt=\"#{desc}\""
560 end
560 end
561 "src=\"#{image_url}\"#{alt}"
561 "src=\"#{image_url}\"#{alt}"
562 else
562 else
563 m
563 m
564 end
564 end
565 end
565 end
566 end
566 end
567 end
567 end
568
568
569 # Wiki links
569 # Wiki links
570 #
570 #
571 # Examples:
571 # Examples:
572 # [[mypage]]
572 # [[mypage]]
573 # [[mypage|mytext]]
573 # [[mypage|mytext]]
574 # wiki links can refer other project wikis, using project name or identifier:
574 # wiki links can refer other project wikis, using project name or identifier:
575 # [[project:]] -> wiki starting page
575 # [[project:]] -> wiki starting page
576 # [[project:|mytext]]
576 # [[project:|mytext]]
577 # [[project:mypage]]
577 # [[project:mypage]]
578 # [[project:mypage|mytext]]
578 # [[project:mypage|mytext]]
579 def parse_wiki_links(text, project, obj, attr, only_path, options)
579 def parse_wiki_links(text, project, obj, attr, only_path, options)
580 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
580 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
581 link_project = project
581 link_project = project
582 esc, all, page, title = $1, $2, $3, $5
582 esc, all, page, title = $1, $2, $3, $5
583 if esc.nil?
583 if esc.nil?
584 if page =~ /^([^\:]+)\:(.*)$/
584 if page =~ /^([^\:]+)\:(.*)$/
585 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
585 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
586 page = $2
586 page = $2
587 title ||= $1 if page.blank?
587 title ||= $1 if page.blank?
588 end
588 end
589
589
590 if link_project && link_project.wiki
590 if link_project && link_project.wiki
591 # extract anchor
591 # extract anchor
592 anchor = nil
592 anchor = nil
593 if page =~ /^(.+?)\#(.+)$/
593 if page =~ /^(.+?)\#(.+)$/
594 page, anchor = $1, $2
594 page, anchor = $1, $2
595 end
595 end
596 anchor = sanitize_anchor_name(anchor) if anchor.present?
596 anchor = sanitize_anchor_name(anchor) if anchor.present?
597 # check if page exists
597 # check if page exists
598 wiki_page = link_project.wiki.find_page(page)
598 wiki_page = link_project.wiki.find_page(page)
599 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
599 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
600 "##{anchor}"
600 "##{anchor}"
601 else
601 else
602 case options[:wiki_links]
602 case options[:wiki_links]
603 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
603 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
604 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
604 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
605 else
605 else
606 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
606 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
607 parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
607 parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
608 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
608 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
609 :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
609 :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
610 end
610 end
611 end
611 end
612 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
612 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
613 else
613 else
614 # project or wiki doesn't exist
614 # project or wiki doesn't exist
615 all
615 all
616 end
616 end
617 else
617 else
618 all
618 all
619 end
619 end
620 end
620 end
621 end
621 end
622
622
623 # Redmine links
623 # Redmine links
624 #
624 #
625 # Examples:
625 # Examples:
626 # Issues:
626 # Issues:
627 # #52 -> Link to issue #52
627 # #52 -> Link to issue #52
628 # Changesets:
628 # Changesets:
629 # r52 -> Link to revision 52
629 # r52 -> Link to revision 52
630 # commit:a85130f -> Link to scmid starting with a85130f
630 # commit:a85130f -> Link to scmid starting with a85130f
631 # Documents:
631 # Documents:
632 # document#17 -> Link to document with id 17
632 # document#17 -> Link to document with id 17
633 # document:Greetings -> Link to the document with title "Greetings"
633 # document:Greetings -> Link to the document with title "Greetings"
634 # document:"Some document" -> Link to the document with title "Some document"
634 # document:"Some document" -> Link to the document with title "Some document"
635 # Versions:
635 # Versions:
636 # version#3 -> Link to version with id 3
636 # version#3 -> Link to version with id 3
637 # version:1.0.0 -> Link to version named "1.0.0"
637 # version:1.0.0 -> Link to version named "1.0.0"
638 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
638 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
639 # Attachments:
639 # Attachments:
640 # attachment:file.zip -> Link to the attachment of the current object named file.zip
640 # attachment:file.zip -> Link to the attachment of the current object named file.zip
641 # Source files:
641 # Source files:
642 # source:some/file -> Link to the file located at /some/file in the project's repository
642 # source:some/file -> Link to the file located at /some/file in the project's repository
643 # source:some/file@52 -> Link to the file's revision 52
643 # source:some/file@52 -> Link to the file's revision 52
644 # source:some/file#L120 -> Link to line 120 of the file
644 # source:some/file#L120 -> Link to line 120 of the file
645 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
645 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
646 # export:some/file -> Force the download of the file
646 # export:some/file -> Force the download of the file
647 # Forum messages:
647 # Forum messages:
648 # message#1218 -> Link to message with id 1218
648 # message#1218 -> Link to message with id 1218
649 #
649 #
650 # Links can refer other objects from other projects, using project identifier:
650 # Links can refer other objects from other projects, using project identifier:
651 # identifier:r52
651 # identifier:r52
652 # identifier:document:"Some document"
652 # identifier:document:"Some document"
653 # identifier:version:1.0.0
653 # identifier:version:1.0.0
654 # identifier:source:some/file
654 # identifier:source:some/file
655 def parse_redmine_links(text, project, obj, attr, only_path, options)
655 def parse_redmine_links(text, project, obj, attr, only_path, options)
656 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|
656 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|
657 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
657 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
658 link = nil
658 link = nil
659 if project_identifier
659 if project_identifier
660 project = Project.visible.find_by_identifier(project_identifier)
660 project = Project.visible.find_by_identifier(project_identifier)
661 end
661 end
662 if esc.nil?
662 if esc.nil?
663 if prefix.nil? && sep == 'r'
663 if prefix.nil? && sep == 'r'
664 if project
664 if project
665 repository = nil
665 repository = nil
666 if repo_identifier
666 if repo_identifier
667 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
667 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
668 else
668 else
669 repository = project.repository
669 repository = project.repository
670 end
670 end
671 # project.changesets.visible raises an SQL error because of a double join on repositories
671 # project.changesets.visible raises an SQL error because of a double join on repositories
672 if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
672 if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
673 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},
673 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},
674 :class => 'changeset',
674 :class => 'changeset',
675 :title => truncate_single_line(changeset.comments, :length => 100))
675 :title => truncate_single_line(changeset.comments, :length => 100))
676 end
676 end
677 end
677 end
678 elsif sep == '#'
678 elsif sep == '#'
679 oid = identifier.to_i
679 oid = identifier.to_i
680 case prefix
680 case prefix
681 when nil
681 when nil
682 if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
682 if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
683 anchor = comment_id ? "note-#{comment_id}" : nil
683 anchor = comment_id ? "note-#{comment_id}" : nil
684 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
684 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
685 :class => issue.css_classes,
685 :class => issue.css_classes,
686 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
686 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
687 end
687 end
688 when 'document'
688 when 'document'
689 if document = Document.visible.find_by_id(oid)
689 if document = Document.visible.find_by_id(oid)
690 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
690 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
691 :class => 'document'
691 :class => 'document'
692 end
692 end
693 when 'version'
693 when 'version'
694 if version = Version.visible.find_by_id(oid)
694 if version = Version.visible.find_by_id(oid)
695 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
695 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
696 :class => 'version'
696 :class => 'version'
697 end
697 end
698 when 'message'
698 when 'message'
699 if message = Message.visible.find_by_id(oid, :include => :parent)
699 if message = Message.visible.find_by_id(oid, :include => :parent)
700 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
700 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
701 end
701 end
702 when 'forum'
702 when 'forum'
703 if board = Board.visible.find_by_id(oid)
703 if board = Board.visible.find_by_id(oid)
704 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
704 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
705 :class => 'board'
705 :class => 'board'
706 end
706 end
707 when 'news'
707 when 'news'
708 if news = News.visible.find_by_id(oid)
708 if news = News.visible.find_by_id(oid)
709 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
709 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
710 :class => 'news'
710 :class => 'news'
711 end
711 end
712 when 'project'
712 when 'project'
713 if p = Project.visible.find_by_id(oid)
713 if p = Project.visible.find_by_id(oid)
714 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
714 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
715 end
715 end
716 end
716 end
717 elsif sep == ':'
717 elsif sep == ':'
718 # removes the double quotes if any
718 # removes the double quotes if any
719 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
719 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
720 case prefix
720 case prefix
721 when 'document'
721 when 'document'
722 if project && document = project.documents.visible.find_by_title(name)
722 if project && document = project.documents.visible.find_by_title(name)
723 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
723 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
724 :class => 'document'
724 :class => 'document'
725 end
725 end
726 when 'version'
726 when 'version'
727 if project && version = project.versions.visible.find_by_name(name)
727 if project && version = project.versions.visible.find_by_name(name)
728 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
728 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
729 :class => 'version'
729 :class => 'version'
730 end
730 end
731 when 'forum'
731 when 'forum'
732 if project && board = project.boards.visible.find_by_name(name)
732 if project && board = project.boards.visible.find_by_name(name)
733 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
733 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
734 :class => 'board'
734 :class => 'board'
735 end
735 end
736 when 'news'
736 when 'news'
737 if project && news = project.news.visible.find_by_title(name)
737 if project && news = project.news.visible.find_by_title(name)
738 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
738 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
739 :class => 'news'
739 :class => 'news'
740 end
740 end
741 when 'commit', 'source', 'export'
741 when 'commit', 'source', 'export'
742 if project
742 if project
743 repository = nil
743 repository = nil
744 if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
744 if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
745 repo_prefix, repo_identifier, name = $1, $2, $3
745 repo_prefix, repo_identifier, name = $1, $2, $3
746 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
746 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
747 else
747 else
748 repository = project.repository
748 repository = project.repository
749 end
749 end
750 if prefix == 'commit'
750 if prefix == 'commit'
751 if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
751 if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
752 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},
752 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},
753 :class => 'changeset',
753 :class => 'changeset',
754 :title => truncate_single_line(h(changeset.comments), :length => 100)
754 :title => truncate_single_line(h(changeset.comments), :length => 100)
755 end
755 end
756 else
756 else
757 if repository && User.current.allowed_to?(:browse_repository, project)
757 if repository && User.current.allowed_to?(:browse_repository, project)
758 name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
758 name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
759 path, rev, anchor = $1, $3, $5
759 path, rev, anchor = $1, $3, $5
760 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
760 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
761 :path => to_path_param(path),
761 :path => to_path_param(path),
762 :rev => rev,
762 :rev => rev,
763 :anchor => anchor},
763 :anchor => anchor},
764 :class => (prefix == 'export' ? 'source download' : 'source')
764 :class => (prefix == 'export' ? 'source download' : 'source')
765 end
765 end
766 end
766 end
767 repo_prefix = nil
767 repo_prefix = nil
768 end
768 end
769 when 'attachment'
769 when 'attachment'
770 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
770 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
771 if attachments && attachment = attachments.detect {|a| a.filename == name }
771 if attachments && attachment = attachments.detect {|a| a.filename == name }
772 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
772 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
773 :class => 'attachment'
773 :class => 'attachment'
774 end
774 end
775 when 'project'
775 when 'project'
776 if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
776 if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
777 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
777 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
778 end
778 end
779 end
779 end
780 end
780 end
781 end
781 end
782 (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
782 (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
783 end
783 end
784 end
784 end
785
785
786 HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
786 HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
787
787
788 def parse_sections(text, project, obj, attr, only_path, options)
788 def parse_sections(text, project, obj, attr, only_path, options)
789 return unless options[:edit_section_links]
789 return unless options[:edit_section_links]
790 text.gsub!(HEADING_RE) do
790 text.gsub!(HEADING_RE) do
791 heading = $1
791 heading = $1
792 @current_section += 1
792 @current_section += 1
793 if @current_section > 1
793 if @current_section > 1
794 content_tag('div',
794 content_tag('div',
795 link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
795 link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
796 :class => 'contextual',
796 :class => 'contextual',
797 :title => l(:button_edit_section)) + heading.html_safe
797 :title => l(:button_edit_section)) + heading.html_safe
798 else
798 else
799 heading
799 heading
800 end
800 end
801 end
801 end
802 end
802 end
803
803
804 # Headings and TOC
804 # Headings and TOC
805 # Adds ids and links to headings unless options[:headings] is set to false
805 # Adds ids and links to headings unless options[:headings] is set to false
806 def parse_headings(text, project, obj, attr, only_path, options)
806 def parse_headings(text, project, obj, attr, only_path, options)
807 return if options[:headings] == false
807 return if options[:headings] == false
808
808
809 text.gsub!(HEADING_RE) do
809 text.gsub!(HEADING_RE) do
810 level, attrs, content = $2.to_i, $3, $4
810 level, attrs, content = $2.to_i, $3, $4
811 item = strip_tags(content).strip
811 item = strip_tags(content).strip
812 anchor = sanitize_anchor_name(item)
812 anchor = sanitize_anchor_name(item)
813 # used for single-file wiki export
813 # used for single-file wiki export
814 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
814 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
815 @heading_anchors[anchor] ||= 0
815 @heading_anchors[anchor] ||= 0
816 idx = (@heading_anchors[anchor] += 1)
816 idx = (@heading_anchors[anchor] += 1)
817 if idx > 1
817 if idx > 1
818 anchor = "#{anchor}-#{idx}"
818 anchor = "#{anchor}-#{idx}"
819 end
819 end
820 @parsed_headings << [level, anchor, item]
820 @parsed_headings << [level, anchor, item]
821 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
821 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
822 end
822 end
823 end
823 end
824
824
825 MACROS_RE = /(
825 MACROS_RE = /(
826 (!)? # escaping
826 (!)? # escaping
827 (
827 (
828 \{\{ # opening tag
828 \{\{ # opening tag
829 ([\w]+) # macro name
829 ([\w]+) # macro name
830 (\(([^\n\r]*?)\))? # optional arguments
830 (\(([^\n\r]*?)\))? # optional arguments
831 ([\n\r].*?[\n\r])? # optional block of text
831 ([\n\r].*?[\n\r])? # optional block of text
832 \}\} # closing tag
832 \}\} # closing tag
833 )
833 )
834 )/mx unless const_defined?(:MACROS_RE)
834 )/mx unless const_defined?(:MACROS_RE)
835
835
836 MACRO_SUB_RE = /(
836 MACRO_SUB_RE = /(
837 \{\{
837 \{\{
838 macro\((\d+)\)
838 macro\((\d+)\)
839 \}\}
839 \}\}
840 )/x unless const_defined?(:MACRO_SUB_RE)
840 )/x unless const_defined?(:MACRO_SUB_RE)
841
841
842 # Extracts macros from text
842 # Extracts macros from text
843 def catch_macros(text)
843 def catch_macros(text)
844 macros = {}
844 macros = {}
845 text.gsub!(MACROS_RE) do
845 text.gsub!(MACROS_RE) do
846 all, macro = $1, $4.downcase
846 all, macro = $1, $4.downcase
847 if macro_exists?(macro) || all =~ MACRO_SUB_RE
847 if macro_exists?(macro) || all =~ MACRO_SUB_RE
848 index = macros.size
848 index = macros.size
849 macros[index] = all
849 macros[index] = all
850 "{{macro(#{index})}}"
850 "{{macro(#{index})}}"
851 else
851 else
852 all
852 all
853 end
853 end
854 end
854 end
855 macros
855 macros
856 end
856 end
857
857
858 # Executes and replaces macros in text
858 # Executes and replaces macros in text
859 def inject_macros(text, obj, macros, execute=true)
859 def inject_macros(text, obj, macros, execute=true)
860 text.gsub!(MACRO_SUB_RE) do
860 text.gsub!(MACRO_SUB_RE) do
861 all, index = $1, $2.to_i
861 all, index = $1, $2.to_i
862 orig = macros.delete(index)
862 orig = macros.delete(index)
863 if execute && orig && orig =~ MACROS_RE
863 if execute && orig && orig =~ MACROS_RE
864 esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
864 esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
865 if esc.nil?
865 if esc.nil?
866 h(exec_macro(macro, obj, args, block) || all)
866 h(exec_macro(macro, obj, args, block) || all)
867 else
867 else
868 h(all)
868 h(all)
869 end
869 end
870 elsif orig
870 elsif orig
871 h(orig)
871 h(orig)
872 else
872 else
873 h(all)
873 h(all)
874 end
874 end
875 end
875 end
876 end
876 end
877
877
878 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
878 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
879
879
880 # Renders the TOC with given headings
880 # Renders the TOC with given headings
881 def replace_toc(text, headings)
881 def replace_toc(text, headings)
882 text.gsub!(TOC_RE) do
882 text.gsub!(TOC_RE) do
883 # Keep only the 4 first levels
883 # Keep only the 4 first levels
884 headings = headings.select{|level, anchor, item| level <= 4}
884 headings = headings.select{|level, anchor, item| level <= 4}
885 if headings.empty?
885 if headings.empty?
886 ''
886 ''
887 else
887 else
888 div_class = 'toc'
888 div_class = 'toc'
889 div_class << ' right' if $1 == '>'
889 div_class << ' right' if $1 == '>'
890 div_class << ' left' if $1 == '<'
890 div_class << ' left' if $1 == '<'
891 out = "<ul class=\"#{div_class}\"><li>"
891 out = "<ul class=\"#{div_class}\"><li>"
892 root = headings.map(&:first).min
892 root = headings.map(&:first).min
893 current = root
893 current = root
894 started = false
894 started = false
895 headings.each do |level, anchor, item|
895 headings.each do |level, anchor, item|
896 if level > current
896 if level > current
897 out << '<ul><li>' * (level - current)
897 out << '<ul><li>' * (level - current)
898 elsif level < current
898 elsif level < current
899 out << "</li></ul>\n" * (current - level) + "</li><li>"
899 out << "</li></ul>\n" * (current - level) + "</li><li>"
900 elsif started
900 elsif started
901 out << '</li><li>'
901 out << '</li><li>'
902 end
902 end
903 out << "<a href=\"##{anchor}\">#{item}</a>"
903 out << "<a href=\"##{anchor}\">#{item}</a>"
904 current = level
904 current = level
905 started = true
905 started = true
906 end
906 end
907 out << '</li></ul>' * (current - root)
907 out << '</li></ul>' * (current - root)
908 out << '</li></ul>'
908 out << '</li></ul>'
909 end
909 end
910 end
910 end
911 end
911 end
912
912
913 # Same as Rails' simple_format helper without using paragraphs
913 # Same as Rails' simple_format helper without using paragraphs
914 def simple_format_without_paragraph(text)
914 def simple_format_without_paragraph(text)
915 text.to_s.
915 text.to_s.
916 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
916 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
917 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
917 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
918 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
918 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
919 html_safe
919 html_safe
920 end
920 end
921
921
922 def lang_options_for_select(blank=true)
922 def lang_options_for_select(blank=true)
923 (blank ? [["(auto)", ""]] : []) + languages_options
923 (blank ? [["(auto)", ""]] : []) + languages_options
924 end
924 end
925
925
926 def label_tag_for(name, option_tags = nil, options = {})
926 def label_tag_for(name, option_tags = nil, options = {})
927 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
927 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
928 content_tag("label", label_text)
928 content_tag("label", label_text)
929 end
929 end
930
930
931 def labelled_form_for(*args, &proc)
931 def labelled_form_for(*args, &proc)
932 args << {} unless args.last.is_a?(Hash)
932 args << {} unless args.last.is_a?(Hash)
933 options = args.last
933 options = args.last
934 if args.first.is_a?(Symbol)
934 if args.first.is_a?(Symbol)
935 options.merge!(:as => args.shift)
935 options.merge!(:as => args.shift)
936 end
936 end
937 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
937 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
938 form_for(*args, &proc)
938 form_for(*args, &proc)
939 end
939 end
940
940
941 def labelled_fields_for(*args, &proc)
941 def labelled_fields_for(*args, &proc)
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.merge!({:builder => Redmine::Views::LabelledFormBuilder})
944 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
945 fields_for(*args, &proc)
945 fields_for(*args, &proc)
946 end
946 end
947
947
948 def labelled_remote_form_for(*args, &proc)
948 def labelled_remote_form_for(*args, &proc)
949 ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_remote_form_for is deprecated and will be removed in Redmine 2.2."
949 ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_remote_form_for is deprecated and will be removed in Redmine 2.2."
950 args << {} unless args.last.is_a?(Hash)
950 args << {} unless args.last.is_a?(Hash)
951 options = args.last
951 options = args.last
952 options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
952 options.merge!({:builder => Redmine::Views::LabelledFormBuilder, :remote => true})
953 form_for(*args, &proc)
953 form_for(*args, &proc)
954 end
954 end
955
955
956 def error_messages_for(*objects)
956 def error_messages_for(*objects)
957 html = ""
957 html = ""
958 objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
958 objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
959 errors = objects.map {|o| o.errors.full_messages}.flatten
959 errors = objects.map {|o| o.errors.full_messages}.flatten
960 if errors.any?
960 if errors.any?
961 html << "<div id='errorExplanation'><ul>\n"
961 html << "<div id='errorExplanation'><ul>\n"
962 errors.each do |error|
962 errors.each do |error|
963 html << "<li>#{h error}</li>\n"
963 html << "<li>#{h error}</li>\n"
964 end
964 end
965 html << "</ul></div>\n"
965 html << "</ul></div>\n"
966 end
966 end
967 html.html_safe
967 html.html_safe
968 end
968 end
969
969
970 def delete_link(url, options={})
970 def delete_link(url, options={})
971 options = {
971 options = {
972 :method => :delete,
972 :method => :delete,
973 :data => {:confirm => l(:text_are_you_sure)},
973 :data => {:confirm => l(:text_are_you_sure)},
974 :class => 'icon icon-del'
974 :class => 'icon icon-del'
975 }.merge(options)
975 }.merge(options)
976
976
977 link_to l(:button_delete), url, options
977 link_to l(:button_delete), url, options
978 end
978 end
979
979
980 def preview_link(url, form, target='preview', options={})
980 def preview_link(url, form, target='preview', options={})
981 content_tag 'a', l(:label_preview), {
981 content_tag 'a', l(:label_preview), {
982 :href => "#",
982 :href => "#",
983 :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
983 :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
984 :accesskey => accesskey(:preview)
984 :accesskey => accesskey(:preview)
985 }.merge(options)
985 }.merge(options)
986 end
986 end
987
987
988 def link_to_function(name, function, html_options={})
988 def link_to_function(name, function, html_options={})
989 content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
989 content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
990 end
990 end
991
991
992 # Helper to render JSON in views
992 # Helper to render JSON in views
993 def raw_json(arg)
993 def raw_json(arg)
994 arg.to_json.to_s.gsub('/', '\/').html_safe
994 arg.to_json.to_s.gsub('/', '\/').html_safe
995 end
995 end
996
996
997 def back_url
997 def back_url
998 url = params[:back_url]
998 url = params[:back_url]
999 if url.nil? && referer = request.env['HTTP_REFERER']
999 if url.nil? && referer = request.env['HTTP_REFERER']
1000 url = CGI.unescape(referer.to_s)
1000 url = CGI.unescape(referer.to_s)
1001 end
1001 end
1002 url
1002 url
1003 end
1003 end
1004
1004
1005 def back_url_hidden_field_tag
1005 def back_url_hidden_field_tag
1006 url = back_url
1006 url = back_url
1007 hidden_field_tag('back_url', url, :id => nil) unless url.blank?
1007 hidden_field_tag('back_url', url, :id => nil) unless url.blank?
1008 end
1008 end
1009
1009
1010 def check_all_links(form_name)
1010 def check_all_links(form_name)
1011 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
1011 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
1012 " | ".html_safe +
1012 " | ".html_safe +
1013 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
1013 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
1014 end
1014 end
1015
1015
1016 def progress_bar(pcts, options={})
1016 def progress_bar(pcts, options={})
1017 pcts = [pcts, pcts] unless pcts.is_a?(Array)
1017 pcts = [pcts, pcts] unless pcts.is_a?(Array)
1018 pcts = pcts.collect(&:round)
1018 pcts = pcts.collect(&:round)
1019 pcts[1] = pcts[1] - pcts[0]
1019 pcts[1] = pcts[1] - pcts[0]
1020 pcts << (100 - pcts[1] - pcts[0])
1020 pcts << (100 - pcts[1] - pcts[0])
1021 width = options[:width] || '100px;'
1021 width = options[:width] || '100px;'
1022 legend = options[:legend] || ''
1022 legend = options[:legend] || ''
1023 content_tag('table',
1023 content_tag('table',
1024 content_tag('tr',
1024 content_tag('tr',
1025 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
1025 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
1026 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
1026 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
1027 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
1027 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
1028 ), :class => 'progress', :style => "width: #{width};").html_safe +
1028 ), :class => 'progress', :style => "width: #{width};").html_safe +
1029 content_tag('p', legend, :class => 'pourcent').html_safe
1029 content_tag('p', legend, :class => 'pourcent').html_safe
1030 end
1030 end
1031
1031
1032 def checked_image(checked=true)
1032 def checked_image(checked=true)
1033 if checked
1033 if checked
1034 image_tag 'toggle_check.png'
1034 image_tag 'toggle_check.png'
1035 end
1035 end
1036 end
1036 end
1037
1037
1038 def context_menu(url)
1038 def context_menu(url)
1039 unless @context_menu_included
1039 unless @context_menu_included
1040 content_for :header_tags do
1040 content_for :header_tags do
1041 javascript_include_tag('context_menu') +
1041 javascript_include_tag('context_menu') +
1042 stylesheet_link_tag('context_menu')
1042 stylesheet_link_tag('context_menu')
1043 end
1043 end
1044 if l(:direction) == 'rtl'
1044 if l(:direction) == 'rtl'
1045 content_for :header_tags do
1045 content_for :header_tags do
1046 stylesheet_link_tag('context_menu_rtl')
1046 stylesheet_link_tag('context_menu_rtl')
1047 end
1047 end
1048 end
1048 end
1049 @context_menu_included = true
1049 @context_menu_included = true
1050 end
1050 end
1051 javascript_tag "contextMenuInit('#{ url_for(url) }')"
1051 javascript_tag "contextMenuInit('#{ url_for(url) }')"
1052 end
1052 end
1053
1053
1054 def calendar_for(field_id)
1054 def calendar_for(field_id)
1055 include_calendar_headers_tags
1055 include_calendar_headers_tags
1056 javascript_tag("$(function() { $('##{field_id}').datepicker(datepickerOptions); });")
1056 javascript_tag("$(function() { $('##{field_id}').datepicker(datepickerOptions); });")
1057 end
1057 end
1058
1058
1059 def include_calendar_headers_tags
1059 def include_calendar_headers_tags
1060 unless @calendar_headers_tags_included
1060 unless @calendar_headers_tags_included
1061 @calendar_headers_tags_included = true
1061 @calendar_headers_tags_included = true
1062 content_for :header_tags do
1062 content_for :header_tags do
1063 start_of_week = Setting.start_of_week
1063 start_of_week = Setting.start_of_week
1064 start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
1064 start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
1065 # Redmine uses 1..7 (monday..sunday) in settings and locales
1065 # Redmine uses 1..7 (monday..sunday) in settings and locales
1066 # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
1066 # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
1067 start_of_week = start_of_week.to_i % 7
1067 start_of_week = start_of_week.to_i % 7
1068
1068
1069 tags = javascript_tag(
1069 tags = javascript_tag(
1070 "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
1070 "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
1071 "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
1071 "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
1072 path_to_image('/images/calendar.png') +
1072 path_to_image('/images/calendar.png') +
1073 "', showButtonPanel: true};")
1073 "', showButtonPanel: true};")
1074 jquery_locale = l('jquery.locale', :default => current_language.to_s)
1074 jquery_locale = l('jquery.locale', :default => current_language.to_s)
1075 unless jquery_locale == 'en'
1075 unless jquery_locale == 'en'
1076 tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
1076 tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
1077 end
1077 end
1078 tags
1078 tags
1079 end
1079 end
1080 end
1080 end
1081 end
1081 end
1082
1082
1083 # Overrides Rails' stylesheet_link_tag with themes and plugins support.
1083 # Overrides Rails' stylesheet_link_tag with themes and plugins support.
1084 # Examples:
1084 # Examples:
1085 # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
1085 # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
1086 # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
1086 # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
1087 #
1087 #
1088 def stylesheet_link_tag(*sources)
1088 def stylesheet_link_tag(*sources)
1089 options = sources.last.is_a?(Hash) ? sources.pop : {}
1089 options = sources.last.is_a?(Hash) ? sources.pop : {}
1090 plugin = options.delete(:plugin)
1090 plugin = options.delete(:plugin)
1091 sources = sources.map do |source|
1091 sources = sources.map do |source|
1092 if plugin
1092 if plugin
1093 "/plugin_assets/#{plugin}/stylesheets/#{source}"
1093 "/plugin_assets/#{plugin}/stylesheets/#{source}"
1094 elsif current_theme && current_theme.stylesheets.include?(source)
1094 elsif current_theme && current_theme.stylesheets.include?(source)
1095 current_theme.stylesheet_path(source)
1095 current_theme.stylesheet_path(source)
1096 else
1096 else
1097 source
1097 source
1098 end
1098 end
1099 end
1099 end
1100 super sources, options
1100 super sources, options
1101 end
1101 end
1102
1102
1103 # Overrides Rails' image_tag with themes and plugins support.
1103 # Overrides Rails' image_tag with themes and plugins support.
1104 # Examples:
1104 # Examples:
1105 # image_tag('image.png') # => picks image.png from the current theme or defaults
1105 # image_tag('image.png') # => picks image.png from the current theme or defaults
1106 # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
1106 # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
1107 #
1107 #
1108 def image_tag(source, options={})
1108 def image_tag(source, options={})
1109 if plugin = options.delete(:plugin)
1109 if plugin = options.delete(:plugin)
1110 source = "/plugin_assets/#{plugin}/images/#{source}"
1110 source = "/plugin_assets/#{plugin}/images/#{source}"
1111 elsif current_theme && current_theme.images.include?(source)
1111 elsif current_theme && current_theme.images.include?(source)
1112 source = current_theme.image_path(source)
1112 source = current_theme.image_path(source)
1113 end
1113 end
1114 super source, options
1114 super source, options
1115 end
1115 end
1116
1116
1117 # Overrides Rails' javascript_include_tag with plugins support
1117 # Overrides Rails' javascript_include_tag with plugins support
1118 # Examples:
1118 # Examples:
1119 # javascript_include_tag('scripts') # => picks scripts.js from defaults
1119 # javascript_include_tag('scripts') # => picks scripts.js from defaults
1120 # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
1120 # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
1121 #
1121 #
1122 def javascript_include_tag(*sources)
1122 def javascript_include_tag(*sources)
1123 options = sources.last.is_a?(Hash) ? sources.pop : {}
1123 options = sources.last.is_a?(Hash) ? sources.pop : {}
1124 if plugin = options.delete(:plugin)
1124 if plugin = options.delete(:plugin)
1125 sources = sources.map do |source|
1125 sources = sources.map do |source|
1126 if plugin
1126 if plugin
1127 "/plugin_assets/#{plugin}/javascripts/#{source}"
1127 "/plugin_assets/#{plugin}/javascripts/#{source}"
1128 else
1128 else
1129 source
1129 source
1130 end
1130 end
1131 end
1131 end
1132 end
1132 end
1133 super sources, options
1133 super sources, options
1134 end
1134 end
1135
1135
1136 def content_for(name, content = nil, &block)
1136 def content_for(name, content = nil, &block)
1137 @has_content ||= {}
1137 @has_content ||= {}
1138 @has_content[name] = true
1138 @has_content[name] = true
1139 super(name, content, &block)
1139 super(name, content, &block)
1140 end
1140 end
1141
1141
1142 def has_content?(name)
1142 def has_content?(name)
1143 (@has_content && @has_content[name]) || false
1143 (@has_content && @has_content[name]) || false
1144 end
1144 end
1145
1145
1146 def sidebar_content?
1146 def sidebar_content?
1147 has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1147 has_content?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1148 end
1148 end
1149
1149
1150 def view_layouts_base_sidebar_hook_response
1150 def view_layouts_base_sidebar_hook_response
1151 @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1151 @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1152 end
1152 end
1153
1153
1154 def email_delivery_enabled?
1154 def email_delivery_enabled?
1155 !!ActionMailer::Base.perform_deliveries
1155 !!ActionMailer::Base.perform_deliveries
1156 end
1156 end
1157
1157
1158 # Returns the avatar image tag for the given +user+ if avatars are enabled
1158 # Returns the avatar image tag for the given +user+ if avatars are enabled
1159 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
1159 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
1160 def avatar(user, options = { })
1160 def avatar(user, options = { })
1161 if Setting.gravatar_enabled?
1161 if Setting.gravatar_enabled?
1162 options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default})
1162 options.merge!({:ssl => (request && request.ssl?), :default => Setting.gravatar_default})
1163 email = nil
1163 email = nil
1164 if user.respond_to?(:mail)
1164 if user.respond_to?(:mail)
1165 email = user.mail
1165 email = user.mail
1166 elsif user.to_s =~ %r{<(.+?)>}
1166 elsif user.to_s =~ %r{<(.+?)>}
1167 email = $1
1167 email = $1
1168 end
1168 end
1169 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1169 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1170 else
1170 else
1171 ''
1171 ''
1172 end
1172 end
1173 end
1173 end
1174
1174
1175 def sanitize_anchor_name(anchor)
1175 def sanitize_anchor_name(anchor)
1176 if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
1176 if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
1177 anchor.gsub(%r{[^\p{Word}\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1177 anchor.gsub(%r{[^\p{Word}\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1178 else
1178 else
1179 # TODO: remove when ruby1.8 is no longer supported
1179 # TODO: remove when ruby1.8 is no longer supported
1180 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1180 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1181 end
1181 end
1182 end
1182 end
1183
1183
1184 # Returns the javascript tags that are included in the html layout head
1184 # Returns the javascript tags that are included in the html layout head
1185 def javascript_heads
1185 def javascript_heads
1186 tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.3', 'application')
1186 tags = javascript_include_tag('jquery-1.8.3-ui-1.9.2-ujs-2.0.3', 'application')
1187 unless User.current.pref.warn_on_leaving_unsaved == '0'
1187 unless User.current.pref.warn_on_leaving_unsaved == '0'
1188 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1188 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1189 end
1189 end
1190 tags
1190 tags
1191 end
1191 end
1192
1192
1193 def favicon
1193 def favicon
1194 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
1194 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
1195 end
1195 end
1196
1196
1197 def robot_exclusion_tag
1197 def robot_exclusion_tag
1198 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1198 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1199 end
1199 end
1200
1200
1201 # Returns true if arg is expected in the API response
1201 # Returns true if arg is expected in the API response
1202 def include_in_api_response?(arg)
1202 def include_in_api_response?(arg)
1203 unless @included_in_api_response
1203 unless @included_in_api_response
1204 param = params[:include]
1204 param = params[:include]
1205 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1205 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1206 @included_in_api_response.collect!(&:strip)
1206 @included_in_api_response.collect!(&:strip)
1207 end
1207 end
1208 @included_in_api_response.include?(arg.to_s)
1208 @included_in_api_response.include?(arg.to_s)
1209 end
1209 end
1210
1210
1211 # Returns options or nil if nometa param or X-Redmine-Nometa header
1211 # Returns options or nil if nometa param or X-Redmine-Nometa header
1212 # was set in the request
1212 # was set in the request
1213 def api_meta(options)
1213 def api_meta(options)
1214 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1214 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1215 # compatibility mode for activeresource clients that raise
1215 # compatibility mode for activeresource clients that raise
1216 # an error when unserializing an array with attributes
1216 # an error when unserializing an array with attributes
1217 nil
1217 nil
1218 else
1218 else
1219 options
1219 options
1220 end
1220 end
1221 end
1221 end
1222
1222
1223 private
1223 private
1224
1224
1225 def wiki_helper
1225 def wiki_helper
1226 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1226 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1227 extend helper
1227 extend helper
1228 return self
1228 return self
1229 end
1229 end
1230
1230
1231 def link_to_content_update(text, url_params = {}, html_options = {})
1231 def link_to_content_update(text, url_params = {}, html_options = {})
1232 link_to(text, url_params, html_options)
1232 link_to(text, url_params, html_options)
1233 end
1233 end
1234 end
1234 end
@@ -1,80 +1,80
1 <!DOCTYPE html>
1 <!DOCTYPE html>
2 <html lang="en">
2 <html lang="en">
3 <head>
3 <head>
4 <meta charset="utf-8" />
4 <meta charset="utf-8" />
5 <title><%=h html_title %></title>
5 <title><%=h html_title %></title>
6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
7 <meta name="keywords" content="issue,bug,tracker" />
7 <meta name="keywords" content="issue,bug,tracker" />
8 <%= csrf_meta_tag %>
8 <%= csrf_meta_tag %>
9 <%= favicon %>
9 <%= favicon %>
10 <%= stylesheet_link_tag 'jquery/jquery-ui-1.8.21', 'application', :media => 'all' %>
10 <%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', :media => 'all' %>
11 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
11 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
12 <%= javascript_heads %>
12 <%= javascript_heads %>
13 <%= heads_for_theme %>
13 <%= heads_for_theme %>
14 <%= call_hook :view_layouts_base_html_head %>
14 <%= call_hook :view_layouts_base_html_head %>
15 <!-- page specific tags -->
15 <!-- page specific tags -->
16 <%= yield :header_tags -%>
16 <%= yield :header_tags -%>
17 </head>
17 </head>
18 <body class="<%=h body_css_classes %>">
18 <body class="<%=h body_css_classes %>">
19 <div id="wrapper">
19 <div id="wrapper">
20 <div id="wrapper2">
20 <div id="wrapper2">
21 <div id="wrapper3">
21 <div id="wrapper3">
22 <div id="top-menu">
22 <div id="top-menu">
23 <div id="account">
23 <div id="account">
24 <%= render_menu :account_menu -%>
24 <%= render_menu :account_menu -%>
25 </div>
25 </div>
26 <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}".html_safe, :id => 'loggedas') if User.current.logged? %>
26 <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}".html_safe, :id => 'loggedas') if User.current.logged? %>
27 <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
27 <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
28 </div>
28 </div>
29
29
30 <div id="header">
30 <div id="header">
31 <% if User.current.logged? || !Setting.login_required? %>
31 <% if User.current.logged? || !Setting.login_required? %>
32 <div id="quick-search">
32 <div id="quick-search">
33 <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
33 <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
34 <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
34 <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
35 <label for='q'>
35 <label for='q'>
36 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
36 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
37 </label>
37 </label>
38 <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
38 <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
39 <% end %>
39 <% end %>
40 <%= render_project_jump_box %>
40 <%= render_project_jump_box %>
41 </div>
41 </div>
42 <% end %>
42 <% end %>
43
43
44 <h1><%= page_header_title %></h1>
44 <h1><%= page_header_title %></h1>
45
45
46 <% if display_main_menu?(@project) %>
46 <% if display_main_menu?(@project) %>
47 <div id="main-menu">
47 <div id="main-menu">
48 <%= render_main_menu(@project) %>
48 <%= render_main_menu(@project) %>
49 </div>
49 </div>
50 <% end %>
50 <% end %>
51 </div>
51 </div>
52
52
53 <div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>">
53 <div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>">
54 <div id="sidebar">
54 <div id="sidebar">
55 <%= yield :sidebar %>
55 <%= yield :sidebar %>
56 <%= view_layouts_base_sidebar_hook_response %>
56 <%= view_layouts_base_sidebar_hook_response %>
57 </div>
57 </div>
58
58
59 <div id="content">
59 <div id="content">
60 <%= render_flash_messages %>
60 <%= render_flash_messages %>
61 <%= yield %>
61 <%= yield %>
62 <%= call_hook :view_layouts_base_content %>
62 <%= call_hook :view_layouts_base_content %>
63 <div style="clear:both;"></div>
63 <div style="clear:both;"></div>
64 </div>
64 </div>
65 </div>
65 </div>
66 </div>
66 </div>
67
67
68 <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
68 <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
69 <div id="ajax-modal" style="display:none;"></div>
69 <div id="ajax-modal" style="display:none;"></div>
70
70
71 <div id="footer">
71 <div id="footer">
72 <div class="bgl"><div class="bgr">
72 <div class="bgl"><div class="bgr">
73 Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2012 Jean-Philippe Lang
73 Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2012 Jean-Philippe Lang
74 </div></div>
74 </div></div>
75 </div>
75 </div>
76 </div>
76 </div>
77 </div>
77 </div>
78 <%= call_hook :view_layouts_base_body_bottom %>
78 <%= call_hook :view_layouts_base_body_bottom %>
79 </body>
79 </body>
80 </html>
80 </html>
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now