##// END OF EJS Templates
Forum list can be reordered with drag and drop (#12909)....
Jean-Philippe Lang -
r14955:fb6b565a1ec9
parent child
Show More
@@ -1,111 +1,120
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class BoardsController < ApplicationController
19 19 default_search_scope :messages
20 20 before_filter :find_project_by_project_id, :find_board_if_available, :authorize
21 21 accept_rss_auth :index, :show
22 22
23 23 helper :sort
24 24 include SortHelper
25 25 helper :watchers
26 26
27 27 def index
28 28 @boards = @project.boards.preload(:project, :last_message => :author).to_a
29 29 # show the board if there is only one
30 30 if @boards.size == 1
31 31 @board = @boards.first
32 32 show
33 33 end
34 34 end
35 35
36 36 def show
37 37 respond_to do |format|
38 38 format.html {
39 39 sort_init 'updated_on', 'desc'
40 40 sort_update 'created_on' => "#{Message.table_name}.created_on",
41 41 'replies' => "#{Message.table_name}.replies_count",
42 42 'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)"
43 43
44 44 @topic_count = @board.topics.count
45 45 @topic_pages = Paginator.new @topic_count, per_page_option, params['page']
46 46 @topics = @board.topics.
47 47 reorder("#{Message.table_name}.sticky DESC").
48 48 joins("LEFT OUTER JOIN #{Message.table_name} last_replies_messages ON last_replies_messages.id = #{Message.table_name}.last_reply_id").
49 49 limit(@topic_pages.per_page).
50 50 offset(@topic_pages.offset).
51 51 order(sort_clause).
52 52 preload(:author, {:last_reply => :author}).
53 53 to_a
54 54 @message = Message.new(:board => @board)
55 55 render :action => 'show', :layout => !request.xhr?
56 56 }
57 57 format.atom {
58 58 @messages = @board.messages.
59 59 reorder('created_on DESC').
60 60 includes(:author, :board).
61 61 limit(Setting.feeds_limit.to_i).
62 62 to_a
63 63 render_feed(@messages, :title => "#{@project}: #{@board}")
64 64 }
65 65 end
66 66 end
67 67
68 68 def new
69 69 @board = @project.boards.build
70 70 @board.safe_attributes = params[:board]
71 71 end
72 72
73 73 def create
74 74 @board = @project.boards.build
75 75 @board.safe_attributes = params[:board]
76 76 if @board.save
77 77 flash[:notice] = l(:notice_successful_create)
78 78 redirect_to_settings_in_projects
79 79 else
80 80 render :action => 'new'
81 81 end
82 82 end
83 83
84 84 def edit
85 85 end
86 86
87 87 def update
88 88 @board.safe_attributes = params[:board]
89 89 if @board.save
90 redirect_to_settings_in_projects
90 respond_to do |format|
91 format.html {
92 flash[:notice] = l(:notice_successful_update)
93 redirect_to_settings_in_projects
94 }
95 format.js { render :nothing => true }
96 end
91 97 else
92 render :action => 'edit'
98 respond_to do |format|
99 format.html { render :action => 'edit' }
100 format.js { render :nothing => true, :status => 422 }
101 end
93 102 end
94 103 end
95 104
96 105 def destroy
97 106 @board.destroy
98 107 redirect_to_settings_in_projects
99 108 end
100 109
101 110 private
102 111 def redirect_to_settings_in_projects
103 112 redirect_to settings_project_path(@project, :tab => 'boards')
104 113 end
105 114
106 115 def find_board_if_available
107 116 @board = @project.boards.find(params[:id]) if params[:id]
108 117 rescue ActiveRecord::RecordNotFound
109 118 render_404
110 119 end
111 120 end
@@ -1,123 +1,136
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 module ProjectsHelper
21 21 def project_settings_tabs
22 22 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
23 23 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
24 24 {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
25 25 {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
26 26 {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
27 27 {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
28 28 {:name => 'repositories', :action => :manage_repository, :partial => 'projects/settings/repositories', :label => :label_repository_plural},
29 29 {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
30 30 {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
31 31 ]
32 32 tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
33 33 end
34 34
35 35 def parent_project_select_tag(project)
36 36 selected = project.parent
37 37 # retrieve the requested parent project
38 38 parent_id = (params[:project] && params[:project][:parent_id]) || params[:parent_id]
39 39 if parent_id
40 40 selected = (parent_id.blank? ? nil : Project.find(parent_id))
41 41 end
42 42
43 43 options = ''
44 44 options << "<option value=''>&nbsp;</option>" if project.allowed_parents.include?(nil)
45 45 options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
46 46 content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
47 47 end
48 48
49 49 def render_project_action_links
50 50 links = []
51 51 if User.current.allowed_to?(:add_project, nil, :global => true)
52 52 links << link_to(l(:label_project_new), new_project_path, :class => 'icon icon-add')
53 53 end
54 54 if User.current.allowed_to?(:view_issues, nil, :global => true)
55 55 links << link_to(l(:label_issue_view_all), issues_path)
56 56 end
57 57 if User.current.allowed_to?(:view_time_entries, nil, :global => true)
58 58 links << link_to(l(:label_overall_spent_time), time_entries_path)
59 59 end
60 60 links << link_to(l(:label_overall_activity), activity_path)
61 61 links.join(" | ").html_safe
62 62 end
63 63
64 64 # Renders the projects index
65 65 def render_project_hierarchy(projects)
66 66 render_project_nested_lists(projects) do |project|
67 67 s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
68 68 if project.description.present?
69 69 s << content_tag('div', textilizable(project.short_description, :project => project), :class => 'wiki description')
70 70 end
71 71 s
72 72 end
73 73 end
74 74
75 75 # Returns a set of options for a select field, grouped by project.
76 76 def version_options_for_select(versions, selected=nil)
77 77 grouped = Hash.new {|h,k| h[k] = []}
78 78 versions.each do |version|
79 79 grouped[version.project.name] << [version.name, version.id]
80 80 end
81 81
82 82 selected = selected.is_a?(Version) ? selected.id : selected
83 83 if grouped.keys.size > 1
84 84 grouped_options_for_select(grouped, selected)
85 85 else
86 86 options_for_select((grouped.values.first || []), selected)
87 87 end
88 88 end
89 89
90 90 def project_default_version_options(project)
91 91 versions = project.shared_versions.open.to_a
92 92 if project.default_version && !versions.include?(project.default_version)
93 93 versions << project.default_version
94 94 end
95 95 version_options_for_select(versions, project.default_version)
96 96 end
97 97
98 98 def format_version_sharing(sharing)
99 99 sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing)
100 100 l("label_version_sharing_#{sharing}")
101 101 end
102 102
103 def render_boards_tree(boards, parent=nil, level=0, &block)
104 selection = boards.select {|b| b.parent == parent}
105 return '' if selection.empty?
106
107 s = ''.html_safe
108 selection.each do |board|
109 node = capture(board, level, &block)
110 node << render_boards_tree(boards, board, level+1, &block)
111 s << content_tag('div', node)
112 end
113 content_tag('div', s, :class => 'sort-level')
114 end
115
103 116 def render_api_includes(project, api)
104 117 api.array :trackers do
105 118 project.trackers.each do |tracker|
106 119 api.tracker(:id => tracker.id, :name => tracker.name)
107 120 end
108 121 end if include_in_api_response?('trackers')
109 122
110 123 api.array :issue_categories do
111 124 project.issue_categories.each do |category|
112 125 api.issue_category(:id => category.id, :name => category.name)
113 126 end
114 127 end if include_in_api_response?('issue_categories')
115 128
116 129 api.array :enabled_modules do
117 130 project.enabled_modules.each do |enabled_module|
118 131 api.enabled_module(:id => enabled_module.id, :name => enabled_module.name)
119 132 end
120 133 end if include_in_api_response?('enabled_modules')
121 134
122 135 end
123 136 end
@@ -1,69 +1,73
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 module RoutesHelper
21 21
22 22 # Returns the path to project issues or to the cross-project
23 23 # issue list if project is nil
24 24 def _project_issues_path(project, *args)
25 25 if project
26 26 project_issues_path(project, *args)
27 27 else
28 28 issues_path(*args)
29 29 end
30 30 end
31 31
32 32 def _project_calendar_path(project, *args)
33 33 project ? project_calendar_path(project, *args) : issues_calendar_path(*args)
34 34 end
35 35
36 36 def _project_gantt_path(project, *args)
37 37 project ? project_gantt_path(project, *args) : issues_gantt_path(*args)
38 38 end
39 39
40 40 def _time_entries_path(project, issue, *args)
41 41 if issue
42 42 issue_time_entries_path(issue, *args)
43 43 elsif project
44 44 project_time_entries_path(project, *args)
45 45 else
46 46 time_entries_path(*args)
47 47 end
48 48 end
49 49
50 50 def _report_time_entries_path(project, issue, *args)
51 51 if issue
52 52 report_issue_time_entries_path(issue, *args)
53 53 elsif project
54 54 report_project_time_entries_path(project, *args)
55 55 else
56 56 report_time_entries_path(*args)
57 57 end
58 58 end
59 59
60 60 def _new_time_entry_path(project, issue, *args)
61 61 if issue
62 62 new_issue_time_entry_path(issue, *args)
63 63 elsif project
64 64 new_project_time_entry_path(project, *args)
65 65 else
66 66 new_time_entry_path(*args)
67 67 end
68 68 end
69
70 def board_path(board, *args)
71 project_board_path(board.project, board, *args)
72 end
69 73 end
@@ -1,96 +1,96
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Board < ActiveRecord::Base
19 19 include Redmine::SafeAttributes
20 20 belongs_to :project
21 21 has_many :messages, lambda {order("#{Message.table_name}.created_on DESC")}, :dependent => :destroy
22 22 belongs_to :last_message, :class_name => 'Message'
23 23 acts_as_tree :dependent => :nullify
24 24 acts_as_positioned :scope => [:project_id, :parent_id]
25 25 acts_as_watchable
26 26
27 27 validates_presence_of :name, :description
28 28 validates_length_of :name, :maximum => 30
29 29 validates_length_of :description, :maximum => 255
30 30 validate :validate_board
31 31 attr_protected :id
32 32
33 33 scope :visible, lambda {|*args|
34 34 joins(:project).
35 35 where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
36 36 }
37 37
38 safe_attributes 'name', 'description', 'parent_id', 'move_to'
38 safe_attributes 'name', 'description', 'parent_id', 'position'
39 39
40 40 def visible?(user=User.current)
41 41 !user.nil? && user.allowed_to?(:view_messages, project)
42 42 end
43 43
44 44 def reload(*args)
45 45 @valid_parents = nil
46 46 super
47 47 end
48 48
49 49 def to_s
50 50 name
51 51 end
52 52
53 53 # Returns a scope for the board topics (messages without parent)
54 54 def topics
55 55 messages.where(:parent_id => nil)
56 56 end
57 57
58 58 def valid_parents
59 59 @valid_parents ||= project.boards - self_and_descendants
60 60 end
61 61
62 62 def reset_counters!
63 63 self.class.reset_counters!(id)
64 64 end
65 65
66 66 # Updates topics_count, messages_count and last_message_id attributes for +board_id+
67 67 def self.reset_counters!(board_id)
68 68 board_id = board_id.to_i
69 69 Board.where(:id => board_id).
70 70 update_all(["topics_count = (SELECT COUNT(*) FROM #{Message.table_name} WHERE board_id=:id AND parent_id IS NULL)," +
71 71 " messages_count = (SELECT COUNT(*) FROM #{Message.table_name} WHERE board_id=:id)," +
72 72 " last_message_id = (SELECT MAX(id) FROM #{Message.table_name} WHERE board_id=:id)", :id => board_id])
73 73 end
74 74
75 75 def self.board_tree(boards, parent_id=nil, level=0)
76 76 tree = []
77 77 boards.select {|board| board.parent_id == parent_id}.sort_by(&:position).each do |board|
78 78 tree << [board, level]
79 79 tree += board_tree(boards, board.id, level+1)
80 80 end
81 81 if block_given?
82 82 tree.each do |board, level|
83 83 yield board, level
84 84 end
85 85 end
86 86 tree
87 87 end
88 88
89 89 protected
90 90
91 91 def validate_board
92 92 if parent_id && parent_id_changed?
93 93 errors.add(:parent_id, :invalid) unless valid_parents.include?(parent)
94 94 end
95 95 end
96 96 end
@@ -1,36 +1,33
1 1 <% if @project.boards.any? %>
2 <table class="list">
3 <thead><tr>
4 <th><%= l(:label_board) %></th>
5 <th><%= l(:field_description) %></th>
6 <th></th>
7 <th></th>
8 </tr></thead>
9 <tbody>
10 <% Board.board_tree(@project.boards) do |board, level|
11 next if board.new_record? %>
12 <tr class="<%= cycle 'odd', 'even' %>">
13 <td class="name" style="padding-left: <%= level * 18 %>px;"><%= link_to board.name, project_board_path(@project, board) %></td>
14 <td class="description"><%= board.description %></td>
15 <td class="reorder">
16 <% if authorize_for("boards", "edit") %>
17 <%= reorder_links('board', {:controller => 'boards', :action => 'update', :project_id => @project, :id => board}, :put) %>
18 <% end %>
19 </td>
20 <td class="buttons">
2 <div class="table-list boards">
3 <div class="table-list-header">
4 <div class="table-list-cell"><%= l(:label_board) %></div>
5 </div>
6 <%= render_boards_tree(@project.boards) do |board, level| %>
7 <div class="table-list-row <%= cycle 'odd', 'even' %>">
8 <div class="table-list-cell name" style="padding-left: <%= 2 + level * 16 %>px">
9 <%= link_to board.name, project_board_path(@project, board) %>
10 </div>
11 <div class="table-list-cell description"><%= board.description %></div>
12 <div class="table-list-cell buttons">
21 13 <% if User.current.allowed_to?(:manage_boards, @project) %>
14 <%= reorder_handle(board) %>
22 15 <%= link_to l(:button_edit), edit_project_board_path(@project, board), :class => 'icon icon-edit' %>
23 16 <%= delete_link project_board_path(@project, board) %>
24 17 <% end %>
25 </td>
26 </tr>
18 </div>
19 </div>
20 <% end %>
21 </div>
22
23 <%= javascript_tag do %>
24 $(function() { $("div.sort-level").positionedItems(); });
27 25 <% end %>
28 </tbody>
29 </table>
26
30 27 <% else %>
31 28 <p class="nodata"><%= l(:label_no_data) %></p>
32 29 <% end %>
33 30
34 31 <% if User.current.allowed_to?(:manage_boards, @project) %>
35 32 <p><%= link_to l(:label_board_new), new_project_board_path(@project), :class => 'icon icon-add' %></p>
36 33 <% end %>
@@ -1,742 +1,742
1 1 /* Redmine - project management software
2 2 Copyright (C) 2006-2016 Jean-Philippe Lang */
3 3
4 4 function checkAll(id, checked) {
5 5 $('#'+id).find('input[type=checkbox]:enabled').prop('checked', checked);
6 6 }
7 7
8 8 function toggleCheckboxesBySelector(selector) {
9 9 var all_checked = true;
10 10 $(selector).each(function(index) {
11 11 if (!$(this).is(':checked')) { all_checked = false; }
12 12 });
13 13 $(selector).prop('checked', !all_checked);
14 14 }
15 15
16 16 function showAndScrollTo(id, focus) {
17 17 $('#'+id).show();
18 18 if (focus !== null) {
19 19 $('#'+focus).focus();
20 20 }
21 21 $('html, body').animate({scrollTop: $('#'+id).offset().top}, 100);
22 22 }
23 23
24 24 function toggleRowGroup(el) {
25 25 var tr = $(el).parents('tr').first();
26 26 var n = tr.next();
27 27 tr.toggleClass('open');
28 28 while (n.length && !n.hasClass('group')) {
29 29 n.toggle();
30 30 n = n.next('tr');
31 31 }
32 32 }
33 33
34 34 function collapseAllRowGroups(el) {
35 35 var tbody = $(el).parents('tbody').first();
36 36 tbody.children('tr').each(function(index) {
37 37 if ($(this).hasClass('group')) {
38 38 $(this).removeClass('open');
39 39 } else {
40 40 $(this).hide();
41 41 }
42 42 });
43 43 }
44 44
45 45 function expandAllRowGroups(el) {
46 46 var tbody = $(el).parents('tbody').first();
47 47 tbody.children('tr').each(function(index) {
48 48 if ($(this).hasClass('group')) {
49 49 $(this).addClass('open');
50 50 } else {
51 51 $(this).show();
52 52 }
53 53 });
54 54 }
55 55
56 56 function toggleAllRowGroups(el) {
57 57 var tr = $(el).parents('tr').first();
58 58 if (tr.hasClass('open')) {
59 59 collapseAllRowGroups(el);
60 60 } else {
61 61 expandAllRowGroups(el);
62 62 }
63 63 }
64 64
65 65 function toggleFieldset(el) {
66 66 var fieldset = $(el).parents('fieldset').first();
67 67 fieldset.toggleClass('collapsed');
68 68 fieldset.children('div').toggle();
69 69 }
70 70
71 71 function hideFieldset(el) {
72 72 var fieldset = $(el).parents('fieldset').first();
73 73 fieldset.toggleClass('collapsed');
74 74 fieldset.children('div').hide();
75 75 }
76 76
77 77 // columns selection
78 78 function moveOptions(theSelFrom, theSelTo) {
79 79 $(theSelFrom).find('option:selected').detach().prop("selected", false).appendTo($(theSelTo));
80 80 }
81 81
82 82 function moveOptionUp(theSel) {
83 83 $(theSel).find('option:selected').each(function(){
84 84 $(this).prev(':not(:selected)').detach().insertAfter($(this));
85 85 });
86 86 }
87 87
88 88 function moveOptionTop(theSel) {
89 89 $(theSel).find('option:selected').detach().prependTo($(theSel));
90 90 }
91 91
92 92 function moveOptionDown(theSel) {
93 93 $($(theSel).find('option:selected').get().reverse()).each(function(){
94 94 $(this).next(':not(:selected)').detach().insertBefore($(this));
95 95 });
96 96 }
97 97
98 98 function moveOptionBottom(theSel) {
99 99 $(theSel).find('option:selected').detach().appendTo($(theSel));
100 100 }
101 101
102 102 function initFilters() {
103 103 $('#add_filter_select').change(function() {
104 104 addFilter($(this).val(), '', []);
105 105 });
106 106 $('#filters-table td.field input[type=checkbox]').each(function() {
107 107 toggleFilter($(this).val());
108 108 });
109 109 $('#filters-table').on('click', 'td.field input[type=checkbox]', function() {
110 110 toggleFilter($(this).val());
111 111 });
112 112 $('#filters-table').on('click', '.toggle-multiselect', function() {
113 113 toggleMultiSelect($(this).siblings('select'));
114 114 });
115 115 $('#filters-table').on('keypress', 'input[type=text]', function(e) {
116 116 if (e.keyCode == 13) $(this).closest('form').submit();
117 117 });
118 118 }
119 119
120 120 function addFilter(field, operator, values) {
121 121 var fieldId = field.replace('.', '_');
122 122 var tr = $('#tr_'+fieldId);
123 123 if (tr.length > 0) {
124 124 tr.show();
125 125 } else {
126 126 buildFilterRow(field, operator, values);
127 127 }
128 128 $('#cb_'+fieldId).prop('checked', true);
129 129 toggleFilter(field);
130 130 $('#add_filter_select').val('').find('option').each(function() {
131 131 if ($(this).attr('value') == field) {
132 132 $(this).attr('disabled', true);
133 133 }
134 134 });
135 135 }
136 136
137 137 function buildFilterRow(field, operator, values) {
138 138 var fieldId = field.replace('.', '_');
139 139 var filterTable = $("#filters-table");
140 140 var filterOptions = availableFilters[field];
141 141 if (!filterOptions) return;
142 142 var operators = operatorByType[filterOptions['type']];
143 143 var filterValues = filterOptions['values'];
144 144 var i, select;
145 145
146 146 var tr = $('<tr class="filter">').attr('id', 'tr_'+fieldId).html(
147 147 '<td class="field"><input checked="checked" id="cb_'+fieldId+'" name="f[]" value="'+field+'" type="checkbox"><label for="cb_'+fieldId+'"> '+filterOptions['name']+'</label></td>' +
148 148 '<td class="operator"><select id="operators_'+fieldId+'" name="op['+field+']"></td>' +
149 149 '<td class="values"></td>'
150 150 );
151 151 filterTable.append(tr);
152 152
153 153 select = tr.find('td.operator select');
154 154 for (i = 0; i < operators.length; i++) {
155 155 var option = $('<option>').val(operators[i]).text(operatorLabels[operators[i]]);
156 156 if (operators[i] == operator) { option.attr('selected', true); }
157 157 select.append(option);
158 158 }
159 159 select.change(function(){ toggleOperator(field); });
160 160
161 161 switch (filterOptions['type']) {
162 162 case "list":
163 163 case "list_optional":
164 164 case "list_status":
165 165 case "list_subprojects":
166 166 tr.find('td.values').append(
167 167 '<span style="display:none;"><select class="value" id="values_'+fieldId+'_1" name="v['+field+'][]"></select>' +
168 168 ' <span class="toggle-multiselect">&nbsp;</span></span>'
169 169 );
170 170 select = tr.find('td.values select');
171 171 if (values.length > 1) { select.attr('multiple', true); }
172 172 for (i = 0; i < filterValues.length; i++) {
173 173 var filterValue = filterValues[i];
174 174 var option = $('<option>');
175 175 if ($.isArray(filterValue)) {
176 176 option.val(filterValue[1]).text(filterValue[0]);
177 177 if ($.inArray(filterValue[1], values) > -1) {option.attr('selected', true);}
178 178 } else {
179 179 option.val(filterValue).text(filterValue);
180 180 if ($.inArray(filterValue, values) > -1) {option.attr('selected', true);}
181 181 }
182 182 select.append(option);
183 183 }
184 184 break;
185 185 case "date":
186 186 case "date_past":
187 187 tr.find('td.values').append(
188 188 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="10" class="value date_value" /></span>' +
189 189 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="10" class="value date_value" /></span>' +
190 190 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="3" class="value" /> '+labelDayPlural+'</span>'
191 191 );
192 192 $('#values_'+fieldId+'_1').val(values[0]).datepicker(datepickerOptions);
193 193 $('#values_'+fieldId+'_2').val(values[1]).datepicker(datepickerOptions);
194 194 $('#values_'+fieldId).val(values[0]);
195 195 break;
196 196 case "string":
197 197 case "text":
198 198 tr.find('td.values').append(
199 199 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="30" class="value" /></span>'
200 200 );
201 201 $('#values_'+fieldId).val(values[0]);
202 202 break;
203 203 case "relation":
204 204 tr.find('td.values').append(
205 205 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="6" class="value" /></span>' +
206 206 '<span style="display:none;"><select class="value" name="v['+field+'][]" id="values_'+fieldId+'_1"></select></span>'
207 207 );
208 208 $('#values_'+fieldId).val(values[0]);
209 209 select = tr.find('td.values select');
210 210 for (i = 0; i < allProjects.length; i++) {
211 211 var filterValue = allProjects[i];
212 212 var option = $('<option>');
213 213 option.val(filterValue[1]).text(filterValue[0]);
214 214 if (values[0] == filterValue[1]) { option.attr('selected', true); }
215 215 select.append(option);
216 216 }
217 217 break;
218 218 case "integer":
219 219 case "float":
220 220 case "tree":
221 221 tr.find('td.values').append(
222 222 '<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_1" size="6" class="value" /></span>' +
223 223 ' <span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'_2" size="6" class="value" /></span>'
224 224 );
225 225 $('#values_'+fieldId+'_1').val(values[0]);
226 226 $('#values_'+fieldId+'_2').val(values[1]);
227 227 break;
228 228 }
229 229 }
230 230
231 231 function toggleFilter(field) {
232 232 var fieldId = field.replace('.', '_');
233 233 if ($('#cb_' + fieldId).is(':checked')) {
234 234 $("#operators_" + fieldId).show().removeAttr('disabled');
235 235 toggleOperator(field);
236 236 } else {
237 237 $("#operators_" + fieldId).hide().attr('disabled', true);
238 238 enableValues(field, []);
239 239 }
240 240 }
241 241
242 242 function enableValues(field, indexes) {
243 243 var fieldId = field.replace('.', '_');
244 244 $('#tr_'+fieldId+' td.values .value').each(function(index) {
245 245 if ($.inArray(index, indexes) >= 0) {
246 246 $(this).removeAttr('disabled');
247 247 $(this).parents('span').first().show();
248 248 } else {
249 249 $(this).val('');
250 250 $(this).attr('disabled', true);
251 251 $(this).parents('span').first().hide();
252 252 }
253 253
254 254 if ($(this).hasClass('group')) {
255 255 $(this).addClass('open');
256 256 } else {
257 257 $(this).show();
258 258 }
259 259 });
260 260 }
261 261
262 262 function toggleOperator(field) {
263 263 var fieldId = field.replace('.', '_');
264 264 var operator = $("#operators_" + fieldId);
265 265 switch (operator.val()) {
266 266 case "!*":
267 267 case "*":
268 268 case "t":
269 269 case "ld":
270 270 case "w":
271 271 case "lw":
272 272 case "l2w":
273 273 case "m":
274 274 case "lm":
275 275 case "y":
276 276 case "o":
277 277 case "c":
278 278 case "*o":
279 279 case "!o":
280 280 enableValues(field, []);
281 281 break;
282 282 case "><":
283 283 enableValues(field, [0,1]);
284 284 break;
285 285 case "<t+":
286 286 case ">t+":
287 287 case "><t+":
288 288 case "t+":
289 289 case ">t-":
290 290 case "<t-":
291 291 case "><t-":
292 292 case "t-":
293 293 enableValues(field, [2]);
294 294 break;
295 295 case "=p":
296 296 case "=!p":
297 297 case "!p":
298 298 enableValues(field, [1]);
299 299 break;
300 300 default:
301 301 enableValues(field, [0]);
302 302 break;
303 303 }
304 304 }
305 305
306 306 function toggleMultiSelect(el) {
307 307 if (el.attr('multiple')) {
308 308 el.removeAttr('multiple');
309 309 el.attr('size', 1);
310 310 } else {
311 311 el.attr('multiple', true);
312 312 if (el.children().length > 10)
313 313 el.attr('size', 10);
314 314 else
315 315 el.attr('size', 4);
316 316 }
317 317 }
318 318
319 319 function showTab(name, url) {
320 320 $('#tab-content-' + name).parent().find('.tab-content').hide();
321 321 $('#tab-content-' + name).parent().find('div.tabs a').removeClass('selected');
322 322 $('#tab-content-' + name).show();
323 323 $('#tab-' + name).addClass('selected');
324 324 //replaces current URL with the "href" attribute of the current link
325 325 //(only triggered if supported by browser)
326 326 if ("replaceState" in window.history) {
327 327 window.history.replaceState(null, document.title, url);
328 328 }
329 329 return false;
330 330 }
331 331
332 332 function moveTabRight(el) {
333 333 var lis = $(el).parents('div.tabs').first().find('ul').children();
334 334 var bw = $(el).parents('div.tabs-buttons').outerWidth(true);
335 335 var tabsWidth = 0;
336 336 var i = 0;
337 337 lis.each(function() {
338 338 if ($(this).is(':visible')) {
339 339 tabsWidth += $(this).outerWidth(true);
340 340 }
341 341 });
342 342 if (tabsWidth < $(el).parents('div.tabs').first().width() - bw) { return; }
343 343 $(el).siblings('.tab-left').removeClass('disabled');
344 344 while (i<lis.length && !lis.eq(i).is(':visible')) { i++; }
345 345 var w = lis.eq(i).width();
346 346 lis.eq(i).hide();
347 347 if (tabsWidth - w < $(el).parents('div.tabs').first().width() - bw) {
348 348 $(el).addClass('disabled');
349 349 }
350 350 }
351 351
352 352 function moveTabLeft(el) {
353 353 var lis = $(el).parents('div.tabs').first().find('ul').children();
354 354 var i = 0;
355 355 while (i < lis.length && !lis.eq(i).is(':visible')) { i++; }
356 356 if (i > 0) {
357 357 lis.eq(i-1).show();
358 358 $(el).siblings('.tab-right').removeClass('disabled');
359 359 }
360 360 if (i <= 1) {
361 361 $(el).addClass('disabled');
362 362 }
363 363 }
364 364
365 365 function displayTabsButtons() {
366 366 var lis;
367 367 var tabsWidth;
368 368 var el;
369 369 var numHidden;
370 370 $('div.tabs').each(function() {
371 371 el = $(this);
372 372 lis = el.find('ul').children();
373 373 tabsWidth = 0;
374 374 numHidden = 0;
375 375 lis.each(function(){
376 376 if ($(this).is(':visible')) {
377 377 tabsWidth += $(this).outerWidth(true);
378 378 } else {
379 379 numHidden++;
380 380 }
381 381 });
382 382 var bw = $(el).parents('div.tabs-buttons').outerWidth(true);
383 383 if ((tabsWidth < el.width() - bw) && (lis.first().is(':visible'))) {
384 384 el.find('div.tabs-buttons').hide();
385 385 } else {
386 386 el.find('div.tabs-buttons').show().children('button.tab-left').toggleClass('disabled', numHidden == 0);
387 387 }
388 388 });
389 389 }
390 390
391 391 function setPredecessorFieldsVisibility() {
392 392 var relationType = $('#relation_relation_type');
393 393 if (relationType.val() == "precedes" || relationType.val() == "follows") {
394 394 $('#predecessor_fields').show();
395 395 } else {
396 396 $('#predecessor_fields').hide();
397 397 }
398 398 }
399 399
400 400 function showModal(id, width, title) {
401 401 var el = $('#'+id).first();
402 402 if (el.length === 0 || el.is(':visible')) {return;}
403 403 if (!title) title = el.find('h3.title').text();
404 404 // moves existing modals behind the transparent background
405 405 $(".modal").zIndex(99);
406 406 el.dialog({
407 407 width: width,
408 408 modal: true,
409 409 resizable: false,
410 410 dialogClass: 'modal',
411 411 title: title
412 412 }).on('dialogclose', function(){
413 413 $(".modal").zIndex(101);
414 414 });
415 415 el.find("input[type=text], input[type=submit]").first().focus();
416 416 }
417 417
418 418 function hideModal(el) {
419 419 var modal;
420 420 if (el) {
421 421 modal = $(el).parents('.ui-dialog-content');
422 422 } else {
423 423 modal = $('#ajax-modal');
424 424 }
425 425 modal.dialog("close");
426 426 }
427 427
428 428 function submitPreview(url, form, target) {
429 429 $.ajax({
430 430 url: url,
431 431 type: 'post',
432 432 data: $('#'+form).serialize(),
433 433 success: function(data){
434 434 $('#'+target).html(data);
435 435 }
436 436 });
437 437 }
438 438
439 439 function collapseScmEntry(id) {
440 440 $('.'+id).each(function() {
441 441 if ($(this).hasClass('open')) {
442 442 collapseScmEntry($(this).attr('id'));
443 443 }
444 444 $(this).hide();
445 445 });
446 446 $('#'+id).removeClass('open');
447 447 }
448 448
449 449 function expandScmEntry(id) {
450 450 $('.'+id).each(function() {
451 451 $(this).show();
452 452 if ($(this).hasClass('loaded') && !$(this).hasClass('collapsed')) {
453 453 expandScmEntry($(this).attr('id'));
454 454 }
455 455 });
456 456 $('#'+id).addClass('open');
457 457 }
458 458
459 459 function scmEntryClick(id, url) {
460 460 var el = $('#'+id);
461 461 if (el.hasClass('open')) {
462 462 collapseScmEntry(id);
463 463 el.addClass('collapsed');
464 464 return false;
465 465 } else if (el.hasClass('loaded')) {
466 466 expandScmEntry(id);
467 467 el.removeClass('collapsed');
468 468 return false;
469 469 }
470 470 if (el.hasClass('loading')) {
471 471 return false;
472 472 }
473 473 el.addClass('loading');
474 474 $.ajax({
475 475 url: url,
476 476 success: function(data) {
477 477 el.after(data);
478 478 el.addClass('open').addClass('loaded').removeClass('loading');
479 479 }
480 480 });
481 481 return true;
482 482 }
483 483
484 484 function randomKey(size) {
485 485 var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
486 486 var key = '';
487 487 for (var i = 0; i < size; i++) {
488 488 key += chars.charAt(Math.floor(Math.random() * chars.length));
489 489 }
490 490 return key;
491 491 }
492 492
493 493 function updateIssueFrom(url, el) {
494 494 $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
495 495 $(this).data('valuebeforeupdate', $(this).val());
496 496 });
497 497 if (el) {
498 498 $("#form_update_triggered_by").val($(el).attr('id'));
499 499 }
500 500 return $.ajax({
501 501 url: url,
502 502 type: 'post',
503 503 data: $('#issue-form').serialize()
504 504 });
505 505 }
506 506
507 507 function replaceIssueFormWith(html){
508 508 var replacement = $(html);
509 509 $('#all_attributes input, #all_attributes textarea, #all_attributes select').each(function(){
510 510 var object_id = $(this).attr('id');
511 511 if (object_id && $(this).data('valuebeforeupdate')!=$(this).val()) {
512 512 replacement.find('#'+object_id).val($(this).val());
513 513 }
514 514 });
515 515 $('#all_attributes').empty();
516 516 $('#all_attributes').prepend(replacement);
517 517 }
518 518
519 519 function updateBulkEditFrom(url) {
520 520 $.ajax({
521 521 url: url,
522 522 type: 'post',
523 523 data: $('#bulk_edit_form').serialize()
524 524 });
525 525 }
526 526
527 527 function observeAutocompleteField(fieldId, url, options) {
528 528 $(document).ready(function() {
529 529 $('#'+fieldId).autocomplete($.extend({
530 530 source: url,
531 531 minLength: 2,
532 532 position: {collision: "flipfit"},
533 533 search: function(){$('#'+fieldId).addClass('ajax-loading');},
534 534 response: function(){$('#'+fieldId).removeClass('ajax-loading');}
535 535 }, options));
536 536 $('#'+fieldId).addClass('autocomplete');
537 537 });
538 538 }
539 539
540 540 function observeSearchfield(fieldId, targetId, url) {
541 541 $('#'+fieldId).each(function() {
542 542 var $this = $(this);
543 543 $this.addClass('autocomplete');
544 544 $this.attr('data-value-was', $this.val());
545 545 var check = function() {
546 546 var val = $this.val();
547 547 if ($this.attr('data-value-was') != val){
548 548 $this.attr('data-value-was', val);
549 549 $.ajax({
550 550 url: url,
551 551 type: 'get',
552 552 data: {q: $this.val()},
553 553 success: function(data){ if(targetId) $('#'+targetId).html(data); },
554 554 beforeSend: function(){ $this.addClass('ajax-loading'); },
555 555 complete: function(){ $this.removeClass('ajax-loading'); }
556 556 });
557 557 }
558 558 };
559 559 var reset = function() {
560 560 if (timer) {
561 561 clearInterval(timer);
562 562 timer = setInterval(check, 300);
563 563 }
564 564 };
565 565 var timer = setInterval(check, 300);
566 566 $this.bind('keyup click mousemove', reset);
567 567 });
568 568 }
569 569
570 570 function beforeShowDatePicker(input, inst) {
571 571 var default_date = null;
572 572 switch ($(input).attr("id")) {
573 573 case "issue_start_date" :
574 574 if ($("#issue_due_date").size() > 0) {
575 575 default_date = $("#issue_due_date").val();
576 576 }
577 577 break;
578 578 case "issue_due_date" :
579 579 if ($("#issue_start_date").size() > 0) {
580 580 var start_date = $("#issue_start_date").val();
581 581 if (start_date != "") {
582 582 start_date = new Date(Date.parse(start_date));
583 583 if (start_date > new Date()) {
584 584 default_date = $("#issue_start_date").val();
585 585 }
586 586 }
587 587 }
588 588 break;
589 589 }
590 590 $(input).datepicker("option", "defaultDate", default_date);
591 591 }
592 592
593 593 (function($){
594 594 $.fn.positionedItems = function(sortableOptions, options){
595 595 var settings = $.extend({
596 596 firstPosition: 1
597 597 }, options );
598 598
599 599 return this.sortable($.extend({
600 600 handle: ".sort-handle",
601 601 helper: function(event, ui){
602 ui.children().each(function(){
602 ui.children('td').each(function(){
603 603 $(this).width($(this).width());
604 604 });
605 605 return ui;
606 606 },
607 607 update: function(event, ui) {
608 608 var sortable = $(this);
609 609 var url = ui.item.find(".sort-handle").data("reorder-url");
610 610 var param = ui.item.find(".sort-handle").data("reorder-param");
611 611 var data = {};
612 612 data[param] = {position: ui.item.index() + settings['firstPosition']};
613 613 $.ajax({
614 614 url: url,
615 615 type: 'put',
616 616 dataType: 'script',
617 617 data: data,
618 618 success: function(data){
619 619 sortable.children(":even").removeClass("even").addClass("odd");
620 620 sortable.children(":odd").removeClass("odd").addClass("even");
621 621 },
622 622 error: function(jqXHR, textStatus, errorThrown){
623 623 alert(jqXHR.status);
624 624 sortable.sortable("cancel");
625 625 }
626 626 });
627 627 },
628 628 }, sortableOptions));
629 629 }
630 630 }( jQuery ));
631 631
632 632 function initMyPageSortable(list, url) {
633 633 $('#list-'+list).sortable({
634 634 connectWith: '.block-receiver',
635 635 tolerance: 'pointer',
636 636 update: function(){
637 637 $.ajax({
638 638 url: url,
639 639 type: 'post',
640 640 data: {'blocks': $.map($('#list-'+list).children(), function(el){return $(el).attr('id');})}
641 641 });
642 642 }
643 643 });
644 644 $("#list-top, #list-left, #list-right").disableSelection();
645 645 }
646 646
647 647 var warnLeavingUnsavedMessage;
648 648 function warnLeavingUnsaved(message) {
649 649 warnLeavingUnsavedMessage = message;
650 650 $(document).on('submit', 'form', function(){
651 651 $('textarea').removeData('changed');
652 652 });
653 653 $(document).on('change', 'textarea', function(){
654 654 $(this).data('changed', 'changed');
655 655 });
656 656 window.onbeforeunload = function(){
657 657 var warn = false;
658 658 $('textarea').blur().each(function(){
659 659 if ($(this).data('changed')) {
660 660 warn = true;
661 661 }
662 662 });
663 663 if (warn) {return warnLeavingUnsavedMessage;}
664 664 };
665 665 }
666 666
667 667 function setupAjaxIndicator() {
668 668 $(document).bind('ajaxSend', function(event, xhr, settings) {
669 669 if ($('.ajax-loading').length === 0 && settings.contentType != 'application/octet-stream') {
670 670 $('#ajax-indicator').show();
671 671 }
672 672 });
673 673 $(document).bind('ajaxStop', function() {
674 674 $('#ajax-indicator').hide();
675 675 });
676 676 }
677 677
678 678 function setupTabs() {
679 679 if($('.tabs').length > 0) {
680 680 displayTabsButtons();
681 681 $(window).resize(displayTabsButtons);
682 682 }
683 683 }
684 684
685 685 function hideOnLoad() {
686 686 $('.hol').hide();
687 687 }
688 688
689 689 function addFormObserversForDoubleSubmit() {
690 690 $('form[method=post]').each(function() {
691 691 if (!$(this).hasClass('multiple-submit')) {
692 692 $(this).submit(function(form_submission) {
693 693 if ($(form_submission.target).attr('data-submitted')) {
694 694 form_submission.preventDefault();
695 695 } else {
696 696 $(form_submission.target).attr('data-submitted', true);
697 697 }
698 698 });
699 699 }
700 700 });
701 701 }
702 702
703 703 function defaultFocus(){
704 704 if (($('#content :focus').length == 0) && (window.location.hash == '')) {
705 705 $('#content input[type=text], #content textarea').first().focus();
706 706 }
707 707 }
708 708
709 709 function blockEventPropagation(event) {
710 710 event.stopPropagation();
711 711 event.preventDefault();
712 712 }
713 713
714 714 function toggleDisabledOnChange() {
715 715 var checked = $(this).is(':checked');
716 716 $($(this).data('disables')).attr('disabled', checked);
717 717 $($(this).data('enables')).attr('disabled', !checked);
718 718 }
719 719 function toggleDisabledInit() {
720 720 $('input[data-disables], input[data-enables]').each(toggleDisabledOnChange);
721 721 }
722 722 $(document).ready(function(){
723 723 $('#content').on('change', 'input[data-disables], input[data-enables]', toggleDisabledOnChange);
724 724 toggleDisabledInit();
725 725 });
726 726
727 727 function keepAnchorOnSignIn(form){
728 728 var hash = decodeURIComponent(self.document.location.hash);
729 729 if (hash) {
730 730 if (hash.indexOf("#") === -1) {
731 731 hash = "#" + hash;
732 732 }
733 733 form.action = form.action + hash;
734 734 }
735 735 return true;
736 736 }
737 737
738 738 $(document).ready(setupAjaxIndicator);
739 739 $(document).ready(hideOnLoad);
740 740 $(document).ready(addFormObserversForDoubleSubmit);
741 741 $(document).ready(defaultFocus);
742 742 $(document).ready(setupTabs);
@@ -1,1369 +1,1373
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 a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
55 55 #main-menu .tabs-buttons {
56 56 right: 6px;
57 57 background-color: transparent;
58 58 border-bottom-color: transparent;
59 59 }
60 60
61 61 #admin-menu ul {margin: 0; padding: 0;}
62 62 #admin-menu li {margin: 0; padding: 0 0 6px 0; list-style-type:none;}
63 63
64 64 #admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;}
65 65 #admin-menu a.projects { background-image: url(../images/projects.png); }
66 66 #admin-menu a.users { background-image: url(../images/user.png); }
67 67 #admin-menu a.groups { background-image: url(../images/group.png); }
68 68 #admin-menu a.roles { background-image: url(../images/database_key.png); }
69 69 #admin-menu a.trackers { background-image: url(../images/ticket.png); }
70 70 #admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); }
71 71 #admin-menu a.workflows { background-image: url(../images/ticket_go.png); }
72 72 #admin-menu a.custom_fields { background-image: url(../images/textfield.png); }
73 73 #admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); }
74 74 #admin-menu a.settings { background-image: url(../images/changeset.png); }
75 75 #admin-menu a.plugins { background-image: url(../images/plugin.png); }
76 76 #admin-menu a.info { background-image: url(../images/help.png); }
77 77 #admin-menu a.server_authentication { background-image: url(../images/server_key.png); }
78 78
79 79 #main {background-color:#EEEEEE;}
80 80
81 81 #sidebar{ float: right; width: 22%; position: relative; z-index: 9; padding: 0; margin: 0;}
82 82 * html #sidebar{ width: 22%; }
83 83 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
84 84 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
85 85 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
86 86 #sidebar .contextual { margin-right: 1em; }
87 87 #sidebar ul, ul.flat {margin: 0; padding: 0;}
88 88 #sidebar ul li, ul.flat li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;}
89 89 #sidebar div.wiki h3 {font-size:13px; margin-top:0; color:#555;}
90 90 #sidebar div.wiki ul {margin:inherit; padding-left:40px;}
91 91 #sidebar div.wiki ul li {list-style-type:inherit;}
92 92
93 93 #content { width: 75%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
94 94 * html #content{ width: 75%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
95 95 html>body #content { min-height: 600px; }
96 96 * html body #content { height: 600px; } /* IE */
97 97
98 98 #main.nosidebar #sidebar{ display: none; }
99 99 #main.nosidebar #content{ width: auto; border-right: 0; }
100 100
101 101 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
102 102
103 103 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
104 104 #login-form table td {padding: 6px;}
105 105 #login-form label {font-weight: bold;}
106 106 #login-form input#username, #login-form input#password { width: 300px; }
107 107
108 108 div.modal { border-radius:5px; background:#fff; z-index:50; padding:4px;}
109 109 div.modal h3.title {display:none;}
110 110 div.modal p.buttons {text-align:right; margin-bottom:0;}
111 111 div.modal .box p {margin: 0.3em 0;}
112 112
113 113 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
114 114
115 115 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
116 116
117 117 .mobile-show {display: none;}
118 118
119 119 /***** Links *****/
120 120 a, a:link, a:visited{ color: #169; text-decoration: none; }
121 121 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
122 122 a img{ border: 0; }
123 123
124 124 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
125 125 a.project.closed, a.project.closed:link, a.project.closed:visited { color: #999; }
126 126 a.user.locked, a.user.locked:link, a.user.locked:visited {color: #999;}
127 127
128 128 #sidebar a.selected {line-height:1.7em; padding:1px 3px 2px 2px; margin-left:-2px; background-color:#9DB9D5; color:#fff; border-radius:2px;}
129 129 #sidebar a.selected:hover {text-decoration:none;}
130 130 #admin-menu a {line-height:1.7em;}
131 131 #admin-menu a.selected {padding-left: 20px !important; background-position: 2px 40%;}
132 132
133 133 a.collapsible {padding-left: 12px; background: url(../images/arrow_expanded.png) no-repeat -3px 40%;}
134 134 a.collapsible.collapsed {background: url(../images/arrow_collapsed.png) no-repeat -5px 40%;}
135 135
136 136 a#toggle-completed-versions {color:#999;}
137 137
138 138 a.toggle-checkboxes { margin-left: 5px; padding-left: 12px; background: url(../images/toggle_check.png) no-repeat 0% 50%; }
139 139
140 140 /***** Tables *****/
141 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
142 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
141 table.list, .table-list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
142 table.list th, .table-list-header { background-color:#EEEEEE; padding: 4px; white-space:nowrap; font-weight:bold; }
143 143 table.list td {text-align:center; vertical-align:top; padding-right:10px;}
144 144 table.list td.id { width: 2%; text-align: center;}
145 145 table.list td.name, table.list td.description, table.list td.subject, table.list td.comments, table.list td.roles {text-align: left;}
146 146 table.list td.tick {width:15%}
147 147 table.list td.checkbox { width: 15px; padding: 2px 0 0 0; }
148 148 table.list td.checkbox input {padding:0px;}
149 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
150 table.list td.buttons a { margin-right: 0.6em; }
151 table.list td.buttons img {vertical-align:middle;}
149 table.list td.buttons, div.buttons { width: 15%; white-space:nowrap; text-align: right; }
150 table.list td.buttons a, div.buttons a { margin-right: 0.6em; }
151 table.list td.buttons img, div.buttons img {vertical-align:middle;}
152 152 table.list td.reorder {width:15%; white-space:nowrap; text-align:center; }
153 153 table.list table.progress td {padding-right:0px;}
154 154 table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; }
155 155
156 .table-list-cell {display: table-cell; vertical-align: top; padding:2px; }
157
156 158 tr.project td.name a { white-space:nowrap; }
157 159 tr.project.closed, tr.project.archived { color: #aaa; }
158 160 tr.project.closed a, tr.project.archived a { color: #aaa; }
159 161
160 162 tr.project.idnt td.name span {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
161 163 tr.project.idnt-1 td.name {padding-left: 0.5em;}
162 164 tr.project.idnt-2 td.name {padding-left: 2em;}
163 165 tr.project.idnt-3 td.name {padding-left: 3.5em;}
164 166 tr.project.idnt-4 td.name {padding-left: 5em;}
165 167 tr.project.idnt-5 td.name {padding-left: 6.5em;}
166 168 tr.project.idnt-6 td.name {padding-left: 8em;}
167 169 tr.project.idnt-7 td.name {padding-left: 9.5em;}
168 170 tr.project.idnt-8 td.name {padding-left: 11em;}
169 171 tr.project.idnt-9 td.name {padding-left: 12.5em;}
170 172
171 173 tr.issue { text-align: center; white-space: nowrap; }
172 174 tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text, tr.issue td.relations, tr.issue td.parent { white-space: normal; }
173 175 tr.issue td.relations { text-align: left; }
174 176 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
175 177 tr.issue td.relations span {white-space: nowrap;}
176 178 table.issues td.description {color:#777; font-size:90%; padding:4px 4px 4px 24px; text-align:left; white-space:normal;}
177 179 table.issues td.description pre {white-space:normal;}
178 180
179 181 tr.issue.idnt td.subject a {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
180 182 tr.issue.idnt-1 td.subject {padding-left: 0.5em;}
181 183 tr.issue.idnt-2 td.subject {padding-left: 2em;}
182 184 tr.issue.idnt-3 td.subject {padding-left: 3.5em;}
183 185 tr.issue.idnt-4 td.subject {padding-left: 5em;}
184 186 tr.issue.idnt-5 td.subject {padding-left: 6.5em;}
185 187 tr.issue.idnt-6 td.subject {padding-left: 8em;}
186 188 tr.issue.idnt-7 td.subject {padding-left: 9.5em;}
187 189 tr.issue.idnt-8 td.subject {padding-left: 11em;}
188 190 tr.issue.idnt-9 td.subject {padding-left: 12.5em;}
189 191
190 192 table.issue-report {table-layout:fixed;}
191 193
192 194 tr.entry { border: 1px solid #f8f8f8; }
193 195 tr.entry td { white-space: nowrap; }
194 196 tr.entry td.filename {width:30%; text-align:left;}
195 197 tr.entry td.filename_no_report {width:70%; text-align:left;}
196 198 tr.entry td.size { text-align: right; font-size: 90%; }
197 199 tr.entry td.revision, tr.entry td.author { text-align: center; }
198 200 tr.entry td.age { text-align: right; }
199 201 tr.entry.file td.filename a { margin-left: 16px; }
200 202 tr.entry.file td.filename_no_report a { margin-left: 16px; }
201 203
202 204 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
203 205 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
204 206
205 207 tr.changeset { height: 20px }
206 208 tr.changeset ul, ol { margin-top: 0px; margin-bottom: 0px; }
207 209 tr.changeset td.revision_graph { width: 15%; background-color: #fffffb; }
208 210 tr.changeset td.author { text-align: center; width: 15%; white-space:nowrap;}
209 211 tr.changeset td.committed_on { text-align: center; width: 15%; white-space:nowrap;}
210 212
211 213 table.files tbody th {text-align:left;}
212 214 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
213 215 table.files tr.file td.digest { font-size: 80%; }
214 216
215 217 table.members td.roles, table.memberships td.roles { width: 45%; }
216 218
217 219 tr.message { height: 2.6em; }
218 220 tr.message td.subject { padding-left: 20px; }
219 221 tr.message td.created_on { white-space: nowrap; }
220 222 tr.message td.last_message { font-size: 80%; white-space: nowrap; }
221 223 tr.message.locked td.subject { background: url(../images/locked.png) no-repeat 0 1px; }
222 224 tr.message.sticky td.subject { background: url(../images/bullet_go.png) no-repeat 0 1px; font-weight: bold; }
223 225
224 226 tr.version.closed, tr.version.closed a { color: #999; }
225 227 tr.version td.name { padding-left: 20px; }
226 228 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
227 229 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; white-space:nowrap; }
228 230
229 231 tr.user td {width:13%;white-space: nowrap;}
230 232 td.username, td.firstname, td.lastname, td.email {text-align:left !important;}
231 233 tr.user td.email { width:18%; }
232 234 tr.user.locked, tr.user.registered { color: #aaa; }
233 235 tr.user.locked a, tr.user.registered a { color: #aaa; }
234 236
235 237 table.permissions td.role {color:#999;font-size:90%;font-weight:normal !important;text-align:center;vertical-align:bottom;}
236 238
237 239 tr.wiki-page-version td.updated_on, tr.wiki-page-version td.author {text-align:center;}
238 240
239 241 tr.time-entry { text-align: center; white-space: nowrap; }
240 242 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; }
241 243 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
242 244 td.hours .hours-dec { font-size: 0.9em; }
243 245
244 246 table.plugins td { vertical-align: middle; }
245 247 table.plugins td.configure { text-align: right; padding-right: 1em; }
246 248 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
247 249 table.plugins span.description { display: block; font-size: 0.9em; }
248 250 table.plugins span.url { display: block; font-size: 0.9em; }
249 251
250 252 tr.group td { padding: 0.8em 0 0.5em 0.3em; border-bottom: 1px solid #ccc; text-align:left; }
251 253 tr.group span.name {font-weight:bold;}
252 254 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;}
253 255 tr.group span.totals {color: #aaa; font-size: 80%;}
254 256 tr.group span.totals .value {font-weight:bold; color:#777;}
255 257 tr.group a.toggle-all { color: #aaa; font-size: 80%; display:none; float:right; margin-right:4px;}
256 258 tr.group:hover a.toggle-all { display:inline;}
257 259 a.toggle-all:hover {text-decoration:none;}
258 260
259 261 table.list tbody tr:hover { background-color:#ffffdd; }
260 262 table.list tbody tr.group:hover { background-color:inherit; }
261 263 table td {padding:2px;}
262 264 table p {margin:0;}
263 265 .odd {background-color:#f6f7f8;}
264 266 .even {background-color: #fff;}
265 267
266 268 tr.builtin td.name {font-style:italic;}
267 269
268 270 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
269 271 a.sort.asc { background-image: url(../images/sort_asc.png); }
270 272 a.sort.desc { background-image: url(../images/sort_desc.png); }
271 273
272 274 table.boards a.board, h3.comments { background: url(../images/comment.png) no-repeat 0% 50%; padding-left: 20px; }
273 275 table.boards td.last-message {text-align:left;font-size:80%;}
274 276
277 div.table-list.boards .table-list-cell.name {width: 30%;}
278
275 279 table.messages td.last_message {text-align:left;}
276 280
277 281 #query_form_content {font-size:90%;}
278 282
279 283 .query_sort_criteria_count {
280 284 display: inline-block;
281 285 min-width: 1em;
282 286 }
283 287
284 288 table.query-columns {
285 289 border-collapse: collapse;
286 290 border: 0;
287 291 }
288 292
289 293 table.query-columns td.buttons {
290 294 vertical-align: middle;
291 295 text-align: center;
292 296 }
293 297 table.query-columns td.buttons input[type=button] {width:35px;}
294 298 .query-totals {text-align:right; margin-top:-2.3em;}
295 299 .query-totals>span {margin-left:0.6em;}
296 300 .query-totals .value {font-weight:bold;}
297 301
298 302 td.center {text-align:center;}
299 303
300 304 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
301 305
302 306 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
303 307 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
304 308 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
305 309 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
306 310
307 311 #watchers select {width: 95%; display: block;}
308 312 #watchers a.delete {opacity: 0.4; margin-left: 5px;}
309 313 #watchers a.delete:hover {opacity: 1;}
310 314 #watchers img.gravatar {margin: 0 4px 2px 0;}
311 315
312 316 span#watchers_inputs {overflow:auto; display:block;}
313 317 span.search_for_watchers {display:block;}
314 318 span.search_for_watchers, span.add_attachment {font-size:80%; line-height:2.5em;}
315 319 span.search_for_watchers a, span.add_attachment a {padding-left:16px; background: url(../images/bullet_add.png) no-repeat 0 50%; }
316 320
317 321
318 322 .highlight { background-color: #FCFD8D;}
319 323 .highlight.token-1 { background-color: #faa;}
320 324 .highlight.token-2 { background-color: #afa;}
321 325 .highlight.token-3 { background-color: #aaf;}
322 326
323 327 .box{
324 328 padding:6px;
325 329 margin-bottom: 10px;
326 330 background-color:#f6f6f6;
327 331 color:#505050;
328 332 line-height:1.5em;
329 333 border: 1px solid #e4e4e4;
330 334 word-wrap: break-word;
331 335 border-radius: 3px;
332 336 }
333 337 .pagination .per-page span.selected {
334 338 font-weight: bold;
335 339 }
336 340
337 341 div.square {
338 342 border: 1px solid #999;
339 343 float: left;
340 344 margin: .3em .4em 0 .4em;
341 345 overflow: hidden;
342 346 width: .6em; height: .6em;
343 347 }
344 348 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
345 349 .contextual input, .contextual select {font-size:0.9em;}
346 350 .message .contextual { margin-top: 0; }
347 351
348 352 .splitcontent {overflow:auto;}
349 353 .splitcontentleft{float:left; width:49%;}
350 354 .splitcontentright{float:right; width:49%;}
351 355 form {display: inline;}
352 356 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
353 357 fieldset {border: 1px solid #e4e4e4; margin:0;}
354 358 legend {color: #333;}
355 359 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
356 360 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
357 361 blockquote blockquote { margin-left: 0;}
358 362 abbr, span.field-description[title] { border-bottom: 1px dotted #aaa; cursor: help; }
359 363 textarea.wiki-edit {width:99%; resize:vertical;}
360 364 li p {margin-top: 0;}
361 365 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px; border: 1px solid #d7d7d7; border-radius:3px;}
362 366 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
363 367 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
364 368 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
365 369 .ltr {direction:ltr !important; unicode-bidi:bidi-override;}
366 370 .rtl {direction:rtl !important; unicode-bidi:bidi-override;}
367 371
368 372 div.issue div.subject div div { padding-left: 16px; }
369 373 div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color: #999;}
370 374 div.issue div.subject>div>p { margin-top: 0.5em; }
371 375 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
372 376 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;}
373 377 div.issue .next-prev-links {color:#999;}
374 378 div.issue .attributes {margin-top: 2em;}
375 379 div.issue .attribute {padding-left:180px; clear:left; min-height: 1.8em;}
376 380 div.issue .attribute .label {width: 170px; margin-left:-180px; font-weight:bold; float:left;}
377 381 div.issue.overdue .due-date .value { color: #c22; }
378 382
379 383 #issue_tree table.issues, #relations table.issues { border: 0; }
380 384 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
381 385 #relations td.buttons {padding:0;}
382 386
383 387 fieldset.collapsible {border-width: 1px 0 0 0;}
384 388 fieldset.collapsible>legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
385 389 fieldset.collapsible.collapsed>legend { background-image: url(../images/arrow_collapsed.png); }
386 390
387 391 fieldset#date-range p { margin: 2px 0 2px 0; }
388 392 fieldset#filters table { border-collapse: collapse; }
389 393 fieldset#filters table td { padding: 0; vertical-align: middle; }
390 394 fieldset#filters tr.filter { height: 2.1em; }
391 395 fieldset#filters td.field { width:230px; }
392 396 fieldset#filters td.operator { width:180px; }
393 397 fieldset#filters td.operator select {max-width:170px;}
394 398 fieldset#filters td.values { white-space:nowrap; }
395 399 fieldset#filters td.values select {min-width:130px;}
396 400 fieldset#filters td.values input {height:1em;}
397 401
398 402 #filters-table {width:60%; float:left;}
399 403 .add-filter {width:35%; float:right; text-align: right; vertical-align: top;}
400 404
401 405 #issue_is_private_wrap {float:right; margin-right:1em;}
402 406 .toggle-multiselect {background: url(../images/bullet_toggle_plus.png) no-repeat 0% 40%; padding-left:16px; margin-left:0; margin-right:5px; cursor:pointer;}
403 407 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
404 408
405 409 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
406 410 div#issue-changesets div.changeset { padding: 4px;}
407 411 div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
408 412 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
409 413
410 414 .journal ul.details img {margin:0 0 -3px 4px;}
411 415 div.journal {overflow:auto;}
412 416 div.journal.private-notes {border-left:2px solid #d22; padding-left:4px; margin-left:-6px;}
413 417 div.journal ul.details {color:#959595; margin-bottom: 1.5em;}
414 418 div.journal ul.details a {color:#70A7CD;}
415 419 div.journal ul.details a:hover {color:#D14848;}
416 420
417 421 div#activity dl, #search-results { margin-left: 2em; }
418 422 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
419 423 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
420 424 div#activity dt.me .time { border-bottom: 1px solid #999; }
421 425 div#activity dt .time { color: #777; font-size: 80%; }
422 426 div#activity dd .description, #search-results dd .description { font-style: italic; }
423 427 div#activity span.project:after, #search-results span.project:after { content: " -"; }
424 428 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
425 429 div#activity dt.grouped {margin-left:5em;}
426 430 div#activity dd.grouped {margin-left:9em;}
427 431
428 432 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
429 433
430 434 div#search-results-counts {float:right;}
431 435 div#search-results-counts ul { margin-top: 0.5em; }
432 436 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
433 437
434 438 dt.issue { background-image: url(../images/ticket.png); }
435 439 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
436 440 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
437 441 dt.issue-note { background-image: url(../images/ticket_note.png); }
438 442 dt.changeset { background-image: url(../images/changeset.png); }
439 443 dt.news { background-image: url(../images/news.png); }
440 444 dt.message { background-image: url(../images/message.png); }
441 445 dt.reply { background-image: url(../images/comments.png); }
442 446 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
443 447 dt.attachment { background-image: url(../images/attachment.png); }
444 448 dt.document { background-image: url(../images/document.png); }
445 449 dt.project { background-image: url(../images/projects.png); }
446 450 dt.time-entry { background-image: url(../images/time.png); }
447 451
448 452 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
449 453
450 454 div#roadmap .related-issues { margin-bottom: 1em; }
451 455 div#roadmap .related-issues td.checkbox { display: none; }
452 456 div#roadmap .wiki h1:first-child { display: none; }
453 457 div#roadmap .wiki h1 { font-size: 120%; }
454 458 div#roadmap .wiki h2 { font-size: 110%; }
455 459 body.controller-versions.action-show div#roadmap .related-issues {width:70%;}
456 460
457 461 div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
458 462 div#version-summary fieldset { margin-bottom: 1em; }
459 463 div#version-summary fieldset.time-tracking table { width:100%; }
460 464 div#version-summary th, div#version-summary td.total-hours { text-align: right; }
461 465
462 466 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
463 467 table#time-report tbody tr.subtotal { font-style: italic; color:#777;}
464 468 table#time-report tbody tr.subtotal td.hours { color:#b0b0b0; }
465 469 table#time-report tbody tr.total { font-weight: bold; background-color:#EEEEEE; border-top:1px solid #e4e4e4;}
466 470 table#time-report .hours-dec { font-size: 0.9em; }
467 471
468 472 div.wiki-page .contextual a {opacity: 0.4}
469 473 div.wiki-page .contextual a:hover {opacity: 1}
470 474
471 475 form .attributes select { width: 60%; }
472 476 form .attributes select + a.icon-only { vertical-align: middle; margin-left: 4px; }
473 477 input#issue_subject, input#document_title { width: 99%; }
474 478 select#issue_done_ratio { width: 95px; }
475 479
476 480 ul.projects {margin:0; padding-left:1em;}
477 481 ul.projects ul {padding-left:1.6em;}
478 482 ul.projects.root {margin:0; padding:0;}
479 483 ul.projects li {list-style-type:none;}
480 484
481 485 #projects-index ul.projects ul.projects { border-left: 3px solid #e0e0e0; padding-left:1em;}
482 486 #projects-index ul.projects li.root {margin-bottom: 1em;}
483 487 #projects-index ul.projects li.child {margin-top: 1em;}
484 488 #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; }
485 489 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
486 490
487 491 #notified-projects>ul, #tracker_project_ids>ul, #custom_field_project_ids>ul {max-height:250px; overflow-y:auto;}
488 492
489 493 #related-issues li img {vertical-align:middle;}
490 494
491 495 ul.properties {padding:0; font-size: 0.9em; color: #777;}
492 496 ul.properties li {list-style-type:none;}
493 497 ul.properties li span {font-style:italic;}
494 498
495 499 .total-hours { font-size: 110%; font-weight: bold; }
496 500 .total-hours span.hours-int { font-size: 120%; }
497 501
498 502 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em; position: relative;}
499 503 #user_login, #user_firstname, #user_lastname, #user_mail, #my_account_form select, #user_form select, #user_identity_url { width: 90%; }
500 504
501 505 #workflow_copy_form select { width: 200px; }
502 506 table.transitions td.enabled {background: #bfb;}
503 507 #workflow_form table select {font-size:90%; max-width:100px;}
504 508 table.fields_permissions td.readonly {background:#ddd;}
505 509 table.fields_permissions td.required {background:#d88;}
506 510
507 511 select.expandable {vertical-align:top;}
508 512
509 513 textarea#custom_field_possible_values {width: 95%; resize:vertical}
510 514 textarea#custom_field_default_value {width: 95%; resize:vertical}
511 515 .sort-handle {display:inline-block; vertical-align:middle;}
512 516
513 517 input#content_comments {width: 99%}
514 518
515 519 span.pagination {margin-left:3px; color:#888;}
516 520 .pagination ul.pages {
517 521 margin: 0 5px 0 0;
518 522 padding: 0;
519 523 display: inline;
520 524 }
521 525 .pagination ul.pages li {
522 526 display: inline-block;
523 527 padding: 0;
524 528 border: 1px solid #ccc;
525 529 margin-left: -1px;
526 530 line-height: 2em;
527 531 margin-bottom: 1em;
528 532 white-space: nowrap;
529 533 text-align: center;
530 534 }
531 535 .pagination ul.pages li a,
532 536 .pagination ul.pages li span {
533 537 padding: 3px 8px;
534 538 }
535 539 .pagination ul.pages li:first-child {
536 540 border-top-left-radius: 4px;
537 541 border-bottom-left-radius: 4px;
538 542 }
539 543 .pagination ul.pages li:last-child {
540 544 border-top-right-radius: 4px;
541 545 border-bottom-right-radius: 4px;
542 546 }
543 547 .pagination ul.pages li.current {
544 548 color: white;
545 549 background-color: #628DB6;
546 550 border-color: #628DB6;
547 551 }
548 552 .pagination ul.pages li.page:hover {
549 553 background-color: #EEE;
550 554 }
551 555 .pagination ul.pages li.page a:hover,
552 556 .pagination ul.pages li.page a:active {
553 557 color: inherit;
554 558 text-decoration: inherit;
555 559 }
556 560 span.pagination>span {white-space:nowrap;}
557 561
558 562 #search-form fieldset p {margin:0.2em 0;}
559 563
560 564 /***** Tabular forms ******/
561 565 .tabular p{
562 566 margin: 0;
563 567 padding: 3px 0 3px 0;
564 568 padding-left: 180px; /* width of left column containing the label elements */
565 569 min-height: 1.8em;
566 570 clear:left;
567 571 }
568 572
569 573 html>body .tabular p {overflow:hidden;}
570 574
571 575 .tabular input, .tabular select {max-width:95%}
572 576 .tabular textarea {width:95%; resize:vertical;}
573 577
574 578 .tabular label{
575 579 font-weight: bold;
576 580 float: left;
577 581 text-align: right;
578 582 /* width of left column */
579 583 margin-left: -180px;
580 584 /* width of labels. Should be smaller than left column to create some right margin */
581 585 width: 175px;
582 586 }
583 587
584 588 .tabular label.floating{
585 589 font-weight: normal;
586 590 margin-left: 0px;
587 591 text-align: left;
588 592 width: 270px;
589 593 }
590 594
591 595 .tabular label.block{
592 596 font-weight: normal;
593 597 margin-left: 0px !important;
594 598 text-align: left;
595 599 float: none;
596 600 display: block;
597 601 width: auto !important;
598 602 }
599 603
600 604 .tabular label.inline{
601 605 font-weight: normal;
602 606 float:none;
603 607 margin-left: 5px !important;
604 608 width: auto;
605 609 }
606 610
607 611 label.no-css {
608 612 font-weight: inherit;
609 613 float:none;
610 614 text-align:left;
611 615 margin-left:0px;
612 616 width:auto;
613 617 }
614 618 input#time_entry_comments { width: 90%;}
615 619
616 620 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
617 621
618 622 .tabular.settings p{ padding-left: 300px; }
619 623 .tabular.settings label{ margin-left: -300px; width: 295px; }
620 624 .tabular.settings textarea { width: 99%; }
621 625
622 626 .settings.enabled_scm table {width:100%}
623 627 .settings.enabled_scm td.scm_name{ font-weight: bold; }
624 628
625 629 fieldset.settings label { display: block; }
626 630 fieldset#notified_events .parent { padding-left: 20px; }
627 631
628 632 span.required {color: #bb0000;}
629 633 .summary {font-style: italic;}
630 634
631 635 .check_box_group {
632 636 display:block;
633 637 width:95%;
634 638 max-height:300px;
635 639 overflow-y:auto;
636 640 padding:2px 4px 4px 2px;
637 641 background:#fff;
638 642 border:1px solid #9EB1C2;
639 643 border-radius:2px
640 644 }
641 645 .check_box_group label {
642 646 font-weight: normal;
643 647 margin-left: 0px !important;
644 648 text-align: left;
645 649 float: none;
646 650 display: block;
647 651 width: auto;
648 652 }
649 653 .check_box_group.bool_cf {border:0; background:inherit;}
650 654 .check_box_group.bool_cf label {display: inline;}
651 655
652 656 #attachments_fields input.description {margin-left:4px; width:340px;}
653 657 #attachments_fields span {display:block; white-space:nowrap;}
654 658 #attachments_fields input.filename {border:0; height:1.8em; width:250px; color:#555; background-color:inherit; background:url(../images/attachment.png) no-repeat 1px 50%; padding-left:18px;}
655 659 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
656 660 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
657 661 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
658 662 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
659 663 a.remove-upload:hover {text-decoration:none !important;}
660 664
661 665 div.fileover { background-color: lavender; }
662 666
663 667 div.attachments { margin-top: 12px; }
664 668 div.attachments p { margin:4px 0 2px 0; }
665 669 div.attachments img { vertical-align: middle; }
666 670 div.attachments span.author { font-size: 0.9em; color: #888; }
667 671
668 672 div.thumbnails {margin-top:0.6em;}
669 673 div.thumbnails div {background:#fff;border:2px solid #ddd;display:inline-block;margin-right:2px;}
670 674 div.thumbnails img {margin: 3px; vertical-align: middle;}
671 675 #history div.thumbnails {margin-left: 2em;}
672 676
673 677 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
674 678 .other-formats span + span:before { content: "| "; }
675 679
676 680 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
677 681
678 682 em.info {font-style:normal;font-size:90%;color:#888;display:block;}
679 683 em.info.error {padding-left:20px; background:url(../images/exclamation.png) no-repeat 0 50%;}
680 684
681 685 textarea.text_cf {width:95%; resize:vertical;}
682 686 input.string_cf, input.link_cf {width:95%;}
683 687 select.bool_cf {width:auto !important;}
684 688
685 689 #tab-content-modules fieldset p {margin:3px 0 4px 0;}
686 690
687 691 #tab-content-users .splitcontentleft {width: 64%;}
688 692 #tab-content-users .splitcontentright {width: 34%;}
689 693 #tab-content-users fieldset {padding:1em; margin-bottom: 1em;}
690 694 #tab-content-users fieldset legend {font-weight: bold;}
691 695 #tab-content-users fieldset label {display: block;}
692 696 #tab-content-users #principals {max-height: 400px; overflow: auto;}
693 697
694 698 #users_for_watcher {height: 200px; overflow:auto;}
695 699 #users_for_watcher label {display: block;}
696 700
697 701 table.members td.name {padding-left: 20px;}
698 702 table.members td.group, table.members td.groupnonmember, table.members td.groupanonymous {background: url(../images/group.png) no-repeat 0% 1px;}
699 703
700 704 input#principal_search, input#user_search {width:90%}
701 705 .roles-selection label {display:inline-block; width:210px;}
702 706
703 707 input.autocomplete {
704 708 background: #fff url(../images/magnifier.png) no-repeat 2px 50%; padding-left:20px !important;
705 709 border:1px solid #9EB1C2; border-radius:2px; height:1.5em;
706 710 }
707 711 input.autocomplete.ajax-loading {
708 712 background-image: url(../images/loading.gif);
709 713 }
710 714
711 715 .role-visibility {padding-left:2em;}
712 716
713 717 .objects-selection {
714 718 height: 300px;
715 719 overflow: auto;
716 720 margin-bottom: 1em;
717 721 }
718 722
719 723 .objects-selection label {
720 724 display: block;
721 725 }
722 726
723 727 .objects-selection>div {
724 728 column-count: auto;
725 729 column-width: 200px;
726 730 -webkit-column-count: auto;
727 731 -webkit-column-width: 200px;
728 732 -webkit-column-gap : 0.5rem;
729 733 -webkit-column-rule: 1px solid #ccc;
730 734 -moz-column-count: auto;
731 735 -moz-column-width: 200px;
732 736 -moz-column-gap : 0.5rem;
733 737 -moz-column-rule: 1px solid #ccc;
734 738 }
735 739
736 740 /***** Flash & error messages ****/
737 741 #errorExplanation, div.flash, .nodata, .warning, .conflict {
738 742 padding: 4px 4px 4px 30px;
739 743 margin-bottom: 12px;
740 744 font-size: 1.1em;
741 745 border: 2px solid;
742 746 border-radius: 3px;
743 747 }
744 748
745 749 div.flash {margin-top: 8px;}
746 750
747 751 div.flash.error, #errorExplanation {
748 752 background: url(../images/exclamation.png) 8px 50% no-repeat;
749 753 background-color: #ffe3e3;
750 754 border-color: #dd0000;
751 755 color: #880000;
752 756 }
753 757
754 758 div.flash.notice {
755 759 background: url(../images/true.png) 8px 5px no-repeat;
756 760 background-color: #dfffdf;
757 761 border-color: #9fcf9f;
758 762 color: #005f00;
759 763 }
760 764
761 765 div.flash.warning, .conflict {
762 766 background: url(../images/warning.png) 8px 5px no-repeat;
763 767 background-color: #FFEBC1;
764 768 border-color: #FDBF3B;
765 769 color: #A6750C;
766 770 text-align: left;
767 771 }
768 772
769 773 .nodata, .warning {
770 774 text-align: center;
771 775 background-color: #FFEBC1;
772 776 border-color: #FDBF3B;
773 777 color: #A6750C;
774 778 }
775 779
776 780 #errorExplanation ul { font-size: 0.9em;}
777 781 #errorExplanation h2, #errorExplanation p { display: none; }
778 782
779 783 .conflict-details {font-size:80%;}
780 784
781 785 /***** Ajax indicator ******/
782 786 #ajax-indicator {
783 787 position: absolute; /* fixed not supported by IE */
784 788 background-color:#eee;
785 789 border: 1px solid #bbb;
786 790 top:35%;
787 791 left:40%;
788 792 width:20%;
789 793 font-weight:bold;
790 794 text-align:center;
791 795 padding:0.6em;
792 796 z-index:100;
793 797 opacity: 0.5;
794 798 }
795 799
796 800 html>body #ajax-indicator { position: fixed; }
797 801
798 802 #ajax-indicator span {
799 803 background-position: 0% 40%;
800 804 background-repeat: no-repeat;
801 805 background-image: url(../images/loading.gif);
802 806 padding-left: 26px;
803 807 vertical-align: bottom;
804 808 }
805 809
806 810 /***** Calendar *****/
807 811 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
808 812 table.cal thead th {width: 14%; background-color:#EEEEEE; padding: 4px; }
809 813 table.cal thead th.week-number {width: auto;}
810 814 table.cal tbody tr {height: 100px;}
811 815 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
812 816 table.cal td.week-number { background-color:#EEEEEE; padding: 4px; border:none; font-size: 1em;}
813 817 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
814 818 table.cal td.odd p.day-num {color: #bbb;}
815 819 table.cal td.today {background:#ffffdd;}
816 820 table.cal td.today p.day-num {font-weight: bold;}
817 821 table.cal .starting a, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;}
818 822 table.cal .ending a, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;}
819 823 table.cal .starting.ending a, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;}
820 824 p.cal.legend span {display:block;}
821 825
822 826 /***** Tooltips ******/
823 827 .tooltip{position:relative;z-index:24;}
824 828 .tooltip:hover{z-index:25;color:#000;}
825 829 .tooltip span.tip{display: none; text-align:left;}
826 830
827 831 div.tooltip:hover span.tip{
828 832 display:block;
829 833 position:absolute;
830 834 top:12px; width:270px;
831 835 border:1px solid #555;
832 836 background-color:#fff;
833 837 padding: 4px;
834 838 font-size: 0.8em;
835 839 color:#505050;
836 840 }
837 841
838 842 img.ui-datepicker-trigger {
839 843 cursor: pointer;
840 844 vertical-align: middle;
841 845 margin-left: 4px;
842 846 }
843 847
844 848 /***** Progress bar *****/
845 849 table.progress {
846 850 border-collapse: collapse;
847 851 border-spacing: 0pt;
848 852 empty-cells: show;
849 853 text-align: center;
850 854 float:left;
851 855 margin: 1px 6px 1px 0px;
852 856 }
853 857
854 858 table.progress {width:80px;}
855 859 table.progress td { height: 1em; }
856 860 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
857 861 table.progress td.done { background: #D3EDD3 none repeat scroll 0%; }
858 862 table.progress td.todo { background: #eee none repeat scroll 0%; }
859 863 p.percent {font-size: 80%; margin:0;}
860 864 p.progress-info {clear: left; font-size: 80%; margin-top:-4px; color:#777;}
861 865
862 866 .version-overview table.progress {width:40em;}
863 867 .version-overview table.progress td { height: 1.2em; }
864 868
865 869 /***** Tabs *****/
866 870 #content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
867 871 #content .tabs ul {margin:0; position:absolute; bottom:0; padding-left:0.5em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
868 872 #content .tabs ul li {
869 873 float:left;
870 874 list-style-type:none;
871 875 white-space:nowrap;
872 876 margin-right:4px;
873 877 background:#fff;
874 878 position:relative;
875 879 margin-bottom:-1px;
876 880 }
877 881 #content .tabs ul li a{
878 882 display:block;
879 883 font-size: 0.9em;
880 884 text-decoration:none;
881 885 line-height:1.3em;
882 886 padding:4px 6px 4px 6px;
883 887 border: 1px solid #ccc;
884 888 border-bottom: 1px solid #bbbbbb;
885 889 background-color: #f6f6f6;
886 890 color:#999;
887 891 font-weight:bold;
888 892 border-top-left-radius:3px;
889 893 border-top-right-radius:3px;
890 894 }
891 895
892 896 #content .tabs ul li a:hover {
893 897 background-color: #ffffdd;
894 898 text-decoration:none;
895 899 }
896 900
897 901 #content .tabs ul li a.selected {
898 902 background-color: #fff;
899 903 border: 1px solid #bbbbbb;
900 904 border-bottom: 1px solid #fff;
901 905 color:#444;
902 906 }
903 907
904 908 #content .tabs ul li a.selected:hover {background-color: #fff;}
905 909
906 910 div.tabs-buttons { position:absolute; right: 0; width: 54px; height: 24px; background: white; bottom: 0; border-bottom: 1px solid #bbbbbb; }
907 911
908 912 button.tab-left, button.tab-right {
909 913 font-size: 0.9em;
910 914 cursor: pointer;
911 915 height:24px;
912 916 border: 1px solid #ccc;
913 917 border-bottom: 1px solid #bbbbbb;
914 918 position:absolute;
915 919 padding:4px;
916 920 width: 20px;
917 921 bottom: -1px;
918 922 }
919 923 button.tab-left:hover, button.tab-right:hover {
920 924 background-color: #f5f5f5;
921 925 }
922 926 button.tab-left:focus, button.tab-right:focus {
923 927 outline: 0;
924 928 }
925 929
926 930 button.tab-left {
927 931 right: 20px;
928 932 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
929 933 border-top-left-radius:3px;
930 934 }
931 935
932 936 button.tab-right {
933 937 right: 0;
934 938 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;
935 939 border-top-right-radius:3px;
936 940 }
937 941
938 942 button.tab-left.disabled, button.tab-right.disabled {
939 943 background-color: #ccc;
940 944 cursor: unset;
941 945 }
942 946
943 947 /***** Diff *****/
944 948 .diff_out { background: #fcc; }
945 949 .diff_out span { background: #faa; }
946 950 .diff_in { background: #cfc; }
947 951 .diff_in span { background: #afa; }
948 952
949 953 .text-diff {
950 954 padding: 1em;
951 955 background-color:#f6f6f6;
952 956 color:#505050;
953 957 border: 1px solid #e4e4e4;
954 958 }
955 959
956 960 /***** Wiki *****/
957 961 div.wiki table {
958 962 border-collapse: collapse;
959 963 margin-bottom: 1em;
960 964 }
961 965
962 966 div.wiki table, div.wiki td, div.wiki th {
963 967 border: 1px solid #bbb;
964 968 padding: 4px;
965 969 }
966 970
967 971 div.wiki .noborder, div.wiki .noborder td, div.wiki .noborder th {border:0;}
968 972
969 973 div.wiki .external {
970 974 background-position: 0% 60%;
971 975 background-repeat: no-repeat;
972 976 padding-left: 12px;
973 977 background-image: url(../images/external.png);
974 978 }
975 979
976 980 div.wiki a {word-wrap: break-word;}
977 981 div.wiki a.new {color: #b73535;}
978 982
979 983 div.wiki ul, div.wiki ol {margin-bottom:1em;}
980 984 div.wiki li>ul, div.wiki li>ol {margin-bottom: 0;}
981 985
982 986 div.wiki pre {
983 987 margin: 1em 1em 1em 1.6em;
984 988 padding: 8px;
985 989 background-color: #fafafa;
986 990 border: 1px solid #e2e2e2;
987 991 border-radius: 3px;
988 992 width:auto;
989 993 overflow-x: auto;
990 994 overflow-y: hidden;
991 995 }
992 996
993 997 div.wiki ul.toc {
994 998 background-color: #ffffdd;
995 999 border: 1px solid #e4e4e4;
996 1000 padding: 4px;
997 1001 line-height: 1.2em;
998 1002 margin-bottom: 12px;
999 1003 margin-right: 12px;
1000 1004 margin-left: 0;
1001 1005 display: table
1002 1006 }
1003 1007 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
1004 1008
1005 1009 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
1006 1010 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
1007 1011 div.wiki ul.toc ul { margin: 0; padding: 0; }
1008 1012 div.wiki ul.toc li {list-style-type:none; margin: 0; font-size:12px;}
1009 1013 div.wiki ul.toc li li {margin-left: 1.5em; font-size:10px;}
1010 1014 div.wiki ul.toc a {
1011 1015 font-size: 0.9em;
1012 1016 font-weight: normal;
1013 1017 text-decoration: none;
1014 1018 color: #606060;
1015 1019 }
1016 1020 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
1017 1021
1018 1022 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
1019 1023 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
1020 1024 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
1021 1025
1022 1026 div.wiki img {vertical-align:middle; max-width:100%;}
1023 1027
1024 1028 /***** My page layout *****/
1025 1029 .block-receiver {
1026 1030 border:1px dashed #c0c0c0;
1027 1031 margin-bottom: 20px;
1028 1032 padding: 15px 0 15px 0;
1029 1033 }
1030 1034
1031 1035 .mypage-box {
1032 1036 margin:0 0 20px 0;
1033 1037 color:#505050;
1034 1038 line-height:1.5em;
1035 1039 }
1036 1040
1037 1041 .handle {cursor: move;}
1038 1042
1039 1043 a.close-icon {
1040 1044 display:block;
1041 1045 margin-top:3px;
1042 1046 overflow:hidden;
1043 1047 width:12px;
1044 1048 height:12px;
1045 1049 background-repeat: no-repeat;
1046 1050 cursor:pointer;
1047 1051 background-image:url('../images/close.png');
1048 1052 }
1049 1053 a.close-icon:hover {background-image:url('../images/close_hl.png');}
1050 1054
1051 1055 /***** Gantt chart *****/
1052 1056 .gantt_hdr {
1053 1057 position:absolute;
1054 1058 top:0;
1055 1059 height:16px;
1056 1060 border-top: 1px solid #c0c0c0;
1057 1061 border-bottom: 1px solid #c0c0c0;
1058 1062 border-right: 1px solid #c0c0c0;
1059 1063 text-align: center;
1060 1064 overflow: hidden;
1061 1065 }
1062 1066
1063 1067 .gantt_hdr.nwday {background-color:#f1f1f1; color:#999;}
1064 1068
1065 1069 .gantt_subjects { font-size: 0.8em; }
1066 1070 .gantt_subjects div { line-height:16px;height:16px;overflow:hidden;white-space:nowrap;text-overflow: ellipsis; }
1067 1071
1068 1072 .task {
1069 1073 position: absolute;
1070 1074 height:8px;
1071 1075 font-size:0.8em;
1072 1076 color:#888;
1073 1077 padding:0;
1074 1078 margin:0;
1075 1079 line-height:16px;
1076 1080 white-space:nowrap;
1077 1081 }
1078 1082
1079 1083 .task.label {width:100%;}
1080 1084 .task.label.project, .task.label.version { font-weight: bold; }
1081 1085
1082 1086 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
1083 1087 .task_done { background:#00c600 url(../images/task_done.png); border: 1px solid #00c600; }
1084 1088 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
1085 1089
1086 1090 .task_todo.parent { background: #888; border: 1px solid #888; height: 3px;}
1087 1091 .task_late.parent, .task_done.parent { height: 3px;}
1088 1092 .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;}
1089 1093 .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;}
1090 1094
1091 1095 .version.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1092 1096 .version.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1093 1097 .version.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1094 1098 .version.marker { background-image:url(../images/version_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1095 1099
1096 1100 .project.task_late { background:#f66 url(../images/milestone_late.png); border: 1px solid #f66; height: 2px; margin-top: 3px;}
1097 1101 .project.task_done { background:#00c600 url(../images/milestone_done.png); border: 1px solid #00c600; height: 2px; margin-top: 3px;}
1098 1102 .project.task_todo { background:#fff url(../images/milestone_todo.png); border: 1px solid #fff; height: 2px; margin-top: 3px;}
1099 1103 .project.marker { background-image:url(../images/project_marker.png); background-repeat: no-repeat; border: 0; margin-left: -4px; margin-top: 1px; }
1100 1104
1101 1105 .version-behind-schedule a, .issue-behind-schedule a {color: #f66914;}
1102 1106 .version-overdue a, .issue-overdue a, .project-overdue a {color: #f00;}
1103 1107
1104 1108 /***** Icons *****/
1105 1109 .icon {
1106 1110 background-position: 0% 50%;
1107 1111 background-repeat: no-repeat;
1108 1112 padding-left: 20px;
1109 1113 padding-top: 2px;
1110 1114 padding-bottom: 3px;
1111 1115 }
1112 1116 .icon-only {
1113 1117 background-position: 0% 50%;
1114 1118 background-repeat: no-repeat;
1115 1119 padding-left: 16px;
1116 1120 }
1117 1121 a.icon-only {
1118 1122 display: inline-block;
1119 1123 width: 0;
1120 1124 height: 16px;
1121 1125 overflow: hidden;
1122 1126 padding-top: 0;
1123 1127 padding-bottom: 0;
1124 1128 font-size: 8px;
1125 1129 vertical-align: text-bottom;
1126 1130 }
1127 1131 a.icon-only::after {
1128 1132 content: "&nbsp;";
1129 1133 }
1130 1134
1131 1135 .icon-add { background-image: url(../images/add.png); }
1132 1136 .icon-edit { background-image: url(../images/edit.png); }
1133 1137 .icon-copy { background-image: url(../images/copy.png); }
1134 1138 .icon-duplicate { background-image: url(../images/duplicate.png); }
1135 1139 .icon-del { background-image: url(../images/delete.png); }
1136 1140 .icon-move { background-image: url(../images/move.png); }
1137 1141 .icon-save { background-image: url(../images/save.png); }
1138 1142 .icon-cancel { background-image: url(../images/cancel.png); }
1139 1143 .icon-multiple { background-image: url(../images/table_multiple.png); }
1140 1144 .icon-folder { background-image: url(../images/folder.png); }
1141 1145 .open .icon-folder { background-image: url(../images/folder_open.png); }
1142 1146 .icon-package { background-image: url(../images/package.png); }
1143 1147 .icon-user { background-image: url(../images/user.png); }
1144 1148 .icon-projects { background-image: url(../images/projects.png); }
1145 1149 .icon-help { background-image: url(../images/help.png); }
1146 1150 .icon-attachment { background-image: url(../images/attachment.png); }
1147 1151 .icon-history { background-image: url(../images/history.png); }
1148 1152 .icon-time { background-image: url(../images/time.png); }
1149 1153 .icon-time-add { background-image: url(../images/time_add.png); }
1150 1154 .icon-stats { background-image: url(../images/stats.png); }
1151 1155 .icon-warning { background-image: url(../images/warning.png); }
1152 1156 .icon-error { background-image: url(../images/exclamation.png); }
1153 1157 .icon-fav { background-image: url(../images/fav.png); }
1154 1158 .icon-fav-off { background-image: url(../images/fav_off.png); }
1155 1159 .icon-reload { background-image: url(../images/reload.png); }
1156 1160 .icon-lock { background-image: url(../images/locked.png); }
1157 1161 .icon-unlock { background-image: url(../images/unlock.png); }
1158 1162 .icon-checked { background-image: url(../images/toggle_check.png); }
1159 1163 .icon-details { background-image: url(../images/zoom_in.png); }
1160 1164 .icon-report { background-image: url(../images/report.png); }
1161 1165 .icon-comment { background-image: url(../images/comment.png); }
1162 1166 .icon-summary { background-image: url(../images/lightning.png); }
1163 1167 .icon-server-authentication { background-image: url(../images/server_key.png); }
1164 1168 .icon-issue { background-image: url(../images/ticket.png); }
1165 1169 .icon-zoom-in { background-image: url(../images/zoom_in.png); }
1166 1170 .icon-zoom-out { background-image: url(../images/zoom_out.png); }
1167 1171 .icon-magnifier { background-image: url(../images/magnifier.png); }
1168 1172 .icon-passwd { background-image: url(../images/textfield_key.png); }
1169 1173 .icon-test { background-image: url(../images/bullet_go.png); }
1170 1174 .icon-email { background-image: url(../images/email.png); }
1171 1175 .icon-email-disabled { background-image: url(../images/email_disabled.png); }
1172 1176 .icon-email-add { background-image: url(../images/email_add.png); }
1173 1177 .icon-move-up { background-image: url(../images/1uparrow.png); }
1174 1178 .icon-move-top { background-image: url(../images/2uparrow.png); }
1175 1179 .icon-move-down { background-image: url(../images/1downarrow.png); }
1176 1180 .icon-move-bottom { background-image: url(../images/2downarrow.png); }
1177 1181 .icon-ok { background-image: url(../images/true.png); }
1178 1182 .icon-not-ok { background-image: url(../images/false.png); }
1179 1183 .icon-link-break { background-image: url(../images/link_break.png); }
1180 1184
1181 1185 .icon-file { background-image: url(../images/files/default.png); }
1182 1186 .icon-file.text-plain { background-image: url(../images/files/text.png); }
1183 1187 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
1184 1188 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
1185 1189 .icon-file.text-x-java { background-image: url(../images/files/java.png); }
1186 1190 .icon-file.text-x-javascript { background-image: url(../images/files/js.png); }
1187 1191 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
1188 1192 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
1189 1193 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
1190 1194 .icon-file.text-css { background-image: url(../images/files/css.png); }
1191 1195 .icon-file.text-html { background-image: url(../images/files/html.png); }
1192 1196 .icon-file.image-gif { background-image: url(../images/files/image.png); }
1193 1197 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
1194 1198 .icon-file.image-png { background-image: url(../images/files/image.png); }
1195 1199 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
1196 1200 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
1197 1201 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
1198 1202 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
1199 1203
1200 1204 .contextual>.icon:not(:first-child), .buttons>.icon:not(:first-child) { margin-left: 5px; }
1201 1205
1202 1206 img.gravatar {
1203 1207 padding: 2px;
1204 1208 border: solid 1px #d5d5d5;
1205 1209 background: #fff;
1206 1210 vertical-align: middle;
1207 1211 }
1208 1212
1209 1213 div.issue img.gravatar {
1210 1214 float: left;
1211 1215 margin: 0 6px 0 0;
1212 1216 padding: 5px;
1213 1217 }
1214 1218
1215 1219 div.issue .attributes img.gravatar {
1216 1220 height: 14px;
1217 1221 width: 14px;
1218 1222 padding: 2px;
1219 1223 float: left;
1220 1224 margin: 0 0.5em 0 0;
1221 1225 }
1222 1226
1223 1227 h2 img.gravatar {margin: -2px 4px -4px 0;}
1224 1228 h3 img.gravatar {margin: -4px 4px -4px 0;}
1225 1229 h4 img.gravatar {margin: -6px 4px -4px 0;}
1226 1230 td.username img.gravatar {margin: 0 0.5em 0 0; vertical-align: top;}
1227 1231 #activity dt img.gravatar {float: left; margin: 0 1em 1em 0;}
1228 1232 /* Used on 12px Gravatar img tags without the icon background */
1229 1233 .icon-gravatar {float: left; margin-right: 4px;}
1230 1234
1231 1235 #activity dt, .journal {clear: left;}
1232 1236
1233 1237 .journal-link {float: right;}
1234 1238
1235 1239 h2 img { vertical-align:middle; }
1236 1240
1237 1241 .hascontextmenu { cursor: context-menu; }
1238 1242
1239 1243 .sample-data {border:1px solid #ccc; border-collapse:collapse; background-color:#fff; margin:0.5em;}
1240 1244 .sample-data td {border:1px solid #ccc; padding: 2px 4px; font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
1241 1245 .sample-data tr:first-child td {font-weight:bold; text-align:center;}
1242 1246
1243 1247 .ui-progressbar {position: relative;}
1244 1248 #progress-label {
1245 1249 position: absolute; left: 50%; top: 4px;
1246 1250 font-weight: bold;
1247 1251 color: #555; text-shadow: 1px 1px 0 #fff;
1248 1252 }
1249 1253
1250 1254 /* Custom JQuery styles */
1251 1255 .ui-datepicker-title select {width:70px !important; margin-top:-2px !important; margin-right:4px !important;}
1252 1256
1253 1257
1254 1258 /************* CodeRay styles *************/
1255 1259 .syntaxhl div {display: inline;}
1256 1260 .syntaxhl .code pre { overflow: auto }
1257 1261
1258 1262 .syntaxhl .annotation { color:#007 }
1259 1263 .syntaxhl .attribute-name { color:#b48 }
1260 1264 .syntaxhl .attribute-value { color:#700 }
1261 1265 .syntaxhl .binary { color:#549 }
1262 1266 .syntaxhl .binary .char { color:#325 }
1263 1267 .syntaxhl .binary .delimiter { color:#325 }
1264 1268 .syntaxhl .char { color:#D20 }
1265 1269 .syntaxhl .char .content { color:#D20 }
1266 1270 .syntaxhl .char .delimiter { color:#710 }
1267 1271 .syntaxhl .class { color:#258; font-weight:bold }
1268 1272 .syntaxhl .class-variable { color:#369 }
1269 1273 .syntaxhl .color { color:#0A0 }
1270 1274 .syntaxhl .comment { color:#385 }
1271 1275 .syntaxhl .comment .char { color:#385 }
1272 1276 .syntaxhl .comment .delimiter { color:#385 }
1273 1277 .syntaxhl .constant { color:#258; font-weight:bold }
1274 1278 .syntaxhl .decorator { color:#B0B }
1275 1279 .syntaxhl .definition { color:#099; font-weight:bold }
1276 1280 .syntaxhl .delimiter { color:black }
1277 1281 .syntaxhl .directive { color:#088; font-weight:bold }
1278 1282 .syntaxhl .docstring { color:#D42; }
1279 1283 .syntaxhl .doctype { color:#34b }
1280 1284 .syntaxhl .done { text-decoration: line-through; color: gray }
1281 1285 .syntaxhl .entity { color:#800; font-weight:bold }
1282 1286 .syntaxhl .error { color:#F00; background-color:#FAA }
1283 1287 .syntaxhl .escape { color:#666 }
1284 1288 .syntaxhl .exception { color:#C00; font-weight:bold }
1285 1289 .syntaxhl .float { color:#06D }
1286 1290 .syntaxhl .function { color:#06B; font-weight:bold }
1287 1291 .syntaxhl .function .delimiter { color:#024; font-weight:bold }
1288 1292 .syntaxhl .global-variable { color:#d70 }
1289 1293 .syntaxhl .hex { color:#02b }
1290 1294 .syntaxhl .id { color:#33D; font-weight:bold }
1291 1295 .syntaxhl .include { color:#B44; font-weight:bold }
1292 1296 .syntaxhl .inline { background-color: hsla(0,0%,0%,0.07); color: black }
1293 1297 .syntaxhl .inline-delimiter { font-weight: bold; color: #666 }
1294 1298 .syntaxhl .instance-variable { color:#33B }
1295 1299 .syntaxhl .integer { color:#06D }
1296 1300 .syntaxhl .imaginary { color:#f00 }
1297 1301 .syntaxhl .important { color:#D00 }
1298 1302 .syntaxhl .key { color: #606 }
1299 1303 .syntaxhl .key .char { color: #60f }
1300 1304 .syntaxhl .key .delimiter { color: #404 }
1301 1305 .syntaxhl .keyword { color:#939; font-weight:bold }
1302 1306 .syntaxhl .label { color:#970; font-weight:bold }
1303 1307 .syntaxhl .local-variable { color:#950 }
1304 1308 .syntaxhl .map .content { color:#808 }
1305 1309 .syntaxhl .map .delimiter { color:#40A}
1306 1310 .syntaxhl .map { background-color:hsla(200,100%,50%,0.06); }
1307 1311 .syntaxhl .namespace { color:#707; font-weight:bold }
1308 1312 .syntaxhl .octal { color:#40E }
1309 1313 .syntaxhl .operator { }
1310 1314 .syntaxhl .predefined { color:#369; font-weight:bold }
1311 1315 .syntaxhl .predefined-constant { color:#069 }
1312 1316 .syntaxhl .predefined-type { color:#0a8; font-weight:bold }
1313 1317 .syntaxhl .preprocessor { color:#579 }
1314 1318 .syntaxhl .pseudo-class { color:#00C; font-weight:bold }
1315 1319 .syntaxhl .regexp { background-color:hsla(300,100%,50%,0.06); }
1316 1320 .syntaxhl .regexp .content { color:#808 }
1317 1321 .syntaxhl .regexp .delimiter { color:#404 }
1318 1322 .syntaxhl .regexp .modifier { color:#C2C }
1319 1323 .syntaxhl .reserved { color:#080; font-weight:bold }
1320 1324 .syntaxhl .shell { background-color:hsla(120,100%,50%,0.06); }
1321 1325 .syntaxhl .shell .content { color:#2B2 }
1322 1326 .syntaxhl .shell .delimiter { color:#161 }
1323 1327 .syntaxhl .string .char { color: #46a }
1324 1328 .syntaxhl .string .content { color: #46a }
1325 1329 .syntaxhl .string .delimiter { color: #46a }
1326 1330 .syntaxhl .string .modifier { color: #46a }
1327 1331 .syntaxhl .symbol { color:#d33 }
1328 1332 .syntaxhl .symbol .content { color:#d33 }
1329 1333 .syntaxhl .symbol .delimiter { color:#d33 }
1330 1334 .syntaxhl .tag { color:#070; font-weight:bold }
1331 1335 .syntaxhl .type { color:#339; font-weight:bold }
1332 1336 .syntaxhl .value { color: #088 }
1333 1337 .syntaxhl .variable { color:#037 }
1334 1338
1335 1339 .syntaxhl .insert { background: hsla(120,100%,50%,0.12) }
1336 1340 .syntaxhl .delete { background: hsla(0,100%,50%,0.12) }
1337 1341 .syntaxhl .change { color: #bbf; background: #007 }
1338 1342 .syntaxhl .head { color: #f8f; background: #505 }
1339 1343 .syntaxhl .head .filename { color: white; }
1340 1344
1341 1345 .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; }
1342 1346 .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; }
1343 1347
1344 1348 .syntaxhl .insert .insert { color: #0c0; background:transparent; font-weight:bold }
1345 1349 .syntaxhl .delete .delete { color: #c00; background:transparent; font-weight:bold }
1346 1350 .syntaxhl .change .change { color: #88f }
1347 1351 .syntaxhl .head .head { color: #f4f }
1348 1352
1349 1353 /***** Media print specific styles *****/
1350 1354 @media print {
1351 1355 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
1352 1356 #main { background: #fff; }
1353 1357 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
1354 1358 #wiki_add_attachment { display:none; }
1355 1359 .hide-when-print { display: none; }
1356 1360 .autoscroll {overflow-x: visible;}
1357 1361 table.list {margin-top:0.5em;}
1358 1362 table.list th, table.list td {border: 1px solid #aaa;}
1359 1363 }
1360 1364
1361 1365 /* Accessibility specific styles */
1362 1366 .hidden-for-sighted {
1363 1367 position:absolute;
1364 1368 left:-10000px;
1365 1369 top:auto;
1366 1370 width:1px;
1367 1371 height:1px;
1368 1372 overflow:hidden;
1369 1373 }
@@ -1,221 +1,221
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class BoardsControllerTest < ActionController::TestCase
21 21 fixtures :projects, :users, :members, :member_roles, :roles, :boards, :messages, :enabled_modules
22 22
23 23 def setup
24 24 User.current = nil
25 25 end
26 26
27 27 def test_index
28 28 get :index, :project_id => 1
29 29 assert_response :success
30 30 assert_template 'index'
31 31 assert_not_nil assigns(:boards)
32 32 assert_not_nil assigns(:project)
33 33 end
34 34
35 35 def test_index_not_found
36 36 get :index, :project_id => 97
37 37 assert_response 404
38 38 end
39 39
40 40 def test_index_should_show_messages_if_only_one_board
41 41 Project.find(1).boards.slice(1..-1).each(&:destroy)
42 42
43 43 get :index, :project_id => 1
44 44 assert_response :success
45 45 assert_template 'show'
46 46 assert_not_nil assigns(:topics)
47 47 end
48 48
49 49 def test_show
50 50 get :show, :project_id => 1, :id => 1
51 51 assert_response :success
52 52 assert_template 'show'
53 53 assert_not_nil assigns(:board)
54 54 assert_not_nil assigns(:project)
55 55 assert_not_nil assigns(:topics)
56 56 end
57 57
58 58 def test_show_should_display_sticky_messages_first
59 59 Message.update_all(:sticky => 0)
60 60 Message.where({:id => 1}).update_all({:sticky => 1})
61 61
62 62 get :show, :project_id => 1, :id => 1
63 63 assert_response :success
64 64
65 65 topics = assigns(:topics)
66 66 assert_not_nil topics
67 67 assert topics.size > 1, "topics size was #{topics.size}"
68 68 assert topics.first.sticky?
69 69 assert topics.first.updated_on < topics.second.updated_on
70 70 end
71 71
72 72 def test_show_should_display_message_with_last_reply_first
73 73 Message.update_all(:sticky => 0)
74 74
75 75 # Reply to an old topic
76 76 old_topic = Message.where(:board_id => 1, :parent_id => nil).order('created_on ASC').first
77 77 reply = Message.new(:board_id => 1, :subject => 'New reply', :content => 'New reply', :author_id => 2)
78 78 old_topic.children << reply
79 79
80 80 get :show, :project_id => 1, :id => 1
81 81 assert_response :success
82 82 topics = assigns(:topics)
83 83 assert_not_nil topics
84 84 assert_equal old_topic, topics.first
85 85 end
86 86
87 87 def test_show_with_permission_should_display_the_new_message_form
88 88 @request.session[:user_id] = 2
89 89 get :show, :project_id => 1, :id => 1
90 90 assert_response :success
91 91 assert_template 'show'
92 92
93 93 assert_select 'form#message-form' do
94 94 assert_select 'input[name=?]', 'message[subject]'
95 95 end
96 96 end
97 97
98 98 def test_show_atom
99 99 get :show, :project_id => 1, :id => 1, :format => 'atom'
100 100 assert_response :success
101 101 assert_template 'common/feed'
102 102 assert_not_nil assigns(:board)
103 103 assert_not_nil assigns(:project)
104 104 assert_not_nil assigns(:messages)
105 105 end
106 106
107 107 def test_show_not_found
108 108 get :index, :project_id => 1, :id => 97
109 109 assert_response 404
110 110 end
111 111
112 112 def test_new
113 113 @request.session[:user_id] = 2
114 114 get :new, :project_id => 1
115 115 assert_response :success
116 116 assert_template 'new'
117 117
118 118 assert_select 'select[name=?]', 'board[parent_id]' do
119 119 assert_select 'option', (Project.find(1).boards.size + 1)
120 120 assert_select 'option[value=""]'
121 121 assert_select 'option[value="1"]', :text => 'Help'
122 122 end
123 123
124 124 # &nbsp; replaced by nokogiri, not easy to test in DOM assertions
125 125 assert_not_include '<option value=""></option>', response.body
126 126 assert_include '<option value="">&nbsp;</option>', response.body
127 127 end
128 128
129 129 def test_new_without_project_boards
130 130 Project.find(1).boards.delete_all
131 131 @request.session[:user_id] = 2
132 132
133 133 get :new, :project_id => 1
134 134 assert_response :success
135 135 assert_template 'new'
136 136
137 137 assert_select 'select[name=?]', 'board[parent_id]', 0
138 138 end
139 139
140 140 def test_create
141 141 @request.session[:user_id] = 2
142 142 assert_difference 'Board.count' do
143 143 post :create, :project_id => 1, :board => { :name => 'Testing', :description => 'Testing board creation'}
144 144 end
145 145 assert_redirected_to '/projects/ecookbook/settings/boards'
146 146 board = Board.order('id DESC').first
147 147 assert_equal 'Testing', board.name
148 148 assert_equal 'Testing board creation', board.description
149 149 end
150 150
151 151 def test_create_with_parent
152 152 @request.session[:user_id] = 2
153 153 assert_difference 'Board.count' do
154 154 post :create, :project_id => 1, :board => { :name => 'Testing', :description => 'Testing', :parent_id => 2}
155 155 end
156 156 assert_redirected_to '/projects/ecookbook/settings/boards'
157 157 board = Board.order('id DESC').first
158 158 assert_equal Board.find(2), board.parent
159 159 end
160 160
161 161 def test_create_with_failure
162 162 @request.session[:user_id] = 2
163 163 assert_no_difference 'Board.count' do
164 164 post :create, :project_id => 1, :board => { :name => '', :description => 'Testing board creation'}
165 165 end
166 166 assert_response :success
167 167 assert_template 'new'
168 168 end
169 169
170 170 def test_edit
171 171 @request.session[:user_id] = 2
172 172 get :edit, :project_id => 1, :id => 2
173 173 assert_response :success
174 174 assert_template 'edit'
175 175 end
176 176
177 177 def test_edit_with_parent
178 178 board = Board.generate!(:project_id => 1, :parent_id => 2)
179 179 @request.session[:user_id] = 2
180 180 get :edit, :project_id => 1, :id => board.id
181 181 assert_response :success
182 182 assert_template 'edit'
183 183
184 184 assert_select 'select[name=?]', 'board[parent_id]' do
185 185 assert_select 'option[value="2"][selected=selected]'
186 186 end
187 187 end
188 188
189 189 def test_update
190 190 @request.session[:user_id] = 2
191 191 assert_no_difference 'Board.count' do
192 192 put :update, :project_id => 1, :id => 2, :board => { :name => 'Testing', :description => 'Testing board update'}
193 193 end
194 194 assert_redirected_to '/projects/ecookbook/settings/boards'
195 195 assert_equal 'Testing', Board.find(2).name
196 196 end
197 197
198 198 def test_update_position
199 199 @request.session[:user_id] = 2
200 put :update, :project_id => 1, :id => 2, :board => { :move_to => 'highest'}
200 put :update, :project_id => 1, :id => 2, :board => { :position => 1}
201 201 assert_redirected_to '/projects/ecookbook/settings/boards'
202 202 board = Board.find(2)
203 203 assert_equal 1, board.position
204 204 end
205 205
206 206 def test_update_with_failure
207 207 @request.session[:user_id] = 2
208 208 put :update, :project_id => 1, :id => 2, :board => { :name => '', :description => 'Testing board update'}
209 209 assert_response :success
210 210 assert_template 'edit'
211 211 end
212 212
213 213 def test_destroy
214 214 @request.session[:user_id] = 2
215 215 assert_difference 'Board.count', -1 do
216 216 delete :destroy, :project_id => 1, :id => 2
217 217 end
218 218 assert_redirected_to '/projects/ecookbook/settings/boards'
219 219 assert_nil Board.find_by_id(2)
220 220 end
221 221 end
General Comments 0
You need to be logged in to leave comments. Login now