##// END OF EJS Templates
Don't truncate activity titles (#23575)....
Jean-Philippe Lang -
r15476:8175afd58d92
parent child
Show More
@@ -1,1371 +1,1372
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require 'forwardable'
20 require 'forwardable'
21 require 'cgi'
21 require 'cgi'
22
22
23 module ApplicationHelper
23 module ApplicationHelper
24 include Redmine::WikiFormatting::Macros::Definitions
24 include Redmine::WikiFormatting::Macros::Definitions
25 include Redmine::I18n
25 include Redmine::I18n
26 include GravatarHelper::PublicMethods
26 include GravatarHelper::PublicMethods
27 include Redmine::Pagination::Helper
27 include Redmine::Pagination::Helper
28 include Redmine::SudoMode::Helper
28 include Redmine::SudoMode::Helper
29 include Redmine::Themes::Helper
29 include Redmine::Themes::Helper
30 include Redmine::Hook::Helper
30 include Redmine::Hook::Helper
31 include Redmine::Helpers::URL
31 include Redmine::Helpers::URL
32
32
33 extend Forwardable
33 extend Forwardable
34 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
34 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
35
35
36 # Return true if user is authorized for controller/action, otherwise false
36 # Return true if user is authorized for controller/action, otherwise false
37 def authorize_for(controller, action)
37 def authorize_for(controller, action)
38 User.current.allowed_to?({:controller => controller, :action => action}, @project)
38 User.current.allowed_to?({:controller => controller, :action => action}, @project)
39 end
39 end
40
40
41 # Display a link if user is authorized
41 # Display a link if user is authorized
42 #
42 #
43 # @param [String] name Anchor text (passed to link_to)
43 # @param [String] name Anchor text (passed to link_to)
44 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
44 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
45 # @param [optional, Hash] html_options Options passed to link_to
45 # @param [optional, Hash] html_options Options passed to link_to
46 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
46 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
47 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
47 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
48 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
48 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
49 end
49 end
50
50
51 # Displays a link to user's account page if active
51 # Displays a link to user's account page if active
52 def link_to_user(user, options={})
52 def link_to_user(user, options={})
53 if user.is_a?(User)
53 if user.is_a?(User)
54 name = h(user.name(options[:format]))
54 name = h(user.name(options[:format]))
55 if user.active? || (User.current.admin? && user.logged?)
55 if user.active? || (User.current.admin? && user.logged?)
56 link_to name, user_path(user), :class => user.css_classes
56 link_to name, user_path(user), :class => user.css_classes
57 else
57 else
58 name
58 name
59 end
59 end
60 else
60 else
61 h(user.to_s)
61 h(user.to_s)
62 end
62 end
63 end
63 end
64
64
65 # Displays a link to +issue+ with its subject.
65 # Displays a link to +issue+ with its subject.
66 # Examples:
66 # Examples:
67 #
67 #
68 # link_to_issue(issue) # => Defect #6: This is the subject
68 # link_to_issue(issue) # => Defect #6: This is the subject
69 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
69 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
70 # link_to_issue(issue, :subject => false) # => Defect #6
70 # link_to_issue(issue, :subject => false) # => Defect #6
71 # link_to_issue(issue, :project => true) # => Foo - Defect #6
71 # link_to_issue(issue, :project => true) # => Foo - Defect #6
72 # link_to_issue(issue, :subject => false, :tracker => false) # => #6
72 # link_to_issue(issue, :subject => false, :tracker => false) # => #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 text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
77 text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
78 if options[:subject] == false
78 if options[:subject] == false
79 title = issue.subject.truncate(60)
79 title = issue.subject.truncate(60)
80 else
80 else
81 subject = issue.subject
81 subject = issue.subject
82 if truncate_length = options[:truncate]
82 if truncate_length = options[:truncate]
83 subject = subject.truncate(truncate_length)
83 subject = subject.truncate(truncate_length)
84 end
84 end
85 end
85 end
86 only_path = options[:only_path].nil? ? true : options[:only_path]
86 only_path = options[:only_path].nil? ? true : options[:only_path]
87 s = link_to(text, issue_url(issue, :only_path => only_path),
87 s = link_to(text, issue_url(issue, :only_path => only_path),
88 :class => issue.css_classes, :title => title)
88 :class => issue.css_classes, :title => title)
89 s << h(": #{subject}") if subject
89 s << h(": #{subject}") if subject
90 s = h("#{issue.project} - ") + s if options[:project]
90 s = h("#{issue.project} - ") + s if options[:project]
91 s
91 s
92 end
92 end
93
93
94 # Generates a link to an attachment.
94 # Generates a link to an attachment.
95 # Options:
95 # Options:
96 # * :text - Link text (default to attachment filename)
96 # * :text - Link text (default to attachment filename)
97 # * :download - Force download (default: false)
97 # * :download - Force download (default: false)
98 def link_to_attachment(attachment, options={})
98 def link_to_attachment(attachment, options={})
99 text = options.delete(:text) || attachment.filename
99 text = options.delete(:text) || attachment.filename
100 route_method = options.delete(:download) ? :download_named_attachment_url : :named_attachment_url
100 route_method = options.delete(:download) ? :download_named_attachment_url : :named_attachment_url
101 html_options = options.slice!(:only_path)
101 html_options = options.slice!(:only_path)
102 options[:only_path] = true unless options.key?(:only_path)
102 options[:only_path] = true unless options.key?(:only_path)
103 url = send(route_method, attachment, attachment.filename, options)
103 url = send(route_method, attachment, attachment.filename, options)
104 link_to text, url, html_options
104 link_to text, url, html_options
105 end
105 end
106
106
107 # Generates a link to a SCM revision
107 # Generates a link to a SCM revision
108 # Options:
108 # Options:
109 # * :text - Link text (default to the formatted revision)
109 # * :text - Link text (default to the formatted revision)
110 def link_to_revision(revision, repository, options={})
110 def link_to_revision(revision, repository, options={})
111 if repository.is_a?(Project)
111 if repository.is_a?(Project)
112 repository = repository.repository
112 repository = repository.repository
113 end
113 end
114 text = options.delete(:text) || format_revision(revision)
114 text = options.delete(:text) || format_revision(revision)
115 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
115 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
116 link_to(
116 link_to(
117 h(text),
117 h(text),
118 {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
118 {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
119 :title => l(:label_revision_id, format_revision(revision)),
119 :title => l(:label_revision_id, format_revision(revision)),
120 :accesskey => options[:accesskey]
120 :accesskey => options[:accesskey]
121 )
121 )
122 end
122 end
123
123
124 # Generates a link to a message
124 # Generates a link to a message
125 def link_to_message(message, options={}, html_options = nil)
125 def link_to_message(message, options={}, html_options = nil)
126 link_to(
126 link_to(
127 message.subject.truncate(60),
127 message.subject.truncate(60),
128 board_message_url(message.board_id, message.parent_id || message.id, {
128 board_message_url(message.board_id, message.parent_id || message.id, {
129 :r => (message.parent_id && message.id),
129 :r => (message.parent_id && message.id),
130 :anchor => (message.parent_id ? "message-#{message.id}" : nil),
130 :anchor => (message.parent_id ? "message-#{message.id}" : nil),
131 :only_path => true
131 :only_path => true
132 }.merge(options)),
132 }.merge(options)),
133 html_options
133 html_options
134 )
134 )
135 end
135 end
136
136
137 # Generates a link to a project if active
137 # Generates a link to a project if active
138 # Examples:
138 # Examples:
139 #
139 #
140 # link_to_project(project) # => link to the specified project overview
140 # link_to_project(project) # => link to the specified project overview
141 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
141 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
142 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
142 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
143 #
143 #
144 def link_to_project(project, options={}, html_options = nil)
144 def link_to_project(project, options={}, html_options = nil)
145 if project.archived?
145 if project.archived?
146 h(project.name)
146 h(project.name)
147 else
147 else
148 link_to project.name,
148 link_to project.name,
149 project_url(project, {:only_path => true}.merge(options)),
149 project_url(project, {:only_path => true}.merge(options)),
150 html_options
150 html_options
151 end
151 end
152 end
152 end
153
153
154 # Generates a link to a project settings if active
154 # Generates a link to a project settings if active
155 def link_to_project_settings(project, options={}, html_options=nil)
155 def link_to_project_settings(project, options={}, html_options=nil)
156 if project.active?
156 if project.active?
157 link_to project.name, settings_project_path(project, options), html_options
157 link_to project.name, settings_project_path(project, options), html_options
158 elsif project.archived?
158 elsif project.archived?
159 h(project.name)
159 h(project.name)
160 else
160 else
161 link_to project.name, project_path(project, options), html_options
161 link_to project.name, project_path(project, options), html_options
162 end
162 end
163 end
163 end
164
164
165 # Generates a link to a version
165 # Generates a link to a version
166 def link_to_version(version, options = {})
166 def link_to_version(version, options = {})
167 return '' unless version && version.is_a?(Version)
167 return '' unless version && version.is_a?(Version)
168 options = {:title => format_date(version.effective_date)}.merge(options)
168 options = {:title => format_date(version.effective_date)}.merge(options)
169 link_to_if version.visible?, format_version_name(version), version_path(version), options
169 link_to_if version.visible?, format_version_name(version), version_path(version), options
170 end
170 end
171
171
172 # Helper that formats object for html or text rendering
172 # Helper that formats object for html or text rendering
173 def format_object(object, html=true, &block)
173 def format_object(object, html=true, &block)
174 if block_given?
174 if block_given?
175 object = yield object
175 object = yield object
176 end
176 end
177 case object.class.name
177 case object.class.name
178 when 'Array'
178 when 'Array'
179 object.map {|o| format_object(o, html)}.join(', ').html_safe
179 object.map {|o| format_object(o, html)}.join(', ').html_safe
180 when 'Time'
180 when 'Time'
181 format_time(object)
181 format_time(object)
182 when 'Date'
182 when 'Date'
183 format_date(object)
183 format_date(object)
184 when 'Fixnum'
184 when 'Fixnum'
185 object.to_s
185 object.to_s
186 when 'Float'
186 when 'Float'
187 sprintf "%.2f", object
187 sprintf "%.2f", object
188 when 'User'
188 when 'User'
189 html ? link_to_user(object) : object.to_s
189 html ? link_to_user(object) : object.to_s
190 when 'Project'
190 when 'Project'
191 html ? link_to_project(object) : object.to_s
191 html ? link_to_project(object) : object.to_s
192 when 'Version'
192 when 'Version'
193 html ? link_to_version(object) : object.to_s
193 html ? link_to_version(object) : object.to_s
194 when 'TrueClass'
194 when 'TrueClass'
195 l(:general_text_Yes)
195 l(:general_text_Yes)
196 when 'FalseClass'
196 when 'FalseClass'
197 l(:general_text_No)
197 l(:general_text_No)
198 when 'Issue'
198 when 'Issue'
199 object.visible? && html ? link_to_issue(object) : "##{object.id}"
199 object.visible? && html ? link_to_issue(object) : "##{object.id}"
200 when 'CustomValue', 'CustomFieldValue'
200 when 'CustomValue', 'CustomFieldValue'
201 if object.custom_field
201 if object.custom_field
202 f = object.custom_field.format.formatted_custom_value(self, object, html)
202 f = object.custom_field.format.formatted_custom_value(self, object, html)
203 if f.nil? || f.is_a?(String)
203 if f.nil? || f.is_a?(String)
204 f
204 f
205 else
205 else
206 format_object(f, html, &block)
206 format_object(f, html, &block)
207 end
207 end
208 else
208 else
209 object.value.to_s
209 object.value.to_s
210 end
210 end
211 else
211 else
212 html ? h(object) : object.to_s
212 html ? h(object) : object.to_s
213 end
213 end
214 end
214 end
215
215
216 def wiki_page_path(page, options={})
216 def wiki_page_path(page, options={})
217 url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
217 url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
218 end
218 end
219
219
220 def thumbnail_tag(attachment)
220 def thumbnail_tag(attachment)
221 link_to image_tag(thumbnail_path(attachment)),
221 link_to image_tag(thumbnail_path(attachment)),
222 named_attachment_path(attachment, attachment.filename),
222 named_attachment_path(attachment, attachment.filename),
223 :title => attachment.filename
223 :title => attachment.filename
224 end
224 end
225
225
226 def toggle_link(name, id, options={})
226 def toggle_link(name, id, options={})
227 onclick = "$('##{id}').toggle(); "
227 onclick = "$('##{id}').toggle(); "
228 onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
228 onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ")
229 onclick << "return false;"
229 onclick << "return false;"
230 link_to(name, "#", :onclick => onclick)
230 link_to(name, "#", :onclick => onclick)
231 end
231 end
232
232
233 # Used to format item titles on the activity view
233 def format_activity_title(text)
234 def format_activity_title(text)
234 h(truncate_single_line_raw(text, 100))
235 text
235 end
236 end
236
237
237 def format_activity_day(date)
238 def format_activity_day(date)
238 date == User.current.today ? l(:label_today).titleize : format_date(date)
239 date == User.current.today ? l(:label_today).titleize : format_date(date)
239 end
240 end
240
241
241 def format_activity_description(text)
242 def format_activity_description(text)
242 h(text.to_s.truncate(120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
243 h(text.to_s.truncate(120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
243 ).gsub(/[\r\n]+/, "<br />").html_safe
244 ).gsub(/[\r\n]+/, "<br />").html_safe
244 end
245 end
245
246
246 def format_version_name(version)
247 def format_version_name(version)
247 if version.project == @project
248 if version.project == @project
248 h(version)
249 h(version)
249 else
250 else
250 h("#{version.project} - #{version}")
251 h("#{version.project} - #{version}")
251 end
252 end
252 end
253 end
253
254
254 def due_date_distance_in_words(date)
255 def due_date_distance_in_words(date)
255 if date
256 if date
256 l((date < User.current.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(User.current.today, date))
257 l((date < User.current.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(User.current.today, date))
257 end
258 end
258 end
259 end
259
260
260 # Renders a tree of projects as a nested set of unordered lists
261 # Renders a tree of projects as a nested set of unordered lists
261 # The given collection may be a subset of the whole project tree
262 # The given collection may be a subset of the whole project tree
262 # (eg. some intermediate nodes are private and can not be seen)
263 # (eg. some intermediate nodes are private and can not be seen)
263 def render_project_nested_lists(projects, &block)
264 def render_project_nested_lists(projects, &block)
264 s = ''
265 s = ''
265 if projects.any?
266 if projects.any?
266 ancestors = []
267 ancestors = []
267 original_project = @project
268 original_project = @project
268 projects.sort_by(&:lft).each do |project|
269 projects.sort_by(&:lft).each do |project|
269 # set the project environment to please macros.
270 # set the project environment to please macros.
270 @project = project
271 @project = project
271 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
272 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
272 s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
273 s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
273 else
274 else
274 ancestors.pop
275 ancestors.pop
275 s << "</li>"
276 s << "</li>"
276 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
277 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
277 ancestors.pop
278 ancestors.pop
278 s << "</ul></li>\n"
279 s << "</ul></li>\n"
279 end
280 end
280 end
281 end
281 classes = (ancestors.empty? ? 'root' : 'child')
282 classes = (ancestors.empty? ? 'root' : 'child')
282 s << "<li class='#{classes}'><div class='#{classes}'>"
283 s << "<li class='#{classes}'><div class='#{classes}'>"
283 s << h(block_given? ? capture(project, &block) : project.name)
284 s << h(block_given? ? capture(project, &block) : project.name)
284 s << "</div>\n"
285 s << "</div>\n"
285 ancestors << project
286 ancestors << project
286 end
287 end
287 s << ("</li></ul>\n" * ancestors.size)
288 s << ("</li></ul>\n" * ancestors.size)
288 @project = original_project
289 @project = original_project
289 end
290 end
290 s.html_safe
291 s.html_safe
291 end
292 end
292
293
293 def render_page_hierarchy(pages, node=nil, options={})
294 def render_page_hierarchy(pages, node=nil, options={})
294 content = ''
295 content = ''
295 if pages[node]
296 if pages[node]
296 content << "<ul class=\"pages-hierarchy\">\n"
297 content << "<ul class=\"pages-hierarchy\">\n"
297 pages[node].each do |page|
298 pages[node].each do |page|
298 content << "<li>"
299 content << "<li>"
299 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil},
300 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil},
300 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
301 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
301 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
302 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
302 content << "</li>\n"
303 content << "</li>\n"
303 end
304 end
304 content << "</ul>\n"
305 content << "</ul>\n"
305 end
306 end
306 content.html_safe
307 content.html_safe
307 end
308 end
308
309
309 # Renders flash messages
310 # Renders flash messages
310 def render_flash_messages
311 def render_flash_messages
311 s = ''
312 s = ''
312 flash.each do |k,v|
313 flash.each do |k,v|
313 s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
314 s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}")
314 end
315 end
315 s.html_safe
316 s.html_safe
316 end
317 end
317
318
318 # Renders tabs and their content
319 # Renders tabs and their content
319 def render_tabs(tabs, selected=params[:tab])
320 def render_tabs(tabs, selected=params[:tab])
320 if tabs.any?
321 if tabs.any?
321 unless tabs.detect {|tab| tab[:name] == selected}
322 unless tabs.detect {|tab| tab[:name] == selected}
322 selected = nil
323 selected = nil
323 end
324 end
324 selected ||= tabs.first[:name]
325 selected ||= tabs.first[:name]
325 render :partial => 'common/tabs', :locals => {:tabs => tabs, :selected_tab => selected}
326 render :partial => 'common/tabs', :locals => {:tabs => tabs, :selected_tab => selected}
326 else
327 else
327 content_tag 'p', l(:label_no_data), :class => "nodata"
328 content_tag 'p', l(:label_no_data), :class => "nodata"
328 end
329 end
329 end
330 end
330
331
331 # Renders the project quick-jump box
332 # Renders the project quick-jump box
332 def render_project_jump_box
333 def render_project_jump_box
333 return unless User.current.logged?
334 return unless User.current.logged?
334 projects = User.current.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a
335 projects = User.current.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a
335 if projects.any?
336 if projects.any?
336 options =
337 options =
337 ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
338 ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
338 '<option value="" disabled="disabled">---</option>').html_safe
339 '<option value="" disabled="disabled">---</option>').html_safe
339
340
340 options << project_tree_options_for_select(projects, :selected => @project) do |p|
341 options << project_tree_options_for_select(projects, :selected => @project) do |p|
341 { :value => project_path(:id => p, :jump => current_menu_item) }
342 { :value => project_path(:id => p, :jump => current_menu_item) }
342 end
343 end
343
344
344 content_tag( :span, nil, :class => 'jump-box-arrow') +
345 content_tag( :span, nil, :class => 'jump-box-arrow') +
345 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
346 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
346 end
347 end
347 end
348 end
348
349
349 def project_tree_options_for_select(projects, options = {})
350 def project_tree_options_for_select(projects, options = {})
350 s = ''.html_safe
351 s = ''.html_safe
351 if blank_text = options[:include_blank]
352 if blank_text = options[:include_blank]
352 if blank_text == true
353 if blank_text == true
353 blank_text = '&nbsp;'.html_safe
354 blank_text = '&nbsp;'.html_safe
354 end
355 end
355 s << content_tag('option', blank_text, :value => '')
356 s << content_tag('option', blank_text, :value => '')
356 end
357 end
357 project_tree(projects) do |project, level|
358 project_tree(projects) do |project, level|
358 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
359 name_prefix = (level > 0 ? '&nbsp;' * 2 * level + '&#187; ' : '').html_safe
359 tag_options = {:value => project.id}
360 tag_options = {:value => project.id}
360 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
361 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
361 tag_options[:selected] = 'selected'
362 tag_options[:selected] = 'selected'
362 else
363 else
363 tag_options[:selected] = nil
364 tag_options[:selected] = nil
364 end
365 end
365 tag_options.merge!(yield(project)) if block_given?
366 tag_options.merge!(yield(project)) if block_given?
366 s << content_tag('option', name_prefix + h(project), tag_options)
367 s << content_tag('option', name_prefix + h(project), tag_options)
367 end
368 end
368 s.html_safe
369 s.html_safe
369 end
370 end
370
371
371 # Yields the given block for each project with its level in the tree
372 # Yields the given block for each project with its level in the tree
372 #
373 #
373 # Wrapper for Project#project_tree
374 # Wrapper for Project#project_tree
374 def project_tree(projects, options={}, &block)
375 def project_tree(projects, options={}, &block)
375 Project.project_tree(projects, options, &block)
376 Project.project_tree(projects, options, &block)
376 end
377 end
377
378
378 def principals_check_box_tags(name, principals)
379 def principals_check_box_tags(name, principals)
379 s = ''
380 s = ''
380 principals.each do |principal|
381 principals.each do |principal|
381 s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{h principal}</label>\n"
382 s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{h principal}</label>\n"
382 end
383 end
383 s.html_safe
384 s.html_safe
384 end
385 end
385
386
386 # Returns a string for users/groups option tags
387 # Returns a string for users/groups option tags
387 def principals_options_for_select(collection, selected=nil)
388 def principals_options_for_select(collection, selected=nil)
388 s = ''
389 s = ''
389 if collection.include?(User.current)
390 if collection.include?(User.current)
390 s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
391 s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id)
391 end
392 end
392 groups = ''
393 groups = ''
393 collection.sort.each do |element|
394 collection.sort.each do |element|
394 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) || element.id.to_s == selected
395 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) || element.id.to_s == selected
395 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
396 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
396 end
397 end
397 unless groups.empty?
398 unless groups.empty?
398 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
399 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
399 end
400 end
400 s.html_safe
401 s.html_safe
401 end
402 end
402
403
403 def option_tag(name, text, value, selected=nil, options={})
404 def option_tag(name, text, value, selected=nil, options={})
404 content_tag 'option', value, options.merge(:value => value, :selected => (value == selected))
405 content_tag 'option', value, options.merge(:value => value, :selected => (value == selected))
405 end
406 end
406
407
407 def truncate_single_line_raw(string, length)
408 def truncate_single_line_raw(string, length)
408 string.to_s.truncate(length).gsub(%r{[\r\n]+}m, ' ')
409 string.to_s.truncate(length).gsub(%r{[\r\n]+}m, ' ')
409 end
410 end
410
411
411 # Truncates at line break after 250 characters or options[:length]
412 # Truncates at line break after 250 characters or options[:length]
412 def truncate_lines(string, options={})
413 def truncate_lines(string, options={})
413 length = options[:length] || 250
414 length = options[:length] || 250
414 if string.to_s =~ /\A(.{#{length}}.*?)$/m
415 if string.to_s =~ /\A(.{#{length}}.*?)$/m
415 "#{$1}..."
416 "#{$1}..."
416 else
417 else
417 string
418 string
418 end
419 end
419 end
420 end
420
421
421 def anchor(text)
422 def anchor(text)
422 text.to_s.gsub(' ', '_')
423 text.to_s.gsub(' ', '_')
423 end
424 end
424
425
425 def html_hours(text)
426 def html_hours(text)
426 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
427 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
427 end
428 end
428
429
429 def authoring(created, author, options={})
430 def authoring(created, author, options={})
430 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
431 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
431 end
432 end
432
433
433 def time_tag(time)
434 def time_tag(time)
434 text = distance_of_time_in_words(Time.now, time)
435 text = distance_of_time_in_words(Time.now, time)
435 if @project
436 if @project
436 link_to(text, project_activity_path(@project, :from => User.current.time_to_date(time)), :title => format_time(time))
437 link_to(text, project_activity_path(@project, :from => User.current.time_to_date(time)), :title => format_time(time))
437 else
438 else
438 content_tag('abbr', text, :title => format_time(time))
439 content_tag('abbr', text, :title => format_time(time))
439 end
440 end
440 end
441 end
441
442
442 def syntax_highlight_lines(name, content)
443 def syntax_highlight_lines(name, content)
443 lines = []
444 lines = []
444 syntax_highlight(name, content).each_line { |line| lines << line }
445 syntax_highlight(name, content).each_line { |line| lines << line }
445 lines
446 lines
446 end
447 end
447
448
448 def syntax_highlight(name, content)
449 def syntax_highlight(name, content)
449 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
450 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
450 end
451 end
451
452
452 def to_path_param(path)
453 def to_path_param(path)
453 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
454 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
454 str.blank? ? nil : str
455 str.blank? ? nil : str
455 end
456 end
456
457
457 def reorder_links(name, url, method = :post)
458 def reorder_links(name, url, method = :post)
458 # TODO: remove associated styles from application.css too
459 # TODO: remove associated styles from application.css too
459 ActiveSupport::Deprecation.warn "Application#reorder_links will be removed in Redmine 4."
460 ActiveSupport::Deprecation.warn "Application#reorder_links will be removed in Redmine 4."
460
461
461 link_to(l(:label_sort_highest),
462 link_to(l(:label_sort_highest),
462 url.merge({"#{name}[move_to]" => 'highest'}), :method => method,
463 url.merge({"#{name}[move_to]" => 'highest'}), :method => method,
463 :title => l(:label_sort_highest), :class => 'icon-only icon-move-top') +
464 :title => l(:label_sort_highest), :class => 'icon-only icon-move-top') +
464 link_to(l(:label_sort_higher),
465 link_to(l(:label_sort_higher),
465 url.merge({"#{name}[move_to]" => 'higher'}), :method => method,
466 url.merge({"#{name}[move_to]" => 'higher'}), :method => method,
466 :title => l(:label_sort_higher), :class => 'icon-only icon-move-up') +
467 :title => l(:label_sort_higher), :class => 'icon-only icon-move-up') +
467 link_to(l(:label_sort_lower),
468 link_to(l(:label_sort_lower),
468 url.merge({"#{name}[move_to]" => 'lower'}), :method => method,
469 url.merge({"#{name}[move_to]" => 'lower'}), :method => method,
469 :title => l(:label_sort_lower), :class => 'icon-only icon-move-down') +
470 :title => l(:label_sort_lower), :class => 'icon-only icon-move-down') +
470 link_to(l(:label_sort_lowest),
471 link_to(l(:label_sort_lowest),
471 url.merge({"#{name}[move_to]" => 'lowest'}), :method => method,
472 url.merge({"#{name}[move_to]" => 'lowest'}), :method => method,
472 :title => l(:label_sort_lowest), :class => 'icon-only icon-move-bottom')
473 :title => l(:label_sort_lowest), :class => 'icon-only icon-move-bottom')
473 end
474 end
474
475
475 def reorder_handle(object, options={})
476 def reorder_handle(object, options={})
476 data = {
477 data = {
477 :reorder_url => options[:url] || url_for(object),
478 :reorder_url => options[:url] || url_for(object),
478 :reorder_param => options[:param] || object.class.name.underscore
479 :reorder_param => options[:param] || object.class.name.underscore
479 }
480 }
480 content_tag('span', '',
481 content_tag('span', '',
481 :class => "sort-handle",
482 :class => "sort-handle",
482 :data => data,
483 :data => data,
483 :title => l(:button_sort))
484 :title => l(:button_sort))
484 end
485 end
485
486
486 def breadcrumb(*args)
487 def breadcrumb(*args)
487 elements = args.flatten
488 elements = args.flatten
488 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
489 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
489 end
490 end
490
491
491 def other_formats_links(&block)
492 def other_formats_links(&block)
492 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
493 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
493 yield Redmine::Views::OtherFormatsBuilder.new(self)
494 yield Redmine::Views::OtherFormatsBuilder.new(self)
494 concat('</p>'.html_safe)
495 concat('</p>'.html_safe)
495 end
496 end
496
497
497 def page_header_title
498 def page_header_title
498 if @project.nil? || @project.new_record?
499 if @project.nil? || @project.new_record?
499 h(Setting.app_title)
500 h(Setting.app_title)
500 else
501 else
501 b = []
502 b = []
502 ancestors = (@project.root? ? [] : @project.ancestors.visible.to_a)
503 ancestors = (@project.root? ? [] : @project.ancestors.visible.to_a)
503 if ancestors.any?
504 if ancestors.any?
504 root = ancestors.shift
505 root = ancestors.shift
505 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
506 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
506 if ancestors.size > 2
507 if ancestors.size > 2
507 b << "\xe2\x80\xa6"
508 b << "\xe2\x80\xa6"
508 ancestors = ancestors[-2, 2]
509 ancestors = ancestors[-2, 2]
509 end
510 end
510 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
511 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
511 end
512 end
512 b << content_tag(:span, h(@project), class: 'current-project')
513 b << content_tag(:span, h(@project), class: 'current-project')
513 if b.size > 1
514 if b.size > 1
514 separator = content_tag(:span, ' &raquo; '.html_safe, class: 'separator')
515 separator = content_tag(:span, ' &raquo; '.html_safe, class: 'separator')
515 path = safe_join(b[0..-2], separator) + separator
516 path = safe_join(b[0..-2], separator) + separator
516 b = [content_tag(:span, path.html_safe, class: 'breadcrumbs'), b[-1]]
517 b = [content_tag(:span, path.html_safe, class: 'breadcrumbs'), b[-1]]
517 end
518 end
518 safe_join b
519 safe_join b
519 end
520 end
520 end
521 end
521
522
522 # Returns a h2 tag and sets the html title with the given arguments
523 # Returns a h2 tag and sets the html title with the given arguments
523 def title(*args)
524 def title(*args)
524 strings = args.map do |arg|
525 strings = args.map do |arg|
525 if arg.is_a?(Array) && arg.size >= 2
526 if arg.is_a?(Array) && arg.size >= 2
526 link_to(*arg)
527 link_to(*arg)
527 else
528 else
528 h(arg.to_s)
529 h(arg.to_s)
529 end
530 end
530 end
531 end
531 html_title args.reverse.map {|s| (s.is_a?(Array) ? s.first : s).to_s}
532 html_title args.reverse.map {|s| (s.is_a?(Array) ? s.first : s).to_s}
532 content_tag('h2', strings.join(' &#187; ').html_safe)
533 content_tag('h2', strings.join(' &#187; ').html_safe)
533 end
534 end
534
535
535 # Sets the html title
536 # Sets the html title
536 # Returns the html title when called without arguments
537 # Returns the html title when called without arguments
537 # Current project name and app_title and automatically appended
538 # Current project name and app_title and automatically appended
538 # Exemples:
539 # Exemples:
539 # html_title 'Foo', 'Bar'
540 # html_title 'Foo', 'Bar'
540 # html_title # => 'Foo - Bar - My Project - Redmine'
541 # html_title # => 'Foo - Bar - My Project - Redmine'
541 def html_title(*args)
542 def html_title(*args)
542 if args.empty?
543 if args.empty?
543 title = @html_title || []
544 title = @html_title || []
544 title << @project.name if @project
545 title << @project.name if @project
545 title << Setting.app_title unless Setting.app_title == title.last
546 title << Setting.app_title unless Setting.app_title == title.last
546 title.reject(&:blank?).join(' - ')
547 title.reject(&:blank?).join(' - ')
547 else
548 else
548 @html_title ||= []
549 @html_title ||= []
549 @html_title += args
550 @html_title += args
550 end
551 end
551 end
552 end
552
553
553 # Returns the theme, controller name, and action as css classes for the
554 # Returns the theme, controller name, and action as css classes for the
554 # HTML body.
555 # HTML body.
555 def body_css_classes
556 def body_css_classes
556 css = []
557 css = []
557 if theme = Redmine::Themes.theme(Setting.ui_theme)
558 if theme = Redmine::Themes.theme(Setting.ui_theme)
558 css << 'theme-' + theme.name
559 css << 'theme-' + theme.name
559 end
560 end
560
561
561 css << 'project-' + @project.identifier if @project && @project.identifier.present?
562 css << 'project-' + @project.identifier if @project && @project.identifier.present?
562 css << 'controller-' + controller_name
563 css << 'controller-' + controller_name
563 css << 'action-' + action_name
564 css << 'action-' + action_name
564 if UserPreference::TEXTAREA_FONT_OPTIONS.include?(User.current.pref.textarea_font)
565 if UserPreference::TEXTAREA_FONT_OPTIONS.include?(User.current.pref.textarea_font)
565 css << "textarea-#{User.current.pref.textarea_font}"
566 css << "textarea-#{User.current.pref.textarea_font}"
566 end
567 end
567 css.join(' ')
568 css.join(' ')
568 end
569 end
569
570
570 def accesskey(s)
571 def accesskey(s)
571 @used_accesskeys ||= []
572 @used_accesskeys ||= []
572 key = Redmine::AccessKeys.key_for(s)
573 key = Redmine::AccessKeys.key_for(s)
573 return nil if @used_accesskeys.include?(key)
574 return nil if @used_accesskeys.include?(key)
574 @used_accesskeys << key
575 @used_accesskeys << key
575 key
576 key
576 end
577 end
577
578
578 # Formats text according to system settings.
579 # Formats text according to system settings.
579 # 2 ways to call this method:
580 # 2 ways to call this method:
580 # * with a String: textilizable(text, options)
581 # * with a String: textilizable(text, options)
581 # * with an object and one of its attribute: textilizable(issue, :description, options)
582 # * with an object and one of its attribute: textilizable(issue, :description, options)
582 def textilizable(*args)
583 def textilizable(*args)
583 options = args.last.is_a?(Hash) ? args.pop : {}
584 options = args.last.is_a?(Hash) ? args.pop : {}
584 case args.size
585 case args.size
585 when 1
586 when 1
586 obj = options[:object]
587 obj = options[:object]
587 text = args.shift
588 text = args.shift
588 when 2
589 when 2
589 obj = args.shift
590 obj = args.shift
590 attr = args.shift
591 attr = args.shift
591 text = obj.send(attr).to_s
592 text = obj.send(attr).to_s
592 else
593 else
593 raise ArgumentError, 'invalid arguments to textilizable'
594 raise ArgumentError, 'invalid arguments to textilizable'
594 end
595 end
595 return '' if text.blank?
596 return '' if text.blank?
596 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
597 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
597 @only_path = only_path = options.delete(:only_path) == false ? false : true
598 @only_path = only_path = options.delete(:only_path) == false ? false : true
598
599
599 text = text.dup
600 text = text.dup
600 macros = catch_macros(text)
601 macros = catch_macros(text)
601 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
602 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
602
603
603 @parsed_headings = []
604 @parsed_headings = []
604 @heading_anchors = {}
605 @heading_anchors = {}
605 @current_section = 0 if options[:edit_section_links]
606 @current_section = 0 if options[:edit_section_links]
606
607
607 parse_sections(text, project, obj, attr, only_path, options)
608 parse_sections(text, project, obj, attr, only_path, options)
608 text = parse_non_pre_blocks(text, obj, macros) do |text|
609 text = parse_non_pre_blocks(text, obj, macros) do |text|
609 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
610 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name|
610 send method_name, text, project, obj, attr, only_path, options
611 send method_name, text, project, obj, attr, only_path, options
611 end
612 end
612 end
613 end
613 parse_headings(text, project, obj, attr, only_path, options)
614 parse_headings(text, project, obj, attr, only_path, options)
614
615
615 if @parsed_headings.any?
616 if @parsed_headings.any?
616 replace_toc(text, @parsed_headings)
617 replace_toc(text, @parsed_headings)
617 end
618 end
618
619
619 text.html_safe
620 text.html_safe
620 end
621 end
621
622
622 def parse_non_pre_blocks(text, obj, macros)
623 def parse_non_pre_blocks(text, obj, macros)
623 s = StringScanner.new(text)
624 s = StringScanner.new(text)
624 tags = []
625 tags = []
625 parsed = ''
626 parsed = ''
626 while !s.eos?
627 while !s.eos?
627 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
628 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
628 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
629 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
629 if tags.empty?
630 if tags.empty?
630 yield text
631 yield text
631 inject_macros(text, obj, macros) if macros.any?
632 inject_macros(text, obj, macros) if macros.any?
632 else
633 else
633 inject_macros(text, obj, macros, false) if macros.any?
634 inject_macros(text, obj, macros, false) if macros.any?
634 end
635 end
635 parsed << text
636 parsed << text
636 if tag
637 if tag
637 if closing
638 if closing
638 if tags.last && tags.last.casecmp(tag) == 0
639 if tags.last && tags.last.casecmp(tag) == 0
639 tags.pop
640 tags.pop
640 end
641 end
641 else
642 else
642 tags << tag.downcase
643 tags << tag.downcase
643 end
644 end
644 parsed << full_tag
645 parsed << full_tag
645 end
646 end
646 end
647 end
647 # Close any non closing tags
648 # Close any non closing tags
648 while tag = tags.pop
649 while tag = tags.pop
649 parsed << "</#{tag}>"
650 parsed << "</#{tag}>"
650 end
651 end
651 parsed
652 parsed
652 end
653 end
653
654
654 def parse_inline_attachments(text, project, obj, attr, only_path, options)
655 def parse_inline_attachments(text, project, obj, attr, only_path, options)
655 return if options[:inline_attachments] == false
656 return if options[:inline_attachments] == false
656
657
657 # when using an image link, try to use an attachment, if possible
658 # when using an image link, try to use an attachment, if possible
658 attachments = options[:attachments] || []
659 attachments = options[:attachments] || []
659 attachments += obj.attachments if obj.respond_to?(:attachments)
660 attachments += obj.attachments if obj.respond_to?(:attachments)
660 if attachments.present?
661 if attachments.present?
661 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
662 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
662 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
663 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
663 # search for the picture in attachments
664 # search for the picture in attachments
664 if found = Attachment.latest_attach(attachments, CGI.unescape(filename))
665 if found = Attachment.latest_attach(attachments, CGI.unescape(filename))
665 image_url = download_named_attachment_url(found, found.filename, :only_path => only_path)
666 image_url = download_named_attachment_url(found, found.filename, :only_path => only_path)
666 desc = found.description.to_s.gsub('"', '')
667 desc = found.description.to_s.gsub('"', '')
667 if !desc.blank? && alttext.blank?
668 if !desc.blank? && alttext.blank?
668 alt = " title=\"#{desc}\" alt=\"#{desc}\""
669 alt = " title=\"#{desc}\" alt=\"#{desc}\""
669 end
670 end
670 "src=\"#{image_url}\"#{alt}"
671 "src=\"#{image_url}\"#{alt}"
671 else
672 else
672 m
673 m
673 end
674 end
674 end
675 end
675 end
676 end
676 end
677 end
677
678
678 # Wiki links
679 # Wiki links
679 #
680 #
680 # Examples:
681 # Examples:
681 # [[mypage]]
682 # [[mypage]]
682 # [[mypage|mytext]]
683 # [[mypage|mytext]]
683 # wiki links can refer other project wikis, using project name or identifier:
684 # wiki links can refer other project wikis, using project name or identifier:
684 # [[project:]] -> wiki starting page
685 # [[project:]] -> wiki starting page
685 # [[project:|mytext]]
686 # [[project:|mytext]]
686 # [[project:mypage]]
687 # [[project:mypage]]
687 # [[project:mypage|mytext]]
688 # [[project:mypage|mytext]]
688 def parse_wiki_links(text, project, obj, attr, only_path, options)
689 def parse_wiki_links(text, project, obj, attr, only_path, options)
689 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
690 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
690 link_project = project
691 link_project = project
691 esc, all, page, title = $1, $2, $3, $5
692 esc, all, page, title = $1, $2, $3, $5
692 if esc.nil?
693 if esc.nil?
693 if page =~ /^([^\:]+)\:(.*)$/
694 if page =~ /^([^\:]+)\:(.*)$/
694 identifier, page = $1, $2
695 identifier, page = $1, $2
695 link_project = Project.find_by_identifier(identifier) || Project.find_by_name(identifier)
696 link_project = Project.find_by_identifier(identifier) || Project.find_by_name(identifier)
696 title ||= identifier if page.blank?
697 title ||= identifier if page.blank?
697 end
698 end
698
699
699 if link_project && link_project.wiki
700 if link_project && link_project.wiki
700 # extract anchor
701 # extract anchor
701 anchor = nil
702 anchor = nil
702 if page =~ /^(.+?)\#(.+)$/
703 if page =~ /^(.+?)\#(.+)$/
703 page, anchor = $1, $2
704 page, anchor = $1, $2
704 end
705 end
705 anchor = sanitize_anchor_name(anchor) if anchor.present?
706 anchor = sanitize_anchor_name(anchor) if anchor.present?
706 # check if page exists
707 # check if page exists
707 wiki_page = link_project.wiki.find_page(page)
708 wiki_page = link_project.wiki.find_page(page)
708 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
709 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
709 "##{anchor}"
710 "##{anchor}"
710 else
711 else
711 case options[:wiki_links]
712 case options[:wiki_links]
712 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
713 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
713 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
714 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
714 else
715 else
715 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
716 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
716 parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
717 parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
717 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
718 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
718 :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
719 :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
719 end
720 end
720 end
721 end
721 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
722 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
722 else
723 else
723 # project or wiki doesn't exist
724 # project or wiki doesn't exist
724 all
725 all
725 end
726 end
726 else
727 else
727 all
728 all
728 end
729 end
729 end
730 end
730 end
731 end
731
732
732 # Redmine links
733 # Redmine links
733 #
734 #
734 # Examples:
735 # Examples:
735 # Issues:
736 # Issues:
736 # #52 -> Link to issue #52
737 # #52 -> Link to issue #52
737 # Changesets:
738 # Changesets:
738 # r52 -> Link to revision 52
739 # r52 -> Link to revision 52
739 # commit:a85130f -> Link to scmid starting with a85130f
740 # commit:a85130f -> Link to scmid starting with a85130f
740 # Documents:
741 # Documents:
741 # document#17 -> Link to document with id 17
742 # document#17 -> Link to document with id 17
742 # document:Greetings -> Link to the document with title "Greetings"
743 # document:Greetings -> Link to the document with title "Greetings"
743 # document:"Some document" -> Link to the document with title "Some document"
744 # document:"Some document" -> Link to the document with title "Some document"
744 # Versions:
745 # Versions:
745 # version#3 -> Link to version with id 3
746 # version#3 -> Link to version with id 3
746 # version:1.0.0 -> Link to version named "1.0.0"
747 # version:1.0.0 -> Link to version named "1.0.0"
747 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
748 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
748 # Attachments:
749 # Attachments:
749 # attachment:file.zip -> Link to the attachment of the current object named file.zip
750 # attachment:file.zip -> Link to the attachment of the current object named file.zip
750 # Source files:
751 # Source files:
751 # source:some/file -> Link to the file located at /some/file in the project's repository
752 # source:some/file -> Link to the file located at /some/file in the project's repository
752 # source:some/file@52 -> Link to the file's revision 52
753 # source:some/file@52 -> Link to the file's revision 52
753 # source:some/file#L120 -> Link to line 120 of the file
754 # source:some/file#L120 -> Link to line 120 of the file
754 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
755 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
755 # export:some/file -> Force the download of the file
756 # export:some/file -> Force the download of the file
756 # Forum messages:
757 # Forum messages:
757 # message#1218 -> Link to message with id 1218
758 # message#1218 -> Link to message with id 1218
758 # Projects:
759 # Projects:
759 # project:someproject -> Link to project named "someproject"
760 # project:someproject -> Link to project named "someproject"
760 # project#3 -> Link to project with id 3
761 # project#3 -> Link to project with id 3
761 #
762 #
762 # Links can refer other objects from other projects, using project identifier:
763 # Links can refer other objects from other projects, using project identifier:
763 # identifier:r52
764 # identifier:r52
764 # identifier:document:"Some document"
765 # identifier:document:"Some document"
765 # identifier:version:1.0.0
766 # identifier:version:1.0.0
766 # identifier:source:some/file
767 # identifier:source:some/file
767 def parse_redmine_links(text, default_project, obj, attr, only_path, options)
768 def parse_redmine_links(text, default_project, obj, attr, only_path, options)
768 text.gsub!(%r{<a( [^>]+?)?>(.*?)</a>|([\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|
769 text.gsub!(%r{<a( [^>]+?)?>(.*?)</a>|([\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|
769 tag_content, leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $2, $3, $4, $5, $6, $7, $12, $13, $10 || $14 || $20, $16 || $21, $17, $19
770 tag_content, leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $2, $3, $4, $5, $6, $7, $12, $13, $10 || $14 || $20, $16 || $21, $17, $19
770 if tag_content
771 if tag_content
771 $&
772 $&
772 else
773 else
773 link = nil
774 link = nil
774 project = default_project
775 project = default_project
775 if project_identifier
776 if project_identifier
776 project = Project.visible.find_by_identifier(project_identifier)
777 project = Project.visible.find_by_identifier(project_identifier)
777 end
778 end
778 if esc.nil?
779 if esc.nil?
779 if prefix.nil? && sep == 'r'
780 if prefix.nil? && sep == 'r'
780 if project
781 if project
781 repository = nil
782 repository = nil
782 if repo_identifier
783 if repo_identifier
783 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
784 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
784 else
785 else
785 repository = project.repository
786 repository = project.repository
786 end
787 end
787 # project.changesets.visible raises an SQL error because of a double join on repositories
788 # project.changesets.visible raises an SQL error because of a double join on repositories
788 if repository &&
789 if repository &&
789 (changeset = Changeset.visible.
790 (changeset = Changeset.visible.
790 find_by_repository_id_and_revision(repository.id, identifier))
791 find_by_repository_id_and_revision(repository.id, identifier))
791 link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"),
792 link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"),
792 {:only_path => only_path, :controller => 'repositories',
793 {:only_path => only_path, :controller => 'repositories',
793 :action => 'revision', :id => project,
794 :action => 'revision', :id => project,
794 :repository_id => repository.identifier_param,
795 :repository_id => repository.identifier_param,
795 :rev => changeset.revision},
796 :rev => changeset.revision},
796 :class => 'changeset',
797 :class => 'changeset',
797 :title => truncate_single_line_raw(changeset.comments, 100))
798 :title => truncate_single_line_raw(changeset.comments, 100))
798 end
799 end
799 end
800 end
800 elsif sep == '#'
801 elsif sep == '#'
801 oid = identifier.to_i
802 oid = identifier.to_i
802 case prefix
803 case prefix
803 when nil
804 when nil
804 if oid.to_s == identifier &&
805 if oid.to_s == identifier &&
805 issue = Issue.visible.find_by_id(oid)
806 issue = Issue.visible.find_by_id(oid)
806 anchor = comment_id ? "note-#{comment_id}" : nil
807 anchor = comment_id ? "note-#{comment_id}" : nil
807 link = link_to("##{oid}#{comment_suffix}",
808 link = link_to("##{oid}#{comment_suffix}",
808 issue_url(issue, :only_path => only_path, :anchor => anchor),
809 issue_url(issue, :only_path => only_path, :anchor => anchor),
809 :class => issue.css_classes,
810 :class => issue.css_classes,
810 :title => "#{issue.tracker.name}: #{issue.subject.truncate(100)} (#{issue.status.name})")
811 :title => "#{issue.tracker.name}: #{issue.subject.truncate(100)} (#{issue.status.name})")
811 end
812 end
812 when 'document'
813 when 'document'
813 if document = Document.visible.find_by_id(oid)
814 if document = Document.visible.find_by_id(oid)
814 link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document')
815 link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document')
815 end
816 end
816 when 'version'
817 when 'version'
817 if version = Version.visible.find_by_id(oid)
818 if version = Version.visible.find_by_id(oid)
818 link = link_to(version.name, version_url(version, :only_path => only_path), :class => 'version')
819 link = link_to(version.name, version_url(version, :only_path => only_path), :class => 'version')
819 end
820 end
820 when 'message'
821 when 'message'
821 if message = Message.visible.find_by_id(oid)
822 if message = Message.visible.find_by_id(oid)
822 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
823 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
823 end
824 end
824 when 'forum'
825 when 'forum'
825 if board = Board.visible.find_by_id(oid)
826 if board = Board.visible.find_by_id(oid)
826 link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board')
827 link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board')
827 end
828 end
828 when 'news'
829 when 'news'
829 if news = News.visible.find_by_id(oid)
830 if news = News.visible.find_by_id(oid)
830 link = link_to(news.title, news_url(news, :only_path => only_path), :class => 'news')
831 link = link_to(news.title, news_url(news, :only_path => only_path), :class => 'news')
831 end
832 end
832 when 'project'
833 when 'project'
833 if p = Project.visible.find_by_id(oid)
834 if p = Project.visible.find_by_id(oid)
834 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
835 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
835 end
836 end
836 end
837 end
837 elsif sep == ':'
838 elsif sep == ':'
838 # removes the double quotes if any
839 # removes the double quotes if any
839 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
840 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
840 name = CGI.unescapeHTML(name)
841 name = CGI.unescapeHTML(name)
841 case prefix
842 case prefix
842 when 'document'
843 when 'document'
843 if project && document = project.documents.visible.find_by_title(name)
844 if project && document = project.documents.visible.find_by_title(name)
844 link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document')
845 link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document')
845 end
846 end
846 when 'version'
847 when 'version'
847 if project && version = project.versions.visible.find_by_name(name)
848 if project && version = project.versions.visible.find_by_name(name)
848 link = link_to(version.name, version_url(version, :only_path => only_path), :class => 'version')
849 link = link_to(version.name, version_url(version, :only_path => only_path), :class => 'version')
849 end
850 end
850 when 'forum'
851 when 'forum'
851 if project && board = project.boards.visible.find_by_name(name)
852 if project && board = project.boards.visible.find_by_name(name)
852 link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board')
853 link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board')
853 end
854 end
854 when 'news'
855 when 'news'
855 if project && news = project.news.visible.find_by_title(name)
856 if project && news = project.news.visible.find_by_title(name)
856 link = link_to(news.title, news_url(news, :only_path => only_path), :class => 'news')
857 link = link_to(news.title, news_url(news, :only_path => only_path), :class => 'news')
857 end
858 end
858 when 'commit', 'source', 'export'
859 when 'commit', 'source', 'export'
859 if project
860 if project
860 repository = nil
861 repository = nil
861 if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
862 if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
862 repo_prefix, repo_identifier, name = $1, $2, $3
863 repo_prefix, repo_identifier, name = $1, $2, $3
863 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
864 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
864 else
865 else
865 repository = project.repository
866 repository = project.repository
866 end
867 end
867 if prefix == 'commit'
868 if prefix == 'commit'
868 if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
869 if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
869 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},
870 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},
870 :class => 'changeset',
871 :class => 'changeset',
871 :title => truncate_single_line_raw(changeset.comments, 100)
872 :title => truncate_single_line_raw(changeset.comments, 100)
872 end
873 end
873 else
874 else
874 if repository && User.current.allowed_to?(:browse_repository, project)
875 if repository && User.current.allowed_to?(:browse_repository, project)
875 name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
876 name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
876 path, rev, anchor = $1, $3, $5
877 path, rev, anchor = $1, $3, $5
877 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
878 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
878 :path => to_path_param(path),
879 :path => to_path_param(path),
879 :rev => rev,
880 :rev => rev,
880 :anchor => anchor},
881 :anchor => anchor},
881 :class => (prefix == 'export' ? 'source download' : 'source')
882 :class => (prefix == 'export' ? 'source download' : 'source')
882 end
883 end
883 end
884 end
884 repo_prefix = nil
885 repo_prefix = nil
885 end
886 end
886 when 'attachment'
887 when 'attachment'
887 attachments = options[:attachments] || []
888 attachments = options[:attachments] || []
888 attachments += obj.attachments if obj.respond_to?(:attachments)
889 attachments += obj.attachments if obj.respond_to?(:attachments)
889 if attachments && attachment = Attachment.latest_attach(attachments, name)
890 if attachments && attachment = Attachment.latest_attach(attachments, name)
890 link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
891 link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
891 end
892 end
892 when 'project'
893 when 'project'
893 if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
894 if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
894 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
895 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
895 end
896 end
896 end
897 end
897 end
898 end
898 end
899 end
899 (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
900 (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
900 end
901 end
901 end
902 end
902 end
903 end
903
904
904 HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
905 HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE)
905
906
906 def parse_sections(text, project, obj, attr, only_path, options)
907 def parse_sections(text, project, obj, attr, only_path, options)
907 return unless options[:edit_section_links]
908 return unless options[:edit_section_links]
908 text.gsub!(HEADING_RE) do
909 text.gsub!(HEADING_RE) do
909 heading, level = $1, $2
910 heading, level = $1, $2
910 @current_section += 1
911 @current_section += 1
911 if @current_section > 1
912 if @current_section > 1
912 content_tag('div',
913 content_tag('div',
913 link_to(l(:button_edit_section), options[:edit_section_links].merge(:section => @current_section),
914 link_to(l(:button_edit_section), options[:edit_section_links].merge(:section => @current_section),
914 :class => 'icon-only icon-edit'),
915 :class => 'icon-only icon-edit'),
915 :class => "contextual heading-#{level}",
916 :class => "contextual heading-#{level}",
916 :title => l(:button_edit_section),
917 :title => l(:button_edit_section),
917 :id => "section-#{@current_section}") + heading.html_safe
918 :id => "section-#{@current_section}") + heading.html_safe
918 else
919 else
919 heading
920 heading
920 end
921 end
921 end
922 end
922 end
923 end
923
924
924 # Headings and TOC
925 # Headings and TOC
925 # Adds ids and links to headings unless options[:headings] is set to false
926 # Adds ids and links to headings unless options[:headings] is set to false
926 def parse_headings(text, project, obj, attr, only_path, options)
927 def parse_headings(text, project, obj, attr, only_path, options)
927 return if options[:headings] == false
928 return if options[:headings] == false
928
929
929 text.gsub!(HEADING_RE) do
930 text.gsub!(HEADING_RE) do
930 level, attrs, content = $2.to_i, $3, $4
931 level, attrs, content = $2.to_i, $3, $4
931 item = strip_tags(content).strip
932 item = strip_tags(content).strip
932 anchor = sanitize_anchor_name(item)
933 anchor = sanitize_anchor_name(item)
933 # used for single-file wiki export
934 # used for single-file wiki export
934 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
935 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
935 @heading_anchors[anchor] ||= 0
936 @heading_anchors[anchor] ||= 0
936 idx = (@heading_anchors[anchor] += 1)
937 idx = (@heading_anchors[anchor] += 1)
937 if idx > 1
938 if idx > 1
938 anchor = "#{anchor}-#{idx}"
939 anchor = "#{anchor}-#{idx}"
939 end
940 end
940 @parsed_headings << [level, anchor, item]
941 @parsed_headings << [level, anchor, item]
941 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
942 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
942 end
943 end
943 end
944 end
944
945
945 MACROS_RE = /(
946 MACROS_RE = /(
946 (!)? # escaping
947 (!)? # escaping
947 (
948 (
948 \{\{ # opening tag
949 \{\{ # opening tag
949 ([\w]+) # macro name
950 ([\w]+) # macro name
950 (\(([^\n\r]*?)\))? # optional arguments
951 (\(([^\n\r]*?)\))? # optional arguments
951 ([\n\r].*?[\n\r])? # optional block of text
952 ([\n\r].*?[\n\r])? # optional block of text
952 \}\} # closing tag
953 \}\} # closing tag
953 )
954 )
954 )/mx unless const_defined?(:MACROS_RE)
955 )/mx unless const_defined?(:MACROS_RE)
955
956
956 MACRO_SUB_RE = /(
957 MACRO_SUB_RE = /(
957 \{\{
958 \{\{
958 macro\((\d+)\)
959 macro\((\d+)\)
959 \}\}
960 \}\}
960 )/x unless const_defined?(:MACRO_SUB_RE)
961 )/x unless const_defined?(:MACRO_SUB_RE)
961
962
962 # Extracts macros from text
963 # Extracts macros from text
963 def catch_macros(text)
964 def catch_macros(text)
964 macros = {}
965 macros = {}
965 text.gsub!(MACROS_RE) do
966 text.gsub!(MACROS_RE) do
966 all, macro = $1, $4.downcase
967 all, macro = $1, $4.downcase
967 if macro_exists?(macro) || all =~ MACRO_SUB_RE
968 if macro_exists?(macro) || all =~ MACRO_SUB_RE
968 index = macros.size
969 index = macros.size
969 macros[index] = all
970 macros[index] = all
970 "{{macro(#{index})}}"
971 "{{macro(#{index})}}"
971 else
972 else
972 all
973 all
973 end
974 end
974 end
975 end
975 macros
976 macros
976 end
977 end
977
978
978 # Executes and replaces macros in text
979 # Executes and replaces macros in text
979 def inject_macros(text, obj, macros, execute=true)
980 def inject_macros(text, obj, macros, execute=true)
980 text.gsub!(MACRO_SUB_RE) do
981 text.gsub!(MACRO_SUB_RE) do
981 all, index = $1, $2.to_i
982 all, index = $1, $2.to_i
982 orig = macros.delete(index)
983 orig = macros.delete(index)
983 if execute && orig && orig =~ MACROS_RE
984 if execute && orig && orig =~ MACROS_RE
984 esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
985 esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip)
985 if esc.nil?
986 if esc.nil?
986 h(exec_macro(macro, obj, args, block) || all)
987 h(exec_macro(macro, obj, args, block) || all)
987 else
988 else
988 h(all)
989 h(all)
989 end
990 end
990 elsif orig
991 elsif orig
991 h(orig)
992 h(orig)
992 else
993 else
993 h(all)
994 h(all)
994 end
995 end
995 end
996 end
996 end
997 end
997
998
998 TOC_RE = /<p>\{\{((<|&lt;)|(>|&gt;))?toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
999 TOC_RE = /<p>\{\{((<|&lt;)|(>|&gt;))?toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
999
1000
1000 # Renders the TOC with given headings
1001 # Renders the TOC with given headings
1001 def replace_toc(text, headings)
1002 def replace_toc(text, headings)
1002 text.gsub!(TOC_RE) do
1003 text.gsub!(TOC_RE) do
1003 left_align, right_align = $2, $3
1004 left_align, right_align = $2, $3
1004 # Keep only the 4 first levels
1005 # Keep only the 4 first levels
1005 headings = headings.select{|level, anchor, item| level <= 4}
1006 headings = headings.select{|level, anchor, item| level <= 4}
1006 if headings.empty?
1007 if headings.empty?
1007 ''
1008 ''
1008 else
1009 else
1009 div_class = 'toc'
1010 div_class = 'toc'
1010 div_class << ' right' if right_align
1011 div_class << ' right' if right_align
1011 div_class << ' left' if left_align
1012 div_class << ' left' if left_align
1012 out = "<ul class=\"#{div_class}\"><li>"
1013 out = "<ul class=\"#{div_class}\"><li>"
1013 root = headings.map(&:first).min
1014 root = headings.map(&:first).min
1014 current = root
1015 current = root
1015 started = false
1016 started = false
1016 headings.each do |level, anchor, item|
1017 headings.each do |level, anchor, item|
1017 if level > current
1018 if level > current
1018 out << '<ul><li>' * (level - current)
1019 out << '<ul><li>' * (level - current)
1019 elsif level < current
1020 elsif level < current
1020 out << "</li></ul>\n" * (current - level) + "</li><li>"
1021 out << "</li></ul>\n" * (current - level) + "</li><li>"
1021 elsif started
1022 elsif started
1022 out << '</li><li>'
1023 out << '</li><li>'
1023 end
1024 end
1024 out << "<a href=\"##{anchor}\">#{item}</a>"
1025 out << "<a href=\"##{anchor}\">#{item}</a>"
1025 current = level
1026 current = level
1026 started = true
1027 started = true
1027 end
1028 end
1028 out << '</li></ul>' * (current - root)
1029 out << '</li></ul>' * (current - root)
1029 out << '</li></ul>'
1030 out << '</li></ul>'
1030 end
1031 end
1031 end
1032 end
1032 end
1033 end
1033
1034
1034 # Same as Rails' simple_format helper without using paragraphs
1035 # Same as Rails' simple_format helper without using paragraphs
1035 def simple_format_without_paragraph(text)
1036 def simple_format_without_paragraph(text)
1036 text.to_s.
1037 text.to_s.
1037 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
1038 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
1038 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
1039 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
1039 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
1040 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
1040 html_safe
1041 html_safe
1041 end
1042 end
1042
1043
1043 def lang_options_for_select(blank=true)
1044 def lang_options_for_select(blank=true)
1044 (blank ? [["(auto)", ""]] : []) + languages_options
1045 (blank ? [["(auto)", ""]] : []) + languages_options
1045 end
1046 end
1046
1047
1047 def labelled_form_for(*args, &proc)
1048 def labelled_form_for(*args, &proc)
1048 args << {} unless args.last.is_a?(Hash)
1049 args << {} unless args.last.is_a?(Hash)
1049 options = args.last
1050 options = args.last
1050 if args.first.is_a?(Symbol)
1051 if args.first.is_a?(Symbol)
1051 options.merge!(:as => args.shift)
1052 options.merge!(:as => args.shift)
1052 end
1053 end
1053 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
1054 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
1054 form_for(*args, &proc)
1055 form_for(*args, &proc)
1055 end
1056 end
1056
1057
1057 def labelled_fields_for(*args, &proc)
1058 def labelled_fields_for(*args, &proc)
1058 args << {} unless args.last.is_a?(Hash)
1059 args << {} unless args.last.is_a?(Hash)
1059 options = args.last
1060 options = args.last
1060 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
1061 options.merge!({:builder => Redmine::Views::LabelledFormBuilder})
1061 fields_for(*args, &proc)
1062 fields_for(*args, &proc)
1062 end
1063 end
1063
1064
1064 # Render the error messages for the given objects
1065 # Render the error messages for the given objects
1065 def error_messages_for(*objects)
1066 def error_messages_for(*objects)
1066 objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
1067 objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact
1067 errors = objects.map {|o| o.errors.full_messages}.flatten
1068 errors = objects.map {|o| o.errors.full_messages}.flatten
1068 render_error_messages(errors)
1069 render_error_messages(errors)
1069 end
1070 end
1070
1071
1071 # Renders a list of error messages
1072 # Renders a list of error messages
1072 def render_error_messages(errors)
1073 def render_error_messages(errors)
1073 html = ""
1074 html = ""
1074 if errors.present?
1075 if errors.present?
1075 html << "<div id='errorExplanation'><ul>\n"
1076 html << "<div id='errorExplanation'><ul>\n"
1076 errors.each do |error|
1077 errors.each do |error|
1077 html << "<li>#{h error}</li>\n"
1078 html << "<li>#{h error}</li>\n"
1078 end
1079 end
1079 html << "</ul></div>\n"
1080 html << "</ul></div>\n"
1080 end
1081 end
1081 html.html_safe
1082 html.html_safe
1082 end
1083 end
1083
1084
1084 def delete_link(url, options={})
1085 def delete_link(url, options={})
1085 options = {
1086 options = {
1086 :method => :delete,
1087 :method => :delete,
1087 :data => {:confirm => l(:text_are_you_sure)},
1088 :data => {:confirm => l(:text_are_you_sure)},
1088 :class => 'icon icon-del'
1089 :class => 'icon icon-del'
1089 }.merge(options)
1090 }.merge(options)
1090
1091
1091 link_to l(:button_delete), url, options
1092 link_to l(:button_delete), url, options
1092 end
1093 end
1093
1094
1094 def preview_link(url, form, target='preview', options={})
1095 def preview_link(url, form, target='preview', options={})
1095 content_tag 'a', l(:label_preview), {
1096 content_tag 'a', l(:label_preview), {
1096 :href => "#",
1097 :href => "#",
1097 :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
1098 :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
1098 :accesskey => accesskey(:preview)
1099 :accesskey => accesskey(:preview)
1099 }.merge(options)
1100 }.merge(options)
1100 end
1101 end
1101
1102
1102 def link_to_function(name, function, html_options={})
1103 def link_to_function(name, function, html_options={})
1103 content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
1104 content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
1104 end
1105 end
1105
1106
1106 # Helper to render JSON in views
1107 # Helper to render JSON in views
1107 def raw_json(arg)
1108 def raw_json(arg)
1108 arg.to_json.to_s.gsub('/', '\/').html_safe
1109 arg.to_json.to_s.gsub('/', '\/').html_safe
1109 end
1110 end
1110
1111
1111 def back_url
1112 def back_url
1112 url = params[:back_url]
1113 url = params[:back_url]
1113 if url.nil? && referer = request.env['HTTP_REFERER']
1114 if url.nil? && referer = request.env['HTTP_REFERER']
1114 url = CGI.unescape(referer.to_s)
1115 url = CGI.unescape(referer.to_s)
1115 # URLs that contains the utf8=[checkmark] parameter added by Rails are
1116 # URLs that contains the utf8=[checkmark] parameter added by Rails are
1116 # parsed as invalid by URI.parse so the redirect to the back URL would
1117 # parsed as invalid by URI.parse so the redirect to the back URL would
1117 # not be accepted (ApplicationController#validate_back_url would return
1118 # not be accepted (ApplicationController#validate_back_url would return
1118 # false)
1119 # false)
1119 url.gsub!(/(\?|&)utf8=\u2713&?/, '\1')
1120 url.gsub!(/(\?|&)utf8=\u2713&?/, '\1')
1120 end
1121 end
1121 url
1122 url
1122 end
1123 end
1123
1124
1124 def back_url_hidden_field_tag
1125 def back_url_hidden_field_tag
1125 url = back_url
1126 url = back_url
1126 hidden_field_tag('back_url', url, :id => nil) unless url.blank?
1127 hidden_field_tag('back_url', url, :id => nil) unless url.blank?
1127 end
1128 end
1128
1129
1129 def check_all_links(form_name)
1130 def check_all_links(form_name)
1130 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
1131 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
1131 " | ".html_safe +
1132 " | ".html_safe +
1132 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
1133 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
1133 end
1134 end
1134
1135
1135 def toggle_checkboxes_link(selector)
1136 def toggle_checkboxes_link(selector)
1136 link_to_function '',
1137 link_to_function '',
1137 "toggleCheckboxesBySelector('#{selector}')",
1138 "toggleCheckboxesBySelector('#{selector}')",
1138 :title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}",
1139 :title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}",
1139 :class => 'toggle-checkboxes'
1140 :class => 'toggle-checkboxes'
1140 end
1141 end
1141
1142
1142 def progress_bar(pcts, options={})
1143 def progress_bar(pcts, options={})
1143 pcts = [pcts, pcts] unless pcts.is_a?(Array)
1144 pcts = [pcts, pcts] unless pcts.is_a?(Array)
1144 pcts = pcts.collect(&:round)
1145 pcts = pcts.collect(&:round)
1145 pcts[1] = pcts[1] - pcts[0]
1146 pcts[1] = pcts[1] - pcts[0]
1146 pcts << (100 - pcts[1] - pcts[0])
1147 pcts << (100 - pcts[1] - pcts[0])
1147 titles = options[:titles].to_a
1148 titles = options[:titles].to_a
1148 titles[0] = "#{pcts[0]}%" if titles[0].blank?
1149 titles[0] = "#{pcts[0]}%" if titles[0].blank?
1149 legend = options[:legend] || ''
1150 legend = options[:legend] || ''
1150 content_tag('table',
1151 content_tag('table',
1151 content_tag('tr',
1152 content_tag('tr',
1152 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed', :title => titles[0]) : ''.html_safe) +
1153 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed', :title => titles[0]) : ''.html_safe) +
1153 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done', :title => titles[1]) : ''.html_safe) +
1154 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done', :title => titles[1]) : ''.html_safe) +
1154 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo', :title => titles[2]) : ''.html_safe)
1155 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo', :title => titles[2]) : ''.html_safe)
1155 ), :class => "progress progress-#{pcts[0]}").html_safe +
1156 ), :class => "progress progress-#{pcts[0]}").html_safe +
1156 content_tag('p', legend, :class => 'percent').html_safe
1157 content_tag('p', legend, :class => 'percent').html_safe
1157 end
1158 end
1158
1159
1159 def checked_image(checked=true)
1160 def checked_image(checked=true)
1160 if checked
1161 if checked
1161 @checked_image_tag ||= content_tag(:span, nil, :class => 'icon-only icon-checked')
1162 @checked_image_tag ||= content_tag(:span, nil, :class => 'icon-only icon-checked')
1162 end
1163 end
1163 end
1164 end
1164
1165
1165 def context_menu(url)
1166 def context_menu(url)
1166 unless @context_menu_included
1167 unless @context_menu_included
1167 content_for :header_tags do
1168 content_for :header_tags do
1168 javascript_include_tag('context_menu') +
1169 javascript_include_tag('context_menu') +
1169 stylesheet_link_tag('context_menu')
1170 stylesheet_link_tag('context_menu')
1170 end
1171 end
1171 if l(:direction) == 'rtl'
1172 if l(:direction) == 'rtl'
1172 content_for :header_tags do
1173 content_for :header_tags do
1173 stylesheet_link_tag('context_menu_rtl')
1174 stylesheet_link_tag('context_menu_rtl')
1174 end
1175 end
1175 end
1176 end
1176 @context_menu_included = true
1177 @context_menu_included = true
1177 end
1178 end
1178 javascript_tag "contextMenuInit('#{ url_for(url) }')"
1179 javascript_tag "contextMenuInit('#{ url_for(url) }')"
1179 end
1180 end
1180
1181
1181 def calendar_for(field_id)
1182 def calendar_for(field_id)
1182 include_calendar_headers_tags
1183 include_calendar_headers_tags
1183 javascript_tag("$(function() { $('##{field_id}').addClass('date').datepickerFallback(datepickerOptions); });")
1184 javascript_tag("$(function() { $('##{field_id}').addClass('date').datepickerFallback(datepickerOptions); });")
1184 end
1185 end
1185
1186
1186 def include_calendar_headers_tags
1187 def include_calendar_headers_tags
1187 unless @calendar_headers_tags_included
1188 unless @calendar_headers_tags_included
1188 tags = ''.html_safe
1189 tags = ''.html_safe
1189 @calendar_headers_tags_included = true
1190 @calendar_headers_tags_included = true
1190 content_for :header_tags do
1191 content_for :header_tags do
1191 start_of_week = Setting.start_of_week
1192 start_of_week = Setting.start_of_week
1192 start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
1193 start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank?
1193 # Redmine uses 1..7 (monday..sunday) in settings and locales
1194 # Redmine uses 1..7 (monday..sunday) in settings and locales
1194 # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
1195 # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
1195 start_of_week = start_of_week.to_i % 7
1196 start_of_week = start_of_week.to_i % 7
1196 tags << javascript_tag(
1197 tags << javascript_tag(
1197 "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
1198 "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
1198 "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
1199 "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
1199 path_to_image('/images/calendar.png') +
1200 path_to_image('/images/calendar.png') +
1200 "', showButtonPanel: true, showWeek: true, showOtherMonths: true, " +
1201 "', showButtonPanel: true, showWeek: true, showOtherMonths: true, " +
1201 "selectOtherMonths: true, changeMonth: true, changeYear: true, " +
1202 "selectOtherMonths: true, changeMonth: true, changeYear: true, " +
1202 "beforeShow: beforeShowDatePicker};")
1203 "beforeShow: beforeShowDatePicker};")
1203 jquery_locale = l('jquery.locale', :default => current_language.to_s)
1204 jquery_locale = l('jquery.locale', :default => current_language.to_s)
1204 unless jquery_locale == 'en'
1205 unless jquery_locale == 'en'
1205 tags << javascript_include_tag("i18n/datepicker-#{jquery_locale}.js")
1206 tags << javascript_include_tag("i18n/datepicker-#{jquery_locale}.js")
1206 end
1207 end
1207 tags
1208 tags
1208 end
1209 end
1209 end
1210 end
1210 end
1211 end
1211
1212
1212 # Overrides Rails' stylesheet_link_tag with themes and plugins support.
1213 # Overrides Rails' stylesheet_link_tag with themes and plugins support.
1213 # Examples:
1214 # Examples:
1214 # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
1215 # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults
1215 # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
1216 # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets
1216 #
1217 #
1217 def stylesheet_link_tag(*sources)
1218 def stylesheet_link_tag(*sources)
1218 options = sources.last.is_a?(Hash) ? sources.pop : {}
1219 options = sources.last.is_a?(Hash) ? sources.pop : {}
1219 plugin = options.delete(:plugin)
1220 plugin = options.delete(:plugin)
1220 sources = sources.map do |source|
1221 sources = sources.map do |source|
1221 if plugin
1222 if plugin
1222 "/plugin_assets/#{plugin}/stylesheets/#{source}"
1223 "/plugin_assets/#{plugin}/stylesheets/#{source}"
1223 elsif current_theme && current_theme.stylesheets.include?(source)
1224 elsif current_theme && current_theme.stylesheets.include?(source)
1224 current_theme.stylesheet_path(source)
1225 current_theme.stylesheet_path(source)
1225 else
1226 else
1226 source
1227 source
1227 end
1228 end
1228 end
1229 end
1229 super *sources, options
1230 super *sources, options
1230 end
1231 end
1231
1232
1232 # Overrides Rails' image_tag with themes and plugins support.
1233 # Overrides Rails' image_tag with themes and plugins support.
1233 # Examples:
1234 # Examples:
1234 # image_tag('image.png') # => picks image.png from the current theme or defaults
1235 # image_tag('image.png') # => picks image.png from the current theme or defaults
1235 # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
1236 # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets
1236 #
1237 #
1237 def image_tag(source, options={})
1238 def image_tag(source, options={})
1238 if plugin = options.delete(:plugin)
1239 if plugin = options.delete(:plugin)
1239 source = "/plugin_assets/#{plugin}/images/#{source}"
1240 source = "/plugin_assets/#{plugin}/images/#{source}"
1240 elsif current_theme && current_theme.images.include?(source)
1241 elsif current_theme && current_theme.images.include?(source)
1241 source = current_theme.image_path(source)
1242 source = current_theme.image_path(source)
1242 end
1243 end
1243 super source, options
1244 super source, options
1244 end
1245 end
1245
1246
1246 # Overrides Rails' javascript_include_tag with plugins support
1247 # Overrides Rails' javascript_include_tag with plugins support
1247 # Examples:
1248 # Examples:
1248 # javascript_include_tag('scripts') # => picks scripts.js from defaults
1249 # javascript_include_tag('scripts') # => picks scripts.js from defaults
1249 # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
1250 # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets
1250 #
1251 #
1251 def javascript_include_tag(*sources)
1252 def javascript_include_tag(*sources)
1252 options = sources.last.is_a?(Hash) ? sources.pop : {}
1253 options = sources.last.is_a?(Hash) ? sources.pop : {}
1253 if plugin = options.delete(:plugin)
1254 if plugin = options.delete(:plugin)
1254 sources = sources.map do |source|
1255 sources = sources.map do |source|
1255 if plugin
1256 if plugin
1256 "/plugin_assets/#{plugin}/javascripts/#{source}"
1257 "/plugin_assets/#{plugin}/javascripts/#{source}"
1257 else
1258 else
1258 source
1259 source
1259 end
1260 end
1260 end
1261 end
1261 end
1262 end
1262 super *sources, options
1263 super *sources, options
1263 end
1264 end
1264
1265
1265 def sidebar_content?
1266 def sidebar_content?
1266 content_for?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1267 content_for?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1267 end
1268 end
1268
1269
1269 def view_layouts_base_sidebar_hook_response
1270 def view_layouts_base_sidebar_hook_response
1270 @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1271 @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1271 end
1272 end
1272
1273
1273 def email_delivery_enabled?
1274 def email_delivery_enabled?
1274 !!ActionMailer::Base.perform_deliveries
1275 !!ActionMailer::Base.perform_deliveries
1275 end
1276 end
1276
1277
1277 # Returns the avatar image tag for the given +user+ if avatars are enabled
1278 # Returns the avatar image tag for the given +user+ if avatars are enabled
1278 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
1279 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
1279 def avatar(user, options = { })
1280 def avatar(user, options = { })
1280 if Setting.gravatar_enabled?
1281 if Setting.gravatar_enabled?
1281 options.merge!(:default => Setting.gravatar_default)
1282 options.merge!(:default => Setting.gravatar_default)
1282 email = nil
1283 email = nil
1283 if user.respond_to?(:mail)
1284 if user.respond_to?(:mail)
1284 email = user.mail
1285 email = user.mail
1285 elsif user.to_s =~ %r{<(.+?)>}
1286 elsif user.to_s =~ %r{<(.+?)>}
1286 email = $1
1287 email = $1
1287 end
1288 end
1288 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1289 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1289 else
1290 else
1290 ''
1291 ''
1291 end
1292 end
1292 end
1293 end
1293
1294
1294 # Returns a link to edit user's avatar if avatars are enabled
1295 # Returns a link to edit user's avatar if avatars are enabled
1295 def avatar_edit_link(user, options={})
1296 def avatar_edit_link(user, options={})
1296 if Setting.gravatar_enabled?
1297 if Setting.gravatar_enabled?
1297 url = "https://gravatar.com"
1298 url = "https://gravatar.com"
1298 link_to avatar(user, {:title => l(:button_edit)}.merge(options)), url, :target => '_blank'
1299 link_to avatar(user, {:title => l(:button_edit)}.merge(options)), url, :target => '_blank'
1299 end
1300 end
1300 end
1301 end
1301
1302
1302 def sanitize_anchor_name(anchor)
1303 def sanitize_anchor_name(anchor)
1303 anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1304 anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1304 end
1305 end
1305
1306
1306 # Returns the javascript tags that are included in the html layout head
1307 # Returns the javascript tags that are included in the html layout head
1307 def javascript_heads
1308 def javascript_heads
1308 tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.4', 'application', 'responsive')
1309 tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.4', 'application', 'responsive')
1309 unless User.current.pref.warn_on_leaving_unsaved == '0'
1310 unless User.current.pref.warn_on_leaving_unsaved == '0'
1310 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1311 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1311 end
1312 end
1312 tags
1313 tags
1313 end
1314 end
1314
1315
1315 def favicon
1316 def favicon
1316 "<link rel='shortcut icon' href='#{favicon_path}' />".html_safe
1317 "<link rel='shortcut icon' href='#{favicon_path}' />".html_safe
1317 end
1318 end
1318
1319
1319 # Returns the path to the favicon
1320 # Returns the path to the favicon
1320 def favicon_path
1321 def favicon_path
1321 icon = (current_theme && current_theme.favicon?) ? current_theme.favicon_path : '/favicon.ico'
1322 icon = (current_theme && current_theme.favicon?) ? current_theme.favicon_path : '/favicon.ico'
1322 image_path(icon)
1323 image_path(icon)
1323 end
1324 end
1324
1325
1325 # Returns the full URL to the favicon
1326 # Returns the full URL to the favicon
1326 def favicon_url
1327 def favicon_url
1327 # TODO: use #image_url introduced in Rails4
1328 # TODO: use #image_url introduced in Rails4
1328 path = favicon_path
1329 path = favicon_path
1329 base = url_for(:controller => 'welcome', :action => 'index', :only_path => false)
1330 base = url_for(:controller => 'welcome', :action => 'index', :only_path => false)
1330 base.sub(%r{/+$},'') + '/' + path.sub(%r{^/+},'')
1331 base.sub(%r{/+$},'') + '/' + path.sub(%r{^/+},'')
1331 end
1332 end
1332
1333
1333 def robot_exclusion_tag
1334 def robot_exclusion_tag
1334 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1335 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1335 end
1336 end
1336
1337
1337 # Returns true if arg is expected in the API response
1338 # Returns true if arg is expected in the API response
1338 def include_in_api_response?(arg)
1339 def include_in_api_response?(arg)
1339 unless @included_in_api_response
1340 unless @included_in_api_response
1340 param = params[:include]
1341 param = params[:include]
1341 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1342 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1342 @included_in_api_response.collect!(&:strip)
1343 @included_in_api_response.collect!(&:strip)
1343 end
1344 end
1344 @included_in_api_response.include?(arg.to_s)
1345 @included_in_api_response.include?(arg.to_s)
1345 end
1346 end
1346
1347
1347 # Returns options or nil if nometa param or X-Redmine-Nometa header
1348 # Returns options or nil if nometa param or X-Redmine-Nometa header
1348 # was set in the request
1349 # was set in the request
1349 def api_meta(options)
1350 def api_meta(options)
1350 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1351 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1351 # compatibility mode for activeresource clients that raise
1352 # compatibility mode for activeresource clients that raise
1352 # an error when deserializing an array with attributes
1353 # an error when deserializing an array with attributes
1353 nil
1354 nil
1354 else
1355 else
1355 options
1356 options
1356 end
1357 end
1357 end
1358 end
1358
1359
1359 def generate_csv(&block)
1360 def generate_csv(&block)
1360 decimal_separator = l(:general_csv_decimal_separator)
1361 decimal_separator = l(:general_csv_decimal_separator)
1361 encoding = l(:general_csv_encoding)
1362 encoding = l(:general_csv_encoding)
1362 end
1363 end
1363
1364
1364 private
1365 private
1365
1366
1366 def wiki_helper
1367 def wiki_helper
1367 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1368 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1368 extend helper
1369 extend helper
1369 return self
1370 return self
1370 end
1371 end
1371 end
1372 end
@@ -1,1391 +1,1392
1 html {overflow-y:scroll;}
1 html {overflow-y:scroll;}
2 body { font-family: Verdana, sans-serif; font-size: 12px; color:#333; margin: 0; padding: 0; min-width: 900px; }
2 body { font-family: Verdana, sans-serif; font-size: 12px; color:#333; margin: 0; padding: 0; min-width: 900px; }
3
3
4 h1, h2, h3, h4 {font-family: "Trebuchet MS", Verdana, sans-serif;padding: 2px 10px 1px 0px;margin: 0 0 10px 0;}
4 h1, h2, h3, h4 {font-family: "Trebuchet MS", Verdana, sans-serif;padding: 2px 10px 1px 0px;margin: 0 0 10px 0;}
5 #content h1, h2, h3, h4 {color: #555;}
5 #content h1, h2, h3, h4 {color: #555;}
6 h2, .wiki h1 {font-size: 20px;}
6 h2, .wiki h1 {font-size: 20px;}
7 h3, .wiki h2 {font-size: 16px;}
7 h3, .wiki h2 {font-size: 16px;}
8 h4, .wiki h3 {font-size: 13px;}
8 h4, .wiki h3 {font-size: 13px;}
9 h4 {border-bottom: 1px dotted #bbb;}
9 h4 {border-bottom: 1px dotted #bbb;}
10 pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
10 pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
11
11
12 /***** Layout *****/
12 /***** Layout *****/
13 #wrapper {background: white;overflow: hidden;}
13 #wrapper {background: white;overflow: hidden;}
14
14
15 #top-menu {background: #3E5B76; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
15 #top-menu {background: #3E5B76; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
16 #top-menu ul {margin: 0; padding: 0;}
16 #top-menu ul {margin: 0; padding: 0;}
17 #top-menu li {
17 #top-menu li {
18 float:left;
18 float:left;
19 list-style-type:none;
19 list-style-type:none;
20 margin: 0px 0px 0px 0px;
20 margin: 0px 0px 0px 0px;
21 padding: 0px 0px 0px 0px;
21 padding: 0px 0px 0px 0px;
22 white-space:nowrap;
22 white-space:nowrap;
23 }
23 }
24 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
24 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
25 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
25 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
26
26
27 #account {float:right;}
27 #account {float:right;}
28
28
29 #header {min-height:5.3em;margin:0;background-color:#628DB6;color:#f8f8f8; padding: 4px 8px 20px 6px; position:relative;}
29 #header {min-height:5.3em;margin:0;background-color:#628DB6;color:#f8f8f8; padding: 4px 8px 20px 6px; position:relative;}
30 #header a {color:#f8f8f8;}
30 #header a {color:#f8f8f8;}
31 #header h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
31 #header h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
32 #header h1 .breadcrumbs { display:block; font-size: .6em; font-weight: normal; }
32 #header h1 .breadcrumbs { display:block; font-size: .6em; font-weight: normal; }
33 #quick-search {float:right;}
33 #quick-search {float:right;}
34
34
35 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px; width: 100%;}
35 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px; width: 100%;}
36 #main-menu ul {margin: 0; padding: 0; width: 100%; white-space: nowrap;}
36 #main-menu ul {margin: 0; padding: 0; width: 100%; white-space: nowrap;}
37 #main-menu li {
37 #main-menu li {
38 float:none;
38 float:none;
39 list-style-type:none;
39 list-style-type:none;
40 margin: 0px 2px 0px 0px;
40 margin: 0px 2px 0px 0px;
41 padding: 0px 0px 0px 0px;
41 padding: 0px 0px 0px 0px;
42 white-space:nowrap;
42 white-space:nowrap;
43 display:inline-block;
43 display:inline-block;
44 }
44 }
45 #main-menu li a {
45 #main-menu li a {
46 display: block;
46 display: block;
47 color: #fff;
47 color: #fff;
48 text-decoration: none;
48 text-decoration: none;
49 font-weight: bold;
49 font-weight: bold;
50 margin: 0;
50 margin: 0;
51 padding: 4px 10px 4px 10px;
51 padding: 4px 10px 4px 10px;
52 }
52 }
53 #main-menu li a:hover {background:#759FCF; color:#fff;}
53 #main-menu li a:hover {background:#759FCF; color:#fff;}
54 #main-menu li:hover ul.menu-children, #main-menu li ul.menu-children.visible {display: block;}
54 #main-menu li:hover ul.menu-children, #main-menu li ul.menu-children.visible {display: block;}
55 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
55 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
56 #main-menu li a.new-object { background-color:#759FCF; }
56 #main-menu li a.new-object { background-color:#759FCF; }
57
57
58 #main-menu .menu-children {
58 #main-menu .menu-children {
59 display: none;
59 display: none;
60 position:absolute;
60 position:absolute;
61 width: inherit;
61 width: inherit;
62 z-index:1;
62 z-index:1;
63 background-color:#fff;
63 background-color:#fff;
64 border-right: 1px solid #759FCF;
64 border-right: 1px solid #759FCF;
65 border-bottom: 1px solid #759FCF;
65 border-bottom: 1px solid #759FCF;
66 border-left: 1px solid #759FCF;
66 border-left: 1px solid #759FCF;
67 }
67 }
68 #main-menu .menu-children li {float:left; clear:both; width:100%;}
68 #main-menu .menu-children li {float:left; clear:both; width:100%;}
69 #main-menu .menu-children li a {color: #555; background-color:#fff; font-weight:normal;}
69 #main-menu .menu-children li a {color: #555; background-color:#fff; font-weight:normal;}
70 #main-menu .menu-children li a:hover {color: #fff; background-color: #759FCF;}
70 #main-menu .menu-children li a:hover {color: #fff; background-color: #759FCF;}
71
71
72 #main-menu .tabs-buttons {
72 #main-menu .tabs-buttons {
73 right: 6px;
73 right: 6px;
74 background-color: transparent;
74 background-color: transparent;
75 border-bottom-color: transparent;
75 border-bottom-color: transparent;
76 }
76 }
77
77
78 #admin-menu ul {margin: 0; padding: 0;}
78 #admin-menu ul {margin: 0; padding: 0;}
79 #admin-menu li {margin: 0; padding: 0 0 6px 0; list-style-type:none;}
79 #admin-menu li {margin: 0; padding: 0 0 6px 0; list-style-type:none;}
80
80
81 #admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;}
81 #admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;}
82 #admin-menu a.projects { background-image: url(../images/projects.png); }
82 #admin-menu a.projects { background-image: url(../images/projects.png); }
83 #admin-menu a.users { background-image: url(../images/user.png); }
83 #admin-menu a.users { background-image: url(../images/user.png); }
84 #admin-menu a.groups { background-image: url(../images/group.png); }
84 #admin-menu a.groups { background-image: url(../images/group.png); }
85 #admin-menu a.roles { background-image: url(../images/database_key.png); }
85 #admin-menu a.roles { background-image: url(../images/database_key.png); }
86 #admin-menu a.trackers { background-image: url(../images/ticket.png); }
86 #admin-menu a.trackers { background-image: url(../images/ticket.png); }
87 #admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); }
87 #admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); }
88 #admin-menu a.workflows { background-image: url(../images/ticket_go.png); }
88 #admin-menu a.workflows { background-image: url(../images/ticket_go.png); }
89 #admin-menu a.custom_fields { background-image: url(../images/textfield.png); }
89 #admin-menu a.custom_fields { background-image: url(../images/textfield.png); }
90 #admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); }
90 #admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); }
91 #admin-menu a.settings { background-image: url(../images/changeset.png); }
91 #admin-menu a.settings { background-image: url(../images/changeset.png); }
92 #admin-menu a.plugins { background-image: url(../images/plugin.png); }
92 #admin-menu a.plugins { background-image: url(../images/plugin.png); }
93 #admin-menu a.info { background-image: url(../images/help.png); }
93 #admin-menu a.info { background-image: url(../images/help.png); }
94 #admin-menu a.server_authentication { background-image: url(../images/server_key.png); }
94 #admin-menu a.server_authentication { background-image: url(../images/server_key.png); }
95
95
96 #main {background-color:#EEEEEE;}
96 #main {background-color:#EEEEEE;}
97
97
98 #sidebar{ float: right; width: 22%; position: relative; z-index: 9; padding: 0; margin: 0;}
98 #sidebar{ float: right; width: 22%; position: relative; z-index: 9; padding: 0; margin: 0;}
99 * html #sidebar{ width: 22%; }
99 * html #sidebar{ width: 22%; }
100 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
100 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
101 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
101 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
102 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
102 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
103 #sidebar .contextual { margin-right: 1em; }
103 #sidebar .contextual { margin-right: 1em; }
104 #sidebar ul, ul.flat {margin: 0; padding: 0;}
104 #sidebar ul, ul.flat {margin: 0; padding: 0;}
105 #sidebar ul li, ul.flat li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;}
105 #sidebar ul li, ul.flat li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;}
106 #sidebar div.wiki ul {margin:inherit; padding-left:40px;}
106 #sidebar div.wiki ul {margin:inherit; padding-left:40px;}
107 #sidebar div.wiki ul li {list-style-type:inherit;}
107 #sidebar div.wiki ul li {list-style-type:inherit;}
108
108
109 #content { width: 75%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
109 #content { width: 75%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
110 * html #content{ width: 75%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
110 * html #content{ width: 75%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
111 html>body #content { min-height: 600px; }
111 html>body #content { min-height: 600px; }
112 * html body #content { height: 600px; } /* IE */
112 * html body #content { height: 600px; } /* IE */
113
113
114 #main.nosidebar #sidebar{ display: none; }
114 #main.nosidebar #sidebar{ display: none; }
115 #main.nosidebar #content{ width: auto; border-right: 0; }
115 #main.nosidebar #content{ width: auto; border-right: 0; }
116
116
117 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
117 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
118
118
119 #login-form {margin:5em auto 2em auto; padding:20px; width:340px; border:1px solid #FDBF3B; background-color:#FFEBC1; border-radius:4px; box-sizing: border-box;}
119 #login-form {margin:5em auto 2em auto; padding:20px; width:340px; border:1px solid #FDBF3B; background-color:#FFEBC1; border-radius:4px; box-sizing: border-box;}
120 #login-form label {display:block; margin-bottom:5px;}
120 #login-form label {display:block; margin-bottom:5px;}
121 #login-form input[type=text], #login-form input[type=password] {border:1px solid #ccc; border-radius:3px; margin-bottom:15px; padding:7px; display:block; width:100%; box-sizing: border-box;}
121 #login-form input[type=text], #login-form input[type=password] {border:1px solid #ccc; border-radius:3px; margin-bottom:15px; padding:7px; display:block; width:100%; box-sizing: border-box;}
122 #login-form label {font-weight:bold;}
122 #login-form label {font-weight:bold;}
123 #login-form label[for=autologin] {font-weight:normal;}
123 #login-form label[for=autologin] {font-weight:normal;}
124 #login-form a.lost_password {float:right; font-weight:normal;}
124 #login-form a.lost_password {float:right; font-weight:normal;}
125 #login-form input#openid_url {background:#fff url(../images/openid-bg.gif) no-repeat 4px 50%; padding-left:24px !important;}
125 #login-form input#openid_url {background:#fff url(../images/openid-bg.gif) no-repeat 4px 50%; padding-left:24px !important;}
126 #login-form input#login-submit {margin-top:15px; padding:7px; display:block; width:100%; box-sizing: border-box;}
126 #login-form input#login-submit {margin-top:15px; padding:7px; display:block; width:100%; box-sizing: border-box;}
127
127
128 div.modal { border-radius:5px; background:#fff; z-index:50; padding:4px;}
128 div.modal { border-radius:5px; background:#fff; z-index:50; padding:4px;}
129 div.modal h3.title {display:none;}
129 div.modal h3.title {display:none;}
130 div.modal p.buttons {text-align:right; margin-bottom:0;}
130 div.modal p.buttons {text-align:right; margin-bottom:0;}
131 div.modal .box p {margin: 0.3em 0;}
131 div.modal .box p {margin: 0.3em 0;}
132
132
133 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
133 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
134
134
135 .mobile-show {display: none;}
135 .mobile-show {display: none;}
136
136
137 /***** Links *****/
137 /***** Links *****/
138 a, a:link, a:visited{ color: #169; text-decoration: none; }
138 a, a:link, a:visited{ color: #169; text-decoration: none; }
139 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
139 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
140 a img{ border: 0; }
140 a img{ border: 0; }
141
141
142 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
142 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
143 a.project.closed, a.project.closed:link, a.project.closed:visited { color: #999; }
143 a.project.closed, a.project.closed:link, a.project.closed:visited { color: #999; }
144 a.user.locked, a.user.locked:link, a.user.locked:visited {color: #999;}
144 a.user.locked, a.user.locked:link, a.user.locked:visited {color: #999;}
145
145
146 #sidebar a.selected {line-height:1.7em; padding:1px 3px 2px 2px; margin-left:-2px; background-color:#9DB9D5; color:#fff; border-radius:2px;}
146 #sidebar a.selected {line-height:1.7em; padding:1px 3px 2px 2px; margin-left:-2px; background-color:#9DB9D5; color:#fff; border-radius:2px;}
147 #sidebar a.selected:hover {text-decoration:none;}
147 #sidebar a.selected:hover {text-decoration:none;}
148 #admin-menu a {line-height:1.7em;}
148 #admin-menu a {line-height:1.7em;}
149 #admin-menu a.selected {padding-left: 20px !important; background-position: 2px 40%;}
149 #admin-menu a.selected {padding-left: 20px !important; background-position: 2px 40%;}
150
150
151 a.collapsible {padding-left: 12px; background: url(../images/arrow_expanded.png) no-repeat -3px 40%;}
151 a.collapsible {padding-left: 12px; background: url(../images/arrow_expanded.png) no-repeat -3px 40%;}
152 a.collapsible.collapsed {background: url(../images/arrow_collapsed.png) no-repeat -5px 40%;}
152 a.collapsible.collapsed {background: url(../images/arrow_collapsed.png) no-repeat -5px 40%;}
153
153
154 a#toggle-completed-versions {color:#999;}
154 a#toggle-completed-versions {color:#999;}
155
155
156 a.toggle-checkboxes { margin-left: 5px; padding-left: 12px; background: url(../images/toggle_check.png) no-repeat 0% 50%; }
156 a.toggle-checkboxes { margin-left: 5px; padding-left: 12px; background: url(../images/toggle_check.png) no-repeat 0% 50%; }
157
157
158 /***** Tables *****/
158 /***** Tables *****/
159 table.list, .table-list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
159 table.list, .table-list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
160 table.list th, .table-list-header { background-color:#EEEEEE; padding: 4px; white-space:nowrap; font-weight:bold; }
160 table.list th, .table-list-header { background-color:#EEEEEE; padding: 4px; white-space:nowrap; font-weight:bold; }
161 table.list td {text-align:center; vertical-align:top; padding-right:10px;}
161 table.list td {text-align:center; vertical-align:top; padding-right:10px;}
162 table.list td.id { width: 2%; text-align: center;}
162 table.list td.id { width: 2%; text-align: center;}
163 table.list td.name, table.list td.description, table.list td.subject, table.list td.comments, table.list td.roles {text-align: left;}
163 table.list td.name, table.list td.description, table.list td.subject, table.list td.comments, table.list td.roles {text-align: left;}
164 table.list td.tick {width:15%}
164 table.list td.tick {width:15%}
165 table.list td.checkbox { width: 15px; padding: 2px 0 0 0; }
165 table.list td.checkbox { width: 15px; padding: 2px 0 0 0; }
166 table.list td.checkbox input {padding:0px;}
166 table.list td.checkbox input {padding:0px;}
167 table.list td.buttons, div.buttons { white-space:nowrap; text-align: right; }
167 table.list td.buttons, div.buttons { white-space:nowrap; text-align: right; }
168 table.list td.buttons a, div.buttons a { margin-right: 0.6em; }
168 table.list td.buttons a, div.buttons a { margin-right: 0.6em; }
169 table.list td.buttons img, div.buttons img {vertical-align:middle;}
169 table.list td.buttons img, div.buttons img {vertical-align:middle;}
170 table.list td.reorder {width:15%; white-space:nowrap; text-align:center; }
170 table.list td.reorder {width:15%; white-space:nowrap; text-align:center; }
171 table.list table.progress td {padding-right:0px;}
171 table.list table.progress td {padding-right:0px;}
172 table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; }
172 table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; }
173 #role-permissions-trackers table.list th {white-space:normal;}
173 #role-permissions-trackers table.list th {white-space:normal;}
174
174
175 .table-list-cell {display: table-cell; vertical-align: top; padding:2px; }
175 .table-list-cell {display: table-cell; vertical-align: top; padding:2px; }
176
176
177 tr.project td.name a { white-space:nowrap; }
177 tr.project td.name a { white-space:nowrap; }
178 tr.project.closed, tr.project.archived { color: #aaa; }
178 tr.project.closed, tr.project.archived { color: #aaa; }
179 tr.project.closed a, tr.project.archived a { color: #aaa; }
179 tr.project.closed a, tr.project.archived a { color: #aaa; }
180
180
181 tr.project.idnt td.name span {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
181 tr.project.idnt td.name span {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
182 tr.project.idnt-1 td.name {padding-left: 0.5em;}
182 tr.project.idnt-1 td.name {padding-left: 0.5em;}
183 tr.project.idnt-2 td.name {padding-left: 2em;}
183 tr.project.idnt-2 td.name {padding-left: 2em;}
184 tr.project.idnt-3 td.name {padding-left: 3.5em;}
184 tr.project.idnt-3 td.name {padding-left: 3.5em;}
185 tr.project.idnt-4 td.name {padding-left: 5em;}
185 tr.project.idnt-4 td.name {padding-left: 5em;}
186 tr.project.idnt-5 td.name {padding-left: 6.5em;}
186 tr.project.idnt-5 td.name {padding-left: 6.5em;}
187 tr.project.idnt-6 td.name {padding-left: 8em;}
187 tr.project.idnt-6 td.name {padding-left: 8em;}
188 tr.project.idnt-7 td.name {padding-left: 9.5em;}
188 tr.project.idnt-7 td.name {padding-left: 9.5em;}
189 tr.project.idnt-8 td.name {padding-left: 11em;}
189 tr.project.idnt-8 td.name {padding-left: 11em;}
190 tr.project.idnt-9 td.name {padding-left: 12.5em;}
190 tr.project.idnt-9 td.name {padding-left: 12.5em;}
191
191
192 tr.issue { text-align: center; white-space: nowrap; }
192 tr.issue { text-align: center; white-space: nowrap; }
193 tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text, tr.issue td.list, tr.issue td.relations, tr.issue td.parent { white-space: normal; }
193 tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text, tr.issue td.list, tr.issue td.relations, tr.issue td.parent { white-space: normal; }
194 tr.issue td.relations { text-align: left; }
194 tr.issue td.relations { text-align: left; }
195 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
195 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
196 tr.issue td.relations span {white-space: nowrap;}
196 tr.issue td.relations span {white-space: nowrap;}
197 table.issues td.description {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;}
197 table.issues td.description {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;}
198 table.issues td.description pre {white-space:normal;}
198 table.issues td.description pre {white-space:normal;}
199
199
200 tr.issue.idnt td.subject {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%;}
200 tr.issue.idnt td.subject {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%;}
201 tr.issue.idnt-1 td.subject {padding-left: 24px; background-position: 8px 50%;}
201 tr.issue.idnt-1 td.subject {padding-left: 24px; background-position: 8px 50%;}
202 tr.issue.idnt-2 td.subject {padding-left: 40px; background-position: 24px 50%;}
202 tr.issue.idnt-2 td.subject {padding-left: 40px; background-position: 24px 50%;}
203 tr.issue.idnt-3 td.subject {padding-left: 56px; background-position: 40px 50%;}
203 tr.issue.idnt-3 td.subject {padding-left: 56px; background-position: 40px 50%;}
204 tr.issue.idnt-4 td.subject {padding-left: 72px; background-position: 56px 50%;}
204 tr.issue.idnt-4 td.subject {padding-left: 72px; background-position: 56px 50%;}
205 tr.issue.idnt-5 td.subject {padding-left: 88px; background-position: 72px 50%;}
205 tr.issue.idnt-5 td.subject {padding-left: 88px; background-position: 72px 50%;}
206 tr.issue.idnt-6 td.subject {padding-left: 104px; background-position: 88px 50%;}
206 tr.issue.idnt-6 td.subject {padding-left: 104px; background-position: 88px 50%;}
207 tr.issue.idnt-7 td.subject {padding-left: 120px; background-position: 104px 50%;}
207 tr.issue.idnt-7 td.subject {padding-left: 120px; background-position: 104px 50%;}
208 tr.issue.idnt-8 td.subject {padding-left: 136px; background-position: 120px 50%;}
208 tr.issue.idnt-8 td.subject {padding-left: 136px; background-position: 120px 50%;}
209 tr.issue.idnt-9 td.subject {padding-left: 152px; background-position: 136px 50%;}
209 tr.issue.idnt-9 td.subject {padding-left: 152px; background-position: 136px 50%;}
210
210
211 table.issue-report {table-layout:fixed;}
211 table.issue-report {table-layout:fixed;}
212
212
213 tr.entry { border: 1px solid #f8f8f8; }
213 tr.entry { border: 1px solid #f8f8f8; }
214 tr.entry td { white-space: nowrap; }
214 tr.entry td { white-space: nowrap; }
215 tr.entry td.filename {width:30%; text-align:left;}
215 tr.entry td.filename {width:30%; text-align:left;}
216 tr.entry td.filename_no_report {width:70%; text-align:left;}
216 tr.entry td.filename_no_report {width:70%; text-align:left;}
217 tr.entry td.size { text-align: right; font-size: 90%; }
217 tr.entry td.size { text-align: right; font-size: 90%; }
218 tr.entry td.revision, tr.entry td.author { text-align: center; }
218 tr.entry td.revision, tr.entry td.author { text-align: center; }
219 tr.entry td.age { text-align: right; }
219 tr.entry td.age { text-align: right; }
220 tr.entry.file td.filename a { margin-left: 16px; }
220 tr.entry.file td.filename a { margin-left: 16px; }
221 tr.entry.file td.filename_no_report a { margin-left: 16px; }
221 tr.entry.file td.filename_no_report a { margin-left: 16px; }
222
222
223 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
223 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
224 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
224 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
225
225
226 tr.changeset { height: 20px }
226 tr.changeset { height: 20px }
227 tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
227 tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
228 tr.changeset td.revision_graph { width: 15%; background-color: #fffffb; }
228 tr.changeset td.revision_graph { width: 15%; background-color: #fffffb; }
229 tr.changeset td.author { text-align: center; width: 15%; white-space:nowrap;}
229 tr.changeset td.author { text-align: center; width: 15%; white-space:nowrap;}
230 tr.changeset td.committed_on { text-align: center; width: 15%; white-space:nowrap;}
230 tr.changeset td.committed_on { text-align: center; width: 15%; white-space:nowrap;}
231
231
232 table.files tbody th {text-align:left;}
232 table.files tbody th {text-align:left;}
233 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
233 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
234 table.files tr.file td.digest { font-size: 80%; }
234 table.files tr.file td.digest { font-size: 80%; }
235
235
236 table.members td.roles, table.memberships td.roles { width: 45%; }
236 table.members td.roles, table.memberships td.roles { width: 45%; }
237
237
238 tr.message { height: 2.6em; }
238 tr.message { height: 2.6em; }
239 tr.message td.subject { padding-left: 20px; }
239 tr.message td.subject { padding-left: 20px; }
240 tr.message td.created_on { white-space: nowrap; }
240 tr.message td.created_on { white-space: nowrap; }
241 tr.message td.last_message { font-size: 80%; white-space: nowrap; }
241 tr.message td.last_message { font-size: 80%; white-space: nowrap; }
242 tr.message.locked td.subject { background: url(../images/locked.png) no-repeat 0 1px; }
242 tr.message.locked td.subject { background: url(../images/locked.png) no-repeat 0 1px; }
243 tr.message.sticky td.subject { background: url(../images/bullet_go.png) no-repeat 0 1px; font-weight: bold; }
243 tr.message.sticky td.subject { background: url(../images/bullet_go.png) no-repeat 0 1px; font-weight: bold; }
244
244
245 tr.version.closed, tr.version.closed a { color: #999; }
245 tr.version.closed, tr.version.closed a { color: #999; }
246 tr.version td.name { padding-left: 20px; }
246 tr.version td.name { padding-left: 20px; }
247 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
247 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
248 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; }
248 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; }
249
249
250 tr.user td {width:13%;white-space: nowrap;}
250 tr.user td {width:13%;white-space: nowrap;}
251 td.username, td.firstname, td.lastname, td.email {text-align:left !important;}
251 td.username, td.firstname, td.lastname, td.email {text-align:left !important;}
252 tr.user td.email { width:18%; }
252 tr.user td.email { width:18%; }
253 tr.user.locked, tr.user.registered { color: #aaa; }
253 tr.user.locked, tr.user.registered { color: #aaa; }
254 tr.user.locked a, tr.user.registered a { color: #aaa; }
254 tr.user.locked a, tr.user.registered a { color: #aaa; }
255
255
256 table.permissions td.role {color:#999;font-size:90%;font-weight:normal !important;text-align:center;vertical-align:bottom;}
256 table.permissions td.role {color:#999;font-size:90%;font-weight:normal !important;text-align:center;vertical-align:bottom;}
257
257
258 tr.wiki-page-version td.updated_on, tr.wiki-page-version td.author {text-align:center;}
258 tr.wiki-page-version td.updated_on, tr.wiki-page-version td.author {text-align:center;}
259
259
260 tr.time-entry { text-align: center; white-space: nowrap; }
260 tr.time-entry { text-align: center; white-space: nowrap; }
261 tr.time-entry td.issue, tr.time-entry td.comments, tr.time-entry td.subject, tr.time-entry td.activity { text-align: left; white-space: normal; }
261 tr.time-entry td.issue, tr.time-entry td.comments, tr.time-entry td.subject, tr.time-entry td.activity { text-align: left; white-space: normal; }
262 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
262 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
263 td.hours .hours-dec { font-size: 0.9em; }
263 td.hours .hours-dec { font-size: 0.9em; }
264
264
265 table.plugins td { vertical-align: middle; }
265 table.plugins td { vertical-align: middle; }
266 table.plugins td.configure { text-align: right; padding-right: 1em; }
266 table.plugins td.configure { text-align: right; padding-right: 1em; }
267 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
267 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
268 table.plugins span.description { display: block; font-size: 0.9em; }
268 table.plugins span.description { display: block; font-size: 0.9em; }
269 table.plugins span.url { display: block; font-size: 0.9em; }
269 table.plugins span.url { display: block; font-size: 0.9em; }
270
270
271 tr.group td { padding: 0.8em 0 0.5em 0.3em; border-bottom: 1px solid #ccc; text-align:left; }
271 tr.group td { padding: 0.8em 0 0.5em 0.3em; border-bottom: 1px solid #ccc; text-align:left; }
272 tr.group span.name {font-weight:bold;}
272 tr.group span.name {font-weight:bold;}
273 tr.group span.count {font-weight:bold; position:relative; top:-1px; color:#fff; font-size:10px; background:#9DB9D5; padding:0px 6px 1px 6px; border-radius:3px; margin-left:4px;}
273 tr.group span.count {font-weight:bold; position:relative; top:-1px; color:#fff; font-size:10px; background:#9DB9D5; padding:0px 6px 1px 6px; border-radius:3px; margin-left:4px;}
274 tr.group span.totals {color: #aaa; font-size: 80%;}
274 tr.group span.totals {color: #aaa; font-size: 80%;}
275 tr.group span.totals .value {font-weight:bold; color:#777;}
275 tr.group span.totals .value {font-weight:bold; color:#777;}
276 tr.group a.toggle-all { color: #aaa; font-size: 80%; display:none; float:right; margin-right:4px;}
276 tr.group a.toggle-all { color: #aaa; font-size: 80%; display:none; float:right; margin-right:4px;}
277 tr.group:hover a.toggle-all { display:inline;}
277 tr.group:hover a.toggle-all { display:inline;}
278 a.toggle-all:hover {text-decoration:none;}
278 a.toggle-all:hover {text-decoration:none;}
279
279
280 table.list tbody tr:hover { background-color:#ffffdd; }
280 table.list tbody tr:hover { background-color:#ffffdd; }
281 table.list tbody tr.group:hover { background-color:inherit; }
281 table.list tbody tr.group:hover { background-color:inherit; }
282 table td {padding:2px;}
282 table td {padding:2px;}
283 table p {margin:0;}
283 table p {margin:0;}
284 .odd {background-color:#f6f7f8;}
284 .odd {background-color:#f6f7f8;}
285 .even {background-color: #fff;}
285 .even {background-color: #fff;}
286
286
287 tr.builtin td.name {font-style:italic;}
287 tr.builtin td.name {font-style:italic;}
288
288
289 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
289 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
290 a.sort.asc { background-image: url(../images/sort_asc.png); }
290 a.sort.asc { background-image: url(../images/sort_asc.png); }
291 a.sort.desc { background-image: url(../images/sort_desc.png); }
291 a.sort.desc { background-image: url(../images/sort_desc.png); }
292
292
293 table.boards a.board, h3.comments { background: url(../images/comment.png) no-repeat 0% 50%; padding-left: 20px; }
293 table.boards a.board, h3.comments { background: url(../images/comment.png) no-repeat 0% 50%; padding-left: 20px; }
294 table.boards td.last-message {text-align:left;font-size:80%;}
294 table.boards td.last-message {text-align:left;font-size:80%;}
295
295
296 div.table-list.boards .table-list-cell.name {width: 30%;}
296 div.table-list.boards .table-list-cell.name {width: 30%;}
297
297
298 table.messages td.last_message {text-align:left;}
298 table.messages td.last_message {text-align:left;}
299
299
300 #query_form_content {font-size:90%;}
300 #query_form_content {font-size:90%;}
301
301
302 .query_sort_criteria_count {
302 .query_sort_criteria_count {
303 display: inline-block;
303 display: inline-block;
304 min-width: 1em;
304 min-width: 1em;
305 }
305 }
306
306
307 table.query-columns {
307 table.query-columns {
308 border-collapse: collapse;
308 border-collapse: collapse;
309 border: 0;
309 border: 0;
310 }
310 }
311
311
312 table.query-columns td.buttons {
312 table.query-columns td.buttons {
313 vertical-align: middle;
313 vertical-align: middle;
314 text-align: center;
314 text-align: center;
315 }
315 }
316 table.query-columns td.buttons input[type=button] {width:35px;}
316 table.query-columns td.buttons input[type=button] {width:35px;}
317 .query-totals {text-align:right;}
317 .query-totals {text-align:right;}
318 .query-totals>span {margin-left:0.6em;}
318 .query-totals>span {margin-left:0.6em;}
319 .query-totals .value {font-weight:bold;}
319 .query-totals .value {font-weight:bold;}
320 body.controller-issues .query-totals {margin-top:-2.3em;}
320 body.controller-issues .query-totals {margin-top:-2.3em;}
321
321
322 td.center {text-align:center;}
322 td.center {text-align:center;}
323
323
324 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
324 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
325
325
326 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
326 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
327 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
327 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
328 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
328 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
329 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
329 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
330 div.spent_time h3 { background: url(../images/time.png) no-repeat 0% 50%; padding-left: 20px; }
330 div.spent_time h3 { background: url(../images/time.png) no-repeat 0% 50%; padding-left: 20px; }
331
331
332 #watchers select {width: 95%; display: block;}
332 #watchers select {width: 95%; display: block;}
333 #watchers a.delete {opacity: 0.4; margin-left: 5px;}
333 #watchers a.delete {opacity: 0.4; margin-left: 5px;}
334 #watchers a.delete:hover {opacity: 1;}
334 #watchers a.delete:hover {opacity: 1;}
335 #watchers img.gravatar {margin: 0 4px 2px 0;}
335 #watchers img.gravatar {margin: 0 4px 2px 0;}
336
336
337 span#watchers_inputs {overflow:auto; display:block;}
337 span#watchers_inputs {overflow:auto; display:block;}
338 span.search_for_watchers {display:block;}
338 span.search_for_watchers {display:block;}
339 span.search_for_watchers, span.add_attachment {font-size:80%; line-height:2.5em;}
339 span.search_for_watchers, span.add_attachment {font-size:80%; line-height:2.5em;}
340 span.search_for_watchers a, span.add_attachment a {padding-left:16px; background: url(../images/bullet_add.png) no-repeat 0 50%; }
340 span.search_for_watchers a, span.add_attachment a {padding-left:16px; background: url(../images/bullet_add.png) no-repeat 0 50%; }
341
341
342
342
343 .highlight { background-color: #FCFD8D;}
343 .highlight { background-color: #FCFD8D;}
344 .highlight.token-1 { background-color: #faa;}
344 .highlight.token-1 { background-color: #faa;}
345 .highlight.token-2 { background-color: #afa;}
345 .highlight.token-2 { background-color: #afa;}
346 .highlight.token-3 { background-color: #aaf;}
346 .highlight.token-3 { background-color: #aaf;}
347
347
348 .box{
348 .box{
349 padding:6px;
349 padding:6px;
350 margin-bottom: 10px;
350 margin-bottom: 10px;
351 background-color:#f6f6f6;
351 background-color:#f6f6f6;
352 color:#505050;
352 color:#505050;
353 line-height:1.5em;
353 line-height:1.5em;
354 border: 1px solid #e4e4e4;
354 border: 1px solid #e4e4e4;
355 word-wrap: break-word;
355 word-wrap: break-word;
356 border-radius: 3px;
356 border-radius: 3px;
357 }
357 }
358
358
359 div.square {
359 div.square {
360 border: 1px solid #999;
360 border: 1px solid #999;
361 float: left;
361 float: left;
362 margin: .3em .4em 0 .4em;
362 margin: .3em .4em 0 .4em;
363 overflow: hidden;
363 overflow: hidden;
364 width: .6em; height: .6em;
364 width: .6em; height: .6em;
365 }
365 }
366 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin:5px 0px; padding-left: 10px; font-size:0.9em;}
366 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin:5px 0px; padding-left: 10px; font-size:0.9em;}
367 .contextual input, .contextual select {font-size:0.9em;}
367 .contextual input, .contextual select {font-size:0.9em;}
368 .message .contextual { margin-top: 0; }
368 .message .contextual { margin-top: 0; }
369
369
370 .splitcontent {overflow:auto;}
370 .splitcontent {overflow:auto;}
371 .splitcontentleft{float:left; width:49%;}
371 .splitcontentleft{float:left; width:49%;}
372 .splitcontentright{float:right; width:49%;}
372 .splitcontentright{float:right; width:49%;}
373 form {display: inline;}
373 form {display: inline;}
374 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
374 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
375 fieldset {border: 1px solid #e4e4e4; margin:0;}
375 fieldset {border: 1px solid #e4e4e4; margin:0;}
376 legend {color: #333;}
376 legend {color: #333;}
377 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
377 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
378 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
378 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
379 blockquote blockquote { margin-left: 0;}
379 blockquote blockquote { margin-left: 0;}
380 abbr, span.field-description[title] { border-bottom: 1px dotted #aaa; cursor: help; }
380 abbr, span.field-description[title] { border-bottom: 1px dotted #aaa; cursor: help; }
381 textarea.wiki-edit {width:99%; resize:vertical;}
381 textarea.wiki-edit {width:99%; resize:vertical;}
382 body.textarea-monospace textarea.wiki-edit {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace; font-size: 12px;}
382 body.textarea-monospace textarea.wiki-edit {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace; font-size: 12px;}
383 body.textarea-proportional textarea.wiki-edit {font-family: Verdana, sans-serif; font-size: 12px;}
383 body.textarea-proportional textarea.wiki-edit {font-family: Verdana, sans-serif; font-size: 12px;}
384 li p {margin-top: 0;}
384 li p {margin-top: 0;}
385 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px; border: 1px solid #d7d7d7; border-radius:3px;}
385 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px; border: 1px solid #d7d7d7; border-radius:3px;}
386 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
386 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
387 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
387 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
388 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
388 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
389 .ltr {direction:ltr !important; unicode-bidi:bidi-override;}
389 .ltr {direction:ltr !important; unicode-bidi:bidi-override;}
390 .rtl {direction:rtl !important; unicode-bidi:bidi-override;}
390 .rtl {direction:rtl !important; unicode-bidi:bidi-override;}
391
391
392 div.issue div.subject div div { padding-left: 16px; }
392 div.issue div.subject div div { padding-left: 16px; }
393 div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color: #999;}
393 div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color: #999;}
394 div.issue div.subject>div>p { margin-top: 0.5em; }
394 div.issue div.subject>div>p { margin-top: 0.5em; }
395 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
395 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
396 div.issue span.private, div.journal span.private { position:relative; bottom: 2px; text-transform: uppercase; background: #d22; color: #fff; font-weight:bold; padding: 0px 2px 0px 2px; font-size: 60%; margin-right: 2px; border-radius: 2px;}
396 div.issue span.private, div.journal span.private { position:relative; bottom: 2px; text-transform: uppercase; background: #d22; color: #fff; font-weight:bold; padding: 0px 2px 0px 2px; font-size: 60%; margin-right: 2px; border-radius: 2px;}
397 div.issue .next-prev-links {color:#999;}
397 div.issue .next-prev-links {color:#999;}
398 div.issue .attributes {margin-top: 2em;}
398 div.issue .attributes {margin-top: 2em;}
399 div.issue .attribute {padding-left:180px; clear:left; min-height: 1.8em;}
399 div.issue .attribute {padding-left:180px; clear:left; min-height: 1.8em;}
400 div.issue .attribute .label {width: 170px; margin-left:-180px; font-weight:bold; float:left;}
400 div.issue .attribute .label {width: 170px; margin-left:-180px; font-weight:bold; float:left;}
401 div.issue.overdue .due-date .value { color: #c22; }
401 div.issue.overdue .due-date .value { color: #c22; }
402
402
403 #issue_tree table.issues, #relations table.issues { border: 0; }
403 #issue_tree table.issues, #relations table.issues { border: 0; }
404 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
404 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
405 #relations td.buttons {padding:0;}
405 #relations td.buttons {padding:0;}
406
406
407 fieldset.collapsible {border-width: 1px 0 0 0;}
407 fieldset.collapsible {border-width: 1px 0 0 0;}
408 fieldset.collapsible>legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
408 fieldset.collapsible>legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
409 fieldset.collapsible.collapsed>legend { background-image: url(../images/arrow_collapsed.png); }
409 fieldset.collapsible.collapsed>legend { background-image: url(../images/arrow_collapsed.png); }
410
410
411 fieldset#date-range p { margin: 2px 0 2px 0; }
411 fieldset#date-range p { margin: 2px 0 2px 0; }
412 fieldset#filters table { border-collapse: collapse; }
412 fieldset#filters table { border-collapse: collapse; }
413 fieldset#filters table td { padding: 0; vertical-align: middle; }
413 fieldset#filters table td { padding: 0; vertical-align: middle; }
414 fieldset#filters tr.filter { height: 2.1em; }
414 fieldset#filters tr.filter { height: 2.1em; }
415 fieldset#filters td.field { width:230px; }
415 fieldset#filters td.field { width:230px; }
416 fieldset#filters td.operator { width:130px; }
416 fieldset#filters td.operator { width:130px; }
417 fieldset#filters td.operator select {max-width:120px;}
417 fieldset#filters td.operator select {max-width:120px;}
418 fieldset#filters td.values { white-space:nowrap; }
418 fieldset#filters td.values { white-space:nowrap; }
419 fieldset#filters td.values select {min-width:130px; max-width:200px;}
419 fieldset#filters td.values select {min-width:130px; max-width:200px;}
420 fieldset#filters td.values input {height:1em;}
420 fieldset#filters td.values input {height:1em;}
421
421
422 #filters-table {width:60%; float:left;}
422 #filters-table {width:60%; float:left;}
423 .add-filter {width:35%; float:right; text-align: right; vertical-align: top;}
423 .add-filter {width:35%; float:right; text-align: right; vertical-align: top;}
424
424
425 #issue_is_private_wrap {float:right; margin-right:1em;}
425 #issue_is_private_wrap {float:right; margin-right:1em;}
426 .toggle-multiselect {background: url(../images/bullet_toggle_plus.png) no-repeat 0% 40%; padding-left:16px; margin-left:0; margin-right:5px; cursor:pointer;}
426 .toggle-multiselect {background: url(../images/bullet_toggle_plus.png) no-repeat 0% 40%; padding-left:16px; margin-left:0; margin-right:5px; cursor:pointer;}
427 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
427 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
428
428
429 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
429 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
430 div#issue-changesets div.changeset { padding: 4px;}
430 div#issue-changesets div.changeset { padding: 4px;}
431 div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
431 div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
432 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
432 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
433
433
434 div.journal {overflow:auto;}
434 div.journal {overflow:auto;}
435 div.journal.private-notes {border-left:2px solid #d22; padding-left:4px; margin-left:-6px;}
435 div.journal.private-notes {border-left:2px solid #d22; padding-left:4px; margin-left:-6px;}
436 div.journal ul.details, ul.revision-info {color:#959595; margin-bottom: 1.5em;}
436 div.journal ul.details, ul.revision-info {color:#959595; margin-bottom: 1.5em;}
437 div.journal ul.details a, ul.revision-info a {color:#70A7CD;}
437 div.journal ul.details a, ul.revision-info a {color:#70A7CD;}
438 div.journal ul.details a:hover, ul.revision-info a:hover {color:#D14848;}
438 div.journal ul.details a:hover, ul.revision-info a:hover {color:#D14848;}
439
439
440 div#activity dl, #search-results { margin-left: 2em; }
440 div#activity dl, #search-results { margin-left: 2em; }
441 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
441 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
442 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
442 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
443 div#activity dt.me .time { border-bottom: 1px solid #999; }
443 div#activity dt.me .time { border-bottom: 1px solid #999; }
444 div#activity dt .time { color: #777; font-size: 80%; }
444 div#activity dt .time { color: #777; font-size: 80%; }
445 div#activity dd .description, #search-results dd .description { font-style: italic; }
445 div#activity dd .description, #search-results dd .description { font-style: italic; }
446 div#activity span.project:after, #search-results span.project:after { content: " -"; }
446 div#activity span.project:after, #search-results span.project:after { content: " -"; }
447 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
447 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
448 div#activity dt.grouped {margin-left:5em;}
448 div#activity dt.grouped {margin-left:5em;}
449 div#activity dd.grouped {margin-left:9em;}
449 div#activity dd.grouped {margin-left:9em;}
450 div#activity dt { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; height: 18px;}
450
451
451 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
452 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
452
453
453 div#search-results-counts {float:right;}
454 div#search-results-counts {float:right;}
454 div#search-results-counts ul { margin-top: 0.5em; }
455 div#search-results-counts ul { margin-top: 0.5em; }
455 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
456 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
456
457
457 dt.issue { background-image: url(../images/ticket.png); }
458 dt.issue { background-image: url(../images/ticket.png); }
458 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
459 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
459 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
460 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
460 dt.issue-note { background-image: url(../images/ticket_note.png); }
461 dt.issue-note { background-image: url(../images/ticket_note.png); }
461 dt.changeset { background-image: url(../images/changeset.png); }
462 dt.changeset { background-image: url(../images/changeset.png); }
462 dt.news { background-image: url(../images/news.png); }
463 dt.news { background-image: url(../images/news.png); }
463 dt.message { background-image: url(../images/message.png); }
464 dt.message { background-image: url(../images/message.png); }
464 dt.reply { background-image: url(../images/comments.png); }
465 dt.reply { background-image: url(../images/comments.png); }
465 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
466 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
466 dt.attachment { background-image: url(../images/attachment.png); }
467 dt.attachment { background-image: url(../images/attachment.png); }
467 dt.document { background-image: url(../images/document.png); }
468 dt.document { background-image: url(../images/document.png); }
468 dt.project { background-image: url(../images/projects.png); }
469 dt.project { background-image: url(../images/projects.png); }
469 dt.time-entry { background-image: url(../images/time.png); }
470 dt.time-entry { background-image: url(../images/time.png); }
470
471
471 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
472 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
472
473
473 div#roadmap .related-issues { margin-bottom: 1em; }
474 div#roadmap .related-issues { margin-bottom: 1em; }
474 div#roadmap .related-issues td.checkbox { display: none; }
475 div#roadmap .related-issues td.checkbox { display: none; }
475 div#roadmap .wiki h1:first-child { display: none; }
476 div#roadmap .wiki h1:first-child { display: none; }
476 div#roadmap .wiki h1 { font-size: 120%; }
477 div#roadmap .wiki h1 { font-size: 120%; }
477 div#roadmap .wiki h2 { font-size: 110%; }
478 div#roadmap .wiki h2 { font-size: 110%; }
478 body.controller-versions.action-show div#roadmap .related-issues {width:70%;}
479 body.controller-versions.action-show div#roadmap .related-issues {width:70%;}
479
480
480 div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
481 div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
481 div#version-summary fieldset { margin-bottom: 1em; }
482 div#version-summary fieldset { margin-bottom: 1em; }
482 div#version-summary fieldset.time-tracking table { width:100%; }
483 div#version-summary fieldset.time-tracking table { width:100%; }
483 div#version-summary th, div#version-summary td.total-hours { text-align: right; }
484 div#version-summary th, div#version-summary td.total-hours { text-align: right; }
484
485
485 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
486 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
486 table#time-report tbody tr.subtotal { font-style: italic; color:#777;}
487 table#time-report tbody tr.subtotal { font-style: italic; color:#777;}
487 table#time-report tbody tr.subtotal td.hours { color:#b0b0b0; }
488 table#time-report tbody tr.subtotal td.hours { color:#b0b0b0; }
488 table#time-report tbody tr.total { font-weight: bold; background-color:#EEEEEE; border-top:1px solid #e4e4e4;}
489 table#time-report tbody tr.total { font-weight: bold; background-color:#EEEEEE; border-top:1px solid #e4e4e4;}
489 table#time-report .hours-dec { font-size: 0.9em; }
490 table#time-report .hours-dec { font-size: 0.9em; }
490
491
491 div.wiki-page .contextual a {opacity: 0.4}
492 div.wiki-page .contextual a {opacity: 0.4}
492 div.wiki-page .contextual a:hover {opacity: 1}
493 div.wiki-page .contextual a:hover {opacity: 1}
493
494
494 form .attributes select { width: 60%; }
495 form .attributes select { width: 60%; }
495 form .attributes select + a.icon-only { vertical-align: middle; margin-left: 4px; }
496 form .attributes select + a.icon-only { vertical-align: middle; margin-left: 4px; }
496 input#issue_subject, input#document_title { width: 99%; }
497 input#issue_subject, input#document_title { width: 99%; }
497 select#issue_done_ratio { width: 95px; }
498 select#issue_done_ratio { width: 95px; }
498
499
499 ul.projects {margin:0; padding-left:1em;}
500 ul.projects {margin:0; padding-left:1em;}
500 ul.projects ul {padding-left:1.6em;}
501 ul.projects ul {padding-left:1.6em;}
501 ul.projects.root {margin:0; padding:0;}
502 ul.projects.root {margin:0; padding:0;}
502 ul.projects li {list-style-type:none;}
503 ul.projects li {list-style-type:none;}
503
504
504 #projects-index ul.projects ul.projects { border-left: 3px solid #e0e0e0; padding-left:1em;}
505 #projects-index ul.projects ul.projects { border-left: 3px solid #e0e0e0; padding-left:1em;}
505 #projects-index ul.projects li.root {margin-bottom: 1em;}
506 #projects-index ul.projects li.root {margin-bottom: 1em;}
506 #projects-index ul.projects li.child {margin-top: 1em;}
507 #projects-index ul.projects li.child {margin-top: 1em;}
507 #projects-index ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
508 #projects-index ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
508 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
509 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
509
510
510 #notified-projects>ul, #tracker_project_ids>ul, #custom_field_project_ids>ul {max-height:250px; overflow-y:auto;}
511 #notified-projects>ul, #tracker_project_ids>ul, #custom_field_project_ids>ul {max-height:250px; overflow-y:auto;}
511
512
512 #related-issues li img {vertical-align:middle;}
513 #related-issues li img {vertical-align:middle;}
513
514
514 ul.properties {padding:0; font-size: 0.9em; color: #777;}
515 ul.properties {padding:0; font-size: 0.9em; color: #777;}
515 ul.properties li {list-style-type:none;}
516 ul.properties li {list-style-type:none;}
516 ul.properties li span {font-style:italic;}
517 ul.properties li span {font-style:italic;}
517
518
518 .total-hours { font-size: 110%; font-weight: bold; }
519 .total-hours { font-size: 110%; font-weight: bold; }
519 .total-hours span.hours-int { font-size: 120%; }
520 .total-hours span.hours-int { font-size: 120%; }
520
521
521 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em; position: relative;}
522 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em; position: relative;}
522 #user_login, #user_firstname, #user_lastname, #user_mail, #my_account_form select, #user_form select, #user_identity_url { width: 90%; }
523 #user_login, #user_firstname, #user_lastname, #user_mail, #my_account_form select, #user_form select, #user_identity_url { width: 90%; }
523
524
524 #workflow_copy_form select { width: 200px; }
525 #workflow_copy_form select { width: 200px; }
525 table.transitions td.enabled {background: #bfb;}
526 table.transitions td.enabled {background: #bfb;}
526 #workflow_form table select {font-size:90%; max-width:100px;}
527 #workflow_form table select {font-size:90%; max-width:100px;}
527 table.fields_permissions td.readonly {background:#ddd;}
528 table.fields_permissions td.readonly {background:#ddd;}
528 table.fields_permissions td.required {background:#d88;}
529 table.fields_permissions td.required {background:#d88;}
529
530
530 select.expandable {vertical-align:top;}
531 select.expandable {vertical-align:top;}
531
532
532 textarea#custom_field_possible_values {width: 95%; resize:vertical}
533 textarea#custom_field_possible_values {width: 95%; resize:vertical}
533 textarea#custom_field_default_value {width: 95%; resize:vertical}
534 textarea#custom_field_default_value {width: 95%; resize:vertical}
534 .sort-handle {display:inline-block; vertical-align:middle;}
535 .sort-handle {display:inline-block; vertical-align:middle;}
535
536
536 input#content_comments {width: 99%}
537 input#content_comments {width: 99%}
537
538
538 span.pagination {margin-left:3px; color:#888; display:block;}
539 span.pagination {margin-left:3px; color:#888; display:block;}
539 .pagination ul.pages {
540 .pagination ul.pages {
540 margin: 0 5px 0 0;
541 margin: 0 5px 0 0;
541 padding: 0;
542 padding: 0;
542 display: inline;
543 display: inline;
543 }
544 }
544 .pagination ul.pages li {
545 .pagination ul.pages li {
545 display: inline-block;
546 display: inline-block;
546 padding: 0;
547 padding: 0;
547 border: 1px solid #ddd;
548 border: 1px solid #ddd;
548 margin-left: -1px;
549 margin-left: -1px;
549 line-height: 2em;
550 line-height: 2em;
550 margin-bottom: 1em;
551 margin-bottom: 1em;
551 white-space: nowrap;
552 white-space: nowrap;
552 text-align: center;
553 text-align: center;
553 }
554 }
554 .pagination ul.pages li a,
555 .pagination ul.pages li a,
555 .pagination ul.pages li span {
556 .pagination ul.pages li span {
556 padding: 3px 8px;
557 padding: 3px 8px;
557 }
558 }
558 .pagination ul.pages li:first-child {
559 .pagination ul.pages li:first-child {
559 border-top-left-radius: 4px;
560 border-top-left-radius: 4px;
560 border-bottom-left-radius: 4px;
561 border-bottom-left-radius: 4px;
561 }
562 }
562 .pagination ul.pages li:last-child {
563 .pagination ul.pages li:last-child {
563 border-top-right-radius: 4px;
564 border-top-right-radius: 4px;
564 border-bottom-right-radius: 4px;
565 border-bottom-right-radius: 4px;
565 }
566 }
566 .pagination ul.pages li.current {
567 .pagination ul.pages li.current {
567 color: white;
568 color: white;
568 background-color: #628DB6;
569 background-color: #628DB6;
569 border-color: #628DB6;
570 border-color: #628DB6;
570 }
571 }
571 .pagination ul.pages li.page:hover {
572 .pagination ul.pages li.page:hover {
572 background-color: #ddd;
573 background-color: #ddd;
573 }
574 }
574 .pagination ul.pages li.page a:hover,
575 .pagination ul.pages li.page a:hover,
575 .pagination ul.pages li.page a:active {
576 .pagination ul.pages li.page a:active {
576 color: #169;
577 color: #169;
577 text-decoration: inherit;
578 text-decoration: inherit;
578 }
579 }
579 .pagination .per-page span.selected {
580 .pagination .per-page span.selected {
580 font-weight: bold;
581 font-weight: bold;
581 }
582 }
582 span.pagination>span {white-space:nowrap;}
583 span.pagination>span {white-space:nowrap;}
583
584
584 #search-form fieldset p {margin:0.2em 0;}
585 #search-form fieldset p {margin:0.2em 0;}
585
586
586 /***** Tabular forms ******/
587 /***** Tabular forms ******/
587 .tabular p{
588 .tabular p{
588 margin: 0;
589 margin: 0;
589 padding: 3px 0 3px 0;
590 padding: 3px 0 3px 0;
590 padding-left: 180px; /* width of left column containing the label elements */
591 padding-left: 180px; /* width of left column containing the label elements */
591 min-height: 1.8em;
592 min-height: 1.8em;
592 clear:left;
593 clear:left;
593 }
594 }
594
595
595 html>body .tabular p {overflow:hidden;}
596 html>body .tabular p {overflow:hidden;}
596
597
597 .tabular input, .tabular select {max-width:95%}
598 .tabular input, .tabular select {max-width:95%}
598 .tabular textarea {width:95%; resize:vertical;}
599 .tabular textarea {width:95%; resize:vertical;}
599
600
600 .tabular label{
601 .tabular label{
601 font-weight: bold;
602 font-weight: bold;
602 float: left;
603 float: left;
603 text-align: right;
604 text-align: right;
604 /* width of left column */
605 /* width of left column */
605 margin-left: -180px;
606 margin-left: -180px;
606 /* width of labels. Should be smaller than left column to create some right margin */
607 /* width of labels. Should be smaller than left column to create some right margin */
607 width: 175px;
608 width: 175px;
608 }
609 }
609
610
610 .tabular label.floating{
611 .tabular label.floating{
611 font-weight: normal;
612 font-weight: normal;
612 margin-left: 0px;
613 margin-left: 0px;
613 text-align: left;
614 text-align: left;
614 width: 270px;
615 width: 270px;
615 }
616 }
616
617
617 .tabular label.block{
618 .tabular label.block{
618 font-weight: normal;
619 font-weight: normal;
619 margin-left: 0px !important;
620 margin-left: 0px !important;
620 text-align: left;
621 text-align: left;
621 float: none;
622 float: none;
622 display: block;
623 display: block;
623 width: auto !important;
624 width: auto !important;
624 }
625 }
625
626
626 .tabular label.inline{
627 .tabular label.inline{
627 font-weight: normal;
628 font-weight: normal;
628 float:none;
629 float:none;
629 margin-left: 5px !important;
630 margin-left: 5px !important;
630 width: auto;
631 width: auto;
631 }
632 }
632
633
633 label.no-css {
634 label.no-css {
634 font-weight: inherit;
635 font-weight: inherit;
635 float:none;
636 float:none;
636 text-align:left;
637 text-align:left;
637 margin-left:0px;
638 margin-left:0px;
638 width:auto;
639 width:auto;
639 }
640 }
640 input#time_entry_comments { width: 90%;}
641 input#time_entry_comments { width: 90%;}
641
642
642 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
643 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
643
644
644 .tabular.settings p{ padding-left: 300px; }
645 .tabular.settings p{ padding-left: 300px; }
645 .tabular.settings label{ margin-left: -300px; width: 295px; }
646 .tabular.settings label{ margin-left: -300px; width: 295px; }
646 .tabular.settings textarea { width: 99%; }
647 .tabular.settings textarea { width: 99%; }
647
648
648 .settings.enabled_scm table {width:100%}
649 .settings.enabled_scm table {width:100%}
649 .settings.enabled_scm td.scm_name{ font-weight: bold; }
650 .settings.enabled_scm td.scm_name{ font-weight: bold; }
650
651
651 fieldset.settings label { display: block; }
652 fieldset.settings label { display: block; }
652 fieldset#notified_events .parent { padding-left: 20px; }
653 fieldset#notified_events .parent { padding-left: 20px; }
653
654
654 span.required {color: #bb0000;}
655 span.required {color: #bb0000;}
655 .summary {font-style: italic;}
656 .summary {font-style: italic;}
656
657
657 .check_box_group {
658 .check_box_group {
658 display:block;
659 display:block;
659 width:95%;
660 width:95%;
660 max-height:300px;
661 max-height:300px;
661 overflow-y:auto;
662 overflow-y:auto;
662 padding:2px 4px 4px 2px;
663 padding:2px 4px 4px 2px;
663 background:#fff;
664 background:#fff;
664 border:1px solid #9EB1C2;
665 border:1px solid #9EB1C2;
665 border-radius:2px
666 border-radius:2px
666 }
667 }
667 .check_box_group label {
668 .check_box_group label {
668 font-weight: normal;
669 font-weight: normal;
669 margin-left: 0px !important;
670 margin-left: 0px !important;
670 text-align: left;
671 text-align: left;
671 float: none;
672 float: none;
672 display: block;
673 display: block;
673 width: auto;
674 width: auto;
674 }
675 }
675 .check_box_group.bool_cf {border:0; background:inherit;}
676 .check_box_group.bool_cf {border:0; background:inherit;}
676 .check_box_group.bool_cf label {display: inline;}
677 .check_box_group.bool_cf label {display: inline;}
677
678
678 #attachments_fields input.description, #existing-attachments input.description {margin-left:4px; width:340px;}
679 #attachments_fields input.description, #existing-attachments input.description {margin-left:4px; width:340px;}
679 #attachments_fields>span, #existing-attachments>span {display:block; white-space:nowrap;}
680 #attachments_fields>span, #existing-attachments>span {display:block; white-space:nowrap;}
680 #attachments_fields input.filename, #existing-attachments .filename {border:0; width:250px; color:#555; background-color:inherit; background:url(../images/attachment.png) no-repeat 1px 50%; padding-left:18px;}
681 #attachments_fields input.filename, #existing-attachments .filename {border:0; width:250px; color:#555; background-color:inherit; background:url(../images/attachment.png) no-repeat 1px 50%; padding-left:18px;}
681 #attachments_fields input.filename {height:1.8em;}
682 #attachments_fields input.filename {height:1.8em;}
682 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
683 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
683 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
684 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
684 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
685 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
685 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
686 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
686 a.remove-upload:hover {text-decoration:none !important;}
687 a.remove-upload:hover {text-decoration:none !important;}
687 .existing-attachment.deleted .filename {text-decoration:line-through; color:#999 !important;}
688 .existing-attachment.deleted .filename {text-decoration:line-through; color:#999 !important;}
688
689
689 div.fileover { background-color: lavender; }
690 div.fileover { background-color: lavender; }
690
691
691 div.attachments { margin: 12px 0; }
692 div.attachments { margin: 12px 0; }
692 div.attachments p { margin:4px 0 2px 0; }
693 div.attachments p { margin:4px 0 2px 0; }
693 div.attachments img { vertical-align: middle; }
694 div.attachments img { vertical-align: middle; }
694 div.attachments span.author { font-size: 0.9em; color: #888; }
695 div.attachments span.author { font-size: 0.9em; color: #888; }
695
696
696 div.thumbnails {margin:0.6em;}
697 div.thumbnails {margin:0.6em;}
697 div.thumbnails div {background:#fff;border:2px solid #ddd;display:inline-block;margin-right:2px;}
698 div.thumbnails div {background:#fff;border:2px solid #ddd;display:inline-block;margin-right:2px;}
698 div.thumbnails img {margin: 3px; vertical-align: middle;}
699 div.thumbnails img {margin: 3px; vertical-align: middle;}
699 #history div.thumbnails {margin-left: 2em;}
700 #history div.thumbnails {margin-left: 2em;}
700
701
701 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
702 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
702 .other-formats span + span:before { content: "| "; }
703 .other-formats span + span:before { content: "| "; }
703
704
704 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
705 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
705
706
706 em.info {font-style:normal;font-size:90%;color:#888;display:block;}
707 em.info {font-style:normal;font-size:90%;color:#888;display:block;}
707 em.info.error {padding-left:20px; background:url(../images/exclamation.png) no-repeat 0 50%;}
708 em.info.error {padding-left:20px; background:url(../images/exclamation.png) no-repeat 0 50%;}
708
709
709 textarea.text_cf {width:95%; resize:vertical;}
710 textarea.text_cf {width:95%; resize:vertical;}
710 input.string_cf, input.link_cf {width:95%;}
711 input.string_cf, input.link_cf {width:95%;}
711 select.bool_cf {width:auto !important;}
712 select.bool_cf {width:auto !important;}
712
713
713 #tab-content-modules fieldset p {margin:3px 0 4px 0;}
714 #tab-content-modules fieldset p {margin:3px 0 4px 0;}
714
715
715 #tab-content-users .splitcontentleft {width: 64%;}
716 #tab-content-users .splitcontentleft {width: 64%;}
716 #tab-content-users .splitcontentright {width: 34%;}
717 #tab-content-users .splitcontentright {width: 34%;}
717 #tab-content-users fieldset {padding:1em; margin-bottom: 1em;}
718 #tab-content-users fieldset {padding:1em; margin-bottom: 1em;}
718 #tab-content-users fieldset legend {font-weight: bold;}
719 #tab-content-users fieldset legend {font-weight: bold;}
719 #tab-content-users fieldset label {display: block;}
720 #tab-content-users fieldset label {display: block;}
720 #tab-content-users #principals {max-height: 400px; overflow: auto;}
721 #tab-content-users #principals {max-height: 400px; overflow: auto;}
721
722
722 #users_for_watcher {height: 200px; overflow:auto;}
723 #users_for_watcher {height: 200px; overflow:auto;}
723 #users_for_watcher label {display: block;}
724 #users_for_watcher label {display: block;}
724
725
725 table.members td.name {padding-left: 20px;}
726 table.members td.name {padding-left: 20px;}
726 table.members td.group, table.members td.groupnonmember, table.members td.groupanonymous {background: url(../images/group.png) no-repeat 0% 1px;}
727 table.members td.group, table.members td.groupnonmember, table.members td.groupanonymous {background: url(../images/group.png) no-repeat 0% 1px;}
727
728
728 input#principal_search, input#user_search {width:90%}
729 input#principal_search, input#user_search {width:90%}
729 .roles-selection label {display:inline-block; width:210px;}
730 .roles-selection label {display:inline-block; width:210px;}
730
731
731 input.autocomplete {
732 input.autocomplete {
732 background: #fff url(../images/magnifier.png) no-repeat 2px 50%; padding-left:20px !important;
733 background: #fff url(../images/magnifier.png) no-repeat 2px 50%; padding-left:20px !important;
733 border:1px solid #9EB1C2; border-radius:2px; height:1.5em;
734 border:1px solid #9EB1C2; border-radius:2px; height:1.5em;
734 }
735 }
735 input.autocomplete.ajax-loading {
736 input.autocomplete.ajax-loading {
736 background-image: url(../images/loading.gif);
737 background-image: url(../images/loading.gif);
737 }
738 }
738
739
739 .role-visibility {padding-left:2em;}
740 .role-visibility {padding-left:2em;}
740
741
741 .objects-selection {
742 .objects-selection {
742 height: 300px;
743 height: 300px;
743 overflow: auto;
744 overflow: auto;
744 margin-bottom: 1em;
745 margin-bottom: 1em;
745 }
746 }
746
747
747 .objects-selection label {
748 .objects-selection label {
748 display: block;
749 display: block;
749 }
750 }
750
751
751 .objects-selection>div, #user_group_ids {
752 .objects-selection>div, #user_group_ids {
752 column-count: auto;
753 column-count: auto;
753 column-width: 200px;
754 column-width: 200px;
754 -webkit-column-count: auto;
755 -webkit-column-count: auto;
755 -webkit-column-width: 200px;
756 -webkit-column-width: 200px;
756 -webkit-column-gap : 0.5rem;
757 -webkit-column-gap : 0.5rem;
757 -webkit-column-rule: 1px solid #ccc;
758 -webkit-column-rule: 1px solid #ccc;
758 -moz-column-count: auto;
759 -moz-column-count: auto;
759 -moz-column-width: 200px;
760 -moz-column-width: 200px;
760 -moz-column-gap : 0.5rem;
761 -moz-column-gap : 0.5rem;
761 -moz-column-rule: 1px solid #ccc;
762 -moz-column-rule: 1px solid #ccc;
762 }
763 }
763
764
764 /***** Flash & error messages ****/
765 /***** Flash & error messages ****/
765 #errorExplanation, div.flash, .nodata, .warning, .conflict {
766 #errorExplanation, div.flash, .nodata, .warning, .conflict {
766 padding: 6px 4px 6px 30px;
767 padding: 6px 4px 6px 30px;
767 margin-bottom: 12px;
768 margin-bottom: 12px;
768 font-size: 1.1em;
769 font-size: 1.1em;
769 border: 1px solid;
770 border: 1px solid;
770 border-radius: 3px;
771 border-radius: 3px;
771 }
772 }
772
773
773 div.flash {margin-top: 8px;}
774 div.flash {margin-top: 8px;}
774
775
775 div.flash.error, #errorExplanation {
776 div.flash.error, #errorExplanation {
776 background: url(../images/exclamation.png) 8px 50% no-repeat;
777 background: url(../images/exclamation.png) 8px 50% no-repeat;
777 background-color: #ffe3e3;
778 background-color: #ffe3e3;
778 border-color: #d88;
779 border-color: #d88;
779 color: #880000;
780 color: #880000;
780 }
781 }
781
782
782 div.flash.notice {
783 div.flash.notice {
783 background: url(../images/true.png) 8px 5px no-repeat;
784 background: url(../images/true.png) 8px 5px no-repeat;
784 background-color: #dfffdf;
785 background-color: #dfffdf;
785 border-color: #9fcf9f;
786 border-color: #9fcf9f;
786 color: #005f00;
787 color: #005f00;
787 }
788 }
788
789
789 div.flash.warning, .conflict {
790 div.flash.warning, .conflict {
790 background: url(../images/warning.png) 8px 5px no-repeat;
791 background: url(../images/warning.png) 8px 5px no-repeat;
791 background-color: #F3EDD1;
792 background-color: #F3EDD1;
792 border-color: #eadbbc;
793 border-color: #eadbbc;
793 color: #A6750C;
794 color: #A6750C;
794 text-align: left;
795 text-align: left;
795 }
796 }
796
797
797 .nodata, .warning {
798 .nodata, .warning {
798 text-align: center;
799 text-align: center;
799 background-color: #F3EDD1;
800 background-color: #F3EDD1;
800 border-color: #eadbbc;
801 border-color: #eadbbc;
801 color: #A6750C;
802 color: #A6750C;
802 }
803 }
803
804
804 #errorExplanation ul { font-size: 0.9em;}
805 #errorExplanation ul { font-size: 0.9em;}
805 #errorExplanation h2, #errorExplanation p { display: none; }
806 #errorExplanation h2, #errorExplanation p { display: none; }
806
807
807 .conflict-details {font-size:80%;}
808 .conflict-details {font-size:80%;}
808
809
809 /***** Ajax indicator ******/
810 /***** Ajax indicator ******/
810 #ajax-indicator {
811 #ajax-indicator {
811 position: absolute; /* fixed not supported by IE */
812 position: absolute; /* fixed not supported by IE */
812 background-color:#eee;
813 background-color:#eee;
813 border: 1px solid #bbb;
814 border: 1px solid #bbb;
814 top:35%;
815 top:35%;
815 left:40%;
816 left:40%;
816 width:20%;
817 width:20%;
817 font-weight:bold;
818 font-weight:bold;
818 text-align:center;
819 text-align:center;
819 padding:0.6em;
820 padding:0.6em;
820 z-index:100;
821 z-index:100;
821 opacity: 0.5;
822 opacity: 0.5;
822 }
823 }
823
824
824 html>body #ajax-indicator { position: fixed; }
825 html>body #ajax-indicator { position: fixed; }
825
826
826 #ajax-indicator span {
827 #ajax-indicator span {
827 background-position: 0% 40%;
828 background-position: 0% 40%;
828 background-repeat: no-repeat;
829 background-repeat: no-repeat;
829 background-image: url(../images/loading.gif);
830 background-image: url(../images/loading.gif);
830 padding-left: 26px;
831 padding-left: 26px;
831 vertical-align: bottom;
832 vertical-align: bottom;
832 }
833 }
833
834
834 /***** Calendar *****/
835 /***** Calendar *****/
835 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
836 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
836 table.cal thead th {width: 14%; background-color:#EEEEEE; padding: 4px; }
837 table.cal thead th {width: 14%; background-color:#EEEEEE; padding: 4px; }
837 table.cal thead th.week-number {width: auto;}
838 table.cal thead th.week-number {width: auto;}
838 table.cal tbody tr {height: 100px;}
839 table.cal tbody tr {height: 100px;}
839 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
840 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
840 table.cal td.week-number { background-color:#EEEEEE; padding: 4px; border:none; font-size: 1em;}
841 table.cal td.week-number { background-color:#EEEEEE; padding: 4px; border:none; font-size: 1em;}
841 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
842 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
842 table.cal td.odd p.day-num {color: #bbb;}
843 table.cal td.odd p.day-num {color: #bbb;}
843 table.cal td.today {background:#ffffdd;}
844 table.cal td.today {background:#ffffdd;}
844 table.cal td.today p.day-num {font-weight: bold;}
845 table.cal td.today p.day-num {font-weight: bold;}
845 table.cal .starting a, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;}
846 table.cal .starting a, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;}
846 table.cal .ending a, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
847 table.cal .ending a, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
847 table.cal .starting.ending a, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;}
848 table.cal .starting.ending a, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;}
848 p.cal.legend span {display:block;}
849 p.cal.legend span {display:block;}
849
850
850 /***** Tooltips ******/
851 /***** Tooltips ******/
851 .tooltip{position:relative;z-index:24;}
852 .tooltip{position:relative;z-index:24;}
852 .tooltip:hover{z-index:25;color:#000;}
853 .tooltip:hover{z-index:25;color:#000;}
853 .tooltip span.tip{display: none; text-align:left;}
854 .tooltip span.tip{display: none; text-align:left;}
854
855
855 div.tooltip:hover span.tip{
856 div.tooltip:hover span.tip{
856 display:block;
857 display:block;
857 position:absolute;
858 position:absolute;
858 top:12px; width:270px;
859 top:12px; width:270px;
859 border:1px solid #555;
860 border:1px solid #555;
860 background-color:#fff;
861 background-color:#fff;
861 padding: 4px;
862 padding: 4px;
862 font-size: 0.8em;
863 font-size: 0.8em;
863 color:#505050;
864 color:#505050;
864 }
865 }
865
866
866 img.ui-datepicker-trigger {
867 img.ui-datepicker-trigger {
867 cursor: pointer;
868 cursor: pointer;
868 vertical-align: middle;
869 vertical-align: middle;
869 margin-left: 4px;
870 margin-left: 4px;
870 }
871 }
871
872
872 /***** Progress bar *****/
873 /***** Progress bar *****/
873 table.progress {
874 table.progress {
874 border-collapse: collapse;
875 border-collapse: collapse;
875 border-spacing: 0pt;
876 border-spacing: 0pt;
876 empty-cells: show;
877 empty-cells: show;
877 text-align: center;
878 text-align: center;
878 float:left;
879 float:left;
879 margin: 1px 6px 1px 0px;
880 margin: 1px 6px 1px 0px;
880 }
881 }
881
882
882 table.progress {width:80px;}
883 table.progress {width:80px;}
883 table.progress td { height: 1em; }
884 table.progress td { height: 1em; }
884 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
885 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
885 table.progress td.done { background: #D3EDD3 none repeat scroll 0%; }
886 table.progress td.done { background: #D3EDD3 none repeat scroll 0%; }
886 table.progress td.todo { background: #eee none repeat scroll 0%; }
887 table.progress td.todo { background: #eee none repeat scroll 0%; }
887 p.percent {font-size: 80%; margin:0;}
888 p.percent {font-size: 80%; margin:0;}
888 p.progress-info {clear: left; font-size: 80%; margin-top:-4px; color:#777;}
889 p.progress-info {clear: left; font-size: 80%; margin-top:-4px; color:#777;}
889
890
890 .version-overview table.progress {width:40em;}
891 .version-overview table.progress {width:40em;}
891 .version-overview table.progress td { height: 1.2em; }
892 .version-overview table.progress td { height: 1.2em; }
892
893
893 /***** Tabs *****/
894 /***** Tabs *****/
894 #content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
895 #content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
895 #content .tabs ul {margin:0; position:absolute; bottom:0; padding-left:0.5em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
896 #content .tabs ul {margin:0; position:absolute; bottom:0; padding-left:0.5em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
896 #content .tabs ul li {
897 #content .tabs ul li {
897 float:left;
898 float:left;
898 list-style-type:none;
899 list-style-type:none;
899 white-space:nowrap;
900 white-space:nowrap;
900 margin-right:4px;
901 margin-right:4px;
901 background:#fff;
902 background:#fff;
902 position:relative;
903 position:relative;
903 margin-bottom:-1px;
904 margin-bottom:-1px;
904 }
905 }
905 #content .tabs ul li a{
906 #content .tabs ul li a{
906 display:block;
907 display:block;
907 font-size: 0.9em;
908 font-size: 0.9em;
908 text-decoration:none;
909 text-decoration:none;
909 line-height:1.3em;
910 line-height:1.3em;
910 padding:4px 6px 4px 6px;
911 padding:4px 6px 4px 6px;
911 border: 1px solid #ccc;
912 border: 1px solid #ccc;
912 border-bottom: 1px solid #bbbbbb;
913 border-bottom: 1px solid #bbbbbb;
913 background-color: #f6f6f6;
914 background-color: #f6f6f6;
914 color:#999;
915 color:#999;
915 font-weight:bold;
916 font-weight:bold;
916 border-top-left-radius:3px;
917 border-top-left-radius:3px;
917 border-top-right-radius:3px;
918 border-top-right-radius:3px;
918 }
919 }
919
920
920 #content .tabs ul li a:hover {
921 #content .tabs ul li a:hover {
921 background-color: #ffffdd;
922 background-color: #ffffdd;
922 text-decoration:none;
923 text-decoration:none;
923 }
924 }
924
925
925 #content .tabs ul li a.selected {
926 #content .tabs ul li a.selected {
926 background-color: #fff;
927 background-color: #fff;
927 border: 1px solid #bbbbbb;
928 border: 1px solid #bbbbbb;
928 border-bottom: 1px solid #fff;
929 border-bottom: 1px solid #fff;
929 color:#444;
930 color:#444;
930 }
931 }
931
932
932 #content .tabs ul li a.selected:hover {background-color: #fff;}
933 #content .tabs ul li a.selected:hover {background-color: #fff;}
933
934
934 div.tabs-buttons { position:absolute; right: 0; width: 54px; height: 24px; background: white; bottom: 0; border-bottom: 1px solid #bbbbbb; }
935 div.tabs-buttons { position:absolute; right: 0; width: 54px; height: 24px; background: white; bottom: 0; border-bottom: 1px solid #bbbbbb; }
935
936
936 button.tab-left, button.tab-right {
937 button.tab-left, button.tab-right {
937 font-size: 0.9em;
938 font-size: 0.9em;
938 cursor: pointer;
939 cursor: pointer;
939 height:24px;
940 height:24px;
940 border: 1px solid #ccc;
941 border: 1px solid #ccc;
941 border-bottom: 1px solid #bbbbbb;
942 border-bottom: 1px solid #bbbbbb;
942 position:absolute;
943 position:absolute;
943 padding:4px;
944 padding:4px;
944 width: 20px;
945 width: 20px;
945 bottom: -1px;
946 bottom: -1px;
946 }
947 }
947 button.tab-left:hover, button.tab-right:hover {
948 button.tab-left:hover, button.tab-right:hover {
948 background-color: #f5f5f5;
949 background-color: #f5f5f5;
949 }
950 }
950 button.tab-left:focus, button.tab-right:focus {
951 button.tab-left:focus, button.tab-right:focus {
951 outline: 0;
952 outline: 0;
952 }
953 }
953
954
954 button.tab-left {
955 button.tab-left {
955 right: 20px;
956 right: 20px;
956 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
957 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
957 border-top-left-radius:3px;
958 border-top-left-radius:3px;
958 }
959 }
959
960
960 button.tab-right {
961 button.tab-right {
961 right: 0;
962 right: 0;
962 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;
963 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;
963 border-top-right-radius:3px;
964 border-top-right-radius:3px;
964 }
965 }
965
966
966 button.tab-left.disabled, button.tab-right.disabled {
967 button.tab-left.disabled, button.tab-right.disabled {
967 background-color: #ccc;
968 background-color: #ccc;
968 cursor: unset;
969 cursor: unset;
969 }
970 }
970
971
971 /***** Diff *****/
972 /***** Diff *****/
972 .diff_out { background: #fcc; }
973 .diff_out { background: #fcc; }
973 .diff_out span { background: #faa; }
974 .diff_out span { background: #faa; }
974 .diff_in { background: #cfc; }
975 .diff_in { background: #cfc; }
975 .diff_in span { background: #afa; }
976 .diff_in span { background: #afa; }
976
977
977 .text-diff {
978 .text-diff {
978 padding: 1em;
979 padding: 1em;
979 background-color:#f6f6f6;
980 background-color:#f6f6f6;
980 color:#505050;
981 color:#505050;
981 border: 1px solid #e4e4e4;
982 border: 1px solid #e4e4e4;
982 }
983 }
983
984
984 /***** Wiki *****/
985 /***** Wiki *****/
985 div.wiki table {
986 div.wiki table {
986 border-collapse: collapse;
987 border-collapse: collapse;
987 margin-bottom: 1em;
988 margin-bottom: 1em;
988 }
989 }
989
990
990 div.wiki table, div.wiki td, div.wiki th {
991 div.wiki table, div.wiki td, div.wiki th {
991 border: 1px solid #bbb;
992 border: 1px solid #bbb;
992 padding: 4px;
993 padding: 4px;
993 }
994 }
994
995
995 div.wiki .noborder, div.wiki .noborder td, div.wiki .noborder th {border:0;}
996 div.wiki .noborder, div.wiki .noborder td, div.wiki .noborder th {border:0;}
996
997
997 div.wiki .external {
998 div.wiki .external {
998 background-position: 0% 60%;
999 background-position: 0% 60%;
999 background-repeat: no-repeat;
1000 background-repeat: no-repeat;
1000 padding-left: 12px;
1001 padding-left: 12px;
1001 background-image: url(../images/external.png);
1002 background-image: url(../images/external.png);
1002 }
1003 }
1003
1004
1004 div.wiki a {word-wrap: break-word;}
1005 div.wiki a {word-wrap: break-word;}
1005 div.wiki a.new {color: #b73535;}
1006 div.wiki a.new {color: #b73535;}
1006
1007
1007 div.wiki ul, div.wiki ol {margin-bottom:1em;}
1008 div.wiki ul, div.wiki ol {margin-bottom:1em;}
1008 div.wiki li>ul, div.wiki li>ol {margin-bottom: 0;}
1009 div.wiki li>ul, div.wiki li>ol {margin-bottom: 0;}
1009
1010
1010 div.wiki pre {
1011 div.wiki pre {
1011 margin: 1em 1em 1em 1.6em;
1012 margin: 1em 1em 1em 1.6em;
1012 padding: 8px;
1013 padding: 8px;
1013 background-color: #fafafa;
1014 background-color: #fafafa;
1014 border: 1px solid #e2e2e2;
1015 border: 1px solid #e2e2e2;
1015 border-radius: 3px;
1016 border-radius: 3px;
1016 width:auto;
1017 width:auto;
1017 overflow-x: auto;
1018 overflow-x: auto;
1018 overflow-y: hidden;
1019 overflow-y: hidden;
1019 }
1020 }
1020
1021
1021 div.wiki ul.toc {
1022 div.wiki ul.toc {
1022 background-color: #ffffdd;
1023 background-color: #ffffdd;
1023 border: 1px solid #e4e4e4;
1024 border: 1px solid #e4e4e4;
1024 padding: 4px;
1025 padding: 4px;
1025 line-height: 1.2em;
1026 line-height: 1.2em;
1026 margin-bottom: 12px;
1027 margin-bottom: 12px;
1027 margin-right: 12px;
1028 margin-right: 12px;
1028 margin-left: 0;
1029 margin-left: 0;
1029 display: table
1030 display: table
1030 }
1031 }
1031 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
1032 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
1032
1033
1033 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
1034 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
1034 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
1035 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
1035 div.wiki ul.toc ul { margin: 0; padding: 0; }
1036 div.wiki ul.toc ul { margin: 0; padding: 0; }
1036 div.wiki ul.toc li {list-style-type:none; margin: 0; font-size:12px;}
1037 div.wiki ul.toc li {list-style-type:none; margin: 0; font-size:12px;}
1037 div.wiki ul.toc li li {margin-left: 1.5em; font-size:10px;}
1038 div.wiki ul.toc li li {margin-left: 1.5em; font-size:10px;}
1038 div.wiki ul.toc a {
1039 div.wiki ul.toc a {
1039 font-size: 0.9em;
1040 font-size: 0.9em;
1040 font-weight: normal;
1041 font-weight: normal;
1041 text-decoration: none;
1042 text-decoration: none;
1042 color: #606060;
1043 color: #606060;
1043 }
1044 }
1044 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
1045 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
1045
1046
1046 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
1047 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
1047 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
1048 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
1048 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
1049 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
1049
1050
1050 div.wiki img {vertical-align:middle; max-width:100%;}
1051 div.wiki img {vertical-align:middle; max-width:100%;}
1051
1052
1052 /***** My page layout *****/
1053 /***** My page layout *****/
1053 .block-receiver {
1054 .block-receiver {
1054 border:1px dashed #c0c0c0;
1055 border:1px dashed #c0c0c0;
1055 margin-bottom: 20px;
1056 margin-bottom: 20px;
1056 padding: 15px 0 15px 0;
1057 padding: 15px 0 15px 0;
1057 }
1058 }
1058
1059
1059 .mypage-box {
1060 .mypage-box {
1060 margin:0 0 20px 0;
1061 margin:0 0 20px 0;
1061 color:#505050;
1062 color:#505050;
1062 line-height:1.5em;
1063 line-height:1.5em;
1063 }
1064 }
1064
1065
1065 .handle {cursor: move;}
1066 .handle {cursor: move;}
1066
1067
1067 a.close-icon {
1068 a.close-icon {
1068 display:block;
1069 display:block;
1069 margin-top:3px;
1070 margin-top:3px;
1070 overflow:hidden;
1071 overflow:hidden;
1071 width:12px;
1072 width:12px;
1072 height:12px;
1073 height:12px;
1073 background-repeat: no-repeat;
1074 background-repeat: no-repeat;
1074 cursor:pointer;
1075 cursor:pointer;
1075 background-image:url('../images/close.png');
1076 background-image:url('../images/close.png');
1076 }
1077 }
1077 a.close-icon:hover {background-image:url('../images/close_hl.png');}
1078 a.close-icon:hover {background-image:url('../images/close_hl.png');}
1078
1079
1079 /***** Gantt chart *****/
1080 /***** Gantt chart *****/
1080 .gantt_hdr {
1081 .gantt_hdr {
1081 position:absolute;
1082 position:absolute;
1082 top:0;
1083 top:0;
1083 height:16px;
1084 height:16px;
1084 border-top: 1px solid #c0c0c0;
1085 border-top: 1px solid #c0c0c0;
1085 border-bottom: 1px solid #c0c0c0;
1086 border-bottom: 1px solid #c0c0c0;
1086 border-right: 1px solid #c0c0c0;
1087 border-right: 1px solid #c0c0c0;
1087 text-align: center;
1088 text-align: center;
1088 overflow: hidden;
1089 overflow: hidden;
1089 }
1090 }
1090
1091
1091 .gantt_hdr.nwday {background-color:#f1f1f1; color:#999;}
1092 .gantt_hdr.nwday {background-color:#f1f1f1; color:#999;}
1092
1093
1093 .gantt_subjects { font-size: 0.8em; }
1094 .gantt_subjects { font-size: 0.8em; }
1094 .gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
1095 .gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
1095
1096
1096 .task {
1097 .task {
1097 position: absolute;
1098 position: absolute;
1098 height:8px;
1099 height:8px;
1099 font-size:0.8em;
1100 font-size:0.8em;
1100 color:#888;
1101 color:#888;
1101 padding:0;
1102 padding:0;
1102 margin:0;
1103 margin:0;
1103 line-height:16px;
1104 line-height:16px;
1104 white-space:nowrap;
1105 white-space:nowrap;
1105 }
1106 }
1106
1107
1107 .task.label {width:100%;}
1108 .task.label {width:100%;}
1108 .task.label.project, .task.label.version { font-weight: bold; }
1109 .task.label.project, .task.label.version { font-weight: bold; }
1109
1110
1110 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
1111 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
1111 .task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; }
1112 .task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; }
1112 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
1113 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
1113
1114
1114 .task_todo.parent { background: #888; border: 1px solid #888; height: 3px;}
1115 .task_todo.parent { background: #888; border: 1px solid #888; height: 3px;}
1115 .task_late.parent, .task_done.parent { height: 3px;}
1116 .task_late.parent, .task_done.parent { height: 3px;}
1116 .task.parent.marker.starting { position: absolute; background: url(../images/task_parent_end.png) no-repeat 0 0; width: 8px; height: 16px; margin-left: -4px; left: 0px; top: -1px;}
1117 .task.parent.marker.starting { position: absolute; background: url(../images/task_parent_end.png) no-repeat 0 0; width: 8px; height: 16px; margin-left: -4px; left: 0px; top: -1px;}
1117 .task.parent.marker.ending { position: absolute; background: url(../images/task_parent_end.png) no-repeat 0 0; width: 8px; height: 16px; margin-left: -4px; right: 0px; top: -1px;}
1118 .task.parent.marker.ending { position: absolute; background: url(../images/task_parent_end.png) no-repeat 0 0; width: 8px; height: 16px; margin-left: -4px; right: 0px; top: -1px;}
1118
1119
1119 .version.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1120 .version.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1120 .version.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1121 .version.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1121 .version.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1122 .version.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1122 .version.marker { background-image:url(../images/version_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1123 .version.marker { background-image:url(../images/version_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1123
1124
1124 .project.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1125 .project.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1125 .project.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1126 .project.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1126 .project.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1127 .project.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1127 .project.marker { background-image:url(../images/project_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1128 .project.marker { background-image:url(../images/project_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1128
1129
1129 .version-behind-schedule a, .issue-behind-schedule a {color: #f66914;}
1130 .version-behind-schedule a, .issue-behind-schedule a {color: #f66914;}
1130 .version-overdue a, .issue-overdue a, .project-overdue a {color: #f00;}
1131 .version-overdue a, .issue-overdue a, .project-overdue a {color: #f00;}
1131
1132
1132 /***** Icons *****/
1133 /***** Icons *****/
1133 .icon {
1134 .icon {
1134 background-position: 0% 50%;
1135 background-position: 0% 50%;
1135 background-repeat: no-repeat;
1136 background-repeat: no-repeat;
1136 padding-left: 20px;
1137 padding-left: 20px;
1137 padding-top: 2px;
1138 padding-top: 2px;
1138 padding-bottom: 3px;
1139 padding-bottom: 3px;
1139 }
1140 }
1140 .icon-only {
1141 .icon-only {
1141 background-position: 0% 50%;
1142 background-position: 0% 50%;
1142 background-repeat: no-repeat;
1143 background-repeat: no-repeat;
1143 padding-left: 16px;
1144 padding-left: 16px;
1144 display: inline-block;
1145 display: inline-block;
1145 width: 0;
1146 width: 0;
1146 height: 16px;
1147 height: 16px;
1147 overflow: hidden;
1148 overflow: hidden;
1148 padding-top: 0;
1149 padding-top: 0;
1149 padding-bottom: 0;
1150 padding-bottom: 0;
1150 font-size: 8px;
1151 font-size: 8px;
1151 vertical-align: text-bottom;
1152 vertical-align: text-bottom;
1152 }
1153 }
1153 .icon-only::after {
1154 .icon-only::after {
1154 content: "&nbsp;";
1155 content: "&nbsp;";
1155 }
1156 }
1156
1157
1157 .icon-add { background-image: url(../images/add.png); }
1158 .icon-add { background-image: url(../images/add.png); }
1158 .icon-edit { background-image: url(../images/edit.png); }
1159 .icon-edit { background-image: url(../images/edit.png); }
1159 .icon-copy { background-image: url(../images/copy.png); }
1160 .icon-copy { background-image: url(../images/copy.png); }
1160 .icon-duplicate { background-image: url(../images/duplicate.png); }
1161 .icon-duplicate { background-image: url(../images/duplicate.png); }
1161 .icon-del { background-image: url(../images/delete.png); }
1162 .icon-del { background-image: url(../images/delete.png); }
1162 .icon-move { background-image: url(../images/move.png); }
1163 .icon-move { background-image: url(../images/move.png); }
1163 .icon-save { background-image: url(../images/save.png); }
1164 .icon-save { background-image: url(../images/save.png); }
1164 .icon-cancel { background-image: url(../images/cancel.png); }
1165 .icon-cancel { background-image: url(../images/cancel.png); }
1165 .icon-multiple { background-image: url(../images/table_multiple.png); }
1166 .icon-multiple { background-image: url(../images/table_multiple.png); }
1166 .icon-folder { background-image: url(../images/folder.png); }
1167 .icon-folder { background-image: url(../images/folder.png); }
1167 .open .icon-folder { background-image: url(../images/folder_open.png); }
1168 .open .icon-folder { background-image: url(../images/folder_open.png); }
1168 .icon-package { background-image: url(../images/package.png); }
1169 .icon-package { background-image: url(../images/package.png); }
1169 .icon-user { background-image: url(../images/user.png); }
1170 .icon-user { background-image: url(../images/user.png); }
1170 .icon-projects { background-image: url(../images/projects.png); }
1171 .icon-projects { background-image: url(../images/projects.png); }
1171 .icon-help { background-image: url(../images/help.png); }
1172 .icon-help { background-image: url(../images/help.png); }
1172 .icon-attachment { background-image: url(../images/attachment.png); }
1173 .icon-attachment { background-image: url(../images/attachment.png); }
1173 .icon-history { background-image: url(../images/history.png); }
1174 .icon-history { background-image: url(../images/history.png); }
1174 .icon-time { background-image: url(../images/time.png); }
1175 .icon-time { background-image: url(../images/time.png); }
1175 .icon-time-add { background-image: url(../images/time_add.png); }
1176 .icon-time-add { background-image: url(../images/time_add.png); }
1176 .icon-stats { background-image: url(../images/stats.png); }
1177 .icon-stats { background-image: url(../images/stats.png); }
1177 .icon-warning { background-image: url(../images/warning.png); }
1178 .icon-warning { background-image: url(../images/warning.png); }
1178 .icon-error { background-image: url(../images/exclamation.png); }
1179 .icon-error { background-image: url(../images/exclamation.png); }
1179 .icon-fav { background-image: url(../images/fav.png); }
1180 .icon-fav { background-image: url(../images/fav.png); }
1180 .icon-fav-off { background-image: url(../images/fav_off.png); }
1181 .icon-fav-off { background-image: url(../images/fav_off.png); }
1181 .icon-reload { background-image: url(../images/reload.png); }
1182 .icon-reload { background-image: url(../images/reload.png); }
1182 .icon-lock { background-image: url(../images/locked.png); }
1183 .icon-lock { background-image: url(../images/locked.png); }
1183 .icon-unlock { background-image: url(../images/unlock.png); }
1184 .icon-unlock { background-image: url(../images/unlock.png); }
1184 .icon-checked { background-image: url(../images/toggle_check.png); }
1185 .icon-checked { background-image: url(../images/toggle_check.png); }
1185 .icon-details { background-image: url(../images/zoom_in.png); }
1186 .icon-details { background-image: url(../images/zoom_in.png); }
1186 .icon-report { background-image: url(../images/report.png); }
1187 .icon-report { background-image: url(../images/report.png); }
1187 .icon-comment { background-image: url(../images/comment.png); }
1188 .icon-comment { background-image: url(../images/comment.png); }
1188 .icon-summary { background-image: url(../images/lightning.png); }
1189 .icon-summary { background-image: url(../images/lightning.png); }
1189 .icon-server-authentication { background-image: url(../images/server_key.png); }
1190 .icon-server-authentication { background-image: url(../images/server_key.png); }
1190 .icon-issue { background-image: url(../images/ticket.png); }
1191 .icon-issue { background-image: url(../images/ticket.png); }
1191 .icon-zoom-in { background-image: url(../images/zoom_in.png); }
1192 .icon-zoom-in { background-image: url(../images/zoom_in.png); }
1192 .icon-zoom-out { background-image: url(../images/zoom_out.png); }
1193 .icon-zoom-out { background-image: url(../images/zoom_out.png); }
1193 .icon-magnifier { background-image: url(../images/magnifier.png); }
1194 .icon-magnifier { background-image: url(../images/magnifier.png); }
1194 .icon-passwd { background-image: url(../images/textfield_key.png); }
1195 .icon-passwd { background-image: url(../images/textfield_key.png); }
1195 .icon-test { background-image: url(../images/bullet_go.png); }
1196 .icon-test { background-image: url(../images/bullet_go.png); }
1196 .icon-email { background-image: url(../images/email.png); }
1197 .icon-email { background-image: url(../images/email.png); }
1197 .icon-email-disabled { background-image: url(../images/email_disabled.png); }
1198 .icon-email-disabled { background-image: url(../images/email_disabled.png); }
1198 .icon-email-add { background-image: url(../images/email_add.png); }
1199 .icon-email-add { background-image: url(../images/email_add.png); }
1199 .icon-move-up { background-image: url(../images/1uparrow.png); }
1200 .icon-move-up { background-image: url(../images/1uparrow.png); }
1200 .icon-move-top { background-image: url(../images/2uparrow.png); }
1201 .icon-move-top { background-image: url(../images/2uparrow.png); }
1201 .icon-move-down { background-image: url(../images/1downarrow.png); }
1202 .icon-move-down { background-image: url(../images/1downarrow.png); }
1202 .icon-move-bottom { background-image: url(../images/2downarrow.png); }
1203 .icon-move-bottom { background-image: url(../images/2downarrow.png); }
1203 .icon-ok { background-image: url(../images/true.png); }
1204 .icon-ok { background-image: url(../images/true.png); }
1204 .icon-not-ok { background-image: url(../images/false.png); }
1205 .icon-not-ok { background-image: url(../images/false.png); }
1205 .icon-link-break { background-image: url(../images/link_break.png); }
1206 .icon-link-break { background-image: url(../images/link_break.png); }
1206 .icon-list { background-image: url(../images/text_list_bullets.png); }
1207 .icon-list { background-image: url(../images/text_list_bullets.png); }
1207
1208
1208 .icon-file { background-image: url(../images/files/default.png); }
1209 .icon-file { background-image: url(../images/files/default.png); }
1209 .icon-file.text-plain { background-image: url(../images/files/text.png); }
1210 .icon-file.text-plain { background-image: url(../images/files/text.png); }
1210 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
1211 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
1211 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
1212 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
1212 .icon-file.text-x-java { background-image: url(../images/files/java.png); }
1213 .icon-file.text-x-java { background-image: url(../images/files/java.png); }
1213 .icon-file.text-x-javascript { background-image: url(../images/files/js.png); }
1214 .icon-file.text-x-javascript { background-image: url(../images/files/js.png); }
1214 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
1215 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
1215 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
1216 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
1216 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
1217 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
1217 .icon-file.text-css { background-image: url(../images/files/css.png); }
1218 .icon-file.text-css { background-image: url(../images/files/css.png); }
1218 .icon-file.text-html { background-image: url(../images/files/html.png); }
1219 .icon-file.text-html { background-image: url(../images/files/html.png); }
1219 .icon-file.image-gif { background-image: url(../images/files/image.png); }
1220 .icon-file.image-gif { background-image: url(../images/files/image.png); }
1220 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
1221 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
1221 .icon-file.image-png { background-image: url(../images/files/image.png); }
1222 .icon-file.image-png { background-image: url(../images/files/image.png); }
1222 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
1223 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
1223 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
1224 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
1224 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
1225 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
1225 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
1226 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
1226
1227
1227 .sort-handle { width:16px; height:16px; background:url(../images/reorder.png) no-repeat 0 50%; cursor:move; }
1228 .sort-handle { width:16px; height:16px; background:url(../images/reorder.png) no-repeat 0 50%; cursor:move; }
1228 .sort-handle.ajax-loading { background-image: url(../images/loading.gif); }
1229 .sort-handle.ajax-loading { background-image: url(../images/loading.gif); }
1229 tr.ui-sortable-helper { border:1px solid #e4e4e4; }
1230 tr.ui-sortable-helper { border:1px solid #e4e4e4; }
1230
1231
1231 .contextual>.icon:not(:first-child), .buttons>.icon:not(:first-child) { margin-left: 5px; }
1232 .contextual>.icon:not(:first-child), .buttons>.icon:not(:first-child) { margin-left: 5px; }
1232
1233
1233 img.gravatar {
1234 img.gravatar {
1234 vertical-align: middle;
1235 vertical-align: middle;
1235 border-radius: 20%;
1236 border-radius: 20%;
1236 }
1237 }
1237
1238
1238 div.issue img.gravatar {
1239 div.issue img.gravatar {
1239 float: left;
1240 float: left;
1240 margin: 0 6px 0 0;
1241 margin: 0 6px 0 0;
1241 }
1242 }
1242
1243
1243 h2 img.gravatar {margin: -2px 4px -4px 0;}
1244 h2 img.gravatar {margin: -2px 4px -4px 0;}
1244 h3 img.gravatar {margin: -4px 4px -4px 0;}
1245 h3 img.gravatar {margin: -4px 4px -4px 0;}
1245 h4 img.gravatar {margin: -2px 4px -4px 0;}
1246 h4 img.gravatar {margin: -2px 4px -4px 0;}
1246 td.username img.gravatar {margin: 0 0.5em 0 0; vertical-align: top;}
1247 td.username img.gravatar {margin: 0 0.5em 0 0; vertical-align: top;}
1247 #activity dt img.gravatar {float: left; margin: 0 1em 1em 0;}
1248 #activity dt img.gravatar {float: left; margin: 0 1em 1em 0;}
1248 /* Used on 12px Gravatar img tags without the icon background */
1249 /* Used on 12px Gravatar img tags without the icon background */
1249 .icon-gravatar {float: left; margin-right: 4px;}
1250 .icon-gravatar {float: left; margin-right: 4px;}
1250
1251
1251 #activity dt, .journal {clear: left;}
1252 #activity dt, .journal {clear: left;}
1252
1253
1253 .journal-link {float: right;}
1254 .journal-link {float: right;}
1254
1255
1255 h2 img { vertical-align:middle; }
1256 h2 img { vertical-align:middle; }
1256
1257
1257 .hascontextmenu { cursor: context-menu; }
1258 .hascontextmenu { cursor: context-menu; }
1258
1259
1259 .sample-data {border:1px solid #ccc; border-collapse:collapse; background-color:#fff; margin:0.5em;}
1260 .sample-data {border:1px solid #ccc; border-collapse:collapse; background-color:#fff; margin:0.5em;}
1260 .sample-data td {border:1px solid #ccc; padding: 2px 4px; font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
1261 .sample-data td {border:1px solid #ccc; padding: 2px 4px; font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
1261 .sample-data tr:first-child td {font-weight:bold; text-align:center;}
1262 .sample-data tr:first-child td {font-weight:bold; text-align:center;}
1262
1263
1263 .ui-progressbar {position: relative;}
1264 .ui-progressbar {position: relative;}
1264 #progress-label {
1265 #progress-label {
1265 position: absolute; left: 50%; top: 4px;
1266 position: absolute; left: 50%; top: 4px;
1266 font-weight: bold;
1267 font-weight: bold;
1267 color: #555; text-shadow: 1px 1px 0 #fff;
1268 color: #555; text-shadow: 1px 1px 0 #fff;
1268 }
1269 }
1269
1270
1270 /* Custom JQuery styles */
1271 /* Custom JQuery styles */
1271 .ui-datepicker-title select {width:70px !important; margin-top:-2px !important; margin-right:4px !important;}
1272 .ui-datepicker-title select {width:70px !important; margin-top:-2px !important; margin-right:4px !important;}
1272
1273
1273
1274
1274 /************* CodeRay styles *************/
1275 /************* CodeRay styles *************/
1275 .syntaxhl div {display: inline;}
1276 .syntaxhl div {display: inline;}
1276 .syntaxhl .code pre { overflow: auto }
1277 .syntaxhl .code pre { overflow: auto }
1277
1278
1278 .syntaxhl .annotation { color:#007 }
1279 .syntaxhl .annotation { color:#007 }
1279 .syntaxhl .attribute-name { color:#b48 }
1280 .syntaxhl .attribute-name { color:#b48 }
1280 .syntaxhl .attribute-value { color:#700 }
1281 .syntaxhl .attribute-value { color:#700 }
1281 .syntaxhl .binary { color:#549 }
1282 .syntaxhl .binary { color:#549 }
1282 .syntaxhl .binary .char { color:#325 }
1283 .syntaxhl .binary .char { color:#325 }
1283 .syntaxhl .binary .delimiter { color:#325 }
1284 .syntaxhl .binary .delimiter { color:#325 }
1284 .syntaxhl .char { color:#D20 }
1285 .syntaxhl .char { color:#D20 }
1285 .syntaxhl .char .content { color:#D20 }
1286 .syntaxhl .char .content { color:#D20 }
1286 .syntaxhl .char .delimiter { color:#710 }
1287 .syntaxhl .char .delimiter { color:#710 }
1287 .syntaxhl .class { color:#B06; font-weight:bold }
1288 .syntaxhl .class { color:#B06; font-weight:bold }
1288 .syntaxhl .class-variable { color:#369 }
1289 .syntaxhl .class-variable { color:#369 }
1289 .syntaxhl .color { color:#0A0 }
1290 .syntaxhl .color { color:#0A0 }
1290 .syntaxhl .comment { color:#777 }
1291 .syntaxhl .comment { color:#777 }
1291 .syntaxhl .comment .char { color:#444 }
1292 .syntaxhl .comment .char { color:#444 }
1292 .syntaxhl .comment .delimiter { color:#444 }
1293 .syntaxhl .comment .delimiter { color:#444 }
1293 .syntaxhl .constant { color:#036; font-weight:bold }
1294 .syntaxhl .constant { color:#036; font-weight:bold }
1294 .syntaxhl .decorator { color:#B0B }
1295 .syntaxhl .decorator { color:#B0B }
1295 .syntaxhl .definition { color:#099; font-weight:bold }
1296 .syntaxhl .definition { color:#099; font-weight:bold }
1296 .syntaxhl .delimiter { color:black }
1297 .syntaxhl .delimiter { color:black }
1297 .syntaxhl .directive { color:#088; font-weight:bold }
1298 .syntaxhl .directive { color:#088; font-weight:bold }
1298 .syntaxhl .docstring { color:#D42; }
1299 .syntaxhl .docstring { color:#D42; }
1299 .syntaxhl .doctype { color:#34b }
1300 .syntaxhl .doctype { color:#34b }
1300 .syntaxhl .done { text-decoration: line-through; color: gray }
1301 .syntaxhl .done { text-decoration: line-through; color: gray }
1301 .syntaxhl .entity { color:#800; font-weight:bold }
1302 .syntaxhl .entity { color:#800; font-weight:bold }
1302 .syntaxhl .error { color:#F00; background-color:#FAA }
1303 .syntaxhl .error { color:#F00; background-color:#FAA }
1303 .syntaxhl .escape { color:#666 }
1304 .syntaxhl .escape { color:#666 }
1304 .syntaxhl .exception { color:#C00; font-weight:bold }
1305 .syntaxhl .exception { color:#C00; font-weight:bold }
1305 .syntaxhl .float { color:#60E }
1306 .syntaxhl .float { color:#60E }
1306 .syntaxhl .function { color:#06B; font-weight:bold }
1307 .syntaxhl .function { color:#06B; font-weight:bold }
1307 .syntaxhl .function .delimiter { color:#059 }
1308 .syntaxhl .function .delimiter { color:#059 }
1308 .syntaxhl .function .content { color:#037 }
1309 .syntaxhl .function .content { color:#037 }
1309 .syntaxhl .global-variable { color:#d70 }
1310 .syntaxhl .global-variable { color:#d70 }
1310 .syntaxhl .hex { color:#02b }
1311 .syntaxhl .hex { color:#02b }
1311 .syntaxhl .id { color:#33D; font-weight:bold }
1312 .syntaxhl .id { color:#33D; font-weight:bold }
1312 .syntaxhl .include { color:#B44; font-weight:bold }
1313 .syntaxhl .include { color:#B44; font-weight:bold }
1313 .syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black }
1314 .syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black }
1314 .syntaxhl .inline-delimiter { font-weight: bold; color: #666 }
1315 .syntaxhl .inline-delimiter { font-weight: bold; color: #666 }
1315 .syntaxhl .instance-variable { color:#33B }
1316 .syntaxhl .instance-variable { color:#33B }
1316 .syntaxhl .integer { color:#00D }
1317 .syntaxhl .integer { color:#00D }
1317 .syntaxhl .imaginary { color:#f00 }
1318 .syntaxhl .imaginary { color:#f00 }
1318 .syntaxhl .important { color:#D00 }
1319 .syntaxhl .important { color:#D00 }
1319 .syntaxhl .key { color: #606 }
1320 .syntaxhl .key { color: #606 }
1320 .syntaxhl .key .char { color: #60f }
1321 .syntaxhl .key .char { color: #60f }
1321 .syntaxhl .key .delimiter { color: #404 }
1322 .syntaxhl .key .delimiter { color: #404 }
1322 .syntaxhl .keyword { color:#080; font-weight:bold }
1323 .syntaxhl .keyword { color:#080; font-weight:bold }
1323 .syntaxhl .label { color:#970; font-weight:bold }
1324 .syntaxhl .label { color:#970; font-weight:bold }
1324 .syntaxhl .local-variable { color:#950 }
1325 .syntaxhl .local-variable { color:#950 }
1325 .syntaxhl .map .content { color:#808 }
1326 .syntaxhl .map .content { color:#808 }
1326 .syntaxhl .map .delimiter { color:#40A}
1327 .syntaxhl .map .delimiter { color:#40A}
1327 .syntaxhl .map { background-color:hsla(200,100%,50%,0.06); }
1328 .syntaxhl .map { background-color:hsla(200,100%,50%,0.06); }
1328 .syntaxhl .namespace { color:#707; font-weight:bold }
1329 .syntaxhl .namespace { color:#707; font-weight:bold }
1329 .syntaxhl .octal { color:#40E }
1330 .syntaxhl .octal { color:#40E }
1330 .syntaxhl .operator { }
1331 .syntaxhl .operator { }
1331 .syntaxhl .predefined { color:#369; font-weight:bold }
1332 .syntaxhl .predefined { color:#369; font-weight:bold }
1332 .syntaxhl .predefined-constant { color:#069 }
1333 .syntaxhl .predefined-constant { color:#069 }
1333 .syntaxhl .predefined-type { color:#0a8; font-weight:bold }
1334 .syntaxhl .predefined-type { color:#0a8; font-weight:bold }
1334 .syntaxhl .preprocessor { color:#579 }
1335 .syntaxhl .preprocessor { color:#579 }
1335 .syntaxhl .pseudo-class { color:#00C; font-weight:bold }
1336 .syntaxhl .pseudo-class { color:#00C; font-weight:bold }
1336 .syntaxhl .regexp { background-color:hsla(300,100%,50%,0.06); }
1337 .syntaxhl .regexp { background-color:hsla(300,100%,50%,0.06); }
1337 .syntaxhl .regexp .content { color:#808 }
1338 .syntaxhl .regexp .content { color:#808 }
1338 .syntaxhl .regexp .delimiter { color:#404 }
1339 .syntaxhl .regexp .delimiter { color:#404 }
1339 .syntaxhl .regexp .modifier { color:#C2C }
1340 .syntaxhl .regexp .modifier { color:#C2C }
1340 .syntaxhl .reserved { color:#080; font-weight:bold }
1341 .syntaxhl .reserved { color:#080; font-weight:bold }
1341 .syntaxhl .shell { background-color:hsla(120,100%,50%,0.06); }
1342 .syntaxhl .shell { background-color:hsla(120,100%,50%,0.06); }
1342 .syntaxhl .shell .content { color:#2B2 }
1343 .syntaxhl .shell .content { color:#2B2 }
1343 .syntaxhl .shell .delimiter { color:#161 }
1344 .syntaxhl .shell .delimiter { color:#161 }
1344 .syntaxhl .string { background-color:hsla(0,100%,50%,0.05); }
1345 .syntaxhl .string { background-color:hsla(0,100%,50%,0.05); }
1345 .syntaxhl .string .char { color: #b0b }
1346 .syntaxhl .string .char { color: #b0b }
1346 .syntaxhl .string .content { color: #D20 }
1347 .syntaxhl .string .content { color: #D20 }
1347 .syntaxhl .string .delimiter { color: #710 }
1348 .syntaxhl .string .delimiter { color: #710 }
1348 .syntaxhl .string .modifier { color: #E40 }
1349 .syntaxhl .string .modifier { color: #E40 }
1349 .syntaxhl .symbol { color:#A60 }
1350 .syntaxhl .symbol { color:#A60 }
1350 .syntaxhl .symbol .content { color:#A60 }
1351 .syntaxhl .symbol .content { color:#A60 }
1351 .syntaxhl .symbol .delimiter { color:#740 }
1352 .syntaxhl .symbol .delimiter { color:#740 }
1352 .syntaxhl .tag { color:#070; font-weight:bold }
1353 .syntaxhl .tag { color:#070; font-weight:bold }
1353 .syntaxhl .type { color:#339; font-weight:bold }
1354 .syntaxhl .type { color:#339; font-weight:bold }
1354 .syntaxhl .value { color: #088 }
1355 .syntaxhl .value { color: #088 }
1355 .syntaxhl .variable { color:#037 }
1356 .syntaxhl .variable { color:#037 }
1356
1357
1357 .syntaxhl .insert { background: hsla(120,100%,50%,0.12) }
1358 .syntaxhl .insert { background: hsla(120,100%,50%,0.12) }
1358 .syntaxhl .delete { background: hsla(0,100%,50%,0.12) }
1359 .syntaxhl .delete { background: hsla(0,100%,50%,0.12) }
1359 .syntaxhl .change { color: #bbf; background: #007 }
1360 .syntaxhl .change { color: #bbf; background: #007 }
1360 .syntaxhl .head { color: #f8f; background: #505 }
1361 .syntaxhl .head { color: #f8f; background: #505 }
1361 .syntaxhl .head .filename { color: white; }
1362 .syntaxhl .head .filename { color: white; }
1362
1363
1363 .syntaxhl .delete .eyecatcher { background-color: hsla(0,100%,50%,0.2); border: 1px solid hsla(0,100%,45%,0.5); margin: -1px; border-bottom: none; border-top-left-radius: 5px; border-top-right-radius: 5px; }
1364 .syntaxhl .delete .eyecatcher { background-color: hsla(0,100%,50%,0.2); border: 1px solid hsla(0,100%,45%,0.5); margin: -1px; border-bottom: none; border-top-left-radius: 5px; border-top-right-radius: 5px; }
1364 .syntaxhl .insert .eyecatcher { background-color: hsla(120,100%,50%,0.2); border: 1px solid hsla(120,100%,25%,0.5); margin: -1px; border-top: none; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; }
1365 .syntaxhl .insert .eyecatcher { background-color: hsla(120,100%,50%,0.2); border: 1px solid hsla(120,100%,25%,0.5); margin: -1px; border-top: none; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; }
1365
1366
1366 .syntaxhl .insert .insert { color: #0c0; background:transparent; font-weight:bold }
1367 .syntaxhl .insert .insert { color: #0c0; background:transparent; font-weight:bold }
1367 .syntaxhl .delete .delete { color: #c00; background:transparent; font-weight:bold }
1368 .syntaxhl .delete .delete { color: #c00; background:transparent; font-weight:bold }
1368 .syntaxhl .change .change { color: #88f }
1369 .syntaxhl .change .change { color: #88f }
1369 .syntaxhl .head .head { color: #f4f }
1370 .syntaxhl .head .head { color: #f4f }
1370
1371
1371 /***** Media print specific styles *****/
1372 /***** Media print specific styles *****/
1372 @media print {
1373 @media print {
1373 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
1374 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
1374 #main { background: #fff; }
1375 #main { background: #fff; }
1375 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
1376 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
1376 #wiki_add_attachment { display:none; }
1377 #wiki_add_attachment { display:none; }
1377 .hide-when-print, .pagination ul.pages, .pagination .per-page { display: none !important; }
1378 .hide-when-print, .pagination ul.pages, .pagination .per-page { display: none !important; }
1378 .autoscroll {overflow-x: visible;}
1379 .autoscroll {overflow-x: visible;}
1379 table.list {margin-top:0.5em;}
1380 table.list {margin-top:0.5em;}
1380 table.list th, table.list td {border: 1px solid #aaa;}
1381 table.list th, table.list td {border: 1px solid #aaa;}
1381 }
1382 }
1382
1383
1383 /* Accessibility specific styles */
1384 /* Accessibility specific styles */
1384 .hidden-for-sighted {
1385 .hidden-for-sighted {
1385 position:absolute;
1386 position:absolute;
1386 left:-10000px;
1387 left:-10000px;
1387 top:auto;
1388 top:auto;
1388 width:1px;
1389 width:1px;
1389 height:1px;
1390 height:1px;
1390 overflow:hidden;
1391 overflow:hidden;
1391 }
1392 }
General Comments 0
You need to be logged in to leave comments. Login now