##// END OF EJS Templates
Added AJAX based context menu on the project issue list that provide shortcuts for editing, re-assigning, changing the status or the priority, moving or deleting an issue....
Jean-Philippe Lang -
r859:bb4acc02d06d
parent child
Show More
@@ -0,0 +1,38
1 <% back_to = url_for(:controller => 'projects', :action => 'list_issues', :id => @project) %>
2 <ul>
3 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
4 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
5 <li class="folder">
6 <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
7 <ul>
8 <% @statuses.each do |s| %>
9 <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'change_status', :id => @issue, :new_status_id => s},
10 :selected => (s == @issue.status), :disabled => !(@can[:change_status] && @allowed_statuses.include?(s)) %></li>
11 <% end %>
12 </ul>
13 </li>
14 <li class="folder">
15 <a href="#" class="submenu"><%= l(:field_priority) %></a>
16 <ul>
17 <% @priorities.each do |p| %>
18 <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[priority_id]' => p, :back_to => back_to}, :method => :post,
19 :selected => (p == @issue.priority), :disabled => !@can[:edit] %></li>
20 <% end %>
21 </ul>
22 </li>
23 <li class="folder">
24 <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
25 <ul>
26 <% @assignables.each do |u| %>
27 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => u, :back_to => back_to}, :method => :post,
28 :selected => (u == @issue.assigned_to), :disabled => !(@can[:edit] || @can[:change_status]) %></li>
29 <% end %>
30 <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => '', :back_to => back_to}, :method => :post,
31 :selected => @issue.assigned_to.nil?, :disabled => !(@can[:edit] || @can[:change_status]) %></li>
32 </ul>
33 </li>
34 <li><%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id },
35 :class => 'icon-move', :disabled => !@can[:move] %>
36 <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue},
37 :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon-del', :disabled => !@can[:delete] %></li>
38 </ul>
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,44
1 ContextMenu = Class.create();
2 ContextMenu.prototype = {
3 initialize: function (options) {
4 this.options = Object.extend({selector: '.hascontextmenu'}, options || { });
5
6 Event.observe(document, 'click', function(e){
7 var t = Event.findElement(e, 'a');
8 if ((t != document) && (Element.hasClassName(t, 'disabled') || Element.hasClassName(t, 'submenu'))) {
9 Event.stop(e);
10 } else {
11 $('context-menu').hide();
12 if (this.selection) {
13 this.selection.removeClassName('context-menu-selection');
14 }
15 }
16
17 }.bind(this));
18
19 $$(this.options.selector).invoke('observe', (window.opera ? 'click' : 'contextmenu'), function(e){
20 if (window.opera && !e.ctrlKey) {
21 return;
22 }
23 this.show(e);
24 }.bind(this));
25
26 },
27 show: function(e) {
28 Event.stop(e);
29 Element.hide('context-menu');
30 if (this.selection) {
31 this.selection.removeClassName('context-menu-selection');
32 }
33 $('context-menu').style['left'] = (Event.pointerX(e) + 'px');
34 $('context-menu').style['top'] = (Event.pointerY(e) + 'px');
35 Element.update('context-menu', '');
36
37 var tr = Event.findElement(e, 'tr');
38 tr.addClassName('context-menu-selection');
39 this.selection = tr;
40 var id = tr.id.substring(6, tr.id.length);
41 /* TODO: do not hard code path */
42 new Ajax.Updater('context-menu', '../../issues/context_menu/' + id, {asynchronous:true, evalScripts:true, onComplete:function(request){Effect.Appear('context-menu', {duration: 0.20})}})
43 }
44 }
@@ -0,0 +1,52
1 #context-menu { position: absolute; }
2
3 #context-menu ul, #context-menu li, #context-menu a {
4 display:block;
5 margin:0;
6 padding:0;
7 border:0;
8 }
9
10 #context-menu ul {
11 width:150px;
12 border-top:1px solid #ddd;
13 border-left:1px solid #ddd;
14 border-bottom:1px solid #777;
15 border-right:1px solid #777;
16 background:white;
17 list-style:none;
18 }
19
20 #context-menu li {
21 position:relative;
22 padding:1px;
23 z-index:9;
24 }
25 #context-menu li.folder ul {
26 position:absolute;
27 left:128px; /* IE */
28 top:-2px;
29 }
30 #context-menu li.folder>ul { left:148px; }
31
32 #context-menu a {
33 border:1px solid white;
34 text-decoration:none;
35 background-repeat: no-repeat;
36 background-position: 1px 50%;
37 padding: 2px 0px 2px 20px;
38 width:100%; /* IE */
39 }
40 #context-menu li>a { width:auto; } /* others */
41 #context-menu a.disabled, #context-menu a.disabled:hover {color: #ccc;}
42 #context-menu li a.submenu { background:url("../images/sub.gif") right no-repeat; }
43 #context-menu a:hover { border-color:gray; background-color:#eee; color:#2A5685; }
44 #context-menu li.folder a:hover { background-color:#eee; }
45 #context-menu li.folder:hover { z-index:10; }
46 #context-menu ul ul, #context-menu li:hover ul ul { display:none; }
47 #context-menu li:hover ul, #context-menu li:hover li:hover ul { display:block; }
48
49 /* selected element */
50 .context-menu-selection { background-color:#507AAA !important; color:#f8f8f8 !important; }
51 .context-menu-selection a, .context-menu-selection a:hover { color:#f8f8f8 !important; }
52 .context-menu-selection:hover { background-color:#507AAA !important; color:#f8f8f8 !important; }
@@ -1,201 +1,216
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssuesController < ApplicationController
18 class IssuesController < ApplicationController
19 layout 'base', :except => :export_pdf
19 layout 'base', :except => :export_pdf
20 before_filter :find_project, :authorize, :except => [:index, :preview]
20 before_filter :find_project, :authorize, :except => [:index, :preview]
21 accept_key_auth :index
21 accept_key_auth :index
22
22
23 cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ]
23 cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ]
24
24
25 helper :projects
25 helper :projects
26 include ProjectsHelper
26 include ProjectsHelper
27 helper :custom_fields
27 helper :custom_fields
28 include CustomFieldsHelper
28 include CustomFieldsHelper
29 helper :ifpdf
29 helper :ifpdf
30 include IfpdfHelper
30 include IfpdfHelper
31 helper :issue_relations
31 helper :issue_relations
32 include IssueRelationsHelper
32 include IssueRelationsHelper
33 helper :watchers
33 helper :watchers
34 include WatchersHelper
34 include WatchersHelper
35 helper :attachments
35 helper :attachments
36 include AttachmentsHelper
36 include AttachmentsHelper
37 helper :queries
37 helper :queries
38 helper :sort
38 helper :sort
39 include SortHelper
39 include SortHelper
40
40
41 def index
41 def index
42 sort_init "#{Issue.table_name}.id", "desc"
42 sort_init "#{Issue.table_name}.id", "desc"
43 sort_update
43 sort_update
44 retrieve_query
44 retrieve_query
45 if @query.valid?
45 if @query.valid?
46 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
46 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
47 @issue_pages = Paginator.new self, @issue_count, 25, params['page']
47 @issue_pages = Paginator.new self, @issue_count, 25, params['page']
48 @issues = Issue.find :all, :order => sort_clause,
48 @issues = Issue.find :all, :order => sort_clause,
49 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category ],
49 :include => [ :assigned_to, :status, :tracker, :project, :priority, :category ],
50 :conditions => @query.statement,
50 :conditions => @query.statement,
51 :limit => @issue_pages.items_per_page,
51 :limit => @issue_pages.items_per_page,
52 :offset => @issue_pages.current.offset
52 :offset => @issue_pages.current.offset
53 end
53 end
54 respond_to do |format|
54 respond_to do |format|
55 format.html { render :layout => false if request.xhr? }
55 format.html { render :layout => false if request.xhr? }
56 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
56 format.atom { render_feed(@issues, :title => l(:label_issue_plural)) }
57 end
57 end
58 end
58 end
59
59
60 def show
60 def show
61 @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
61 @custom_values = @issue.custom_values.find(:all, :include => :custom_field)
62 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
62 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
63
63
64 if params[:format]=='pdf'
64 if params[:format]=='pdf'
65 @options_for_rfpdf ||= {}
65 @options_for_rfpdf ||= {}
66 @options_for_rfpdf[:file_name] = "#{@project.identifier}-#{@issue.id}.pdf"
66 @options_for_rfpdf[:file_name] = "#{@project.identifier}-#{@issue.id}.pdf"
67 render :template => 'issues/show.rfpdf', :layout => false
67 render :template => 'issues/show.rfpdf', :layout => false
68 else
68 else
69 @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
69 @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
70 render :template => 'issues/show.rhtml'
70 render :template => 'issues/show.rhtml'
71 end
71 end
72 end
72 end
73
73
74 def edit
74 def edit
75 @priorities = Enumeration::get_values('IPRI')
75 @priorities = Enumeration::get_values('IPRI')
76 if request.get?
76 if request.get?
77 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
77 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) }
78 else
78 else
79 begin
79 begin
80 @issue.init_journal(self.logged_in_user)
80 @issue.init_journal(self.logged_in_user)
81 # Retrieve custom fields and values
81 # Retrieve custom fields and values
82 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
82 if params["custom_fields"]
83 @issue.custom_values = @custom_values
83 @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
84 @issue.custom_values = @custom_values
85 end
84 @issue.attributes = params[:issue]
86 @issue.attributes = params[:issue]
85 if @issue.save
87 if @issue.save
86 flash[:notice] = l(:notice_successful_update)
88 flash[:notice] = l(:notice_successful_update)
87 redirect_to :action => 'show', :id => @issue
89 redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
88 end
90 end
89 rescue ActiveRecord::StaleObjectError
91 rescue ActiveRecord::StaleObjectError
90 # Optimistic locking exception
92 # Optimistic locking exception
91 flash[:error] = l(:notice_locking_conflict)
93 flash[:error] = l(:notice_locking_conflict)
92 end
94 end
93 end
95 end
94 end
96 end
95
97
96 def add_note
98 def add_note
97 journal = @issue.init_journal(User.current, params[:notes])
99 journal = @issue.init_journal(User.current, params[:notes])
98 params[:attachments].each { |file|
100 params[:attachments].each { |file|
99 next unless file.size > 0
101 next unless file.size > 0
100 a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
102 a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
101 journal.details << JournalDetail.new(:property => 'attachment',
103 journal.details << JournalDetail.new(:property => 'attachment',
102 :prop_key => a.id,
104 :prop_key => a.id,
103 :value => a.filename) unless a.new_record?
105 :value => a.filename) unless a.new_record?
104 } if params[:attachments] and params[:attachments].is_a? Array
106 } if params[:attachments] and params[:attachments].is_a? Array
105 if journal.save
107 if journal.save
106 flash[:notice] = l(:notice_successful_update)
108 flash[:notice] = l(:notice_successful_update)
107 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
109 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
108 redirect_to :action => 'show', :id => @issue
110 redirect_to :action => 'show', :id => @issue
109 return
111 return
110 end
112 end
111 show
113 show
112 end
114 end
113
115
114 def change_status
116 def change_status
115 @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
117 @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
116 @new_status = IssueStatus.find(params[:new_status_id])
118 @new_status = IssueStatus.find(params[:new_status_id])
117 if params[:confirm]
119 if params[:confirm]
118 begin
120 begin
119 journal = @issue.init_journal(self.logged_in_user, params[:notes])
121 journal = @issue.init_journal(self.logged_in_user, params[:notes])
120 @issue.status = @new_status
122 @issue.status = @new_status
121 if @issue.update_attributes(params[:issue])
123 if @issue.update_attributes(params[:issue])
122 # Save attachments
124 # Save attachments
123 params[:attachments].each { |file|
125 params[:attachments].each { |file|
124 next unless file.size > 0
126 next unless file.size > 0
125 a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
127 a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user)
126 journal.details << JournalDetail.new(:property => 'attachment',
128 journal.details << JournalDetail.new(:property => 'attachment',
127 :prop_key => a.id,
129 :prop_key => a.id,
128 :value => a.filename) unless a.new_record?
130 :value => a.filename) unless a.new_record?
129 } if params[:attachments] and params[:attachments].is_a? Array
131 } if params[:attachments] and params[:attachments].is_a? Array
130
132
131 # Log time
133 # Log time
132 if current_role.allowed_to?(:log_time)
134 if current_role.allowed_to?(:log_time)
133 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => logged_in_user, :spent_on => Date.today)
135 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => logged_in_user, :spent_on => Date.today)
134 @time_entry.attributes = params[:time_entry]
136 @time_entry.attributes = params[:time_entry]
135 @time_entry.save
137 @time_entry.save
136 end
138 end
137
139
138 flash[:notice] = l(:notice_successful_update)
140 flash[:notice] = l(:notice_successful_update)
139 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
141 Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
140 redirect_to :action => 'show', :id => @issue
142 redirect_to :action => 'show', :id => @issue
141 end
143 end
142 rescue ActiveRecord::StaleObjectError
144 rescue ActiveRecord::StaleObjectError
143 # Optimistic locking exception
145 # Optimistic locking exception
144 flash[:error] = l(:notice_locking_conflict)
146 flash[:error] = l(:notice_locking_conflict)
145 end
147 end
146 end
148 end
147 @assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user }
149 @assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user }
148 @activities = Enumeration::get_values('ACTI')
150 @activities = Enumeration::get_values('ACTI')
149 end
151 end
150
152
151 def destroy
153 def destroy
152 @issue.destroy
154 @issue.destroy
153 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project
155 redirect_to :controller => 'projects', :action => 'list_issues', :id => @project
154 end
156 end
155
157
156 def destroy_attachment
158 def destroy_attachment
157 a = @issue.attachments.find(params[:attachment_id])
159 a = @issue.attachments.find(params[:attachment_id])
158 a.destroy
160 a.destroy
159 journal = @issue.init_journal(self.logged_in_user)
161 journal = @issue.init_journal(self.logged_in_user)
160 journal.details << JournalDetail.new(:property => 'attachment',
162 journal.details << JournalDetail.new(:property => 'attachment',
161 :prop_key => a.id,
163 :prop_key => a.id,
162 :old_value => a.filename)
164 :old_value => a.filename)
163 journal.save
165 journal.save
164 redirect_to :action => 'show', :id => @issue
166 redirect_to :action => 'show', :id => @issue
165 end
167 end
168
169 def context_menu
170 @priorities = Enumeration.get_values('IPRI').reverse
171 @statuses = IssueStatus.find(:all, :order => 'position')
172 @allowed_statuses = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)
173 @assignables = @issue.assignable_users
174 @assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
175 @can = {:edit => User.current.allowed_to?(:edit_issues, @project),
176 :change_status => User.current.allowed_to?(:change_issue_status, @project),
177 :move => User.current.allowed_to?(:move_issues, @project),
178 :delete => User.current.allowed_to?(:delete_issues, @project)}
179 render :layout => false
180 end
166
181
167 def preview
182 def preview
168 issue = Issue.find_by_id(params[:id])
183 issue = Issue.find_by_id(params[:id])
169 @attachements = issue.attachments if issue
184 @attachements = issue.attachments if issue
170 @text = params[:issue][:description]
185 @text = params[:issue][:description]
171 render :partial => 'common/preview'
186 render :partial => 'common/preview'
172 end
187 end
173
188
174 private
189 private
175 def find_project
190 def find_project
176 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
191 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
177 @project = @issue.project
192 @project = @issue.project
178 rescue ActiveRecord::RecordNotFound
193 rescue ActiveRecord::RecordNotFound
179 render_404
194 render_404
180 end
195 end
181
196
182 # Retrieve query from session or build a new query
197 # Retrieve query from session or build a new query
183 def retrieve_query
198 def retrieve_query
184 if params[:set_filter] or !session[:query] or session[:query].project_id
199 if params[:set_filter] or !session[:query] or session[:query].project_id
185 # Give it a name, required to be valid
200 # Give it a name, required to be valid
186 @query = Query.new(:name => "_", :executed_by => logged_in_user)
201 @query = Query.new(:name => "_", :executed_by => logged_in_user)
187 if params[:fields] and params[:fields].is_a? Array
202 if params[:fields] and params[:fields].is_a? Array
188 params[:fields].each do |field|
203 params[:fields].each do |field|
189 @query.add_filter(field, params[:operators][field], params[:values][field])
204 @query.add_filter(field, params[:operators][field], params[:values][field])
190 end
205 end
191 else
206 else
192 @query.available_filters.keys.each do |field|
207 @query.available_filters.keys.each do |field|
193 @query.add_short_filter(field, params[field]) if params[field]
208 @query.add_short_filter(field, params[field]) if params[field]
194 end
209 end
195 end
210 end
196 session[:query] = @query
211 session[:query] = @query
197 else
212 else
198 @query = session[:query]
213 @query = session[:query]
199 end
214 end
200 end
215 end
201 end
216 end
@@ -1,353 +1,369
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module ApplicationHelper
18 module ApplicationHelper
19
19
20 def current_role
20 def current_role
21 @current_role ||= User.current.role_for_project(@project)
21 @current_role ||= User.current.role_for_project(@project)
22 end
22 end
23
23
24 # Return true if user is authorized for controller/action, otherwise false
24 # Return true if user is authorized for controller/action, otherwise false
25 def authorize_for(controller, action)
25 def authorize_for(controller, action)
26 User.current.allowed_to?({:controller => controller, :action => action}, @project)
26 User.current.allowed_to?({:controller => controller, :action => action}, @project)
27 end
27 end
28
28
29 # Display a link if user is authorized
29 # Display a link if user is authorized
30 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
30 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
31 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
31 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
32 end
32 end
33
33
34 # Display a link to user's account page
34 # Display a link to user's account page
35 def link_to_user(user)
35 def link_to_user(user)
36 link_to user.name, :controller => 'account', :action => 'show', :id => user
36 link_to user.name, :controller => 'account', :action => 'show', :id => user
37 end
37 end
38
38
39 def link_to_issue(issue)
39 def link_to_issue(issue)
40 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
40 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
41 end
41 end
42
42
43 def toggle_link(name, id, options={})
43 def toggle_link(name, id, options={})
44 onclick = "Element.toggle('#{id}'); "
44 onclick = "Element.toggle('#{id}'); "
45 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
45 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
46 onclick << "return false;"
46 onclick << "return false;"
47 link_to(name, "#", :onclick => onclick)
47 link_to(name, "#", :onclick => onclick)
48 end
48 end
49
49
50 def show_and_goto_link(name, id, options={})
50 def show_and_goto_link(name, id, options={})
51 onclick = "Element.show('#{id}'); "
51 onclick = "Element.show('#{id}'); "
52 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
52 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
53 onclick << "location.href='##{id}-anchor'; "
53 onclick << "location.href='##{id}-anchor'; "
54 onclick << "return false;"
54 onclick << "return false;"
55 link_to(name, "#", options.merge(:onclick => onclick))
55 link_to(name, "#", options.merge(:onclick => onclick))
56 end
56 end
57
57
58 def image_to_function(name, function, html_options = {})
58 def image_to_function(name, function, html_options = {})
59 html_options.symbolize_keys!
59 html_options.symbolize_keys!
60 tag(:input, html_options.merge({
60 tag(:input, html_options.merge({
61 :type => "image", :src => image_path(name),
61 :type => "image", :src => image_path(name),
62 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
62 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
63 }))
63 }))
64 end
64 end
65
65
66 def prompt_to_remote(name, text, param, url, html_options = {})
66 def prompt_to_remote(name, text, param, url, html_options = {})
67 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
67 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
68 link_to name, {}, html_options
68 link_to name, {}, html_options
69 end
69 end
70
70
71 def format_date(date)
71 def format_date(date)
72 return nil unless date
72 return nil unless date
73 @date_format ||= (Setting.date_format.to_i == 0 ? l(:general_fmt_date) : "%Y-%m-%d")
73 @date_format ||= (Setting.date_format.to_i == 0 ? l(:general_fmt_date) : "%Y-%m-%d")
74 date.strftime(@date_format)
74 date.strftime(@date_format)
75 end
75 end
76
76
77 def format_time(time)
77 def format_time(time)
78 return nil unless time
78 return nil unless time
79 @date_format_setting ||= Setting.date_format.to_i
79 @date_format_setting ||= Setting.date_format.to_i
80 time = time.to_time if time.is_a?(String)
80 time = time.to_time if time.is_a?(String)
81 @date_format_setting == 0 ? l_datetime(time) : (time.strftime("%Y-%m-%d") + ' ' + l_time(time))
81 @date_format_setting == 0 ? l_datetime(time) : (time.strftime("%Y-%m-%d") + ' ' + l_time(time))
82 end
82 end
83
83
84 def authoring(created, author)
84 def authoring(created, author)
85 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
85 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
86 l(:label_added_time_by, author.name, time_tag)
86 l(:label_added_time_by, author.name, time_tag)
87 end
87 end
88
88
89 def day_name(day)
89 def day_name(day)
90 l(:general_day_names).split(',')[day-1]
90 l(:general_day_names).split(',')[day-1]
91 end
91 end
92
92
93 def month_name(month)
93 def month_name(month)
94 l(:actionview_datehelper_select_month_names).split(',')[month-1]
94 l(:actionview_datehelper_select_month_names).split(',')[month-1]
95 end
95 end
96
96
97 def pagination_links_full(paginator, options={}, html_options={})
97 def pagination_links_full(paginator, options={}, html_options={})
98 page_param = options.delete(:page_param) || :page
98 page_param = options.delete(:page_param) || :page
99
99
100 html = ''
100 html = ''
101 html << link_to_remote(('&#171; ' + l(:label_previous)),
101 html << link_to_remote(('&#171; ' + l(:label_previous)),
102 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
102 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
103 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
103 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
104
104
105 html << (pagination_links_each(paginator, options) do |n|
105 html << (pagination_links_each(paginator, options) do |n|
106 link_to_remote(n.to_s,
106 link_to_remote(n.to_s,
107 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
107 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
108 {:href => url_for(:params => options.merge(page_param => n))})
108 {:href => url_for(:params => options.merge(page_param => n))})
109 end || '')
109 end || '')
110
110
111 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
111 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
112 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
112 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
113 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
113 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
114 html
114 html
115 end
115 end
116
116
117 def set_html_title(text)
117 def set_html_title(text)
118 @html_header_title = text
118 @html_header_title = text
119 end
119 end
120
120
121 def html_title
121 def html_title
122 title = []
122 title = []
123 title << @project.name if @project
123 title << @project.name if @project
124 title << @html_header_title
124 title << @html_header_title
125 title << Setting.app_title
125 title << Setting.app_title
126 title.compact.join(' - ')
126 title.compact.join(' - ')
127 end
127 end
128
128
129 ACCESSKEYS = {:edit => 'e',
129 ACCESSKEYS = {:edit => 'e',
130 :preview => 'r',
130 :preview => 'r',
131 :quick_search => 'f',
131 :quick_search => 'f',
132 :search => '4',
132 :search => '4',
133 }.freeze
133 }.freeze
134
134
135 def accesskey(s)
135 def accesskey(s)
136 ACCESSKEYS[s]
136 ACCESSKEYS[s]
137 end
137 end
138
138
139 # format text according to system settings
139 # format text according to system settings
140 def textilizable(text, options = {})
140 def textilizable(text, options = {})
141 return "" if text.blank?
141 return "" if text.blank?
142
142
143 # when using an image link, try to use an attachment, if possible
143 # when using an image link, try to use an attachment, if possible
144 attachments = options[:attachments]
144 attachments = options[:attachments]
145 if attachments
145 if attachments
146 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
146 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
147 align = $1
147 align = $1
148 filename = $2
148 filename = $2
149 rf = Regexp.new(filename, Regexp::IGNORECASE)
149 rf = Regexp.new(filename, Regexp::IGNORECASE)
150 # search for the picture in attachments
150 # search for the picture in attachments
151 if found = attachments.detect { |att| att.filename =~ rf }
151 if found = attachments.detect { |att| att.filename =~ rf }
152 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
152 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
153 "!#{align}#{image_url}!"
153 "!#{align}#{image_url}!"
154 else
154 else
155 "!#{align}#{filename}!"
155 "!#{align}#{filename}!"
156 end
156 end
157 end
157 end
158 end
158 end
159
159
160 text = (Setting.text_formatting == 'textile') ?
160 text = (Setting.text_formatting == 'textile') ?
161 Redmine::WikiFormatting.to_html(text) : simple_format(auto_link(h(text)))
161 Redmine::WikiFormatting.to_html(text) : simple_format(auto_link(h(text)))
162
162
163 # different methods for formatting wiki links
163 # different methods for formatting wiki links
164 case options[:wiki_links]
164 case options[:wiki_links]
165 when :local
165 when :local
166 # used for local links to html files
166 # used for local links to html files
167 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
167 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
168 when :anchor
168 when :anchor
169 # used for single-file wiki export
169 # used for single-file wiki export
170 format_wiki_link = Proc.new {|project, title| "##{title}" }
170 format_wiki_link = Proc.new {|project, title| "##{title}" }
171 else
171 else
172 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
172 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
173 end
173 end
174
174
175 project = options[:project] || @project
175 project = options[:project] || @project
176
176
177 # turn wiki links into html links
177 # turn wiki links into html links
178 # example:
178 # example:
179 # [[mypage]]
179 # [[mypage]]
180 # [[mypage|mytext]]
180 # [[mypage|mytext]]
181 # wiki links can refer other project wikis, using project name or identifier:
181 # wiki links can refer other project wikis, using project name or identifier:
182 # [[project:]] -> wiki starting page
182 # [[project:]] -> wiki starting page
183 # [[project:|mytext]]
183 # [[project:|mytext]]
184 # [[project:mypage]]
184 # [[project:mypage]]
185 # [[project:mypage|mytext]]
185 # [[project:mypage|mytext]]
186 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
186 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
187 link_project = project
187 link_project = project
188 page = $1
188 page = $1
189 title = $3
189 title = $3
190 if page =~ /^([^\:]+)\:(.*)$/
190 if page =~ /^([^\:]+)\:(.*)$/
191 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
191 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
192 page = title || $2
192 page = title || $2
193 title = $1 if page.blank?
193 title = $1 if page.blank?
194 end
194 end
195
195
196 if link_project && link_project.wiki
196 if link_project && link_project.wiki
197 # check if page exists
197 # check if page exists
198 wiki_page = link_project.wiki.find_page(page)
198 wiki_page = link_project.wiki.find_page(page)
199 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
199 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
200 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
200 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
201 else
201 else
202 # project or wiki doesn't exist
202 # project or wiki doesn't exist
203 title || page
203 title || page
204 end
204 end
205 end
205 end
206
206
207 # turn issue and revision ids into links
207 # turn issue and revision ids into links
208 # example:
208 # example:
209 # #52 -> <a href="/issues/show/52">#52</a>
209 # #52 -> <a href="/issues/show/52">#52</a>
210 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6)
210 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6)
211 text = text.gsub(%r{([\s,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m|
211 text = text.gsub(%r{([\s,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m|
212 leading, otype, oid = $1, $2, $3
212 leading, otype, oid = $1, $2, $3
213 link = nil
213 link = nil
214 if otype == 'r'
214 if otype == 'r'
215 if project && (changeset = project.changesets.find_by_revision(oid))
215 if project && (changeset = project.changesets.find_by_revision(oid))
216 link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset',
216 link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset',
217 :title => truncate(changeset.comments, 100))
217 :title => truncate(changeset.comments, 100))
218 end
218 end
219 else
219 else
220 if issue = Issue.find_by_id(oid.to_i, :include => [:project, :status], :conditions => Project.visible_by(User.current))
220 if issue = Issue.find_by_id(oid.to_i, :include => [:project, :status], :conditions => Project.visible_by(User.current))
221 link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue',
221 link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue',
222 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
222 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
223 link = content_tag('del', link) if issue.closed?
223 link = content_tag('del', link) if issue.closed?
224 end
224 end
225 end
225 end
226 leading + (link || "#{otype}#{oid}")
226 leading + (link || "#{otype}#{oid}")
227 end
227 end
228
228
229 text
229 text
230 end
230 end
231
231
232 # Same as Rails' simple_format helper without using paragraphs
232 # Same as Rails' simple_format helper without using paragraphs
233 def simple_format_without_paragraph(text)
233 def simple_format_without_paragraph(text)
234 text.to_s.
234 text.to_s.
235 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
235 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
236 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
236 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
237 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
237 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
238 end
238 end
239
239
240 def error_messages_for(object_name, options = {})
240 def error_messages_for(object_name, options = {})
241 options = options.symbolize_keys
241 options = options.symbolize_keys
242 object = instance_variable_get("@#{object_name}")
242 object = instance_variable_get("@#{object_name}")
243 if object && !object.errors.empty?
243 if object && !object.errors.empty?
244 # build full_messages here with controller current language
244 # build full_messages here with controller current language
245 full_messages = []
245 full_messages = []
246 object.errors.each do |attr, msg|
246 object.errors.each do |attr, msg|
247 next if msg.nil?
247 next if msg.nil?
248 msg = msg.first if msg.is_a? Array
248 msg = msg.first if msg.is_a? Array
249 if attr == "base"
249 if attr == "base"
250 full_messages << l(msg)
250 full_messages << l(msg)
251 else
251 else
252 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
252 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
253 end
253 end
254 end
254 end
255 # retrieve custom values error messages
255 # retrieve custom values error messages
256 if object.errors[:custom_values]
256 if object.errors[:custom_values]
257 object.custom_values.each do |v|
257 object.custom_values.each do |v|
258 v.errors.each do |attr, msg|
258 v.errors.each do |attr, msg|
259 next if msg.nil?
259 next if msg.nil?
260 msg = msg.first if msg.is_a? Array
260 msg = msg.first if msg.is_a? Array
261 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
261 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
262 end
262 end
263 end
263 end
264 end
264 end
265 content_tag("div",
265 content_tag("div",
266 content_tag(
266 content_tag(
267 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
267 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
268 ) +
268 ) +
269 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
269 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
270 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
270 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
271 )
271 )
272 else
272 else
273 ""
273 ""
274 end
274 end
275 end
275 end
276
276
277 def lang_options_for_select(blank=true)
277 def lang_options_for_select(blank=true)
278 (blank ? [["(auto)", ""]] : []) +
278 (blank ? [["(auto)", ""]] : []) +
279 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
279 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
280 end
280 end
281
281
282 def label_tag_for(name, option_tags = nil, options = {})
282 def label_tag_for(name, option_tags = nil, options = {})
283 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
283 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
284 content_tag("label", label_text)
284 content_tag("label", label_text)
285 end
285 end
286
286
287 def labelled_tabular_form_for(name, object, options, &proc)
287 def labelled_tabular_form_for(name, object, options, &proc)
288 options[:html] ||= {}
288 options[:html] ||= {}
289 options[:html].store :class, "tabular"
289 options[:html].store :class, "tabular"
290 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
290 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
291 end
291 end
292
292
293 def check_all_links(form_name)
293 def check_all_links(form_name)
294 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
294 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
295 " | " +
295 " | " +
296 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
296 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
297 end
297 end
298
298
299 def context_menu_link(name, url, options={})
300 options[:class] ||= ''
301 if options.delete(:selected)
302 options[:class] << ' icon-checked disabled'
303 options[:disabled] = true
304 end
305 if options.delete(:disabled)
306 options.delete(:method)
307 options.delete(:confirm)
308 options.delete(:onclick)
309 options[:class] << ' disabled'
310 url = '#'
311 end
312 link_to name, url, options
313 end
314
299 def calendar_for(field_id)
315 def calendar_for(field_id)
300 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
316 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
301 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
317 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
302 end
318 end
303
319
304 def wikitoolbar_for(field_id)
320 def wikitoolbar_for(field_id)
305 return '' unless Setting.text_formatting == 'textile'
321 return '' unless Setting.text_formatting == 'textile'
306 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
322 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
307 end
323 end
308
324
309 def content_for(name, content = nil, &block)
325 def content_for(name, content = nil, &block)
310 @has_content ||= {}
326 @has_content ||= {}
311 @has_content[name] = true
327 @has_content[name] = true
312 super(name, content, &block)
328 super(name, content, &block)
313 end
329 end
314
330
315 def has_content?(name)
331 def has_content?(name)
316 (@has_content && @has_content[name]) || false
332 (@has_content && @has_content[name]) || false
317 end
333 end
318 end
334 end
319
335
320 class TabularFormBuilder < ActionView::Helpers::FormBuilder
336 class TabularFormBuilder < ActionView::Helpers::FormBuilder
321 include GLoc
337 include GLoc
322
338
323 def initialize(object_name, object, template, options, proc)
339 def initialize(object_name, object, template, options, proc)
324 set_language_if_valid options.delete(:lang)
340 set_language_if_valid options.delete(:lang)
325 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
341 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
326 end
342 end
327
343
328 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
344 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
329 src = <<-END_SRC
345 src = <<-END_SRC
330 def #{selector}(field, options = {})
346 def #{selector}(field, options = {})
331 return super if options.delete :no_label
347 return super if options.delete :no_label
332 label_text = l(options[:label]) if options[:label]
348 label_text = l(options[:label]) if options[:label]
333 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
349 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
334 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
350 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
335 label = @template.content_tag("label", label_text,
351 label = @template.content_tag("label", label_text,
336 :class => (@object && @object.errors[field] ? "error" : nil),
352 :class => (@object && @object.errors[field] ? "error" : nil),
337 :for => (@object_name.to_s + "_" + field.to_s))
353 :for => (@object_name.to_s + "_" + field.to_s))
338 label + super
354 label + super
339 end
355 end
340 END_SRC
356 END_SRC
341 class_eval src, __FILE__, __LINE__
357 class_eval src, __FILE__, __LINE__
342 end
358 end
343
359
344 def select(field, choices, options = {}, html_options = {})
360 def select(field, choices, options = {}, html_options = {})
345 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
361 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
346 label = @template.content_tag("label", label_text,
362 label = @template.content_tag("label", label_text,
347 :class => (@object && @object.errors[field] ? "error" : nil),
363 :class => (@object && @object.errors[field] ? "error" : nil),
348 :for => (@object_name.to_s + "_" + field.to_s))
364 :for => (@object_name.to_s + "_" + field.to_s))
349 label + super
365 label + super
350 end
366 end
351
367
352 end
368 end
353
369
@@ -1,25 +1,25
1 <div id="bulk-edit"></div>
1 <div id="bulk-edit"></div>
2 <table class="list">
2 <table class="list">
3 <thead><tr>
3 <thead><tr>
4 <th><%= link_to_remote(image_tag('edit.png'),
4 <th><%= link_to_remote(image_tag('edit.png'),
5 {:url => { :controller => 'projects', :action => 'bulk_edit_issues', :id => @project },
5 {:url => { :controller => 'projects', :action => 'bulk_edit_issues', :id => @project },
6 :method => :get},
6 :method => :get},
7 {:title => l(:label_bulk_edit_selected_issues)}) if @project && User.current.allowed_to?(:edit_issues, @project) %>
7 {:title => l(:label_bulk_edit_selected_issues)}) if @project && User.current.allowed_to?(:edit_issues, @project) %>
8 </th>
8 </th>
9 <%= sort_header_tag("#{Issue.table_name}.id", :caption => '#') %>
9 <%= sort_header_tag("#{Issue.table_name}.id", :caption => '#') %>
10 <% query.columns.each do |column| %>
10 <% query.columns.each do |column| %>
11 <%= column_header(column) %>
11 <%= column_header(column) %>
12 <% end %>
12 <% end %>
13 </tr></thead>
13 </tr></thead>
14 <tbody>
14 <tbody>
15 <% issues.each do |issue| %>
15 <% issues.each do |issue| %>
16 <tr class="issue <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>">
16 <tr id="issue-<%= issue.id %>" class="issue hascontextmenu <%= cycle('odd', 'even') %> <%= "status-#{issue.status.position} priority-#{issue.priority.position}" %>">
17 <td class="checkbox"><%= check_box_tag("issue_ids[]", issue.id, false, :id => "issue_#{issue.id}", :disabled => (!@project || @project != issue.project)) %></td>
17 <td class="checkbox"><%= check_box_tag("issue_ids[]", issue.id, false, :id => "issue_#{issue.id}", :disabled => (!@project || @project != issue.project)) %></td>
18 <td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
18 <td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
19 <% query.columns.each do |column| %>
19 <% query.columns.each do |column| %>
20 <%= content_tag 'td', column_content(column, issue), :class => column.name %>
20 <%= content_tag 'td', column_content(column, issue), :class => column.name %>
21 <% end %>
21 <% end %>
22 </tr>
22 </tr>
23 <% end %>
23 <% end %>
24 </tbody>
24 </tbody>
25 </table>
25 </table>
@@ -1,67 +1,72
1 <% if @query.new_record? %>
1 <% if @query.new_record? %>
2 <h2><%=l(:label_issue_plural)%></h2>
2 <h2><%=l(:label_issue_plural)%></h2>
3 <% set_html_title l(:label_issue_plural) %>
3 <% set_html_title l(:label_issue_plural) %>
4
4
5 <% form_tag({ :controller => 'queries', :action => 'new', :project_id => @project }, :id => 'query_form') do %>
5 <% form_tag({ :controller => 'queries', :action => 'new', :project_id => @project }, :id => 'query_form') do %>
6 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
6 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
7 <div class="contextual">
7 <div class="contextual">
8 <%= link_to_remote l(:button_apply),
8 <%= link_to_remote l(:button_apply),
9 { :url => { :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 },
9 { :url => { :controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1 },
10 :update => "content",
10 :update => "content",
11 :with => "Form.serialize('query_form')"
11 :with => "Form.serialize('query_form')"
12 }, :class => 'icon icon-edit' %>
12 }, :class => 'icon icon-edit' %>
13
13
14 <%= link_to_remote l(:button_clear),
14 <%= link_to_remote l(:button_clear),
15 { :url => {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1},
15 { :url => {:controller => 'projects', :action => 'list_issues', :id => @project, :set_filter => 1},
16 :update => "content",
16 :update => "content",
17 }, :class => 'icon icon-reload' %>
17 }, :class => 'icon icon-reload' %>
18
18
19 <% if current_role.allowed_to?(:save_queries) %>
19 <% if current_role.allowed_to?(:save_queries) %>
20 <%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
20 <%= link_to l(:button_save), {}, :onclick => "$('query_form').submit(); return false;", :class => 'icon icon-save' %>
21 <% end %>
21 <% end %>
22 </div>
22 </div>
23 <br />
23 <br />
24 &nbsp;
24 &nbsp;
25 <% end %>
25 <% end %>
26 <% else %>
26 <% else %>
27 <div class="contextual">
27 <div class="contextual">
28 <% if @query.editable_by?(User.current) %>
28 <% if @query.editable_by?(User.current) %>
29 <%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
29 <%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
30 <%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
30 <%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
31 <% end %>
31 <% end %>
32 </div>
32 </div>
33
33
34 <h2><%= @query.name %></h2>
34 <h2><%= @query.name %></h2>
35 <div id="query_form"></div>
35 <div id="query_form"></div>
36 <% set_html_title @query.name %>
36 <% set_html_title @query.name %>
37 <% end %>
37 <% end %>
38 <%= error_messages_for 'query' %>
38 <%= error_messages_for 'query' %>
39 <% if @query.valid? %>
39 <% if @query.valid? %>
40 <% if @issues.empty? %>
40 <% if @issues.empty? %>
41 <p class="nodata"><%= l(:label_no_data) %></p>
41 <p class="nodata"><%= l(:label_no_data) %></p>
42 <% else %>
42 <% else %>
43 <% form_tag({:controller => 'projects', :action => 'bulk_edit_issues', :id => @project}, :id => 'issues_form', :onsubmit => "if (!checkBulkEdit(this)) {alert('#{l(:notice_no_issue_selected)}'); return false;}" ) do %>
43 <% form_tag({:controller => 'projects', :action => 'bulk_edit_issues', :id => @project}, :id => 'issues_form', :onsubmit => "if (!checkBulkEdit(this)) {alert('#{l(:notice_no_issue_selected)}'); return false;}" ) do %>
44 <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
44 <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
45 <div class="contextual">
45 <div class="contextual">
46 <%= l(:label_export_to) %>
46 <%= l(:label_export_to) %>
47 <%= link_to 'CSV', {:action => 'export_issues_csv', :id => @project}, :class => 'icon icon-csv' %>,
47 <%= link_to 'CSV', {:action => 'export_issues_csv', :id => @project}, :class => 'icon icon-csv' %>,
48 <%= link_to 'PDF', {:action => 'export_issues_pdf', :id => @project}, :class => 'icon icon-pdf' %>
48 <%= link_to 'PDF', {:action => 'export_issues_pdf', :id => @project}, :class => 'icon icon-pdf' %>
49 </div>
49 </div>
50 <p>
50 <p>
51 <%= pagination_links_full @issue_pages %>
51 <%= pagination_links_full @issue_pages %>
52 [ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]
52 [ <%= @issue_pages.current.first_item %> - <%= @issue_pages.current.last_item %> / <%= @issue_count %> ]
53 </p>
53 </p>
54 <% end %>
54 <% end %>
55 <% end %>
55 <% end %>
56 <% end %>
56 <% end %>
57
57
58 <% content_for :sidebar do %>
58 <% content_for :sidebar do %>
59 <%= render :partial => 'issues/sidebar' %>
59 <%= render :partial => 'issues/sidebar' %>
60 <% end %>
60 <% end %>
61
61
62 <% content_for :header_tags do %>
62 <% content_for :header_tags do %>
63 <%= javascript_include_tag 'calendar/calendar' %>
63 <%= javascript_include_tag 'calendar/calendar' %>
64 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
64 <%= javascript_include_tag "calendar/lang/calendar-#{current_language}.js" %>
65 <%= javascript_include_tag 'calendar/calendar-setup' %>
65 <%= javascript_include_tag 'calendar/calendar-setup' %>
66 <%= stylesheet_link_tag 'calendar' %>
66 <%= stylesheet_link_tag 'calendar' %>
67 <%= javascript_include_tag 'context_menu' %>
68 <%= stylesheet_link_tag 'context_menu' %>
67 <% end %>
69 <% end %>
70
71 <div id="context-menu" style="display: none;"></div>
72 <%= javascript_tag 'new ContextMenu({})' %>
@@ -1,103 +1,103
1 require 'redmine/access_control'
1 require 'redmine/access_control'
2 require 'redmine/menu_manager'
2 require 'redmine/menu_manager'
3 require 'redmine/mime_type'
3 require 'redmine/mime_type'
4 require 'redmine/themes'
4 require 'redmine/themes'
5 require 'redmine/plugin'
5 require 'redmine/plugin'
6
6
7 begin
7 begin
8 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
8 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
9 rescue LoadError
9 rescue LoadError
10 # RMagick is not available
10 # RMagick is not available
11 end
11 end
12
12
13 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs )
13 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs )
14
14
15 # Permissions
15 # Permissions
16 Redmine::AccessControl.map do |map|
16 Redmine::AccessControl.map do |map|
17 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
17 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
18 map.permission :search_project, {:search => :index}, :public => true
18 map.permission :search_project, {:search => :index}, :public => true
19 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
19 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
20 map.permission :select_project_modules, {:projects => :modules}, :require => :member
20 map.permission :select_project_modules, {:projects => :modules}, :require => :member
21 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
21 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
22 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
22 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
23
23
24 map.project_module :issue_tracking do |map|
24 map.project_module :issue_tracking do |map|
25 # Issue categories
25 # Issue categories
26 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
26 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
27 # Issues
27 # Issues
28 map.permission :view_issues, {:projects => [:list_issues, :export_issues_csv, :export_issues_pdf, :changelog, :roadmap],
28 map.permission :view_issues, {:projects => [:list_issues, :export_issues_csv, :export_issues_pdf, :changelog, :roadmap],
29 :issues => :show,
29 :issues => [:show, :context_menu],
30 :queries => :index,
30 :queries => :index,
31 :reports => :issue_report}, :public => true
31 :reports => :issue_report}, :public => true
32 map.permission :add_issues, {:projects => :add_issue}, :require => :loggedin
32 map.permission :add_issues, {:projects => :add_issue}, :require => :loggedin
33 map.permission :edit_issues, {:projects => :bulk_edit_issues,
33 map.permission :edit_issues, {:projects => :bulk_edit_issues,
34 :issues => [:edit, :destroy_attachment]}, :require => :loggedin
34 :issues => [:edit, :destroy_attachment]}, :require => :loggedin
35 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}, :require => :loggedin
35 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}, :require => :loggedin
36 map.permission :add_issue_notes, {:issues => :add_note}, :require => :loggedin
36 map.permission :add_issue_notes, {:issues => :add_note}, :require => :loggedin
37 map.permission :change_issue_status, {:issues => :change_status}, :require => :loggedin
37 map.permission :change_issue_status, {:issues => :change_status}, :require => :loggedin
38 map.permission :move_issues, {:projects => :move_issues}, :require => :loggedin
38 map.permission :move_issues, {:projects => :move_issues}, :require => :loggedin
39 map.permission :delete_issues, {:issues => :destroy}, :require => :member
39 map.permission :delete_issues, {:issues => :destroy}, :require => :member
40 # Queries
40 # Queries
41 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
41 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
42 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
42 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
43 # Gantt & calendar
43 # Gantt & calendar
44 map.permission :view_gantt, :projects => :gantt
44 map.permission :view_gantt, :projects => :gantt
45 map.permission :view_calendar, :projects => :calendar
45 map.permission :view_calendar, :projects => :calendar
46 end
46 end
47
47
48 map.project_module :time_tracking do |map|
48 map.project_module :time_tracking do |map|
49 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
49 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
50 map.permission :view_time_entries, :timelog => [:details, :report]
50 map.permission :view_time_entries, :timelog => [:details, :report]
51 end
51 end
52
52
53 map.project_module :news do |map|
53 map.project_module :news do |map|
54 map.permission :manage_news, {:projects => :add_news, :news => [:edit, :destroy, :destroy_comment]}, :require => :member
54 map.permission :manage_news, {:projects => :add_news, :news => [:edit, :destroy, :destroy_comment]}, :require => :member
55 map.permission :view_news, {:projects => :list_news, :news => :show}, :public => true
55 map.permission :view_news, {:projects => :list_news, :news => :show}, :public => true
56 map.permission :comment_news, {:news => :add_comment}, :require => :loggedin
56 map.permission :comment_news, {:news => :add_comment}, :require => :loggedin
57 end
57 end
58
58
59 map.project_module :documents do |map|
59 map.project_module :documents do |map|
60 map.permission :manage_documents, {:projects => :add_document, :documents => [:edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
60 map.permission :manage_documents, {:projects => :add_document, :documents => [:edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
61 map.permission :view_documents, :projects => :list_documents, :documents => [:show, :download]
61 map.permission :view_documents, :projects => :list_documents, :documents => [:show, :download]
62 end
62 end
63
63
64 map.project_module :files do |map|
64 map.project_module :files do |map|
65 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
65 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
66 map.permission :view_files, :projects => :list_files, :versions => :download
66 map.permission :view_files, :projects => :list_files, :versions => :download
67 end
67 end
68
68
69 map.project_module :wiki do |map|
69 map.project_module :wiki do |map|
70 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
70 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
71 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
71 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
72 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
72 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
73 map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :special]
73 map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :special]
74 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
74 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
75 end
75 end
76
76
77 map.project_module :repository do |map|
77 map.project_module :repository do |map|
78 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
78 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
79 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :changes, :diff, :stats, :graph]
79 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :changes, :diff, :stats, :graph]
80 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
80 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
81 end
81 end
82
82
83 map.project_module :boards do |map|
83 map.project_module :boards do |map|
84 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
84 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
85 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
85 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
86 map.permission :add_messages, {:messages => [:new, :reply]}, :require => :loggedin
86 map.permission :add_messages, {:messages => [:new, :reply]}, :require => :loggedin
87 end
87 end
88 end
88 end
89
89
90 # Project menu configuration
90 # Project menu configuration
91 Redmine::MenuManager.map :project_menu do |menu|
91 Redmine::MenuManager.map :project_menu do |menu|
92 menu.push :label_overview, :controller => 'projects', :action => 'show'
92 menu.push :label_overview, :controller => 'projects', :action => 'show'
93 menu.push :label_activity, :controller => 'projects', :action => 'activity'
93 menu.push :label_activity, :controller => 'projects', :action => 'activity'
94 menu.push :label_roadmap, :controller => 'projects', :action => 'roadmap'
94 menu.push :label_roadmap, :controller => 'projects', :action => 'roadmap'
95 menu.push :label_issue_plural, :controller => 'projects', :action => 'list_issues'
95 menu.push :label_issue_plural, :controller => 'projects', :action => 'list_issues'
96 menu.push :label_news_plural, :controller => 'projects', :action => 'list_news'
96 menu.push :label_news_plural, :controller => 'projects', :action => 'list_news'
97 menu.push :label_document_plural, :controller => 'projects', :action => 'list_documents'
97 menu.push :label_document_plural, :controller => 'projects', :action => 'list_documents'
98 menu.push :label_wiki, { :controller => 'wiki', :action => 'index', :page => nil }, :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
98 menu.push :label_wiki, { :controller => 'wiki', :action => 'index', :page => nil }, :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
99 menu.push :label_board_plural, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id, :if => Proc.new { |p| p.boards.any? }
99 menu.push :label_board_plural, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id, :if => Proc.new { |p| p.boards.any? }
100 menu.push :label_attachment_plural, :controller => 'projects', :action => 'list_files'
100 menu.push :label_attachment_plural, :controller => 'projects', :action => 'list_files'
101 menu.push :label_repository, { :controller => 'repositories', :action => 'show' }, :if => Proc.new { |p| p.repository && !p.repository.new_record? }
101 menu.push :label_repository, { :controller => 'repositories', :action => 'show' }, :if => Proc.new { |p| p.repository && !p.repository.new_record? }
102 menu.push :label_settings, :controller => 'projects', :action => 'settings'
102 menu.push :label_settings, :controller => 'projects', :action => 'settings'
103 end
103 end
@@ -1,477 +1,478
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
2
2
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
4 h1 {margin:0; padding:0; font-size: 24px;}
4 h1 {margin:0; padding:0; font-size: 24px;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 12px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 12px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
8
8
9 /***** Layout *****/
9 /***** Layout *****/
10 #top-menu {background: #2C4056;color: #fff;height:1.5em; padding: 2px 6px 0px 6px;}
10 #top-menu {background: #2C4056;color: #fff;height:1.5em; padding: 2px 6px 0px 6px;}
11 #top-menu a {color: #fff; padding-right: 4px;}
11 #top-menu a {color: #fff; padding-right: 4px;}
12 #account {float:right;}
12 #account {float:right;}
13
13
14 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
14 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
15 #header a {color:#f8f8f8;}
15 #header a {color:#f8f8f8;}
16 #quick-search {float:right;}
16 #quick-search {float:right;}
17
17
18 #main-menu {position: absolute; bottom: 0px; left:6px;}
18 #main-menu {position: absolute; bottom: 0px; left:6px;}
19 #main-menu ul {margin: 0; padding: 0;}
19 #main-menu ul {margin: 0; padding: 0;}
20 #main-menu li {
20 #main-menu li {
21 float:left;
21 float:left;
22 list-style-type:none;
22 list-style-type:none;
23 margin: 0px 10px 0px 0px;
23 margin: 0px 10px 0px 0px;
24 padding: 0px 0px 0px 0px;
24 padding: 0px 0px 0px 0px;
25 white-space:nowrap;
25 white-space:nowrap;
26 }
26 }
27 #main-menu li a {
27 #main-menu li a {
28 display: block;
28 display: block;
29 color: #fff;
29 color: #fff;
30 text-decoration: none;
30 text-decoration: none;
31 margin: 0;
31 margin: 0;
32 padding: 4px 4px 4px 4px;
32 padding: 4px 4px 4px 4px;
33 background: #2C4056;
33 background: #2C4056;
34 }
34 }
35 #main-menu li a:hover {background:#759FCF;}
35 #main-menu li a:hover {background:#759FCF;}
36
36
37 #main {background: url(../images/mainbg.png) repeat-x; background-color:#EEEEEE;}
37 #main {background: url(../images/mainbg.png) repeat-x; background-color:#EEEEEE;}
38
38
39 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
39 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
40 * html #sidebar{ width: 17%; }
40 * html #sidebar{ width: 17%; }
41 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
41 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
42 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
42 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
43 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
43 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
44
44
45 #content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; position: relative; z-index: 10; height:600px; min-height: 600px;}
45 #content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;}
46 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
46 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
47 html>body #content {
47 html>body #content {
48 height: auto;
48 height: auto;
49 min-height: 600px;
49 min-height: 600px;
50 }
50 }
51
51
52 #main.nosidebar #sidebar{ display: none; }
52 #main.nosidebar #sidebar{ display: none; }
53 #main.nosidebar #content{ width: auto; border-right: 0; }
53 #main.nosidebar #content{ width: auto; border-right: 0; }
54
54
55 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
55 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
56
56
57 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
57 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
58 #login-form table td {padding: 6px;}
58 #login-form table td {padding: 6px;}
59 #login-form label {font-weight: bold;}
59 #login-form label {font-weight: bold;}
60
60
61 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
61 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
62
62
63 /***** Links *****/
63 /***** Links *****/
64 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
64 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
65 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
65 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
66 a img{ border: 0; }
66 a img{ border: 0; }
67
67
68 /***** Tables *****/
68 /***** Tables *****/
69 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
69 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
70 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
70 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
71 table.list td { overflow: hidden; text-overflow: ellipsis; vertical-align: top;}
71 table.list td { overflow: hidden; text-overflow: ellipsis; vertical-align: top;}
72 table.list td.id { width: 2%; text-align: center;}
72 table.list td.id { width: 2%; text-align: center;}
73 table.list td.checkbox { width: 15px; padding: 0px;}
73 table.list td.checkbox { width: 15px; padding: 0px;}
74
74
75 tr.issue { text-align: center; white-space: nowrap; }
75 tr.issue { text-align: center; white-space: nowrap; }
76 tr.issue td.subject, tr.issue td.category { white-space: normal; }
76 tr.issue td.subject, tr.issue td.category { white-space: normal; }
77 tr.issue td.subject { text-align: left; }
77 tr.issue td.subject { text-align: left; }
78
78
79 table.list tbody tr:hover { background-color:#ffffdd; }
79 table.list tbody tr:hover { background-color:#ffffdd; }
80 table td {padding:2px;}
80 table td {padding:2px;}
81 table p {margin:0;}
81 table p {margin:0;}
82 .odd {background-color:#f6f7f8;}
82 .odd {background-color:#f6f7f8;}
83 .even {background-color: #fff;}
83 .even {background-color: #fff;}
84
84
85 .highlight { background-color: #FCFD8D;}
85 .highlight { background-color: #FCFD8D;}
86 .highlight.token-1 { background-color: #faa;}
86 .highlight.token-1 { background-color: #faa;}
87 .highlight.token-2 { background-color: #afa;}
87 .highlight.token-2 { background-color: #afa;}
88 .highlight.token-3 { background-color: #aaf;}
88 .highlight.token-3 { background-color: #aaf;}
89
89
90 .box{
90 .box{
91 padding:6px;
91 padding:6px;
92 margin-bottom: 10px;
92 margin-bottom: 10px;
93 background-color:#f6f6f6;
93 background-color:#f6f6f6;
94 color:#505050;
94 color:#505050;
95 line-height:1.5em;
95 line-height:1.5em;
96 border: 1px solid #e4e4e4;
96 border: 1px solid #e4e4e4;
97 }
97 }
98
98
99 div.square {
99 div.square {
100 border: 1px solid #999;
100 border: 1px solid #999;
101 float: left;
101 float: left;
102 margin: .3em .4em 0 .4em;
102 margin: .3em .4em 0 .4em;
103 overflow: hidden;
103 overflow: hidden;
104 width: .6em; height: .6em;
104 width: .6em; height: .6em;
105 }
105 }
106
106
107 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px;font-size:0.9em;}
107 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px;font-size:0.9em;}
108 .splitcontentleft{float:left; width:49%;}
108 .splitcontentleft{float:left; width:49%;}
109 .splitcontentright{float:right; width:49%;}
109 .splitcontentright{float:right; width:49%;}
110 form {display: inline;}
110 form {display: inline;}
111 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
111 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
112 fieldset {border: 1px solid #e4e4e4; margin:0;}
112 fieldset {border: 1px solid #e4e4e4; margin:0;}
113 legend {color: #484848;}
113 legend {color: #484848;}
114 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
114 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
115 textarea.wiki-edit { width: 99%; }
115 textarea.wiki-edit { width: 99%; }
116 li p {margin-top: 0;}
116 li p {margin-top: 0;}
117 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
117 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
118 .autoscroll {overflow-x: auto; padding:1px; width:100%;}
118 .autoscroll {overflow-x: auto; padding:1px; width:100%;}
119 #user_firstname, #user_lastname, #user_mail, #notification_option { width: 90%; }
119 #user_firstname, #user_lastname, #user_mail, #notification_option { width: 90%; }
120
120
121 /***** Tabular forms ******/
121 /***** Tabular forms ******/
122 .tabular p{
122 .tabular p{
123 margin: 0;
123 margin: 0;
124 padding: 5px 0 8px 0;
124 padding: 5px 0 8px 0;
125 padding-left: 180px; /*width of left column containing the label elements*/
125 padding-left: 180px; /*width of left column containing the label elements*/
126 height: 1%;
126 height: 1%;
127 clear:left;
127 clear:left;
128 }
128 }
129
129
130 .tabular label{
130 .tabular label{
131 font-weight: bold;
131 font-weight: bold;
132 float: left;
132 float: left;
133 text-align: right;
133 text-align: right;
134 margin-left: -180px; /*width of left column*/
134 margin-left: -180px; /*width of left column*/
135 width: 175px; /*width of labels. Should be smaller than left column to create some right
135 width: 175px; /*width of labels. Should be smaller than left column to create some right
136 margin*/
136 margin*/
137 }
137 }
138
138
139 .tabular label.floating{
139 .tabular label.floating{
140 font-weight: normal;
140 font-weight: normal;
141 margin-left: 0px;
141 margin-left: 0px;
142 text-align: left;
142 text-align: left;
143 width: 200px;
143 width: 200px;
144 }
144 }
145
145
146 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
146 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
147
147
148 #settings .tabular p{ padding-left: 300px; }
148 #settings .tabular p{ padding-left: 300px; }
149 #settings .tabular label{ margin-left: -300px; width: 295px; }
149 #settings .tabular label{ margin-left: -300px; width: 295px; }
150
150
151 .required {color: #bb0000;}
151 .required {color: #bb0000;}
152 .summary {font-style: italic;}
152 .summary {font-style: italic;}
153
153
154 div.attachments p { margin:4px 0 2px 0; }
154 div.attachments p { margin:4px 0 2px 0; }
155
155
156 /***** Flash & error messages ****/
156 /***** Flash & error messages ****/
157 #errorExplanation, div.flash, div.nodata {
157 #errorExplanation, div.flash, .nodata {
158 padding: 4px 4px 4px 30px;
158 padding: 4px 4px 4px 30px;
159 margin-bottom: 12px;
159 margin-bottom: 12px;
160 font-size: 1.1em;
160 font-size: 1.1em;
161 border: 2px solid;
161 border: 2px solid;
162 }
162 }
163
163
164 div.flash {margin-top: 8px;}
164 div.flash {margin-top: 8px;}
165
165
166 div.flash.error, #errorExplanation {
166 div.flash.error, #errorExplanation {
167 background: url(../images/false.png) 8px 5px no-repeat;
167 background: url(../images/false.png) 8px 5px no-repeat;
168 background-color: #ffe3e3;
168 background-color: #ffe3e3;
169 border-color: #dd0000;
169 border-color: #dd0000;
170 color: #550000;
170 color: #550000;
171 }
171 }
172
172
173 div.flash.notice {
173 div.flash.notice {
174 background: url(../images/true.png) 8px 5px no-repeat;
174 background: url(../images/true.png) 8px 5px no-repeat;
175 background-color: #dfffdf;
175 background-color: #dfffdf;
176 border-color: #9fcf9f;
176 border-color: #9fcf9f;
177 color: #005f00;
177 color: #005f00;
178 }
178 }
179
179
180 .nodata {
180 .nodata {
181 text-align: center;
181 text-align: center;
182 background-color: #FFEBC1;
182 background-color: #FFEBC1;
183 border-color: #FDBF3B;
183 border-color: #FDBF3B;
184 color: #A6750C;
184 color: #A6750C;
185 }
185 }
186
186
187 #errorExplanation ul { font-size: 0.9em;}
187 #errorExplanation ul { font-size: 0.9em;}
188
188
189 /***** Ajax indicator ******/
189 /***** Ajax indicator ******/
190 #ajax-indicator {
190 #ajax-indicator {
191 position: absolute; /* fixed not supported by IE */
191 position: absolute; /* fixed not supported by IE */
192 background-color:#eee;
192 background-color:#eee;
193 border: 1px solid #bbb;
193 border: 1px solid #bbb;
194 top:35%;
194 top:35%;
195 left:40%;
195 left:40%;
196 width:20%;
196 width:20%;
197 font-weight:bold;
197 font-weight:bold;
198 text-align:center;
198 text-align:center;
199 padding:0.6em;
199 padding:0.6em;
200 z-index:100;
200 z-index:100;
201 filter:alpha(opacity=50);
201 filter:alpha(opacity=50);
202 -moz-opacity:0.5;
202 -moz-opacity:0.5;
203 opacity: 0.5;
203 opacity: 0.5;
204 -khtml-opacity: 0.5;
204 -khtml-opacity: 0.5;
205 }
205 }
206
206
207 html>body #ajax-indicator { position: fixed; }
207 html>body #ajax-indicator { position: fixed; }
208
208
209 #ajax-indicator span {
209 #ajax-indicator span {
210 background-position: 0% 40%;
210 background-position: 0% 40%;
211 background-repeat: no-repeat;
211 background-repeat: no-repeat;
212 background-image: url(../images/loading.gif);
212 background-image: url(../images/loading.gif);
213 padding-left: 26px;
213 padding-left: 26px;
214 vertical-align: bottom;
214 vertical-align: bottom;
215 }
215 }
216
216
217 /***** Calendar *****/
217 /***** Calendar *****/
218 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
218 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
219 table.cal thead th {width: 14%;}
219 table.cal thead th {width: 14%;}
220 table.cal tbody tr {height: 100px;}
220 table.cal tbody tr {height: 100px;}
221 table.cal th { background-color:#EEEEEE; padding: 4px; }
221 table.cal th { background-color:#EEEEEE; padding: 4px; }
222 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
222 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
223 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
223 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
224 table.cal td.odd p.day-num {color: #bbb;}
224 table.cal td.odd p.day-num {color: #bbb;}
225 table.cal td.today {background:#ffffdd;}
225 table.cal td.today {background:#ffffdd;}
226 table.cal td.today p.day-num {font-weight: bold;}
226 table.cal td.today p.day-num {font-weight: bold;}
227
227
228 /***** Tooltips ******/
228 /***** Tooltips ******/
229 .tooltip{position:relative;z-index:24;}
229 .tooltip{position:relative;z-index:24;}
230 .tooltip:hover{z-index:25;color:#000;}
230 .tooltip:hover{z-index:25;color:#000;}
231 .tooltip span.tip{display: none; text-align:left;}
231 .tooltip span.tip{display: none; text-align:left;}
232
232
233 div.tooltip:hover span.tip{
233 div.tooltip:hover span.tip{
234 display:block;
234 display:block;
235 position:absolute;
235 position:absolute;
236 top:12px; left:24px; width:270px;
236 top:12px; left:24px; width:270px;
237 border:1px solid #555;
237 border:1px solid #555;
238 background-color:#fff;
238 background-color:#fff;
239 padding: 4px;
239 padding: 4px;
240 font-size: 0.8em;
240 font-size: 0.8em;
241 color:#505050;
241 color:#505050;
242 }
242 }
243
243
244 /***** Progress bar *****/
244 /***** Progress bar *****/
245 .progress {
245 .progress {
246 border: 1px solid #D7D7D7;
246 border: 1px solid #D7D7D7;
247 border-collapse: collapse;
247 border-collapse: collapse;
248 border-spacing: 0pt;
248 border-spacing: 0pt;
249 empty-cells: show;
249 empty-cells: show;
250 padding: 3px;
250 padding: 3px;
251 width: 40em;
251 width: 40em;
252 text-align: center;
252 text-align: center;
253 }
253 }
254
254
255 .progress td { height: 1em; }
255 .progress td { height: 1em; }
256 .progress .closed { background: #BAE0BA none repeat scroll 0%; }
256 .progress .closed { background: #BAE0BA none repeat scroll 0%; }
257 .progress .open { background: #FFF none repeat scroll 0%; }
257 .progress .open { background: #FFF none repeat scroll 0%; }
258
258
259 /***** Tabs *****/
259 /***** Tabs *****/
260 #content .tabs{height: 2.6em;}
260 #content .tabs{height: 2.6em;}
261 #content .tabs ul{margin:0;}
261 #content .tabs ul{margin:0;}
262 #content .tabs ul li{
262 #content .tabs ul li{
263 float:left;
263 float:left;
264 list-style-type:none;
264 list-style-type:none;
265 white-space:nowrap;
265 white-space:nowrap;
266 margin-right:8px;
266 margin-right:8px;
267 background:#fff;
267 background:#fff;
268 }
268 }
269 #content .tabs ul li a{
269 #content .tabs ul li a{
270 display:block;
270 display:block;
271 font-size: 0.9em;
271 font-size: 0.9em;
272 text-decoration:none;
272 text-decoration:none;
273 line-height:1em;
273 line-height:1em;
274 padding:4px;
274 padding:4px;
275 border: 1px solid #c0c0c0;
275 border: 1px solid #c0c0c0;
276 }
276 }
277
277
278 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
278 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
279 background-color: #507AAA;
279 background-color: #507AAA;
280 border: 1px solid #507AAA;
280 border: 1px solid #507AAA;
281 color: #fff;
281 color: #fff;
282 text-decoration:none;
282 text-decoration:none;
283 }
283 }
284
284
285 /***** Diff *****/
285 /***** Diff *****/
286 .diff_out { background: #fcc; }
286 .diff_out { background: #fcc; }
287 .diff_in { background: #cfc; }
287 .diff_in { background: #cfc; }
288
288
289 /***** Wiki *****/
289 /***** Wiki *****/
290 div.wiki table {
290 div.wiki table {
291 border: 1px solid #505050;
291 border: 1px solid #505050;
292 border-collapse: collapse;
292 border-collapse: collapse;
293 }
293 }
294
294
295 div.wiki table, div.wiki td, div.wiki th {
295 div.wiki table, div.wiki td, div.wiki th {
296 border: 1px solid #bbb;
296 border: 1px solid #bbb;
297 padding: 4px;
297 padding: 4px;
298 }
298 }
299
299
300 div.wiki .external {
300 div.wiki .external {
301 background-position: 0% 60%;
301 background-position: 0% 60%;
302 background-repeat: no-repeat;
302 background-repeat: no-repeat;
303 padding-left: 12px;
303 padding-left: 12px;
304 background-image: url(../images/external.png);
304 background-image: url(../images/external.png);
305 }
305 }
306
306
307 div.wiki a.new {
307 div.wiki a.new {
308 color: #b73535;
308 color: #b73535;
309 }
309 }
310
310
311 div.wiki pre {
311 div.wiki pre {
312 margin: 1em 1em 1em 1.6em;
312 margin: 1em 1em 1em 1.6em;
313 padding: 2px;
313 padding: 2px;
314 background-color: #fafafa;
314 background-color: #fafafa;
315 border: 1px solid #dadada;
315 border: 1px solid #dadada;
316 width:95%;
316 width:95%;
317 overflow-x: auto;
317 overflow-x: auto;
318 }
318 }
319
319
320 div.wiki div.toc {
320 div.wiki div.toc {
321 background-color: #ffffdd;
321 background-color: #ffffdd;
322 border: 1px solid #e4e4e4;
322 border: 1px solid #e4e4e4;
323 padding: 4px;
323 padding: 4px;
324 line-height: 1.2em;
324 line-height: 1.2em;
325 margin-bottom: 12px;
325 margin-bottom: 12px;
326 margin-right: 12px;
326 margin-right: 12px;
327 display: table
327 display: table
328 }
328 }
329 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
329 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
330
330
331 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
331 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
332 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
332 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
333
333
334 div.wiki div.toc a {
334 div.wiki div.toc a {
335 display: block;
335 display: block;
336 font-size: 0.9em;
336 font-size: 0.9em;
337 font-weight: normal;
337 font-weight: normal;
338 text-decoration: none;
338 text-decoration: none;
339 color: #606060;
339 color: #606060;
340 }
340 }
341 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
341 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
342
342
343 div.wiki div.toc a.heading2 { margin-left: 6px; }
343 div.wiki div.toc a.heading2 { margin-left: 6px; }
344 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
344 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
345
345
346 /***** My page layout *****/
346 /***** My page layout *****/
347 .block-receiver {
347 .block-receiver {
348 border:1px dashed #c0c0c0;
348 border:1px dashed #c0c0c0;
349 margin-bottom: 20px;
349 margin-bottom: 20px;
350 padding: 15px 0 15px 0;
350 padding: 15px 0 15px 0;
351 }
351 }
352
352
353 .mypage-box {
353 .mypage-box {
354 margin:0 0 20px 0;
354 margin:0 0 20px 0;
355 color:#505050;
355 color:#505050;
356 line-height:1.5em;
356 line-height:1.5em;
357 }
357 }
358
358
359 .handle {
359 .handle {
360 cursor: move;
360 cursor: move;
361 }
361 }
362
362
363 a.close-icon {
363 a.close-icon {
364 display:block;
364 display:block;
365 margin-top:3px;
365 margin-top:3px;
366 overflow:hidden;
366 overflow:hidden;
367 width:12px;
367 width:12px;
368 height:12px;
368 height:12px;
369 background-repeat: no-repeat;
369 background-repeat: no-repeat;
370 cursor:pointer;
370 cursor:pointer;
371 background-image:url('../images/close.png');
371 background-image:url('../images/close.png');
372 }
372 }
373
373
374 a.close-icon:hover {
374 a.close-icon:hover {
375 background-image:url('../images/close_hl.png');
375 background-image:url('../images/close_hl.png');
376 }
376 }
377
377
378 /***** Gantt chart *****/
378 /***** Gantt chart *****/
379 .gantt_hdr {
379 .gantt_hdr {
380 position:absolute;
380 position:absolute;
381 top:0;
381 top:0;
382 height:16px;
382 height:16px;
383 border-top: 1px solid #c0c0c0;
383 border-top: 1px solid #c0c0c0;
384 border-bottom: 1px solid #c0c0c0;
384 border-bottom: 1px solid #c0c0c0;
385 border-right: 1px solid #c0c0c0;
385 border-right: 1px solid #c0c0c0;
386 text-align: center;
386 text-align: center;
387 overflow: hidden;
387 overflow: hidden;
388 }
388 }
389
389
390 .task {
390 .task {
391 position: absolute;
391 position: absolute;
392 height:8px;
392 height:8px;
393 font-size:0.8em;
393 font-size:0.8em;
394 color:#888;
394 color:#888;
395 padding:0;
395 padding:0;
396 margin:0;
396 margin:0;
397 line-height:0.8em;
397 line-height:0.8em;
398 }
398 }
399
399
400 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
400 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
401 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
401 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
402 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
402 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
403 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
403 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
404
404
405 /***** Icons *****/
405 /***** Icons *****/
406 .icon {
406 .icon {
407 background-position: 0% 40%;
407 background-position: 0% 40%;
408 background-repeat: no-repeat;
408 background-repeat: no-repeat;
409 padding-left: 20px;
409 padding-left: 20px;
410 padding-top: 2px;
410 padding-top: 2px;
411 padding-bottom: 3px;
411 padding-bottom: 3px;
412 }
412 }
413
413
414 .icon22 {
414 .icon22 {
415 background-position: 0% 40%;
415 background-position: 0% 40%;
416 background-repeat: no-repeat;
416 background-repeat: no-repeat;
417 padding-left: 26px;
417 padding-left: 26px;
418 line-height: 22px;
418 line-height: 22px;
419 vertical-align: middle;
419 vertical-align: middle;
420 }
420 }
421
421
422 .icon-add { background-image: url(../images/add.png); }
422 .icon-add { background-image: url(../images/add.png); }
423 .icon-edit { background-image: url(../images/edit.png); }
423 .icon-edit { background-image: url(../images/edit.png); }
424 .icon-del { background-image: url(../images/delete.png); }
424 .icon-del { background-image: url(../images/delete.png); }
425 .icon-move { background-image: url(../images/move.png); }
425 .icon-move { background-image: url(../images/move.png); }
426 .icon-save { background-image: url(../images/save.png); }
426 .icon-save { background-image: url(../images/save.png); }
427 .icon-cancel { background-image: url(../images/cancel.png); }
427 .icon-cancel { background-image: url(../images/cancel.png); }
428 .icon-pdf { background-image: url(../images/pdf.png); }
428 .icon-pdf { background-image: url(../images/pdf.png); }
429 .icon-csv { background-image: url(../images/csv.png); }
429 .icon-csv { background-image: url(../images/csv.png); }
430 .icon-html { background-image: url(../images/html.png); }
430 .icon-html { background-image: url(../images/html.png); }
431 .icon-image { background-image: url(../images/image.png); }
431 .icon-image { background-image: url(../images/image.png); }
432 .icon-txt { background-image: url(../images/txt.png); }
432 .icon-txt { background-image: url(../images/txt.png); }
433 .icon-file { background-image: url(../images/file.png); }
433 .icon-file { background-image: url(../images/file.png); }
434 .icon-folder { background-image: url(../images/folder.png); }
434 .icon-folder { background-image: url(../images/folder.png); }
435 .open .icon-folder { background-image: url(../images/folder_open.png); }
435 .open .icon-folder { background-image: url(../images/folder_open.png); }
436 .icon-package { background-image: url(../images/package.png); }
436 .icon-package { background-image: url(../images/package.png); }
437 .icon-home { background-image: url(../images/home.png); }
437 .icon-home { background-image: url(../images/home.png); }
438 .icon-user { background-image: url(../images/user.png); }
438 .icon-user { background-image: url(../images/user.png); }
439 .icon-mypage { background-image: url(../images/user_page.png); }
439 .icon-mypage { background-image: url(../images/user_page.png); }
440 .icon-admin { background-image: url(../images/admin.png); }
440 .icon-admin { background-image: url(../images/admin.png); }
441 .icon-projects { background-image: url(../images/projects.png); }
441 .icon-projects { background-image: url(../images/projects.png); }
442 .icon-logout { background-image: url(../images/logout.png); }
442 .icon-logout { background-image: url(../images/logout.png); }
443 .icon-help { background-image: url(../images/help.png); }
443 .icon-help { background-image: url(../images/help.png); }
444 .icon-attachment { background-image: url(../images/attachment.png); }
444 .icon-attachment { background-image: url(../images/attachment.png); }
445 .icon-index { background-image: url(../images/index.png); }
445 .icon-index { background-image: url(../images/index.png); }
446 .icon-history { background-image: url(../images/history.png); }
446 .icon-history { background-image: url(../images/history.png); }
447 .icon-feed { background-image: url(../images/feed.png); }
447 .icon-feed { background-image: url(../images/feed.png); }
448 .icon-time { background-image: url(../images/time.png); }
448 .icon-time { background-image: url(../images/time.png); }
449 .icon-stats { background-image: url(../images/stats.png); }
449 .icon-stats { background-image: url(../images/stats.png); }
450 .icon-warning { background-image: url(../images/warning.png); }
450 .icon-warning { background-image: url(../images/warning.png); }
451 .icon-fav { background-image: url(../images/fav.png); }
451 .icon-fav { background-image: url(../images/fav.png); }
452 .icon-fav-off { background-image: url(../images/fav_off.png); }
452 .icon-fav-off { background-image: url(../images/fav_off.png); }
453 .icon-reload { background-image: url(../images/reload.png); }
453 .icon-reload { background-image: url(../images/reload.png); }
454 .icon-lock { background-image: url(../images/locked.png); }
454 .icon-lock { background-image: url(../images/locked.png); }
455 .icon-unlock { background-image: url(../images/unlock.png); }
455 .icon-unlock { background-image: url(../images/unlock.png); }
456 .icon-note { background-image: url(../images/note.png); }
456 .icon-note { background-image: url(../images/note.png); }
457 .icon-checked { background-image: url(../images/true.png); }
457
458
458 .icon22-projects { background-image: url(../images/22x22/projects.png); }
459 .icon22-projects { background-image: url(../images/22x22/projects.png); }
459 .icon22-users { background-image: url(../images/22x22/users.png); }
460 .icon22-users { background-image: url(../images/22x22/users.png); }
460 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
461 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
461 .icon22-role { background-image: url(../images/22x22/role.png); }
462 .icon22-role { background-image: url(../images/22x22/role.png); }
462 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
463 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
463 .icon22-options { background-image: url(../images/22x22/options.png); }
464 .icon22-options { background-image: url(../images/22x22/options.png); }
464 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
465 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
465 .icon22-authent { background-image: url(../images/22x22/authent.png); }
466 .icon22-authent { background-image: url(../images/22x22/authent.png); }
466 .icon22-info { background-image: url(../images/22x22/info.png); }
467 .icon22-info { background-image: url(../images/22x22/info.png); }
467 .icon22-comment { background-image: url(../images/22x22/comment.png); }
468 .icon22-comment { background-image: url(../images/22x22/comment.png); }
468 .icon22-package { background-image: url(../images/22x22/package.png); }
469 .icon22-package { background-image: url(../images/22x22/package.png); }
469 .icon22-settings { background-image: url(../images/22x22/settings.png); }
470 .icon22-settings { background-image: url(../images/22x22/settings.png); }
470 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
471 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
471
472
472 /***** Media print specific styles *****/
473 /***** Media print specific styles *****/
473 @media print {
474 @media print {
474 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
475 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
475 #main { background: #fff; }
476 #main { background: #fff; }
476 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
477 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
477 }
478 }
General Comments 0
You need to be logged in to leave comments. Login now