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