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