##// END OF EJS Templates
Main project list now displays root projects with their subprojects....
Jean-Philippe Lang -
r718:f2a058f8cf04
parent child
Show More
@@ -1,636 +1,628
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 require 'csv'
18 require 'csv'
19
19
20 class ProjectsController < ApplicationController
20 class ProjectsController < ApplicationController
21 layout 'base'
21 layout 'base'
22 before_filter :find_project, :except => [ :index, :list, :add ]
22 before_filter :find_project, :except => [ :index, :list, :add ]
23 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
23 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
24 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
24 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
25 accept_key_auth :activity, :calendar
25 accept_key_auth :activity, :calendar
26
26
27 cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ]
27 cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ]
28 cache_sweeper :issue_sweeper, :only => [ :add_issue ]
28 cache_sweeper :issue_sweeper, :only => [ :add_issue ]
29 cache_sweeper :version_sweeper, :only => [ :add_version ]
29 cache_sweeper :version_sweeper, :only => [ :add_version ]
30
30
31 helper :sort
31 helper :sort
32 include SortHelper
32 include SortHelper
33 helper :custom_fields
33 helper :custom_fields
34 include CustomFieldsHelper
34 include CustomFieldsHelper
35 helper :ifpdf
35 helper :ifpdf
36 include IfpdfHelper
36 include IfpdfHelper
37 helper IssuesHelper
37 helper IssuesHelper
38 helper :queries
38 helper :queries
39 include QueriesHelper
39 include QueriesHelper
40 helper :repositories
40 helper :repositories
41 include RepositoriesHelper
41 include RepositoriesHelper
42 include ProjectsHelper
42 include ProjectsHelper
43
43
44 def index
44 def index
45 list
45 list
46 render :action => 'list' unless request.xhr?
46 render :action => 'list' unless request.xhr?
47 end
47 end
48
48
49 # Lists public projects
49 # Lists visible projects
50 def list
50 def list
51 sort_init "#{Project.table_name}.name", "asc"
51 projects = Project.find :all,
52 sort_update
52 :conditions => Project.visible_by(logged_in_user),
53 @project_count = Project.count(:all, :conditions => Project.visible_by(logged_in_user))
53 :include => :parent
54 @project_pages = Paginator.new self, @project_count,
54 @project_tree = projects.group_by {|p| p.parent || p}
55 15,
55 @project_tree.each_key {|p| @project_tree[p] -= [p]}
56 params['page']
57 @projects = Project.find :all, :order => sort_clause,
58 :conditions => Project.visible_by(logged_in_user),
59 :include => :parent,
60 :limit => @project_pages.items_per_page,
61 :offset => @project_pages.current.offset
62
63 render :action => "list", :layout => false if request.xhr?
64 end
56 end
65
57
66 # Add a new project
58 # Add a new project
67 def add
59 def add
68 @custom_fields = IssueCustomField.find(:all)
60 @custom_fields = IssueCustomField.find(:all)
69 @root_projects = Project.find(:all, :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}")
61 @root_projects = Project.find(:all, :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}")
70 @project = Project.new(params[:project])
62 @project = Project.new(params[:project])
71 if request.get?
63 if request.get?
72 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
64 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
73 else
65 else
74 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
66 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
75 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
67 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
76 @project.custom_values = @custom_values
68 @project.custom_values = @custom_values
77 if @project.save
69 if @project.save
78 @project.enabled_module_names = params[:enabled_modules]
70 @project.enabled_module_names = params[:enabled_modules]
79 flash[:notice] = l(:notice_successful_create)
71 flash[:notice] = l(:notice_successful_create)
80 redirect_to :controller => 'admin', :action => 'projects'
72 redirect_to :controller => 'admin', :action => 'projects'
81 end
73 end
82 end
74 end
83 end
75 end
84
76
85 # Show @project
77 # Show @project
86 def show
78 def show
87 @custom_values = @project.custom_values.find(:all, :include => :custom_field)
79 @custom_values = @project.custom_values.find(:all, :include => :custom_field)
88 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
80 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
89 @subprojects = @project.active_children
81 @subprojects = @project.active_children
90 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
82 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
91 @trackers = Tracker.find(:all, :order => 'position')
83 @trackers = Tracker.find(:all, :order => 'position')
92 @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
84 @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
93 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
85 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
94 @key = User.current.rss_key
86 @key = User.current.rss_key
95 end
87 end
96
88
97 def settings
89 def settings
98 @root_projects = Project::find(:all, :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id])
90 @root_projects = Project::find(:all, :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id])
99 @custom_fields = IssueCustomField.find(:all)
91 @custom_fields = IssueCustomField.find(:all)
100 @issue_category ||= IssueCategory.new
92 @issue_category ||= IssueCategory.new
101 @member ||= @project.members.new
93 @member ||= @project.members.new
102 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
94 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
103 @repository ||= @project.repository
95 @repository ||= @project.repository
104 @wiki ||= @project.wiki
96 @wiki ||= @project.wiki
105 end
97 end
106
98
107 # Edit @project
99 # Edit @project
108 def edit
100 def edit
109 if request.post?
101 if request.post?
110 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
102 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
111 if params[:custom_fields]
103 if params[:custom_fields]
112 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
104 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
113 @project.custom_values = @custom_values
105 @project.custom_values = @custom_values
114 end
106 end
115 @project.attributes = params[:project]
107 @project.attributes = params[:project]
116 if @project.save
108 if @project.save
117 flash[:notice] = l(:notice_successful_update)
109 flash[:notice] = l(:notice_successful_update)
118 redirect_to :action => 'settings', :id => @project
110 redirect_to :action => 'settings', :id => @project
119 else
111 else
120 settings
112 settings
121 render :action => 'settings'
113 render :action => 'settings'
122 end
114 end
123 end
115 end
124 end
116 end
125
117
126 def modules
118 def modules
127 @project.enabled_module_names = params[:enabled_modules]
119 @project.enabled_module_names = params[:enabled_modules]
128 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
120 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
129 end
121 end
130
122
131 def archive
123 def archive
132 @project.archive if request.post? && @project.active?
124 @project.archive if request.post? && @project.active?
133 redirect_to :controller => 'admin', :action => 'projects'
125 redirect_to :controller => 'admin', :action => 'projects'
134 end
126 end
135
127
136 def unarchive
128 def unarchive
137 @project.unarchive if request.post? && !@project.active?
129 @project.unarchive if request.post? && !@project.active?
138 redirect_to :controller => 'admin', :action => 'projects'
130 redirect_to :controller => 'admin', :action => 'projects'
139 end
131 end
140
132
141 # Delete @project
133 # Delete @project
142 def destroy
134 def destroy
143 @project_to_destroy = @project
135 @project_to_destroy = @project
144 if request.post? and params[:confirm]
136 if request.post? and params[:confirm]
145 @project_to_destroy.destroy
137 @project_to_destroy.destroy
146 redirect_to :controller => 'admin', :action => 'projects'
138 redirect_to :controller => 'admin', :action => 'projects'
147 end
139 end
148 # hide project in layout
140 # hide project in layout
149 @project = nil
141 @project = nil
150 end
142 end
151
143
152 # Add a new issue category to @project
144 # Add a new issue category to @project
153 def add_issue_category
145 def add_issue_category
154 @category = @project.issue_categories.build(params[:category])
146 @category = @project.issue_categories.build(params[:category])
155 if request.post? and @category.save
147 if request.post? and @category.save
156 respond_to do |format|
148 respond_to do |format|
157 format.html do
149 format.html do
158 flash[:notice] = l(:notice_successful_create)
150 flash[:notice] = l(:notice_successful_create)
159 redirect_to :action => 'settings', :tab => 'categories', :id => @project
151 redirect_to :action => 'settings', :tab => 'categories', :id => @project
160 end
152 end
161 format.js do
153 format.js do
162 # IE doesn't support the replace_html rjs method for select box options
154 # IE doesn't support the replace_html rjs method for select box options
163 render(:update) {|page| page.replace "issue_category_id",
155 render(:update) {|page| page.replace "issue_category_id",
164 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
156 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
165 }
157 }
166 end
158 end
167 end
159 end
168 end
160 end
169 end
161 end
170
162
171 # Add a new version to @project
163 # Add a new version to @project
172 def add_version
164 def add_version
173 @version = @project.versions.build(params[:version])
165 @version = @project.versions.build(params[:version])
174 if request.post? and @version.save
166 if request.post? and @version.save
175 flash[:notice] = l(:notice_successful_create)
167 flash[:notice] = l(:notice_successful_create)
176 redirect_to :action => 'settings', :tab => 'versions', :id => @project
168 redirect_to :action => 'settings', :tab => 'versions', :id => @project
177 end
169 end
178 end
170 end
179
171
180 # Add a new document to @project
172 # Add a new document to @project
181 def add_document
173 def add_document
182 @categories = Enumeration::get_values('DCAT')
174 @categories = Enumeration::get_values('DCAT')
183 @document = @project.documents.build(params[:document])
175 @document = @project.documents.build(params[:document])
184 if request.post? and @document.save
176 if request.post? and @document.save
185 # Save the attachments
177 # Save the attachments
186 params[:attachments].each { |a|
178 params[:attachments].each { |a|
187 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
179 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
188 } if params[:attachments] and params[:attachments].is_a? Array
180 } if params[:attachments] and params[:attachments].is_a? Array
189 flash[:notice] = l(:notice_successful_create)
181 flash[:notice] = l(:notice_successful_create)
190 Mailer.deliver_document_add(@document) if Setting.notified_events.include?('document_added')
182 Mailer.deliver_document_add(@document) if Setting.notified_events.include?('document_added')
191 redirect_to :action => 'list_documents', :id => @project
183 redirect_to :action => 'list_documents', :id => @project
192 end
184 end
193 end
185 end
194
186
195 # Show documents list of @project
187 # Show documents list of @project
196 def list_documents
188 def list_documents
197 @documents = @project.documents.find :all, :include => :category
189 @documents = @project.documents.find :all, :include => :category
198 end
190 end
199
191
200 # Add a new issue to @project
192 # Add a new issue to @project
201 def add_issue
193 def add_issue
202 @tracker = Tracker.find(params[:tracker_id])
194 @tracker = Tracker.find(params[:tracker_id])
203 @priorities = Enumeration::get_values('IPRI')
195 @priorities = Enumeration::get_values('IPRI')
204
196
205 default_status = IssueStatus.default
197 default_status = IssueStatus.default
206 unless default_status
198 unless default_status
207 flash.now[:error] = 'No default issue status defined. Please check your configuration.'
199 flash.now[:error] = 'No default issue status defined. Please check your configuration.'
208 render :nothing => true, :layout => true
200 render :nothing => true, :layout => true
209 return
201 return
210 end
202 end
211 @issue = Issue.new(:project => @project, :tracker => @tracker)
203 @issue = Issue.new(:project => @project, :tracker => @tracker)
212 @issue.status = default_status
204 @issue.status = default_status
213 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
205 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
214 if request.get?
206 if request.get?
215 @issue.start_date = Date.today
207 @issue.start_date = Date.today
216 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
208 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
217 else
209 else
218 @issue.attributes = params[:issue]
210 @issue.attributes = params[:issue]
219
211
220 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
212 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
221 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
213 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
222
214
223 @issue.author_id = self.logged_in_user.id if self.logged_in_user
215 @issue.author_id = self.logged_in_user.id if self.logged_in_user
224 # Multiple file upload
216 # Multiple file upload
225 @attachments = []
217 @attachments = []
226 params[:attachments].each { |a|
218 params[:attachments].each { |a|
227 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
219 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
228 } if params[:attachments] and params[:attachments].is_a? Array
220 } if params[:attachments] and params[:attachments].is_a? Array
229 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
221 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
230 @issue.custom_values = @custom_values
222 @issue.custom_values = @custom_values
231 if @issue.save
223 if @issue.save
232 @attachments.each(&:save)
224 @attachments.each(&:save)
233 flash[:notice] = l(:notice_successful_create)
225 flash[:notice] = l(:notice_successful_create)
234 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
226 Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
235 redirect_to :action => 'list_issues', :id => @project
227 redirect_to :action => 'list_issues', :id => @project
236 end
228 end
237 end
229 end
238 end
230 end
239
231
240 # Show filtered/sorted issues list of @project
232 # Show filtered/sorted issues list of @project
241 def list_issues
233 def list_issues
242 sort_init "#{Issue.table_name}.id", "desc"
234 sort_init "#{Issue.table_name}.id", "desc"
243 sort_update
235 sort_update
244
236
245 retrieve_query
237 retrieve_query
246
238
247 @results_per_page_options = [ 15, 25, 50, 100 ]
239 @results_per_page_options = [ 15, 25, 50, 100 ]
248 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
240 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
249 @results_per_page = params[:per_page].to_i
241 @results_per_page = params[:per_page].to_i
250 session[:results_per_page] = @results_per_page
242 session[:results_per_page] = @results_per_page
251 else
243 else
252 @results_per_page = session[:results_per_page] || 25
244 @results_per_page = session[:results_per_page] || 25
253 end
245 end
254
246
255 if @query.valid?
247 if @query.valid?
256 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
248 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
257 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
249 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
258 @issues = Issue.find :all, :order => sort_clause,
250 @issues = Issue.find :all, :order => sort_clause,
259 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
251 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
260 :conditions => @query.statement,
252 :conditions => @query.statement,
261 :limit => @issue_pages.items_per_page,
253 :limit => @issue_pages.items_per_page,
262 :offset => @issue_pages.current.offset
254 :offset => @issue_pages.current.offset
263 end
255 end
264 render :layout => false if request.xhr?
256 render :layout => false if request.xhr?
265 end
257 end
266
258
267 # Export filtered/sorted issues list to CSV
259 # Export filtered/sorted issues list to CSV
268 def export_issues_csv
260 def export_issues_csv
269 sort_init "#{Issue.table_name}.id", "desc"
261 sort_init "#{Issue.table_name}.id", "desc"
270 sort_update
262 sort_update
271
263
272 retrieve_query
264 retrieve_query
273 render :action => 'list_issues' and return unless @query.valid?
265 render :action => 'list_issues' and return unless @query.valid?
274
266
275 @issues = Issue.find :all, :order => sort_clause,
267 @issues = Issue.find :all, :order => sort_clause,
276 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
268 :include => [ :assigned_to, :author, :status, :tracker, :priority, :project, {:custom_values => :custom_field} ],
277 :conditions => @query.statement,
269 :conditions => @query.statement,
278 :limit => Setting.issues_export_limit.to_i
270 :limit => Setting.issues_export_limit.to_i
279
271
280 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
272 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
281 export = StringIO.new
273 export = StringIO.new
282 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
274 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
283 # csv header fields
275 # csv header fields
284 headers = [ "#", l(:field_status),
276 headers = [ "#", l(:field_status),
285 l(:field_project),
277 l(:field_project),
286 l(:field_tracker),
278 l(:field_tracker),
287 l(:field_priority),
279 l(:field_priority),
288 l(:field_subject),
280 l(:field_subject),
289 l(:field_assigned_to),
281 l(:field_assigned_to),
290 l(:field_author),
282 l(:field_author),
291 l(:field_start_date),
283 l(:field_start_date),
292 l(:field_due_date),
284 l(:field_due_date),
293 l(:field_done_ratio),
285 l(:field_done_ratio),
294 l(:field_created_on),
286 l(:field_created_on),
295 l(:field_updated_on)
287 l(:field_updated_on)
296 ]
288 ]
297 for custom_field in @project.all_custom_fields
289 for custom_field in @project.all_custom_fields
298 headers << custom_field.name
290 headers << custom_field.name
299 end
291 end
300 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
292 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
301 # csv lines
293 # csv lines
302 @issues.each do |issue|
294 @issues.each do |issue|
303 fields = [issue.id, issue.status.name,
295 fields = [issue.id, issue.status.name,
304 issue.project.name,
296 issue.project.name,
305 issue.tracker.name,
297 issue.tracker.name,
306 issue.priority.name,
298 issue.priority.name,
307 issue.subject,
299 issue.subject,
308 (issue.assigned_to ? issue.assigned_to.name : ""),
300 (issue.assigned_to ? issue.assigned_to.name : ""),
309 issue.author.name,
301 issue.author.name,
310 issue.start_date ? l_date(issue.start_date) : nil,
302 issue.start_date ? l_date(issue.start_date) : nil,
311 issue.due_date ? l_date(issue.due_date) : nil,
303 issue.due_date ? l_date(issue.due_date) : nil,
312 issue.done_ratio,
304 issue.done_ratio,
313 l_datetime(issue.created_on),
305 l_datetime(issue.created_on),
314 l_datetime(issue.updated_on)
306 l_datetime(issue.updated_on)
315 ]
307 ]
316 for custom_field in @project.all_custom_fields
308 for custom_field in @project.all_custom_fields
317 fields << (show_value issue.custom_value_for(custom_field))
309 fields << (show_value issue.custom_value_for(custom_field))
318 end
310 end
319 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
311 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
320 end
312 end
321 end
313 end
322 export.rewind
314 export.rewind
323 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
315 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
324 end
316 end
325
317
326 # Export filtered/sorted issues to PDF
318 # Export filtered/sorted issues to PDF
327 def export_issues_pdf
319 def export_issues_pdf
328 sort_init "#{Issue.table_name}.id", "desc"
320 sort_init "#{Issue.table_name}.id", "desc"
329 sort_update
321 sort_update
330
322
331 retrieve_query
323 retrieve_query
332 render :action => 'list_issues' and return unless @query.valid?
324 render :action => 'list_issues' and return unless @query.valid?
333
325
334 @issues = Issue.find :all, :order => sort_clause,
326 @issues = Issue.find :all, :order => sort_clause,
335 :include => [ :author, :status, :tracker, :priority, :project ],
327 :include => [ :author, :status, :tracker, :priority, :project ],
336 :conditions => @query.statement,
328 :conditions => @query.statement,
337 :limit => Setting.issues_export_limit.to_i
329 :limit => Setting.issues_export_limit.to_i
338
330
339 @options_for_rfpdf ||= {}
331 @options_for_rfpdf ||= {}
340 @options_for_rfpdf[:file_name] = "export.pdf"
332 @options_for_rfpdf[:file_name] = "export.pdf"
341 render :layout => false
333 render :layout => false
342 end
334 end
343
335
344 def move_issues
336 def move_issues
345 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
337 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
346 redirect_to :action => 'list_issues', :id => @project and return unless @issues
338 redirect_to :action => 'list_issues', :id => @project and return unless @issues
347 @projects = []
339 @projects = []
348 # find projects to which the user is allowed to move the issue
340 # find projects to which the user is allowed to move the issue
349 User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:controller => 'projects', :action => 'move_issues')}
341 User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:controller => 'projects', :action => 'move_issues')}
350 # issue can be moved to any tracker
342 # issue can be moved to any tracker
351 @trackers = Tracker.find(:all)
343 @trackers = Tracker.find(:all)
352 if request.post? and params[:new_project_id] and params[:new_tracker_id]
344 if request.post? and params[:new_project_id] and params[:new_tracker_id]
353 new_project = Project.find_by_id(params[:new_project_id])
345 new_project = Project.find_by_id(params[:new_project_id])
354 new_tracker = Tracker.find_by_id(params[:new_tracker_id])
346 new_tracker = Tracker.find_by_id(params[:new_tracker_id])
355 @issues.each do |i|
347 @issues.each do |i|
356 if new_project && i.project_id != new_project.id
348 if new_project && i.project_id != new_project.id
357 # issue is moved to another project
349 # issue is moved to another project
358 i.category = nil
350 i.category = nil
359 i.fixed_version = nil
351 i.fixed_version = nil
360 # delete issue relations
352 # delete issue relations
361 i.relations_from.clear
353 i.relations_from.clear
362 i.relations_to.clear
354 i.relations_to.clear
363 i.project = new_project
355 i.project = new_project
364 end
356 end
365 if new_tracker
357 if new_tracker
366 i.tracker = new_tracker
358 i.tracker = new_tracker
367 end
359 end
368 i.save
360 i.save
369 end
361 end
370 flash[:notice] = l(:notice_successful_update)
362 flash[:notice] = l(:notice_successful_update)
371 redirect_to :action => 'list_issues', :id => @project
363 redirect_to :action => 'list_issues', :id => @project
372 end
364 end
373 end
365 end
374
366
375 # Add a news to @project
367 # Add a news to @project
376 def add_news
368 def add_news
377 @news = News.new(:project => @project)
369 @news = News.new(:project => @project)
378 if request.post?
370 if request.post?
379 @news.attributes = params[:news]
371 @news.attributes = params[:news]
380 @news.author_id = self.logged_in_user.id if self.logged_in_user
372 @news.author_id = self.logged_in_user.id if self.logged_in_user
381 if @news.save
373 if @news.save
382 flash[:notice] = l(:notice_successful_create)
374 flash[:notice] = l(:notice_successful_create)
383 Mailer.deliver_news_added(@news) if Setting.notified_events.include?('news_added')
375 Mailer.deliver_news_added(@news) if Setting.notified_events.include?('news_added')
384 redirect_to :action => 'list_news', :id => @project
376 redirect_to :action => 'list_news', :id => @project
385 end
377 end
386 end
378 end
387 end
379 end
388
380
389 # Show news list of @project
381 # Show news list of @project
390 def list_news
382 def list_news
391 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
383 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
392
384
393 respond_to do |format|
385 respond_to do |format|
394 format.html { render :layout => false if request.xhr? }
386 format.html { render :layout => false if request.xhr? }
395 format.atom { render_feed(@news, :title => "#{@project.name}: #{l(:label_news_plural)}") }
387 format.atom { render_feed(@news, :title => "#{@project.name}: #{l(:label_news_plural)}") }
396 end
388 end
397 end
389 end
398
390
399 def add_file
391 def add_file
400 if request.post?
392 if request.post?
401 @version = @project.versions.find_by_id(params[:version_id])
393 @version = @project.versions.find_by_id(params[:version_id])
402 # Save the attachments
394 # Save the attachments
403 @attachments = []
395 @attachments = []
404 params[:attachments].each { |file|
396 params[:attachments].each { |file|
405 next unless file.size > 0
397 next unless file.size > 0
406 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
398 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
407 @attachments << a unless a.new_record?
399 @attachments << a unless a.new_record?
408 } if params[:attachments] and params[:attachments].is_a? Array
400 } if params[:attachments] and params[:attachments].is_a? Array
409 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? && Setting.notified_events.include?('file_added')
401 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? && Setting.notified_events.include?('file_added')
410 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
402 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
411 end
403 end
412 @versions = @project.versions.sort
404 @versions = @project.versions.sort
413 end
405 end
414
406
415 def list_files
407 def list_files
416 @versions = @project.versions.sort
408 @versions = @project.versions.sort
417 end
409 end
418
410
419 # Show changelog for @project
411 # Show changelog for @project
420 def changelog
412 def changelog
421 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
413 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
422 retrieve_selected_tracker_ids(@trackers)
414 retrieve_selected_tracker_ids(@trackers)
423 @versions = @project.versions.sort
415 @versions = @project.versions.sort
424 end
416 end
425
417
426 def roadmap
418 def roadmap
427 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
419 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
428 retrieve_selected_tracker_ids(@trackers)
420 retrieve_selected_tracker_ids(@trackers)
429 @versions = @project.versions.sort
421 @versions = @project.versions.sort
430 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
422 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
431 end
423 end
432
424
433 def activity
425 def activity
434 if params[:year] and params[:year].to_i > 1900
426 if params[:year] and params[:year].to_i > 1900
435 @year = params[:year].to_i
427 @year = params[:year].to_i
436 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
428 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
437 @month = params[:month].to_i
429 @month = params[:month].to_i
438 end
430 end
439 end
431 end
440 @year ||= Date.today.year
432 @year ||= Date.today.year
441 @month ||= Date.today.month
433 @month ||= Date.today.month
442
434
443 case params[:format]
435 case params[:format]
444 when 'rss'
436 when 'rss'
445 # 30 last days
437 # 30 last days
446 @date_from = Date.today - 30
438 @date_from = Date.today - 30
447 @date_to = Date.today + 1
439 @date_to = Date.today + 1
448 else
440 else
449 # current month
441 # current month
450 @date_from = Date.civil(@year, @month, 1)
442 @date_from = Date.civil(@year, @month, 1)
451 @date_to = @date_from >> 1
443 @date_to = @date_from >> 1
452 end
444 end
453
445
454 @event_types = %w(issues news attachments documents wiki_edits revisions)
446 @event_types = %w(issues news attachments documents wiki_edits revisions)
455 @event_types.delete('wiki_edits') unless @project.wiki
447 @event_types.delete('wiki_edits') unless @project.wiki
456 @event_types.delete('changesets') unless @project.repository
448 @event_types.delete('changesets') unless @project.repository
457
449
458 @scope = @event_types.select {|t| params["show_#{t}"]}
450 @scope = @event_types.select {|t| params["show_#{t}"]}
459 # default events if none is specified in parameters
451 # default events if none is specified in parameters
460 @scope = (@event_types - %w(wiki_edits))if @scope.empty?
452 @scope = (@event_types - %w(wiki_edits))if @scope.empty?
461
453
462 @events = []
454 @events = []
463
455
464 if @scope.include?('issues')
456 if @scope.include?('issues')
465 @events += @project.issues.find(:all, :include => [:author, :tracker], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] )
457 @events += @project.issues.find(:all, :include => [:author, :tracker], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] )
466 end
458 end
467
459
468 if @scope.include?('news')
460 if @scope.include?('news')
469 @events += @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author )
461 @events += @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author )
470 end
462 end
471
463
472 if @scope.include?('attachments')
464 if @scope.include?('attachments')
473 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
465 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*", :joins => "LEFT JOIN #{Version.table_name} ON #{Version.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Version' and #{Version.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
474 end
466 end
475
467
476 if @scope.include?('documents')
468 if @scope.include?('documents')
477 @events += @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] )
469 @events += @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] )
478 @events += Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
470 @events += Attachment.find(:all, :select => "attachments.*", :joins => "LEFT JOIN #{Document.table_name} ON #{Document.table_name}.id = #{Attachment.table_name}.container_id", :conditions => ["#{Attachment.table_name}.container_type='Document' and #{Document.table_name}.project_id=? and #{Attachment.table_name}.created_on>=? and #{Attachment.table_name}.created_on<=?", @project.id, @date_from, @date_to], :include => :author )
479 end
471 end
480
472
481 if @scope.include?('wiki_edits') && @project.wiki
473 if @scope.include?('wiki_edits') && @project.wiki
482 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
474 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
483 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
475 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
484 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
476 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
485 "#{WikiContent.versioned_table_name}.id"
477 "#{WikiContent.versioned_table_name}.id"
486 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
478 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
487 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
479 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
488 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
480 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
489 @project.id, @date_from, @date_to]
481 @project.id, @date_from, @date_to]
490
482
491 @events += WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions)
483 @events += WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions)
492 end
484 end
493
485
494 if @scope.include?('revisions') && @project.repository
486 if @scope.include?('revisions') && @project.repository
495 @events += @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to])
487 @events += @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to])
496 end
488 end
497
489
498 @events_by_day = @events.group_by(&:event_date)
490 @events_by_day = @events.group_by(&:event_date)
499
491
500 respond_to do |format|
492 respond_to do |format|
501 format.html { render :layout => false if request.xhr? }
493 format.html { render :layout => false if request.xhr? }
502 format.atom { render_feed(@events, :title => "#{@project.name}: #{l(:label_activity)}") }
494 format.atom { render_feed(@events, :title => "#{@project.name}: #{l(:label_activity)}") }
503 end
495 end
504 end
496 end
505
497
506 def calendar
498 def calendar
507 @trackers = Tracker.find(:all, :order => 'position')
499 @trackers = Tracker.find(:all, :order => 'position')
508 retrieve_selected_tracker_ids(@trackers)
500 retrieve_selected_tracker_ids(@trackers)
509
501
510 if params[:year] and params[:year].to_i > 1900
502 if params[:year] and params[:year].to_i > 1900
511 @year = params[:year].to_i
503 @year = params[:year].to_i
512 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
504 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
513 @month = params[:month].to_i
505 @month = params[:month].to_i
514 end
506 end
515 end
507 end
516 @year ||= Date.today.year
508 @year ||= Date.today.year
517 @month ||= Date.today.month
509 @month ||= Date.today.month
518
510
519 @date_from = Date.civil(@year, @month, 1)
511 @date_from = Date.civil(@year, @month, 1)
520 @date_to = (@date_from >> 1)-1
512 @date_to = (@date_from >> 1)-1
521 # start on monday
513 # start on monday
522 @date_from = @date_from - (@date_from.cwday-1)
514 @date_from = @date_from - (@date_from.cwday-1)
523 # finish on sunday
515 # finish on sunday
524 @date_to = @date_to + (7-@date_to.cwday)
516 @date_to = @date_to + (7-@date_to.cwday)
525
517
526 @events = []
518 @events = []
527 @project.issues_with_subprojects(params[:with_subprojects]) do
519 @project.issues_with_subprojects(params[:with_subprojects]) do
528 @events += Issue.find(:all,
520 @events += Issue.find(:all,
529 :include => [:tracker, :status, :assigned_to, :priority, :project],
521 :include => [:tracker, :status, :assigned_to, :priority, :project],
530 :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)) and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", @date_from, @date_to, @date_from, @date_to]
522 :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)) and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", @date_from, @date_to, @date_from, @date_to]
531 ) unless @selected_tracker_ids.empty?
523 ) unless @selected_tracker_ids.empty?
532 end
524 end
533 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
525 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
534
526
535 @ending_events_by_days = @events.group_by {|event| event.due_date}
527 @ending_events_by_days = @events.group_by {|event| event.due_date}
536 @starting_events_by_days = @events.group_by {|event| event.start_date}
528 @starting_events_by_days = @events.group_by {|event| event.start_date}
537
529
538 render :layout => false if request.xhr?
530 render :layout => false if request.xhr?
539 end
531 end
540
532
541 def gantt
533 def gantt
542 @trackers = Tracker.find(:all, :order => 'position')
534 @trackers = Tracker.find(:all, :order => 'position')
543 retrieve_selected_tracker_ids(@trackers)
535 retrieve_selected_tracker_ids(@trackers)
544
536
545 if params[:year] and params[:year].to_i >0
537 if params[:year] and params[:year].to_i >0
546 @year_from = params[:year].to_i
538 @year_from = params[:year].to_i
547 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
539 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
548 @month_from = params[:month].to_i
540 @month_from = params[:month].to_i
549 else
541 else
550 @month_from = 1
542 @month_from = 1
551 end
543 end
552 else
544 else
553 @month_from ||= (Date.today << 1).month
545 @month_from ||= (Date.today << 1).month
554 @year_from ||= (Date.today << 1).year
546 @year_from ||= (Date.today << 1).year
555 end
547 end
556
548
557 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
549 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
558 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
550 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
559
551
560 @date_from = Date.civil(@year_from, @month_from, 1)
552 @date_from = Date.civil(@year_from, @month_from, 1)
561 @date_to = (@date_from >> @months) - 1
553 @date_to = (@date_from >> @months) - 1
562
554
563 @events = []
555 @events = []
564 @project.issues_with_subprojects(params[:with_subprojects]) do
556 @project.issues_with_subprojects(params[:with_subprojects]) do
565 @events += Issue.find(:all,
557 @events += Issue.find(:all,
566 :order => "start_date, due_date",
558 :order => "start_date, due_date",
567 :include => [:tracker, :status, :assigned_to, :priority, :project],
559 :include => [:tracker, :status, :assigned_to, :priority, :project],
568 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
560 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
569 ) unless @selected_tracker_ids.empty?
561 ) unless @selected_tracker_ids.empty?
570 end
562 end
571 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
563 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
572 @events.sort! {|x,y| x.start_date <=> y.start_date }
564 @events.sort! {|x,y| x.start_date <=> y.start_date }
573
565
574 if params[:format]=='pdf'
566 if params[:format]=='pdf'
575 @options_for_rfpdf ||= {}
567 @options_for_rfpdf ||= {}
576 @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
568 @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
577 render :template => "projects/gantt.rfpdf", :layout => false
569 render :template => "projects/gantt.rfpdf", :layout => false
578 elsif params[:format]=='png' && respond_to?('gantt_image')
570 elsif params[:format]=='png' && respond_to?('gantt_image')
579 image = gantt_image(@events, @date_from, @months, @zoom)
571 image = gantt_image(@events, @date_from, @months, @zoom)
580 image.format = 'PNG'
572 image.format = 'PNG'
581 send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
573 send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
582 else
574 else
583 render :template => "projects/gantt.rhtml"
575 render :template => "projects/gantt.rhtml"
584 end
576 end
585 end
577 end
586
578
587 def feeds
579 def feeds
588 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
580 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
589 @key = User.current.rss_key
581 @key = User.current.rss_key
590 end
582 end
591
583
592 private
584 private
593 # Find project of id params[:id]
585 # Find project of id params[:id]
594 # if not found, redirect to project list
586 # if not found, redirect to project list
595 # Used as a before_filter
587 # Used as a before_filter
596 def find_project
588 def find_project
597 @project = Project.find(params[:id])
589 @project = Project.find(params[:id])
598 rescue ActiveRecord::RecordNotFound
590 rescue ActiveRecord::RecordNotFound
599 render_404
591 render_404
600 end
592 end
601
593
602 def retrieve_selected_tracker_ids(selectable_trackers)
594 def retrieve_selected_tracker_ids(selectable_trackers)
603 if ids = params[:tracker_ids]
595 if ids = params[:tracker_ids]
604 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
596 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
605 else
597 else
606 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
598 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
607 end
599 end
608 end
600 end
609
601
610 # Retrieve query from session or build a new query
602 # Retrieve query from session or build a new query
611 def retrieve_query
603 def retrieve_query
612 if params[:query_id]
604 if params[:query_id]
613 @query = @project.queries.find(params[:query_id])
605 @query = @project.queries.find(params[:query_id])
614 @query.executed_by = logged_in_user
606 @query.executed_by = logged_in_user
615 session[:query] = @query
607 session[:query] = @query
616 else
608 else
617 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
609 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
618 # Give it a name, required to be valid
610 # Give it a name, required to be valid
619 @query = Query.new(:name => "_", :executed_by => logged_in_user)
611 @query = Query.new(:name => "_", :executed_by => logged_in_user)
620 @query.project = @project
612 @query.project = @project
621 if params[:fields] and params[:fields].is_a? Array
613 if params[:fields] and params[:fields].is_a? Array
622 params[:fields].each do |field|
614 params[:fields].each do |field|
623 @query.add_filter(field, params[:operators][field], params[:values][field])
615 @query.add_filter(field, params[:operators][field], params[:values][field])
624 end
616 end
625 else
617 else
626 @query.available_filters.keys.each do |field|
618 @query.available_filters.keys.each do |field|
627 @query.add_short_filter(field, params[field]) if params[field]
619 @query.add_short_filter(field, params[field]) if params[field]
628 end
620 end
629 end
621 end
630 session[:query] = @query
622 session[:query] = @query
631 else
623 else
632 @query = session[:query]
624 @query = session[:query]
633 end
625 end
634 end
626 end
635 end
627 end
636 end
628 end
@@ -1,86 +1,91
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 IssuesHelper
18 module IssuesHelper
19
19
20 def show_detail(detail, no_html=false)
20 def show_detail(detail, no_html=false)
21 case detail.property
21 case detail.property
22 when 'attr'
22 when 'attr'
23 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
23 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
24 case detail.prop_key
24 case detail.prop_key
25 when 'due_date', 'start_date'
25 when 'due_date', 'start_date'
26 value = format_date(detail.value.to_date) if detail.value
26 value = format_date(detail.value.to_date) if detail.value
27 old_value = format_date(detail.old_value.to_date) if detail.old_value
27 old_value = format_date(detail.old_value.to_date) if detail.old_value
28 when 'status_id'
28 when 'status_id'
29 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
29 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
30 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
30 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
31 when 'assigned_to_id'
31 when 'assigned_to_id'
32 u = User.find_by_id(detail.value) and value = u.name if detail.value
32 u = User.find_by_id(detail.value) and value = u.name if detail.value
33 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
33 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
34 when 'priority_id'
34 when 'priority_id'
35 e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
35 e = Enumeration.find_by_id(detail.value) and value = e.name if detail.value
36 e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
36 e = Enumeration.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
37 when 'category_id'
37 when 'category_id'
38 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
38 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
39 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
39 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
40 when 'fixed_version_id'
40 when 'fixed_version_id'
41 v = Version.find_by_id(detail.value) and value = v.name if detail.value
41 v = Version.find_by_id(detail.value) and value = v.name if detail.value
42 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
42 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
43 end
43 end
44 when 'cf'
44 when 'cf'
45 custom_field = CustomField.find_by_id(detail.prop_key)
45 custom_field = CustomField.find_by_id(detail.prop_key)
46 if custom_field
46 if custom_field
47 label = custom_field.name
47 label = custom_field.name
48 value = format_value(detail.value, custom_field.field_format) if detail.value
48 value = format_value(detail.value, custom_field.field_format) if detail.value
49 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
49 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
50 end
50 end
51 when 'attachment'
51 when 'attachment'
52 label = l(:label_attachment)
52 label = l(:label_attachment)
53 end
53 end
54
54
55 label ||= detail.prop_key
55 label ||= detail.prop_key
56 value ||= detail.value
56 value ||= detail.value
57 old_value ||= detail.old_value
57 old_value ||= detail.old_value
58
58
59 unless no_html
59 unless no_html
60 label = content_tag('strong', label)
60 label = content_tag('strong', label)
61 old_value = content_tag("i", h(old_value)) if detail.old_value
61 old_value = content_tag("i", h(old_value)) if detail.old_value
62 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
62 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
63 value = content_tag("i", h(value)) if value
63 if detail.property == 'attachment' && !value.blank? && Attachment.find_by_id(detail.prop_key)
64 # Link to the attachment if it has not been removed
65 value = link_to(value, :controller => 'attachments', :action => 'download', :id => detail.prop_key)
66 else
67 value = content_tag("i", h(value)) if value
68 end
64 end
69 end
65
70
66 if !detail.value.blank?
71 if !detail.value.blank?
67 case detail.property
72 case detail.property
68 when 'attr', 'cf'
73 when 'attr', 'cf'
69 if !detail.old_value.blank?
74 if !detail.old_value.blank?
70 label + " " + l(:text_journal_changed, old_value, value)
75 label + " " + l(:text_journal_changed, old_value, value)
71 else
76 else
72 label + " " + l(:text_journal_set_to, value)
77 label + " " + l(:text_journal_set_to, value)
73 end
78 end
74 when 'attachment'
79 when 'attachment'
75 "#{label} #{value} #{l(:label_added)}"
80 "#{label} #{value} #{l(:label_added)}"
76 end
81 end
77 else
82 else
78 case detail.property
83 case detail.property
79 when 'attr', 'cf'
84 when 'attr', 'cf'
80 label + " " + l(:text_journal_deleted) + " (#{old_value})"
85 label + " " + l(:text_journal_deleted) + " (#{old_value})"
81 when 'attachment'
86 when 'attachment'
82 "#{label} #{old_value} #{l(:label_deleted)}"
87 "#{label} #{old_value} #{l(:label_deleted)}"
83 end
88 end
84 end
89 end
85 end
90 end
86 end
91 end
@@ -1,30 +1,21
1 <h2><%=l(:label_project_plural)%></h2>
1 <h2><%=l(:label_project_plural)%></h2>
2
2
3 <table class="list">
3 <dl class="projects">
4 <thead><tr>
4 <% @project_tree.keys.sort.each do |project| %>
5 <%= sort_header_tag("#{Project.table_name}.name", :caption => l(:label_project)) %>
5 <dt><%= link_to project.name, {:action => 'show', :id => project}, :class => (User.current.member_of?(project) ? "icon icon-fav" : "") %>
6 <th><%=l(:field_parent)%></th>
6 <dd><%= textilizable(project.description, :project => project) %>
7 <%= sort_header_tag("#{Project.table_name}.created_on", :caption => l(:field_created_on)) %>
7
8 </tr></thead>
8 <% if @project_tree[project].any? %>
9 <tbody>
9 <%= l(:label_subproject_plural) %>:
10 <% for project in @projects %>
10 <%= @project_tree[project].sort.collect {|subproject|
11 <tr class="<%= cycle("odd", "even") %>">
11 link_to(subproject.name, {:action => 'show', :id => subproject}, :class => (User.current.member_of?(subproject) ? "icon icon-fav" : ""))}.join(', ') %>
12 <td>
12 <% end %>
13 <%= link_to project.name, {:action => 'show', :id => project}, :class => (User.current.member_of?(project) ? "icon icon-fav" : "") %><br />
13 </dd></dt>
14 <%= textilizable project.description, :project => project %>
15 </td>
16 <td><%= link_to(project.parent.name, :action => 'show', :id => project.parent) unless project.parent.nil? %></td>
17 <td align="center"><%= format_date(project.created_on) %></td>
18 </tr>
19 <% end %>
14 <% end %>
20 </tbody>
15 </dl>
21 </table>
22
16
23 <% if User.current.logged? %>
17 <% if User.current.logged? %>
24 <div class="contextual">
18 <div class="contextual">
25 <span class="icon icon-fav"><%= l(:label_my_projects) %></span>
19 <span class="icon icon-fav"><%= l(:label_my_projects) %></span>
26 </div>
20 </div>
27 <% end %>
21 <% end %>
28
29 <%= pagination_links_full @project_pages %>
30 [ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]
@@ -1,734 +1,736
1 /* andreas08 - an open source xhtml/css website layout by Andreas Viklund - http://andreasviklund.com . Free to use in any way and for any purpose as long as the proper credits are given to the original designer. Version: 1.0, November 28, 2005 */
1 /* andreas08 - an open source xhtml/css website layout by Andreas Viklund - http://andreasviklund.com . Free to use in any way and for any purpose as long as the proper credits are given to the original designer. Version: 1.0, November 28, 2005 */
2 /* Edited by Jean-Philippe Lang *>
2 /* Edited by Jean-Philippe Lang *>
3 /**************** Body and tag styles ****************/
3 /**************** Body and tag styles ****************/
4
4
5 #header * {margin:0; padding:0;}
5 #header * {margin:0; padding:0;}
6 p, ul, ol, li {margin:0; padding:0;}
6 p, ul, ol, li {margin:0; padding:0;}
7
7
8 body{
8 body{
9 font:76% Verdana,Tahoma,Arial,sans-serif;
9 font:76% Verdana,Tahoma,Arial,sans-serif;
10 line-height:1.4em;
10 line-height:1.4em;
11 text-align:center;
11 text-align:center;
12 color:#303030;
12 color:#303030;
13 background:#e8eaec;
13 background:#e8eaec;
14 margin:0;
14 margin:0;
15 }
15 }
16
16
17 a{color:#467aa7;font-weight:bold;text-decoration:none;background-color:inherit;}
17 a{color:#467aa7;font-weight:bold;text-decoration:none;background-color:inherit;}
18 a:hover{color:#2a5a8a; text-decoration:none; background-color:inherit;}
18 a:hover{color:#2a5a8a; text-decoration:none; background-color:inherit;}
19 a img{border:none;}
19 a img{border:none;}
20
20
21 p{margin:0 0 1em 0;}
21 p{margin:0 0 1em 0;}
22 p form{margin-top:0; margin-bottom:20px;}
22 p form{margin-top:0; margin-bottom:20px;}
23
23
24 img.left,img.center,img.right{padding:4px; border:1px solid #a0a0a0;}
24 img.left,img.center,img.right{padding:4px; border:1px solid #a0a0a0;}
25 img.left{float:left; margin:0 12px 5px 0;}
25 img.left{float:left; margin:0 12px 5px 0;}
26 img.center{display:block; margin:0 auto 5px auto;}
26 img.center{display:block; margin:0 auto 5px auto;}
27 img.right{float:right; margin:0 0 5px 12px;}
27 img.right{float:right; margin:0 0 5px 12px;}
28
28
29 /**************** Header and navigation styles ****************/
29 /**************** Header and navigation styles ****************/
30
30
31 #container{
31 #container{
32 width:100%;
32 width:100%;
33 min-width: 800px;
33 min-width: 800px;
34 margin:0;
34 margin:0;
35 padding:0;
35 padding:0;
36 text-align:left;
36 text-align:left;
37 background:#ffffff;
37 background:#ffffff;
38 color:#303030;
38 color:#303030;
39 }
39 }
40
40
41 #header{
41 #header{
42 height:4.5em;
42 height:4.5em;
43 margin:0;
43 margin:0;
44 background:#467aa7;
44 background:#467aa7;
45 color:#ffffff;
45 color:#ffffff;
46 margin-bottom:1px;
46 margin-bottom:1px;
47 }
47 }
48
48
49 #header h1{
49 #header h1{
50 padding:10px 0 0 20px;
50 padding:10px 0 0 20px;
51 font-size:2em;
51 font-size:2em;
52 background-color:inherit;
52 background-color:inherit;
53 color:#fff;
53 color:#fff;
54 letter-spacing:-1px;
54 letter-spacing:-1px;
55 font-weight:bold;
55 font-weight:bold;
56 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
56 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
57 }
57 }
58
58
59 #header h2{
59 #header h2{
60 margin:3px 0 0 40px;
60 margin:3px 0 0 40px;
61 font-size:1.5em;
61 font-size:1.5em;
62 background-color:inherit;
62 background-color:inherit;
63 color:#f0f2f4;
63 color:#f0f2f4;
64 letter-spacing:-1px;
64 letter-spacing:-1px;
65 font-weight:normal;
65 font-weight:normal;
66 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
66 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
67 }
67 }
68
68
69 #header a {color:#fff;}
69 #header a {color:#fff;}
70
70
71 #navigation{
71 #navigation{
72 height:2.2em;
72 height:2.2em;
73 line-height:2.2em;
73 line-height:2.2em;
74 margin:0;
74 margin:0;
75 background:#578bb8;
75 background:#578bb8;
76 color:#ffffff;
76 color:#ffffff;
77 }
77 }
78
78
79 #navigation li{
79 #navigation li{
80 float:left;
80 float:left;
81 list-style-type:none;
81 list-style-type:none;
82 border-right:1px solid #ffffff;
82 border-right:1px solid #ffffff;
83 white-space:nowrap;
83 white-space:nowrap;
84 }
84 }
85
85
86 #navigation li.right {
86 #navigation li.right {
87 float:right;
87 float:right;
88 list-style-type:none;
88 list-style-type:none;
89 border-right:0;
89 border-right:0;
90 border-left:1px solid #ffffff;
90 border-left:1px solid #ffffff;
91 white-space:nowrap;
91 white-space:nowrap;
92 }
92 }
93
93
94 #navigation li a{
94 #navigation li a{
95 display:block;
95 display:block;
96 padding:0px 10px 0px 22px;
96 padding:0px 10px 0px 22px;
97 font-size:0.8em;
97 font-size:0.8em;
98 font-weight:normal;
98 font-weight:normal;
99 text-decoration:none;
99 text-decoration:none;
100 background-color:inherit;
100 background-color:inherit;
101 color: #ffffff;
101 color: #ffffff;
102 }
102 }
103
103
104 #navigation li.submenu {background:url(../images/arrow_down.png) 96% 80% no-repeat;}
104 #navigation li.submenu {background:url(../images/arrow_down.png) 96% 80% no-repeat;}
105 #navigation li.submenu a {padding:0px 16px 0px 22px;}
105 #navigation li.submenu a {padding:0px 16px 0px 22px;}
106 * html #navigation a {width:1%;}
106 * html #navigation a {width:1%;}
107
107
108 #navigation .selected,#navigation a:hover{
108 #navigation .selected,#navigation a:hover{
109 color:#ffffff;
109 color:#ffffff;
110 text-decoration:none;
110 text-decoration:none;
111 background-color: #80b0da;
111 background-color: #80b0da;
112 }
112 }
113
113
114 /**************** Icons *******************/
114 /**************** Icons *******************/
115 .icon {
115 .icon {
116 background-position: 0% 40%;
116 background-position: 0% 40%;
117 background-repeat: no-repeat;
117 background-repeat: no-repeat;
118 padding-left: 20px;
118 padding-left: 20px;
119 padding-top: 2px;
119 padding-top: 2px;
120 padding-bottom: 3px;
120 padding-bottom: 3px;
121 vertical-align: middle;
122 }
121 }
123
122
124 #navigation .icon {
123 #navigation .icon {
125 background-position: 4px 50%;
124 background-position: 4px 50%;
126 }
125 }
127
126
128 .icon22 {
127 .icon22 {
129 background-position: 0% 40%;
128 background-position: 0% 40%;
130 background-repeat: no-repeat;
129 background-repeat: no-repeat;
131 padding-left: 26px;
130 padding-left: 26px;
132 line-height: 22px;
131 line-height: 22px;
133 vertical-align: middle;
132 vertical-align: middle;
134 }
133 }
135
134
136 .icon-add { background-image: url(../images/add.png); }
135 .icon-add { background-image: url(../images/add.png); }
137 .icon-edit { background-image: url(../images/edit.png); }
136 .icon-edit { background-image: url(../images/edit.png); }
138 .icon-del { background-image: url(../images/delete.png); }
137 .icon-del { background-image: url(../images/delete.png); }
139 .icon-move { background-image: url(../images/move.png); }
138 .icon-move { background-image: url(../images/move.png); }
140 .icon-save { background-image: url(../images/save.png); }
139 .icon-save { background-image: url(../images/save.png); }
141 .icon-cancel { background-image: url(../images/cancel.png); }
140 .icon-cancel { background-image: url(../images/cancel.png); }
142 .icon-pdf { background-image: url(../images/pdf.png); }
141 .icon-pdf { background-image: url(../images/pdf.png); }
143 .icon-csv { background-image: url(../images/csv.png); }
142 .icon-csv { background-image: url(../images/csv.png); }
144 .icon-html { background-image: url(../images/html.png); }
143 .icon-html { background-image: url(../images/html.png); }
145 .icon-image { background-image: url(../images/image.png); }
144 .icon-image { background-image: url(../images/image.png); }
146 .icon-txt { background-image: url(../images/txt.png); }
145 .icon-txt { background-image: url(../images/txt.png); }
147 .icon-file { background-image: url(../images/file.png); }
146 .icon-file { background-image: url(../images/file.png); }
148 .icon-folder { background-image: url(../images/folder.png); }
147 .icon-folder { background-image: url(../images/folder.png); }
149 .icon-package { background-image: url(../images/package.png); }
148 .icon-package { background-image: url(../images/package.png); }
150 .icon-home { background-image: url(../images/home.png); }
149 .icon-home { background-image: url(../images/home.png); }
151 .icon-user { background-image: url(../images/user.png); }
150 .icon-user { background-image: url(../images/user.png); }
152 .icon-mypage { background-image: url(../images/user_page.png); }
151 .icon-mypage { background-image: url(../images/user_page.png); }
153 .icon-admin { background-image: url(../images/admin.png); }
152 .icon-admin { background-image: url(../images/admin.png); }
154 .icon-projects { background-image: url(../images/projects.png); }
153 .icon-projects { background-image: url(../images/projects.png); }
155 .icon-logout { background-image: url(../images/logout.png); }
154 .icon-logout { background-image: url(../images/logout.png); }
156 .icon-help { background-image: url(../images/help.png); }
155 .icon-help { background-image: url(../images/help.png); }
157 .icon-attachment { background-image: url(../images/attachment.png); }
156 .icon-attachment { background-image: url(../images/attachment.png); }
158 .icon-index { background-image: url(../images/index.png); }
157 .icon-index { background-image: url(../images/index.png); }
159 .icon-history { background-image: url(../images/history.png); }
158 .icon-history { background-image: url(../images/history.png); }
160 .icon-feed { background-image: url(../images/feed.png); }
159 .icon-feed { background-image: url(../images/feed.png); }
161 .icon-time { background-image: url(../images/time.png); }
160 .icon-time { background-image: url(../images/time.png); }
162 .icon-stats { background-image: url(../images/stats.png); }
161 .icon-stats { background-image: url(../images/stats.png); }
163 .icon-warning { background-image: url(../images/warning.png); }
162 .icon-warning { background-image: url(../images/warning.png); }
164 .icon-fav { background-image: url(../images/fav.png); }
163 .icon-fav { background-image: url(../images/fav.png); }
165 .icon-fav-off { background-image: url(../images/fav_off.png); }
164 .icon-fav-off { background-image: url(../images/fav_off.png); }
166 .icon-reload { background-image: url(../images/reload.png); }
165 .icon-reload { background-image: url(../images/reload.png); }
167 .icon-lock { background-image: url(../images/locked.png); }
166 .icon-lock { background-image: url(../images/locked.png); }
168 .icon-unlock { background-image: url(../images/unlock.png); }
167 .icon-unlock { background-image: url(../images/unlock.png); }
169
168
170 .icon22-projects { background-image: url(../images/22x22/projects.png); }
169 .icon22-projects { background-image: url(../images/22x22/projects.png); }
171 .icon22-users { background-image: url(../images/22x22/users.png); }
170 .icon22-users { background-image: url(../images/22x22/users.png); }
172 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
171 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
173 .icon22-role { background-image: url(../images/22x22/role.png); }
172 .icon22-role { background-image: url(../images/22x22/role.png); }
174 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
173 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
175 .icon22-options { background-image: url(../images/22x22/options.png); }
174 .icon22-options { background-image: url(../images/22x22/options.png); }
176 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
175 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
177 .icon22-authent { background-image: url(../images/22x22/authent.png); }
176 .icon22-authent { background-image: url(../images/22x22/authent.png); }
178 .icon22-info { background-image: url(../images/22x22/info.png); }
177 .icon22-info { background-image: url(../images/22x22/info.png); }
179 .icon22-comment { background-image: url(../images/22x22/comment.png); }
178 .icon22-comment { background-image: url(../images/22x22/comment.png); }
180 .icon22-package { background-image: url(../images/22x22/package.png); }
179 .icon22-package { background-image: url(../images/22x22/package.png); }
181 .icon22-settings { background-image: url(../images/22x22/settings.png); }
180 .icon22-settings { background-image: url(../images/22x22/settings.png); }
182
181
183 /**************** Content styles ****************/
182 /**************** Content styles ****************/
184
183
185 html>body #content {
184 html>body #content {
186 height: auto;
185 height: auto;
187 min-height: 500px;
186 min-height: 500px;
188 }
187 }
189
188
190 #content{
189 #content{
191 width: auto;
190 width: auto;
192 height:500px;
191 height:500px;
193 font-size:0.9em;
192 font-size:0.9em;
194 padding:20px 10px 10px 20px;
193 padding:20px 10px 10px 20px;
195 margin-left: 120px;
194 margin-left: 120px;
196 border-left: 1px dashed #c0c0c0;
195 border-left: 1px dashed #c0c0c0;
197
196
198 }
197 }
199
198
200 #content h2, #content div.wiki h1 {
199 #content h2, #content div.wiki h1 {
201 display:block;
200 display:block;
202 margin:0 0 16px 0;
201 margin:0 0 16px 0;
203 font-size:1.7em;
202 font-size:1.7em;
204 font-weight:normal;
203 font-weight:normal;
205 letter-spacing:-1px;
204 letter-spacing:-1px;
206 color:#606060;
205 color:#606060;
207 background-color:inherit;
206 background-color:inherit;
208 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
207 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
209 }
208 }
210
209
211 #content h2 a{font-weight:normal;}
210 #content h2 a{font-weight:normal;}
212 #content h3{margin:0 0 12px 0; font-size:1.4em;color:#707070;font-family: Trebuchet MS,Georgia,"Times New Roman",serif;}
211 #content h3{margin:0 0 12px 0; font-size:1.4em;color:#707070;font-family: Trebuchet MS,Georgia,"Times New Roman",serif;}
213 #content h4{font-size: 1em; margin-bottom: 12px; margin-top: 20px; font-weight: normal; border-bottom: dotted 1px #c0c0c0;}
212 #content h4{font-size: 1em; margin-bottom: 12px; margin-top: 20px; font-weight: normal; border-bottom: dotted 1px #c0c0c0;}
214 #content a:hover,#subcontent a:hover{text-decoration:underline;}
213 #content a:hover,#subcontent a:hover{text-decoration:underline;}
215 #content ul,#content ol{margin:0 5px 16px 35px;}
214 #content ul,#content ol{margin:0 5px 16px 35px;}
216 #content dl{margin:0 5px 10px 25px;}
215 #content dl{margin:0 5px 10px 25px;}
217 #content dt{font-weight:bold; margin-bottom:5px;}
216 #content dt{font-weight:bold; margin-bottom:5px;}
218 #content dd{margin:0 0 10px 15px;}
217 #content dd{margin:0 0 10px 15px;}
219
218
220 #content .tabs{height: 2.6em;}
219 #content .tabs{height: 2.6em;}
221 #content .tabs ul{margin:0;}
220 #content .tabs ul{margin:0;}
222 #content .tabs ul li{
221 #content .tabs ul li{
223 float:left;
222 float:left;
224 list-style-type:none;
223 list-style-type:none;
225 white-space:nowrap;
224 white-space:nowrap;
226 margin-right:8px;
225 margin-right:8px;
227 background:#fff;
226 background:#fff;
228 }
227 }
229 #content .tabs ul li a{
228 #content .tabs ul li a{
230 display:block;
229 display:block;
231 font-size: 0.9em;
230 font-size: 0.9em;
232 text-decoration:none;
231 text-decoration:none;
233 line-height:1em;
232 line-height:1em;
234 padding:4px;
233 padding:4px;
235 border: 1px solid #c0c0c0;
234 border: 1px solid #c0c0c0;
236 }
235 }
237
236
238 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
237 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
239 background-color: #80b0da;
238 background-color: #80b0da;
240 border: 1px solid #80b0da;
239 border: 1px solid #80b0da;
241 color: #fff;
240 color: #fff;
242 text-decoration:none;
241 text-decoration:none;
243 }
242 }
244
243
245 /***********************************************/
244 /***********************************************/
246
245
247 form {display: inline;}
246 form {display: inline;}
248 blockquote {padding-left: 6px; border-left: 2px solid #ccc;}
247 blockquote {padding-left: 6px; border-left: 2px solid #ccc;}
249 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
248 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
250
249
251 input.button-small {font-size: 0.8em;}
250 input.button-small {font-size: 0.8em;}
252 textarea.wiki-edit { width: 99.5%; }
251 textarea.wiki-edit { width: 99.5%; }
253 .select-small {font-size: 0.8em;}
252 .select-small {font-size: 0.8em;}
254 label {font-weight: bold; font-size: 1em; color: #505050;}
253 label {font-weight: bold; font-size: 1em; color: #505050;}
255 fieldset {border:1px solid #c0c0c0; padding: 6px;}
254 fieldset {border:1px solid #c0c0c0; padding: 6px;}
256 legend {color: #505050;}
255 legend {color: #505050;}
257 .required {color: #bb0000;}
256 .required {color: #bb0000;}
258 .odd {background-color:#f6f7f8;}
257 .odd {background-color:#f6f7f8;}
259 .even {background-color: #fff;}
258 .even {background-color: #fff;}
260 hr { border:0; border-top: dotted 1px #fff; border-bottom: dotted 1px #c0c0c0; }
259 hr { border:0; border-top: dotted 1px #fff; border-bottom: dotted 1px #c0c0c0; }
261 table p {margin:0; padding:0;}
260 table p {margin:0; padding:0;}
262
261
263 .highlight { background-color: #FCFD8D;}
262 .highlight { background-color: #FCFD8D;}
264
263
265 div.square {
264 div.square {
266 border: 1px solid #999;
265 border: 1px solid #999;
267 float: left;
266 float: left;
268 margin: .4em .5em 0 0;
267 margin: .4em .5em 0 0;
269 overflow: hidden;
268 overflow: hidden;
270 width: .6em; height: .6em;
269 width: .6em; height: .6em;
271 }
270 }
272
271
273 ul.documents {
272 ul.documents {
274 list-style-type: none;
273 list-style-type: none;
275 padding: 0;
274 padding: 0;
276 margin: 0;
275 margin: 0;
277 }
276 }
278
277
279 ul.documents li {
278 ul.documents li {
280 background-image: url(../images/32x32/file.png);
279 background-image: url(../images/32x32/file.png);
281 background-repeat: no-repeat;
280 background-repeat: no-repeat;
282 background-position: 0 1px;
281 background-position: 0 1px;
283 padding-left: 36px;
282 padding-left: 36px;
284 margin-bottom: 10px;
283 margin-bottom: 10px;
285 margin-left: -37px;
284 margin-left: -37px;
286 }
285 }
287
286
288 /********** Table used to display lists of things ***********/
287 /********** Table used to display lists of things ***********/
289
288
290 table.list {
289 table.list {
291 width:100%;
290 width:100%;
292 border-collapse: collapse;
291 border-collapse: collapse;
293 border: 1px dotted #d0d0d0;
292 border: 1px dotted #d0d0d0;
294 margin-bottom: 6px;
293 margin-bottom: 6px;
295 }
294 }
296
295
297 table.with-cells td {
296 table.with-cells td {
298 border: 1px solid #d7d7d7;
297 border: 1px solid #d7d7d7;
299 }
298 }
300
299
301 table.list td {
300 table.list td {
302 padding:2px;
301 padding:2px;
303 }
302 }
304
303
305 table.list thead th {
304 table.list thead th {
306 text-align: center;
305 text-align: center;
307 background: #eee;
306 background: #eee;
308 border: 1px solid #d7d7d7;
307 border: 1px solid #d7d7d7;
309 color: #777;
308 color: #777;
310 }
309 }
311
310
312 table.list tbody th {
311 table.list tbody th {
313 font-weight: bold;
312 font-weight: bold;
314 background: #eed;
313 background: #eed;
315 border: 1px solid #d7d7d7;
314 border: 1px solid #d7d7d7;
316 color: #777;
315 color: #777;
317 }
316 }
318
317
319 /*========== Drop down menu ==============*/
318 /*========== Drop down menu ==============*/
320 div.menu {
319 div.menu {
321 background-color: #FFFFFF;
320 background-color: #FFFFFF;
322 border-style: solid;
321 border-style: solid;
323 border-width: 1px;
322 border-width: 1px;
324 border-color: #7F9DB9;
323 border-color: #7F9DB9;
325 position: absolute;
324 position: absolute;
326 top: 0px;
325 top: 0px;
327 left: 0px;
326 left: 0px;
328 padding: 0;
327 padding: 0;
329 visibility: hidden;
328 visibility: hidden;
330 z-index: 101;
329 z-index: 101;
331 }
330 }
332
331
333 div.menu a.menuItem {
332 div.menu a.menuItem {
334 font-size: 10px;
333 font-size: 10px;
335 font-weight: normal;
334 font-weight: normal;
336 line-height: 2em;
335 line-height: 2em;
337 color: #000000;
336 color: #000000;
338 background-color: #FFFFFF;
337 background-color: #FFFFFF;
339 cursor: default;
338 cursor: default;
340 display: block;
339 display: block;
341 padding: 0 1em;
340 padding: 0 1em;
342 margin: 0;
341 margin: 0;
343 border: 0;
342 border: 0;
344 text-decoration: none;
343 text-decoration: none;
345 white-space: nowrap;
344 white-space: nowrap;
346 }
345 }
347
346
348 div.menu a.menuItem:hover, div.menu a.menuItemHighlight {
347 div.menu a.menuItem:hover, div.menu a.menuItemHighlight {
349 background-color: #80b0da;
348 background-color: #80b0da;
350 color: #ffffff;
349 color: #ffffff;
351 }
350 }
352
351
353 div.menu a.menuItem span.menuItemText {}
352 div.menu a.menuItem span.menuItemText {}
354
353
355 div.menu a.menuItem span.menuItemArrow {
354 div.menu a.menuItem span.menuItemArrow {
356 margin-right: -.75em;
355 margin-right: -.75em;
357 }
356 }
358
357
359 /**************** Sidebar styles ****************/
358 /**************** Sidebar styles ****************/
360
359
361 #subcontent{
360 #subcontent{
362 position: absolute;
361 position: absolute;
363 left: 0px;
362 left: 0px;
364 width:95px;
363 width:95px;
365 padding:20px 20px 10px 5px;
364 padding:20px 20px 10px 5px;
366 overflow: hidden;
365 overflow: hidden;
367 }
366 }
368
367
369 #subcontent h2{
368 #subcontent h2{
370 display:block;
369 display:block;
371 margin:0 0 5px 0;
370 margin:0 0 5px 0;
372 font-size:1.0em;
371 font-size:1.0em;
373 font-weight:bold;
372 font-weight:bold;
374 text-align:left;
373 text-align:left;
375 color:#606060;
374 color:#606060;
376 background-color:inherit;
375 background-color:inherit;
377 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
376 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
378 }
377 }
379
378
380 #subcontent p{margin:0 0 16px 0; font-size:0.9em;}
379 #subcontent p{margin:0 0 16px 0; font-size:0.9em;}
381
380
382 /**************** Menublock styles ****************/
381 /**************** Menublock styles ****************/
383
382
384 .menublock{margin:0 0 20px 8px; font-size:0.8em;}
383 .menublock{margin:0 0 20px 8px; font-size:0.8em;}
385 .menublock li{list-style:none; display:block; padding:1px; margin-bottom:0px;}
384 .menublock li{list-style:none; display:block; padding:1px; margin-bottom:0px;}
386 .menublock li a{font-weight:bold; text-decoration:none;}
385 .menublock li a{font-weight:bold; text-decoration:none;}
387 .menublock li a:hover{text-decoration:none;}
386 .menublock li a:hover{text-decoration:none;}
388 .menublock li ul{margin:0; font-size:1em; font-weight:normal;}
387 .menublock li ul{margin:0; font-size:1em; font-weight:normal;}
389 .menublock li ul li{margin-bottom:0;}
388 .menublock li ul li{margin-bottom:0;}
390 .menublock li ul a{font-weight:normal;}
389 .menublock li ul a{font-weight:normal;}
391
390
392 /**************** Footer styles ****************/
391 /**************** Footer styles ****************/
393
392
394 #footer{
393 #footer{
395 clear:both;
394 clear:both;
396 padding:5px 0;
395 padding:5px 0;
397 margin:0;
396 margin:0;
398 font-size:0.9em;
397 font-size:0.9em;
399 color:#f0f0f0;
398 color:#f0f0f0;
400 background:#467aa7;
399 background:#467aa7;
401 }
400 }
402
401
403 #footer p{padding:0; margin:0; text-align:center;}
402 #footer p{padding:0; margin:0; text-align:center;}
404 #footer a{color:#f0f0f0; background-color:inherit; font-weight:bold;}
403 #footer a{color:#f0f0f0; background-color:inherit; font-weight:bold;}
405 #footer a:hover{color:#ffffff; background-color:inherit; text-decoration: underline;}
404 #footer a:hover{color:#ffffff; background-color:inherit; text-decoration: underline;}
406
405
407 /**************** Misc classes and styles ****************/
406 /**************** Misc classes and styles ****************/
408
407
409 .splitcontentleft{float:left; width:49%;}
408 .splitcontentleft{float:left; width:49%;}
410 .splitcontentright{float:right; width:49%;}
409 .splitcontentright{float:right; width:49%;}
411 .clear{clear:both;}
410 .clear{clear:both;}
412 .small{font-size:0.8em;line-height:1.4em;padding:0 0 0 0;}
411 .small{font-size:0.8em;line-height:1.4em;padding:0 0 0 0;}
413 .hide{display:none;}
412 .hide{display:none;}
414 .textcenter{text-align:center;}
413 .textcenter{text-align:center;}
415 .textright{text-align:right;}
414 .textright{text-align:right;}
416 .important{color:#f02025; background-color:inherit; font-weight:bold;}
415 .important{color:#f02025; background-color:inherit; font-weight:bold;}
417
416
418 .box{
417 .box{
419 margin:0 0 20px 0;
418 margin:0 0 20px 0;
420 padding:10px;
419 padding:10px;
421 border:1px solid #c0c0c0;
420 border:1px solid #c0c0c0;
422 background-color:#fafbfc;
421 background-color:#fafbfc;
423 color:#505050;
422 color:#505050;
424 line-height:1.5em;
423 line-height:1.5em;
425 }
424 }
426
425
427 a.close-icon {
426 a.close-icon {
428 display:block;
427 display:block;
429 margin-top:3px;
428 margin-top:3px;
430 overflow:hidden;
429 overflow:hidden;
431 width:12px;
430 width:12px;
432 height:12px;
431 height:12px;
433 background-repeat: no-repeat;
432 background-repeat: no-repeat;
434 cursor:pointer;
433 cursor:pointer;
435 background-image:url('../images/close.png');
434 background-image:url('../images/close.png');
436 }
435 }
437
436
438 a.close-icon:hover {
437 a.close-icon:hover {
439 background-image:url('../images/close_hl.png');
438 background-image:url('../images/close_hl.png');
440 }
439 }
441
440
442 .rightbox{
441 .rightbox{
443 background: #fafbfc;
442 background: #fafbfc;
444 border: 1px solid #c0c0c0;
443 border: 1px solid #c0c0c0;
445 float: right;
444 float: right;
446 padding: 8px;
445 padding: 8px;
447 position: relative;
446 position: relative;
448 margin: 0 5px 5px;
447 margin: 0 5px 5px;
449 }
448 }
450
449
451 div.attachments {padding-left: 6px; border-left: 2px solid #ccc; margin-bottom: 8px;}
450 div.attachments {padding-left: 6px; border-left: 2px solid #ccc; margin-bottom: 8px;}
452 div.attachments p {margin-bottom:2px;}
451 div.attachments p {margin-bottom:2px;}
453
452
454 .overlay{
453 .overlay{
455 position: absolute;
454 position: absolute;
456 margin-left:0;
455 margin-left:0;
457 z-index: 50;
456 z-index: 50;
458 }
457 }
459
458
460 .layout-active {
459 .layout-active {
461 background: #ECF3E1;
460 background: #ECF3E1;
462 }
461 }
463
462
464 .block-receiver {
463 .block-receiver {
465 border:1px dashed #c0c0c0;
464 border:1px dashed #c0c0c0;
466 margin-bottom: 20px;
465 margin-bottom: 20px;
467 padding: 15px 0 15px 0;
466 padding: 15px 0 15px 0;
468 }
467 }
469
468
470 .mypage-box {
469 .mypage-box {
471 margin:0 0 20px 0;
470 margin:0 0 20px 0;
472 color:#505050;
471 color:#505050;
473 line-height:1.5em;
472 line-height:1.5em;
474 }
473 }
475
474
476 .handle {
475 .handle {
477 cursor: move;
476 cursor: move;
478 }
477 }
479
478
480 .login {
479 .login {
481 width: 50%;
480 width: 50%;
482 text-align: left;
481 text-align: left;
483 }
482 }
484
483
485 img.calendar-trigger {
484 img.calendar-trigger {
486 cursor: pointer;
485 cursor: pointer;
487 vertical-align: middle;
486 vertical-align: middle;
488 margin-left: 4px;
487 margin-left: 4px;
489 }
488 }
490
489
491 #history p {
490 #history p {
492 margin-left: 34px;
491 margin-left: 34px;
493 }
492 }
494
493
495 .progress {
494 .progress {
496 border: 1px solid #D7D7D7;
495 border: 1px solid #D7D7D7;
497 border-collapse: collapse;
496 border-collapse: collapse;
498 border-spacing: 0pt;
497 border-spacing: 0pt;
499 empty-cells: show;
498 empty-cells: show;
500 padding: 3px;
499 padding: 3px;
501 width: 40em;
500 width: 40em;
502 text-align: center;
501 text-align: center;
503 }
502 }
504
503
505 .progress td { height: 1em; }
504 .progress td { height: 1em; }
506 .progress .closed { background: #BAE0BA none repeat scroll 0%; }
505 .progress .closed { background: #BAE0BA none repeat scroll 0%; }
507 .progress .open { background: #FFF none repeat scroll 0%; }
506 .progress .open { background: #FFF none repeat scroll 0%; }
508
507
509 /***** Contextual links div *****/
508 /***** Contextual links div *****/
510 .contextual {
509 .contextual {
511 float: right;
510 float: right;
512 font-size: 0.8em;
511 font-size: 0.8em;
513 line-height: 16px;
512 line-height: 16px;
514 padding: 2px;
513 padding: 2px;
515 }
514 }
516
515
517 .contextual select, .contextual input {
516 .contextual select, .contextual input {
518 font-size: 1em;
517 font-size: 1em;
519 }
518 }
520
519
521 /***** Gantt chart *****/
520 /***** Gantt chart *****/
522 .gantt_hdr {
521 .gantt_hdr {
523 position:absolute;
522 position:absolute;
524 top:0;
523 top:0;
525 height:16px;
524 height:16px;
526 border-top: 1px solid #c0c0c0;
525 border-top: 1px solid #c0c0c0;
527 border-bottom: 1px solid #c0c0c0;
526 border-bottom: 1px solid #c0c0c0;
528 border-right: 1px solid #c0c0c0;
527 border-right: 1px solid #c0c0c0;
529 text-align: center;
528 text-align: center;
530 overflow: hidden;
529 overflow: hidden;
531 }
530 }
532
531
533 .task {
532 .task {
534 position: absolute;
533 position: absolute;
535 height:8px;
534 height:8px;
536 font-size:0.8em;
535 font-size:0.8em;
537 color:#888;
536 color:#888;
538 padding:0;
537 padding:0;
539 margin:0;
538 margin:0;
540 line-height:0.8em;
539 line-height:0.8em;
541 }
540 }
542
541
543 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
542 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
544 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
543 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
545 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
544 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
546 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
545 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
547
546
547 /***** project list *****/
548 dl.projects dt { font-size: 120%; margin-top:1.2em; padding: 2px 2px 4px 2px; background-color:#fafbfc; }
549
548 /***** Tooltips ******/
550 /***** Tooltips ******/
549 .tooltip{position:relative;z-index:24;}
551 .tooltip{position:relative;z-index:24;}
550 .tooltip:hover{z-index:25;color:#000;}
552 .tooltip:hover{z-index:25;color:#000;}
551 .tooltip span.tip{display: none; text-align:left;}
553 .tooltip span.tip{display: none; text-align:left;}
552
554
553 div.tooltip:hover span.tip{
555 div.tooltip:hover span.tip{
554 display:block;
556 display:block;
555 position:absolute;
557 position:absolute;
556 top:12px; left:24px; width:270px;
558 top:12px; left:24px; width:270px;
557 border:1px solid #555;
559 border:1px solid #555;
558 background-color:#fff;
560 background-color:#fff;
559 padding: 4px;
561 padding: 4px;
560 font-size: 0.8em;
562 font-size: 0.8em;
561 color:#505050;
563 color:#505050;
562 }
564 }
563
565
564 /***** CSS FORM ******/
566 /***** CSS FORM ******/
565 .tabular p{
567 .tabular p{
566 margin: 0;
568 margin: 0;
567 padding: 5px 0 8px 0;
569 padding: 5px 0 8px 0;
568 padding-left: 180px; /*width of left column containing the label elements*/
570 padding-left: 180px; /*width of left column containing the label elements*/
569 height: 1%;
571 height: 1%;
570 clear:both;
572 clear:both;
571 }
573 }
572
574
573 .tabular label{
575 .tabular label{
574 font-weight: bold;
576 font-weight: bold;
575 float: left;
577 float: left;
576 margin-left: -180px; /*width of left column*/
578 margin-left: -180px; /*width of left column*/
577 margin-bottom: 10px;
579 margin-bottom: 10px;
578 width: 175px; /*width of labels. Should be smaller than left column to create some right
580 width: 175px; /*width of labels. Should be smaller than left column to create some right
579 margin*/
581 margin*/
580 }
582 }
581
583
582 .error {
584 .error {
583 color: #cc0000;
585 color: #cc0000;
584 }
586 }
585
587
586 #settings .tabular p{ padding-left: 300px; }
588 #settings .tabular p{ padding-left: 300px; }
587 #settings .tabular label{ margin-left: -300px; width: 295px; }
589 #settings .tabular label{ margin-left: -300px; width: 295px; }
588
590
589 /*.threepxfix class below:
591 /*.threepxfix class below:
590 Targets IE6- ONLY. Adds 3 pixel indent for multi-line form contents.
592 Targets IE6- ONLY. Adds 3 pixel indent for multi-line form contents.
591 to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepxtest.html
593 to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepxtest.html
592 */
594 */
593
595
594 * html .threepxfix{
596 * html .threepxfix{
595 margin-left: 3px;
597 margin-left: 3px;
596 }
598 }
597
599
598 /***** Wiki sections ****/
600 /***** Wiki sections ****/
599 #content div.wiki { font-size: 110%}
601 #content div.wiki { font-size: 110%}
600
602
601 #content div.wiki h2, div.wiki h3 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; color:#606060; }
603 #content div.wiki h2, div.wiki h3 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; color:#606060; }
602 #content div.wiki h2 { font-size: 1.4em;}
604 #content div.wiki h2 { font-size: 1.4em;}
603 #content div.wiki h3 { font-size: 1.2em;}
605 #content div.wiki h3 { font-size: 1.2em;}
604
606
605 div.wiki table {
607 div.wiki table {
606 border: 1px solid #505050;
608 border: 1px solid #505050;
607 border-collapse: collapse;
609 border-collapse: collapse;
608 }
610 }
609
611
610 div.wiki table, div.wiki td, div.wiki th {
612 div.wiki table, div.wiki td, div.wiki th {
611 border: 1px solid #bbb;
613 border: 1px solid #bbb;
612 padding: 4px;
614 padding: 4px;
613 }
615 }
614
616
615 div.wiki a {
617 div.wiki a {
616 background-position: 0% 60%;
618 background-position: 0% 60%;
617 background-repeat: no-repeat;
619 background-repeat: no-repeat;
618 padding-left: 12px;
620 padding-left: 12px;
619 background-image: url(../images/external.png);
621 background-image: url(../images/external.png);
620 }
622 }
621
623
622 div.wiki a.wiki-page, div.wiki a.issue, div.wiki a.changeset, div.wiki a.email, div.wiki div.toc a {
624 div.wiki a.wiki-page, div.wiki a.issue, div.wiki a.changeset, div.wiki a.email, div.wiki div.toc a {
623 padding-left: 0;
625 padding-left: 0;
624 background-image: none;
626 background-image: none;
625 }
627 }
626
628
627 div.wiki a.new {
629 div.wiki a.new {
628 color: #b73535;
630 color: #b73535;
629 }
631 }
630
632
631 div.wiki code {
633 div.wiki code {
632 font-size: 1.2em;
634 font-size: 1.2em;
633 }
635 }
634
636
635 div.wiki img {
637 div.wiki img {
636 margin: 6px;
638 margin: 6px;
637 }
639 }
638
640
639 div.wiki pre {
641 div.wiki pre {
640 margin: 1em 1em 1em 1.6em;
642 margin: 1em 1em 1em 1.6em;
641 padding: 2px;
643 padding: 2px;
642 background-color: #fafafa;
644 background-color: #fafafa;
643 border: 1px solid #dadada;
645 border: 1px solid #dadada;
644 }
646 }
645
647
646 div.wiki div.toc {
648 div.wiki div.toc {
647 background-color: #fdfed0;
649 background-color: #fdfed0;
648 border: 1px solid #dadada;
650 border: 1px solid #dadada;
649 padding: 4px;
651 padding: 4px;
650 line-height: 1.1em;
652 line-height: 1.1em;
651 margin-bottom: 12px;
653 margin-bottom: 12px;
652 float: left;
654 float: left;
653 margin-right: 12px;
655 margin-right: 12px;
654 }
656 }
655
657
656 div.wiki div.toc.right {
658 div.wiki div.toc.right {
657 float: right;
659 float: right;
658 margin-left: 12px;
660 margin-left: 12px;
659 margin-right: 0;
661 margin-right: 0;
660 }
662 }
661
663
662 div.wiki div.toc a {
664 div.wiki div.toc a {
663 display: block;
665 display: block;
664 font-size: 0.9em;
666 font-size: 0.9em;
665 font-weight: normal;
667 font-weight: normal;
666 color: #606060;
668 color: #606060;
667 }
669 }
668
670
669 div.wiki div.toc a.heading2 { margin-left: 6px; }
671 div.wiki div.toc a.heading2 { margin-left: 6px; }
670 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
672 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
671
673
672 div.wiki
674 div.wiki
673
675
674 .diff_out{
676 .diff_out{
675 background: #fcc;
677 background: #fcc;
676 }
678 }
677
679
678 .diff_in{
680 .diff_in{
679 background: #cfc;
681 background: #cfc;
680 }
682 }
681
683
682 #preview .preview { background: #fafbfc url(../images/draft.png); }
684 #preview .preview { background: #fafbfc url(../images/draft.png); }
683
685
684 #ajax-indicator {
686 #ajax-indicator {
685 position: absolute; /* fixed not supported by IE */
687 position: absolute; /* fixed not supported by IE */
686 background-color:#eee;
688 background-color:#eee;
687 border: 1px solid #bbb;
689 border: 1px solid #bbb;
688 top:35%;
690 top:35%;
689 left:40%;
691 left:40%;
690 width:20%;
692 width:20%;
691 font-weight:bold;
693 font-weight:bold;
692 text-align:center;
694 text-align:center;
693 padding:0.6em;
695 padding:0.6em;
694 z-index:100;
696 z-index:100;
695 filter:alpha(opacity=50);
697 filter:alpha(opacity=50);
696 -moz-opacity:0.5;
698 -moz-opacity:0.5;
697 opacity: 0.5;
699 opacity: 0.5;
698 -khtml-opacity: 0.5;
700 -khtml-opacity: 0.5;
699 }
701 }
700
702
701 html>body #ajax-indicator { position: fixed; }
703 html>body #ajax-indicator { position: fixed; }
702
704
703 #ajax-indicator span {
705 #ajax-indicator span {
704 background-position: 0% 40%;
706 background-position: 0% 40%;
705 background-repeat: no-repeat;
707 background-repeat: no-repeat;
706 background-image: url(../images/loading.gif);
708 background-image: url(../images/loading.gif);
707 padding-left: 26px;
709 padding-left: 26px;
708 vertical-align: bottom;
710 vertical-align: bottom;
709 }
711 }
710
712
711 /***** Flash & error messages ****/
713 /***** Flash & error messages ****/
712 #flash div, #errorExplanation {
714 #flash div, #errorExplanation {
713 padding: 4px 4px 4px 30px;
715 padding: 4px 4px 4px 30px;
714 margin-bottom: 16px;
716 margin-bottom: 16px;
715 font-size: 1.1em;
717 font-size: 1.1em;
716 border: 2px solid;
718 border: 2px solid;
717 }
719 }
718
720
719 #flash div.error, #errorExplanation {
721 #flash div.error, #errorExplanation {
720 background: url(../images/false.png) 8px 5px no-repeat;
722 background: url(../images/false.png) 8px 5px no-repeat;
721 background-color: #ffe3e3;
723 background-color: #ffe3e3;
722 border-color: #dd0000;
724 border-color: #dd0000;
723 color: #550000;
725 color: #550000;
724 }
726 }
725
727
726 #flash div.notice {
728 #flash div.notice {
727 background: url(../images/true.png) 8px 5px no-repeat;
729 background: url(../images/true.png) 8px 5px no-repeat;
728 background-color: #dfffdf;
730 background-color: #dfffdf;
729 border-color: #9fcf9f;
731 border-color: #9fcf9f;
730 color: #005f00;
732 color: #005f00;
731 }
733 }
732
734
733 #errorExplanation ul { margin-bottom: 0px; }
735 #errorExplanation ul { margin-bottom: 0px; }
734 #errorExplanation ul li { list-style: none; margin-left: -16px;}
736 #errorExplanation ul li { list-style: none; margin-left: -16px;}
General Comments 0
You need to be logged in to leave comments. Login now