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