##// END OF EJS Templates
Added versions due dates on gantt chart....
Jean-Philippe Lang -
r425:708c3c9ec6ef
parent child
Show More
@@ -1,688 +1,690
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'csv'
19 19
20 20 class ProjectsController < ApplicationController
21 21 layout 'base'
22 22 before_filter :find_project, :authorize, :except => [ :index, :list, :add ]
23 23 before_filter :require_admin, :only => [ :add, :destroy ]
24 24
25 25 helper :sort
26 26 include SortHelper
27 27 helper :custom_fields
28 28 include CustomFieldsHelper
29 29 helper :ifpdf
30 30 include IfpdfHelper
31 31 helper IssuesHelper
32 32 helper :queries
33 33 include QueriesHelper
34 34
35 35 def index
36 36 list
37 37 render :action => 'list' unless request.xhr?
38 38 end
39 39
40 40 # Lists public projects
41 41 def list
42 42 sort_init "#{Project.table_name}.name", "asc"
43 43 sort_update
44 44 @project_count = Project.count(:all, :conditions => ["is_public=?", true])
45 45 @project_pages = Paginator.new self, @project_count,
46 46 15,
47 47 params['page']
48 48 @projects = Project.find :all, :order => sort_clause,
49 49 :conditions => ["#{Project.table_name}.is_public=?", true],
50 50 :include => :parent,
51 51 :limit => @project_pages.items_per_page,
52 52 :offset => @project_pages.current.offset
53 53
54 54 render :action => "list", :layout => false if request.xhr?
55 55 end
56 56
57 57 # Add a new project
58 58 def add
59 59 @custom_fields = IssueCustomField.find(:all)
60 60 @root_projects = Project.find(:all, :conditions => "parent_id is null")
61 61 @project = Project.new(params[:project])
62 62 if request.get?
63 63 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
64 64 else
65 65 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
66 66 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
67 67 @project.custom_values = @custom_values
68 68 if params[:repository_enabled] && params[:repository_enabled] == "1"
69 69 @project.repository = Repository.new
70 70 @project.repository.attributes = params[:repository]
71 71 end
72 72 if "1" == params[:wiki_enabled]
73 73 @project.wiki = Wiki.new
74 74 @project.wiki.attributes = params[:wiki]
75 75 end
76 76 if @project.save
77 77 flash[:notice] = l(:notice_successful_create)
78 78 redirect_to :controller => 'admin', :action => 'projects'
79 79 end
80 80 end
81 81 end
82 82
83 83 # Show @project
84 84 def show
85 85 @custom_values = @project.custom_values.find(:all, :include => :custom_field)
86 86 @members = @project.members.find(:all, :include => [:user, :role], :order => 'position')
87 87 @subprojects = @project.children if @project.children.size > 0
88 88 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
89 89 @trackers = Tracker.find(:all, :order => 'position')
90 90 @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])
91 91 @total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id])
92 92 end
93 93
94 94 def settings
95 95 @root_projects = Project::find(:all, :conditions => ["parent_id is null and id <> ?", @project.id])
96 96 @custom_fields = IssueCustomField.find(:all)
97 97 @issue_category ||= IssueCategory.new
98 98 @member ||= @project.members.new
99 99 @roles = Role.find(:all, :order => 'position')
100 100 @users = User.find_active(:all) - @project.users
101 101 @custom_values ||= ProjectCustomField.find(:all).collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
102 102 end
103 103
104 104 # Edit @project
105 105 def edit
106 106 if request.post?
107 107 @project.custom_fields = IssueCustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
108 108 if params[:custom_fields]
109 109 @custom_values = ProjectCustomField.find(:all).collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
110 110 @project.custom_values = @custom_values
111 111 end
112 112 if params[:repository_enabled]
113 113 case params[:repository_enabled]
114 114 when "0"
115 115 @project.repository = nil
116 116 when "1"
117 117 @project.repository ||= Repository.new
118 118 @project.repository.update_attributes params[:repository]
119 119 end
120 120 end
121 121 if params[:wiki_enabled]
122 122 case params[:wiki_enabled]
123 123 when "0"
124 124 @project.wiki.destroy if @project.wiki
125 125 when "1"
126 126 @project.wiki ||= Wiki.new
127 127 @project.wiki.update_attributes params[:wiki]
128 128 end
129 129 end
130 130 @project.attributes = params[:project]
131 131 if @project.save
132 132 flash[:notice] = l(:notice_successful_update)
133 133 redirect_to :action => 'settings', :id => @project
134 134 else
135 135 settings
136 136 render :action => 'settings'
137 137 end
138 138 end
139 139 end
140 140
141 141 # Delete @project
142 142 def destroy
143 143 if request.post? and params[:confirm]
144 144 @project.destroy
145 145 redirect_to :controller => 'admin', :action => 'projects'
146 146 end
147 147 end
148 148
149 149 # Add a new issue category to @project
150 150 def add_issue_category
151 151 if request.post?
152 152 @issue_category = @project.issue_categories.build(params[:issue_category])
153 153 if @issue_category.save
154 154 flash[:notice] = l(:notice_successful_create)
155 155 redirect_to :action => 'settings', :tab => 'categories', :id => @project
156 156 else
157 157 settings
158 158 render :action => 'settings'
159 159 end
160 160 end
161 161 end
162 162
163 163 # Add a new version to @project
164 164 def add_version
165 165 @version = @project.versions.build(params[:version])
166 166 if request.post? and @version.save
167 167 flash[:notice] = l(:notice_successful_create)
168 168 redirect_to :action => 'settings', :tab => 'versions', :id => @project
169 169 end
170 170 end
171 171
172 172 # Add a new member to @project
173 173 def add_member
174 174 @member = @project.members.build(params[:member])
175 175 if request.post?
176 176 if @member.save
177 177 flash[:notice] = l(:notice_successful_create)
178 178 redirect_to :action => 'settings', :tab => 'members', :id => @project
179 179 else
180 180 settings
181 181 render :action => 'settings'
182 182 end
183 183 end
184 184 end
185 185
186 186 # Show members list of @project
187 187 def list_members
188 188 @members = @project.members.find(:all)
189 189 end
190 190
191 191 # Add a new document to @project
192 192 def add_document
193 193 @categories = Enumeration::get_values('DCAT')
194 194 @document = @project.documents.build(params[:document])
195 195 if request.post? and @document.save
196 196 # Save the attachments
197 197 params[:attachments].each { |a|
198 198 Attachment.create(:container => @document, :file => a, :author => logged_in_user) unless a.size == 0
199 199 } if params[:attachments] and params[:attachments].is_a? Array
200 200 flash[:notice] = l(:notice_successful_create)
201 201 Mailer.deliver_document_add(@document) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
202 202 redirect_to :action => 'list_documents', :id => @project
203 203 end
204 204 end
205 205
206 206 # Show documents list of @project
207 207 def list_documents
208 208 @documents = @project.documents.find :all, :include => :category
209 209 end
210 210
211 211 # Add a new issue to @project
212 212 def add_issue
213 213 @tracker = Tracker.find(params[:tracker_id])
214 214 @priorities = Enumeration::get_values('IPRI')
215 215
216 216 default_status = IssueStatus.default
217 217 @issue = Issue.new(:project => @project, :tracker => @tracker)
218 218 @issue.status = default_status
219 219 @allowed_statuses = default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user
220 220 if request.get?
221 221 @issue.start_date = Date.today
222 222 @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
223 223 else
224 224 @issue.attributes = params[:issue]
225 225
226 226 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
227 227 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
228 228
229 229 @issue.author_id = self.logged_in_user.id if self.logged_in_user
230 230 # Multiple file upload
231 231 @attachments = []
232 232 params[:attachments].each { |a|
233 233 @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
234 234 } if params[:attachments] and params[:attachments].is_a? Array
235 235 @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]) }
236 236 @issue.custom_values = @custom_values
237 237 if @issue.save
238 238 @attachments.each(&:save)
239 239 flash[:notice] = l(:notice_successful_create)
240 240 Mailer.deliver_issue_add(@issue) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
241 241 redirect_to :action => 'list_issues', :id => @project
242 242 end
243 243 end
244 244 end
245 245
246 246 # Show filtered/sorted issues list of @project
247 247 def list_issues
248 248 sort_init "#{Issue.table_name}.id", "desc"
249 249 sort_update
250 250
251 251 retrieve_query
252 252
253 253 @results_per_page_options = [ 15, 25, 50, 100 ]
254 254 if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i
255 255 @results_per_page = params[:per_page].to_i
256 256 session[:results_per_page] = @results_per_page
257 257 else
258 258 @results_per_page = session[:results_per_page] || 25
259 259 end
260 260
261 261 if @query.valid?
262 262 @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement)
263 263 @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page']
264 264 @issues = Issue.find :all, :order => sort_clause,
265 265 :include => [ :assigned_to, :status, :tracker, :project, :priority ],
266 266 :conditions => @query.statement,
267 267 :limit => @issue_pages.items_per_page,
268 268 :offset => @issue_pages.current.offset
269 269 end
270 270 @trackers = Tracker.find :all, :order => 'position'
271 271 render :layout => false if request.xhr?
272 272 end
273 273
274 274 # Export filtered/sorted issues list to CSV
275 275 def export_issues_csv
276 276 sort_init "#{Issue.table_name}.id", "desc"
277 277 sort_update
278 278
279 279 retrieve_query
280 280 render :action => 'list_issues' and return unless @query.valid?
281 281
282 282 @issues = Issue.find :all, :order => sort_clause,
283 283 :include => [ :assigned_to, :author, :status, :tracker, :priority, {:custom_values => :custom_field} ],
284 284 :conditions => @query.statement,
285 285 :limit => Setting.issues_export_limit
286 286
287 287 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
288 288 export = StringIO.new
289 289 CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
290 290 # csv header fields
291 291 headers = [ "#", l(:field_status),
292 292 l(:field_tracker),
293 293 l(:field_priority),
294 294 l(:field_subject),
295 295 l(:field_assigned_to),
296 296 l(:field_author),
297 297 l(:field_start_date),
298 298 l(:field_due_date),
299 299 l(:field_done_ratio),
300 300 l(:field_created_on),
301 301 l(:field_updated_on)
302 302 ]
303 303 for custom_field in @project.all_custom_fields
304 304 headers << custom_field.name
305 305 end
306 306 csv << headers.collect {|c| ic.iconv(c) }
307 307 # csv lines
308 308 @issues.each do |issue|
309 309 fields = [issue.id, issue.status.name,
310 310 issue.tracker.name,
311 311 issue.priority.name,
312 312 issue.subject,
313 313 (issue.assigned_to ? issue.assigned_to.name : ""),
314 314 issue.author.name,
315 315 issue.start_date ? l_date(issue.start_date) : nil,
316 316 issue.due_date ? l_date(issue.due_date) : nil,
317 317 issue.done_ratio,
318 318 l_datetime(issue.created_on),
319 319 l_datetime(issue.updated_on)
320 320 ]
321 321 for custom_field in @project.all_custom_fields
322 322 fields << (show_value issue.custom_value_for(custom_field))
323 323 end
324 324 csv << fields.collect {|c| ic.iconv(c.to_s) }
325 325 end
326 326 end
327 327 export.rewind
328 328 send_data(export.read, :type => 'text/csv; header=present', :filename => 'export.csv')
329 329 end
330 330
331 331 # Export filtered/sorted issues to PDF
332 332 def export_issues_pdf
333 333 sort_init "#{Issue.table_name}.id", "desc"
334 334 sort_update
335 335
336 336 retrieve_query
337 337 render :action => 'list_issues' and return unless @query.valid?
338 338
339 339 @issues = Issue.find :all, :order => sort_clause,
340 340 :include => [ :author, :status, :tracker, :priority ],
341 341 :conditions => @query.statement,
342 342 :limit => Setting.issues_export_limit
343 343
344 344 @options_for_rfpdf ||= {}
345 345 @options_for_rfpdf[:file_name] = "export.pdf"
346 346 render :layout => false
347 347 end
348 348
349 349 def move_issues
350 350 @issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids]
351 351 redirect_to :action => 'list_issues', :id => @project and return unless @issues
352 352 @projects = []
353 353 # find projects to which the user is allowed to move the issue
354 354 @logged_in_user.memberships.each {|m| @projects << m.project if Permission.allowed_to_role("projects/move_issues", m.role)}
355 355 # issue can be moved to any tracker
356 356 @trackers = Tracker.find(:all)
357 357 if request.post? and params[:new_project_id] and params[:new_tracker_id]
358 358 new_project = Project.find(params[:new_project_id])
359 359 new_tracker = Tracker.find(params[:new_tracker_id])
360 360 @issues.each { |i|
361 361 # project dependent properties
362 362 unless i.project_id == new_project.id
363 363 i.category = nil
364 364 i.fixed_version = nil
365 365 end
366 366 # move the issue
367 367 i.project = new_project
368 368 i.tracker = new_tracker
369 369 i.save
370 370 }
371 371 flash[:notice] = l(:notice_successful_update)
372 372 redirect_to :action => 'list_issues', :id => @project
373 373 end
374 374 end
375 375
376 376 def add_query
377 377 @query = Query.new(params[:query])
378 378 @query.project = @project
379 379 @query.user = logged_in_user
380 380
381 381 params[:fields].each do |field|
382 382 @query.add_filter(field, params[:operators][field], params[:values][field])
383 383 end if params[:fields]
384 384
385 385 if request.post? and @query.save
386 386 flash[:notice] = l(:notice_successful_create)
387 387 redirect_to :controller => 'reports', :action => 'issue_report', :id => @project
388 388 end
389 389 render :layout => false if request.xhr?
390 390 end
391 391
392 392 # Add a news to @project
393 393 def add_news
394 394 @news = News.new(:project => @project)
395 395 if request.post?
396 396 @news.attributes = params[:news]
397 397 @news.author_id = self.logged_in_user.id if self.logged_in_user
398 398 if @news.save
399 399 flash[:notice] = l(:notice_successful_create)
400 400 redirect_to :action => 'list_news', :id => @project
401 401 end
402 402 end
403 403 end
404 404
405 405 # Show news list of @project
406 406 def list_news
407 407 @news_pages, @news = paginate :news, :per_page => 10, :conditions => ["project_id=?", @project.id], :include => :author, :order => "#{News.table_name}.created_on DESC"
408 408 render :action => "list_news", :layout => false if request.xhr?
409 409 end
410 410
411 411 def add_file
412 412 if request.post?
413 413 @version = @project.versions.find_by_id(params[:version_id])
414 414 # Save the attachments
415 415 @attachments = []
416 416 params[:attachments].each { |file|
417 417 next unless file.size > 0
418 418 a = Attachment.create(:container => @version, :file => file, :author => logged_in_user)
419 419 @attachments << a unless a.new_record?
420 420 } if params[:attachments] and params[:attachments].is_a? Array
421 421 Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled?
422 422 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
423 423 end
424 424 @versions = @project.versions
425 425 end
426 426
427 427 def list_files
428 428 @versions = @project.versions
429 429 end
430 430
431 431 # Show changelog for @project
432 432 def changelog
433 433 @trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
434 434 retrieve_selected_tracker_ids(@trackers)
435 435
436 436 @fixed_issues = @project.issues.find(:all,
437 437 :include => [ :fixed_version, :status, :tracker ],
438 438 :conditions => [ "#{IssueStatus.table_name}.is_closed=? and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}) and #{Issue.table_name}.fixed_version_id is not null", true],
439 439 :order => "#{Version.table_name}.effective_date DESC, #{Issue.table_name}.id DESC"
440 440 ) unless @selected_tracker_ids.empty?
441 441 @fixed_issues ||= []
442 442 end
443 443
444 444 def roadmap
445 445 @trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
446 446 retrieve_selected_tracker_ids(@trackers)
447 447
448 448 @versions = @project.versions.find(:all,
449 449 :conditions => [ "#{Version.table_name}.effective_date>?", Date.today],
450 450 :order => "#{Version.table_name}.effective_date ASC"
451 451 )
452 452 end
453 453
454 454 def activity
455 455 if params[:year] and params[:year].to_i > 1900
456 456 @year = params[:year].to_i
457 457 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
458 458 @month = params[:month].to_i
459 459 end
460 460 end
461 461 @year ||= Date.today.year
462 462 @month ||= Date.today.month
463 463
464 464 @date_from = Date.civil(@year, @month, 1)
465 465 @date_to = (@date_from >> 1)-1
466 466
467 467 @events_by_day = {}
468 468
469 469 unless params[:show_issues] == "0"
470 470 @project.issues.find(:all, :include => [:author, :status], :conditions => ["#{Issue.table_name}.created_on>=? and #{Issue.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
471 471 @events_by_day[i.created_on.to_date] ||= []
472 472 @events_by_day[i.created_on.to_date] << i
473 473 }
474 474 @show_issues = 1
475 475 end
476 476
477 477 unless params[:show_news] == "0"
478 478 @project.news.find(:all, :conditions => ["#{News.table_name}.created_on>=? and #{News.table_name}.created_on<=?", @date_from, @date_to], :include => :author ).each { |i|
479 479 @events_by_day[i.created_on.to_date] ||= []
480 480 @events_by_day[i.created_on.to_date] << i
481 481 }
482 482 @show_news = 1
483 483 end
484 484
485 485 unless params[:show_files] == "0"
486 486 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 ).each { |i|
487 487 @events_by_day[i.created_on.to_date] ||= []
488 488 @events_by_day[i.created_on.to_date] << i
489 489 }
490 490 @show_files = 1
491 491 end
492 492
493 493 unless params[:show_documents] == "0"
494 494 @project.documents.find(:all, :conditions => ["#{Document.table_name}.created_on>=? and #{Document.table_name}.created_on<=?", @date_from, @date_to] ).each { |i|
495 495 @events_by_day[i.created_on.to_date] ||= []
496 496 @events_by_day[i.created_on.to_date] << i
497 497 }
498 498 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 ).each { |i|
499 499 @events_by_day[i.created_on.to_date] ||= []
500 500 @events_by_day[i.created_on.to_date] << i
501 501 }
502 502 @show_documents = 1
503 503 end
504 504
505 505 unless params[:show_wiki_edits] == "0"
506 506 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comment, " +
507 507 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title"
508 508 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
509 509 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id "
510 510 conditions = ["#{Wiki.table_name}.project_id = ? AND #{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?",
511 511 @project.id, @date_from, @date_to]
512 512
513 513 WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => conditions).each { |i|
514 514 # We provide this alias so all events can be treated in the same manner
515 515 def i.created_on
516 516 self.updated_on
517 517 end
518 518
519 519 @events_by_day[i.created_on.to_date] ||= []
520 520 @events_by_day[i.created_on.to_date] << i
521 521 }
522 522 @show_wiki_edits = 1
523 523 end
524 524
525 525 unless @project.repository.nil? || params[:show_changesets] == "0"
526 526 @project.repository.changesets.find(:all, :conditions => ["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to]).each { |i|
527 527 def i.created_on
528 528 self.committed_on
529 529 end
530 530 @events_by_day[i.created_on.to_date] ||= []
531 531 @events_by_day[i.created_on.to_date] << i
532 532 }
533 533 @show_changesets = 1
534 534 end
535 535
536 536 render :layout => false if request.xhr?
537 537 end
538 538
539 539 def calendar
540 540 @trackers = Tracker.find(:all, :order => 'position')
541 541 retrieve_selected_tracker_ids(@trackers)
542 542
543 543 if params[:year] and params[:year].to_i > 1900
544 544 @year = params[:year].to_i
545 545 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
546 546 @month = params[:month].to_i
547 547 end
548 548 end
549 549 @year ||= Date.today.year
550 550 @month ||= Date.today.month
551 551
552 552 @date_from = Date.civil(@year, @month, 1)
553 553 @date_to = (@date_from >> 1)-1
554 554 # start on monday
555 555 @date_from = @date_from - (@date_from.cwday-1)
556 556 # finish on sunday
557 557 @date_to = @date_to + (7-@date_to.cwday)
558 558
559 559 @project.issues_with_subprojects(params[:with_subprojects]) do
560 560 @issues = Issue.find(:all,
561 561 :include => [:tracker, :status, :assigned_to, :priority],
562 562 :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]
563 563 ) unless @selected_tracker_ids.empty?
564 564 end
565 565 @issues ||=[]
566 566
567 567 @ending_issues_by_days = @issues.group_by {|issue| issue.due_date}
568 568 @starting_issues_by_days = @issues.group_by {|issue| issue.start_date}
569 569
570 570 render :layout => false if request.xhr?
571 571 end
572 572
573 573 def gantt
574 574 @trackers = Tracker.find(:all, :order => 'position')
575 575 retrieve_selected_tracker_ids(@trackers)
576 576
577 577 if params[:year] and params[:year].to_i >0
578 578 @year_from = params[:year].to_i
579 579 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
580 580 @month_from = params[:month].to_i
581 581 else
582 582 @month_from = 1
583 583 end
584 584 else
585 585 @month_from ||= (Date.today << 1).month
586 586 @year_from ||= (Date.today << 1).year
587 587 end
588 588
589 589 @zoom = (params[:zoom].to_i > 0 and params[:zoom].to_i < 5) ? params[:zoom].to_i : 2
590 590 @months = (params[:months].to_i > 0 and params[:months].to_i < 25) ? params[:months].to_i : 6
591 591
592 592 @date_from = Date.civil(@year_from, @month_from, 1)
593 593 @date_to = (@date_from >> @months) - 1
594 594
595 @events = []
595 596 @project.issues_with_subprojects(params[:with_subprojects]) do
596 @issues = Issue.find(:all,
597 @events += Issue.find(:all,
597 598 :order => "start_date, due_date",
598 599 :include => [:tracker, :status, :assigned_to, :priority],
599 600 :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]
600 601 ) unless @selected_tracker_ids.empty?
601 602 end
602 @issues ||=[]
603 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
604 @events.sort! {|x,y| x.start_date <=> y.start_date }
603 605
604 606 if params[:output]=='pdf'
605 607 @options_for_rfpdf ||= {}
606 608 @options_for_rfpdf[:file_name] = "gantt.pdf"
607 609 render :template => "projects/gantt.rfpdf", :layout => false
608 610 else
609 611 render :template => "projects/gantt.rhtml"
610 612 end
611 613 end
612 614
613 615 def search
614 616 @question = params[:q] || ""
615 617 @question.strip!
616 618 @all_words = params[:all_words] || (params[:submit] ? false : true)
617 619 @scope = params[:scope] || (params[:submit] ? [] : %w(issues changesets news documents wiki) )
618 620 # tokens must be at least 3 character long
619 621 @tokens = @question.split.uniq.select {|w| w.length > 2 }
620 622 if !@tokens.empty?
621 623 # no more than 5 tokens to search for
622 624 @tokens.slice! 5..-1 if @tokens.size > 5
623 625 # strings used in sql like statement
624 626 like_tokens = @tokens.collect {|w| "%#{w}%"}
625 627 operator = @all_words ? " AND " : " OR "
626 628 limit = 10
627 629 @results = []
628 630 @results += @project.issues.find(:all, :limit => limit, :include => :author, :conditions => [ (["(LOWER(subject) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'issues'
629 631 @results += @project.news.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort], :include => :author ) if @scope.include? 'news'
630 632 @results += @project.documents.find(:all, :limit => limit, :conditions => [ (["(LOWER(title) like ? OR LOWER(description) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @scope.include? 'documents'
631 633 @results += @project.wiki.pages.find(:all, :limit => limit, :include => :content, :conditions => [ (["(LOWER(title) like ? OR LOWER(text) like ?)"] * like_tokens.size).join(operator), * (like_tokens * 2).sort] ) if @project.wiki && @scope.include?('wiki')
632 634 @results += @project.repository.changesets.find(:all, :limit => limit, :conditions => [ (["(LOWER(comment) like ?)"] * like_tokens.size).join(operator), * (like_tokens).sort] ) if @project.repository && @scope.include?('changesets')
633 635 @question = @tokens.join(" ")
634 636 else
635 637 @question = ""
636 638 end
637 639 end
638 640
639 641 def feeds
640 642 @queries = @project.queries.find :all, :conditions => ["is_public=? or user_id=?", true, (logged_in_user ? logged_in_user.id : 0)]
641 643 @key = logged_in_user.get_or_create_rss_key.value if logged_in_user
642 644 end
643 645
644 646 private
645 647 # Find project of id params[:id]
646 648 # if not found, redirect to project list
647 649 # Used as a before_filter
648 650 def find_project
649 651 @project = Project.find(params[:id])
650 652 @html_title = @project.name
651 653 rescue ActiveRecord::RecordNotFound
652 654 render_404
653 655 end
654 656
655 657 def retrieve_selected_tracker_ids(selectable_trackers)
656 658 if ids = params[:tracker_ids]
657 659 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
658 660 else
659 661 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
660 662 end
661 663 end
662 664
663 665 # Retrieve query from session or build a new query
664 666 def retrieve_query
665 667 if params[:query_id]
666 668 @query = @project.queries.find(params[:query_id])
667 669 session[:query] = @query
668 670 else
669 671 if params[:set_filter] or !session[:query] or session[:query].project_id != @project.id
670 672 # Give it a name, required to be valid
671 673 @query = Query.new(:name => "_")
672 674 @query.project = @project
673 675 if params[:fields] and params[:fields].is_a? Array
674 676 params[:fields].each do |field|
675 677 @query.add_filter(field, params[:operators][field], params[:values][field])
676 678 end
677 679 else
678 680 @query.available_filters.keys.each do |field|
679 681 @query.add_short_filter(field, params[field]) if params[field]
680 682 end
681 683 end
682 684 session[:query] = @query
683 685 else
684 686 @query = session[:query]
685 687 end
686 688 end
687 689 end
688 690 end
@@ -1,32 +1,40
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Version < ActiveRecord::Base
19 19 before_destroy :check_integrity
20 20 belongs_to :project
21 21 has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
22 22 has_many :attachments, :as => :container, :dependent => :destroy
23 23
24 24 validates_presence_of :name
25 25 validates_uniqueness_of :name, :scope => [:project_id]
26 26 validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :activerecord_error_not_a_date
27 27
28 def start_date
29 effective_date
30 end
31
32 def due_date
33 effective_date
34 end
35
28 36 private
29 37 def check_integrity
30 38 raise "Can't delete version" if self.fixed_issues.find(:first)
31 39 end
32 40 end
@@ -1,169 +1,188
1 1 <%
2 2 pdf=IfpdfHelper::IFPDF.new(current_language)
3 3 pdf.SetTitle("#{@project.name} - #{l(:label_gantt)}")
4 4 pdf.AliasNbPages
5 5 pdf.footer_date = format_date(Date.today)
6 6 pdf.AddPage("L")
7 7 pdf.SetFontStyle('B',12)
8 8 pdf.SetX(15)
9 9 pdf.Cell(70, 20, @project.name)
10 10 pdf.Ln
11 11 pdf.SetFontStyle('B',9)
12 12
13 13 subject_width = 70
14 14 header_heigth = 5
15 15
16 16 headers_heigth = header_heigth
17 17 show_weeks = false
18 18 show_days = false
19 19
20 20 if @months < 7
21 21 show_weeks = true
22 22 headers_heigth = 2*header_heigth
23 23 if @months < 3
24 24 show_days = true
25 25 headers_heigth = 3*header_heigth
26 26 end
27 27 end
28 28
29 29 g_width = 210
30 30 zoom = (g_width) / (@date_to - @date_from + 1)
31 31 g_height = 120
32 32 t_height = g_height + headers_heigth
33 33
34 34 y_start = pdf.GetY
35 35
36 36
37 37 #
38 38 # Months headers
39 39 #
40 40 month_f = @date_from
41 41 left = subject_width
42 42 height = header_heigth
43 43 @months.times do
44 44 width = ((month_f >> 1) - month_f) * zoom
45 45 pdf.SetY(y_start)
46 46 pdf.SetX(left)
47 47 pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
48 48 left = left + width
49 49 month_f = month_f >> 1
50 50 end
51 51
52 52 #
53 53 # Weeks headers
54 54 #
55 55 if show_weeks
56 56 left = subject_width
57 57 height = header_heigth
58 58 if @date_from.cwday == 1
59 59 # @date_from is monday
60 60 week_f = @date_from
61 61 else
62 62 # find next monday after @date_from
63 63 week_f = @date_from + (7 - @date_from.cwday + 1)
64 64 width = (7 - @date_from.cwday + 1) * zoom-1
65 65 pdf.SetY(y_start + header_heigth)
66 66 pdf.SetX(left)
67 67 pdf.Cell(width + 1, height, "", "LTR")
68 68 left = left + width+1
69 69 end
70 70 while week_f <= @date_to
71 71 width = (week_f + 6 <= @date_to) ? 7 * zoom : (@date_to - week_f + 1) * zoom
72 72 pdf.SetY(y_start + header_heigth)
73 73 pdf.SetX(left)
74 74 pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
75 75 left = left + width
76 76 week_f = week_f+7
77 77 end
78 78 end
79 79
80 80 #
81 81 # Days headers
82 82 #
83 83 if show_days
84 84 left = subject_width
85 85 height = header_heigth
86 86 wday = @date_from.cwday
87 87 pdf.SetFontStyle('B',7)
88 88 (@date_to - @date_from + 1).to_i.times do
89 89 width = zoom
90 90 pdf.SetY(y_start + 2 * header_heigth)
91 91 pdf.SetX(left)
92 92 pdf.Cell(width, height, day_name(wday)[0,1], "LTR", 0, "C")
93 93 left = left + width
94 94 wday = wday + 1
95 95 wday = 1 if wday > 7
96 96 end
97 97 end
98 98
99 99 pdf.SetY(y_start)
100 100 pdf.SetX(15)
101 101 pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
102 102
103 103
104 104 #
105 105 # Tasks
106 106 #
107 107 top = headers_heigth + y_start
108 108 pdf.SetFontStyle('B',7)
109 @issues.each do |i|
109 @events.each do |i|
110 110 pdf.SetY(top)
111 111 pdf.SetX(15)
112 pdf.Cell(subject_width-15, 5, "#{i.tracker.name} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
112
113 if i.is_a? Issue
114 pdf.Cell(subject_width-15, 5, "#{i.tracker.name} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
115 else
116 pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
117 end
113 118
114 119 pdf.SetY(top)
115 120 pdf.SetX(subject_width)
116 121 pdf.Cell(g_width, 5, "", "LR")
122
123 pdf.SetY(top+1.5)
117 124
118 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
119 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
120
121 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
122 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
123 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
125 if i.is_a? Issue
126 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
127 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
128
129 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
130 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
131 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
132
133 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
134
135 i_left = ((i_start_date - @date_from)*zoom)
136 i_width = ((i_end_date - i_start_date + 1)*zoom)
137 d_width = ((i_done_date - i_start_date)*zoom)
138 l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
139 l_width ||= 0
124 140
125 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
141 pdf.SetX(subject_width + i_left)
142 pdf.SetFillColor(200,200,200)
143 pdf.Cell(i_width, 2, "", 0, 0, "", 1)
126 144
127 i_left = ((i_start_date - @date_from)*zoom)
128 i_width = ((i_end_date - i_start_date + 1)*zoom)
129 d_width = ((i_done_date - i_start_date)*zoom)
130 l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
131 l_width ||= 0
132
133 pdf.SetY(top+1.5)
134 pdf.SetX(subject_width + i_left)
135 pdf.SetFillColor(200,200,200)
136 pdf.Cell(i_width, 2, "", 0, 0, "", 1)
137
138 if l_width > 0
145 if l_width > 0
146 pdf.SetY(top+1.5)
147 pdf.SetX(subject_width + i_left)
148 pdf.SetFillColor(255,100,100)
149 pdf.Cell(l_width, 2, "", 0, 0, "", 1)
150 end
151 if d_width > 0
152 pdf.SetY(top+1.5)
153 pdf.SetX(subject_width + i_left)
154 pdf.SetFillColor(100,100,255)
155 pdf.Cell(d_width, 2, "", 0, 0, "", 1)
156 end
157
139 158 pdf.SetY(top+1.5)
159 pdf.SetX(subject_width + i_left + i_width)
160 pdf.Cell(30, 2, "#{i.status.name} #{i.done_ratio}%")
161 else
162 i_left = ((i.start_date - @date_from)*zoom)
163
140 164 pdf.SetX(subject_width + i_left)
141 pdf.SetFillColor(255,100,100)
142 pdf.Cell(l_width, 2, "", 0, 0, "", 1)
143 end
144 if d_width > 0
165 pdf.SetFillColor(50,200,50)
166 pdf.Cell(2, 2, "", 0, 0, "", 1)
167
145 168 pdf.SetY(top+1.5)
146 pdf.SetX(subject_width + i_left)
147 pdf.SetFillColor(100,100,255)
148 pdf.Cell(d_width, 2, "", 0, 0, "", 1)
169 pdf.SetX(subject_width + i_left + 3)
170 pdf.Cell(30, 2, "#{i.name}")
149 171 end
150 172
151 pdf.SetY(top+1.5)
152 pdf.SetX(subject_width + i_left + i_width)
153 pdf.Cell(30, 2, "#{i.status.name} #{i.done_ratio}%")
154 173
155 174 top = top + 5
156 175 pdf.SetDrawColor(200, 200, 200)
157 176 pdf.Line(15, top, subject_width+g_width, top)
158 177 if pdf.GetY() > 180
159 178 pdf.AddPage("L")
160 179 top = 20
161 180 pdf.Line(15, top, subject_width+g_width, top)
162 181 end
163 182 pdf.SetDrawColor(0, 0, 0)
164 183 end
165 184
166 185 pdf.Line(15, top, subject_width+g_width, top)
167 186
168 187 %>
169 188 <%= pdf.Output %> No newline at end of file
@@ -1,227 +1,239
1 1 <div class="contextual">
2 2 <%= l(:label_export_to) %>
3 3 <%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :output => 'pdf'}, :class => 'icon icon-pdf' %>
4 4 </div>
5 5
6 6 <h2><%= l(:label_gantt) %></h2>
7 7
8 8 <% form_tag do %>
9 9 <table width="100%">
10 10 <tr>
11 11 <td align="left">
12 12 <input type="text" name="months" size="2" value="<%= @months %>" />
13 13 <%= l(:label_months_from) %>
14 14 <%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
15 15 <%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
16 16 <%= hidden_field_tag 'zoom', @zoom %>
17 17 <%= submit_tag l(:button_submit), :class => "button-small" %>
18 18 </td>
19 19 <td>
20 20 <a href="#" onclick="Element.toggle('trackerselect')"><%= l(:label_options) %></a>
21 21 <div id="trackerselect" class="rightbox overlay" style="width:140px; display: none;">
22 22 <p><strong><%=l(:label_tracker_plural)%></strong></p>
23 23 <% @trackers.each do |tracker| %>
24 24 <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
25 25 <%= tracker.name %><br />
26 26 <% end %>
27 27 <% if @project.children.any? %>
28 28 <p><strong><%=l(:label_subproject_plural)%></strong></p>
29 29 <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %>
30 30 <% end %>
31 31 <p><center><%= submit_tag l(:button_apply), :class => 'button-small' %></center></p>
32 32 </div>
33 33 </td>
34 34 <td align="right">
35 35 <%= if @zoom < 4
36 36 link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
37 37 else
38 38 image_tag 'zoom_in_g.png'
39 39 end %>
40 40 <%= if @zoom > 1
41 41 link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
42 42 else
43 43 image_tag 'zoom_out_g.png'
44 44 end %>
45 45 </td>
46 46 </tr>
47 47 </table>
48 48 <% end %>
49 49
50 50 <% zoom = 1
51 51 @zoom.times { zoom = zoom * 2 }
52 52
53 53 subject_width = 260
54 54 header_heigth = 18
55 55
56 56 headers_height = header_heigth
57 57 show_weeks = false
58 58 show_days = false
59 59
60 60 if @zoom >1
61 61 show_weeks = true
62 62 headers_height = 2*header_heigth
63 63 if @zoom > 2
64 64 show_days = true
65 65 headers_height = 3*header_heigth
66 66 end
67 67 end
68 68
69 69 g_width = (@date_to - @date_from + 1)*zoom
70 g_height = [(20 * @issues.length + 6)+150, 206].max
70 g_height = [(20 * @events.length + 6)+150, 206].max
71 71 t_height = g_height + headers_height
72 72 %>
73 73
74 74 <table width="100%" style="border:0; border-collapse: collapse;">
75 75 <tr>
76 76 <td style="width:260px;">
77 77
78 78 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
79 79 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
80 80 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
81 81 <%
82 82 #
83 83 # Tasks subjects
84 84 #
85 85 top = headers_height + 8
86 @issues.each do |i| %>
87 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;">
88 <small><%= link_to "#{i.tracker.name} ##{i.id}", { :controller => 'issues', :action => 'show', :id => i }, :title => "#{i.subject}" %>:
89 <%=h i.subject.sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)') %></small>
90 </div>
86 @events.each do |i| %>
87 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
88 <% if i.is_a? Issue %>
89 <%= link_to "#{i.tracker.name} ##{i.id}", { :controller => 'issues', :action => 'show', :id => i }, :title => "#{i.subject}" %>:
90 <%=h i.subject.sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)') %>
91 <% else %>
92 <strong><%= "#{l(:label_version)}: #{i.name}" %></strong>
93 <% end %>
94 </small></div>
91 95 <% top = top + 20
92 96 end %>
93 97 </div>
94 98 </td>
95 99 <td>
96 100
97 101 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
98 102 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
99 103 <%
100 104 #
101 105 # Months headers
102 106 #
103 107 month_f = @date_from
104 108 left = 0
105 109 height = (show_weeks ? header_heigth : header_heigth + g_height)
106 110 @months.times do
107 111 width = ((month_f >> 1) - month_f) * zoom - 1
108 112 %>
109 113 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
110 114 <%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
111 115 </div>
112 116 <%
113 117 left = left + width + 1
114 118 month_f = month_f >> 1
115 119 end %>
116 120
117 121 <%
118 122 #
119 123 # Weeks headers
120 124 #
121 125 if show_weeks
122 126 left = 0
123 127 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
124 128 if @date_from.cwday == 1
125 129 # @date_from is monday
126 130 week_f = @date_from
127 131 else
128 132 # find next monday after @date_from
129 133 week_f = @date_from + (7 - @date_from.cwday + 1)
130 134 width = (7 - @date_from.cwday + 1) * zoom-1
131 135 %>
132 136 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
133 137 <%
134 138 left = left + width+1
135 139 end %>
136 140 <%
137 141 while week_f <= @date_to
138 142 width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
139 143 %>
140 144 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
141 145 <small><%= week_f.cweek if width >= 16 %></small>
142 146 </div>
143 147 <%
144 148 left = left + width+1
145 149 week_f = week_f+7
146 150 end
147 151 end %>
148 152
149 153 <%
150 154 #
151 155 # Days headers
152 156 #
153 157 if show_days
154 158 left = 0
155 159 height = g_height + header_heigth - 1
156 160 wday = @date_from.cwday
157 161 (@date_to - @date_from + 1).to_i.times do
158 162 width = zoom - 1
159 163 %>
160 164 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
161 165 <%= day_name(wday)[0,1] %>
162 166 </div>
163 167 <%
164 168 left = left + width+1
165 169 wday = wday + 1
166 170 wday = 1 if wday > 7
167 171 end
168 172 end %>
169 173
170 174 <%
171 175 #
172 176 # Today red line
173 177 #
174 178 if Date.today >= @date_from and Date.today <= @date_to %>
175 179 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
176 180 <% end %>
177 181
178 182 <%
179 183 #
180 184 # Tasks
181 185 #
182 186 top = headers_height + 10
183 @issues.each do |i| %>
184 <%
187 @events.each do |i|
188 if i.is_a? Issue
185 189 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
186 190 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
187 191
188 192 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
189 193 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
190 194 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
191 195
192 196 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
193 197
194 198 i_left = ((i_start_date - @date_from)*zoom).floor
195 199 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
196 200 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
197 201 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
198 202 %>
199 203 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
200 204 <% if l_width > 0 %>
201 205 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
202 206 <% end %>
203 207 <% if d_width > 0 %>
204 208 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
205 209 <% end %>
206 210 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
207 211 <%= i.status.name %>
208 212 <%= (i.done_ratio).to_i %>%
209 213 </div>
210 214 <% # === tooltip === %>
211 215 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
212 216 <span class="tip">
213 217 <%= render :partial => "issues/tooltip", :locals => { :issue => i }%>
214 218 </span></div>
219 <% else
220 i_left = ((i.start_date - @date_from)*zoom).floor
221 %>
222 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
223 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
224 <strong><%= i.name %></strong>
225 </div>
226 <% end %>
215 227 <% top = top + 20
216 228 end %>
217 229 </div>
218 230 </td>
219 231 </tr>
220 232 </table>
221 233
222 234 <table width="100%">
223 235 <tr>
224 236 <td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
225 237 <td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
226 238 </tr>
227 239 </table> No newline at end of file
@@ -1,632 +1,633
1 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 2 /* Edited by Jean-Philippe Lang *>
3 3 /**************** Body and tag styles ****************/
4 4
5 5 #header * {margin:0; padding:0;}
6 6 p, ul, ol, li {margin:0; padding:0;}
7 7
8 8 body{
9 9 font:76% Verdana,Tahoma,Arial,sans-serif;
10 10 line-height:1.4em;
11 11 text-align:center;
12 12 color:#303030;
13 13 background:#e8eaec;
14 14 margin:0;
15 15 }
16 16
17 17 a{color:#467aa7;font-weight:bold;text-decoration:none;background-color:inherit;}
18 18 a:hover{color:#2a5a8a; text-decoration:none; background-color:inherit;}
19 19 a img{border:none;}
20 20
21 21 p{margin:0 0 1em 0;}
22 22 p form{margin-top:0; margin-bottom:20px;}
23 23
24 24 img.left,img.center,img.right{padding:4px; border:1px solid #a0a0a0;}
25 25 img.left{float:left; margin:0 12px 5px 0;}
26 26 img.center{display:block; margin:0 auto 5px auto;}
27 27 img.right{float:right; margin:0 0 5px 12px;}
28 28
29 29 /**************** Header and navigation styles ****************/
30 30
31 31 #container{
32 32 width:100%;
33 33 min-width: 800px;
34 34 margin:0;
35 35 padding:0;
36 36 text-align:left;
37 37 background:#ffffff;
38 38 color:#303030;
39 39 }
40 40
41 41 #header{
42 42 height:4.5em;
43 43 margin:0;
44 44 background:#467aa7;
45 45 color:#ffffff;
46 46 margin-bottom:1px;
47 47 }
48 48
49 49 #header h1{
50 50 padding:10px 0 0 20px;
51 51 font-size:2em;
52 52 background-color:inherit;
53 53 color:#fff;
54 54 letter-spacing:-1px;
55 55 font-weight:bold;
56 56 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
57 57 }
58 58
59 59 #header h2{
60 60 margin:3px 0 0 40px;
61 61 font-size:1.5em;
62 62 background-color:inherit;
63 63 color:#f0f2f4;
64 64 letter-spacing:-1px;
65 65 font-weight:normal;
66 66 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
67 67 }
68 68
69 69 #navigation{
70 70 height:2.2em;
71 71 line-height:2.2em;
72 72 margin:0;
73 73 background:#578bb8;
74 74 color:#ffffff;
75 75 }
76 76
77 77 #navigation li{
78 78 float:left;
79 79 list-style-type:none;
80 80 border-right:1px solid #ffffff;
81 81 white-space:nowrap;
82 82 }
83 83
84 84 #navigation li.right {
85 85 float:right;
86 86 list-style-type:none;
87 87 border-right:0;
88 88 border-left:1px solid #ffffff;
89 89 white-space:nowrap;
90 90 }
91 91
92 92 #navigation li a{
93 93 display:block;
94 94 padding:0px 10px 0px 22px;
95 95 font-size:0.8em;
96 96 font-weight:normal;
97 97 text-decoration:none;
98 98 background-color:inherit;
99 99 color: #ffffff;
100 100 }
101 101
102 102 #navigation li.submenu {background:url(../images/arrow_down.png) 96% 80% no-repeat;}
103 103 #navigation li.submenu a {padding:0px 16px 0px 22px;}
104 104 * html #navigation a {width:1%;}
105 105
106 106 #navigation .selected,#navigation a:hover{
107 107 color:#ffffff;
108 108 text-decoration:none;
109 109 background-color: #80b0da;
110 110 }
111 111
112 112 /**************** Icons *******************/
113 113 .icon {
114 114 background-position: 0% 40%;
115 115 background-repeat: no-repeat;
116 116 padding-left: 20px;
117 117 padding-top: 2px;
118 118 padding-bottom: 3px;
119 119 vertical-align: middle;
120 120 }
121 121
122 122 #navigation .icon {
123 123 background-position: 4px 50%;
124 124 }
125 125
126 126 .icon22 {
127 127 background-position: 0% 40%;
128 128 background-repeat: no-repeat;
129 129 padding-left: 26px;
130 130 line-height: 22px;
131 131 vertical-align: middle;
132 132 }
133 133
134 134 .icon-add { background-image: url(../images/add.png); }
135 135 .icon-edit { background-image: url(../images/edit.png); }
136 136 .icon-del { background-image: url(../images/delete.png); }
137 137 .icon-move { background-image: url(../images/move.png); }
138 138 .icon-save { background-image: url(../images/save.png); }
139 139 .icon-cancel { background-image: url(../images/cancel.png); }
140 140 .icon-pdf { background-image: url(../images/pdf.png); }
141 141 .icon-csv { background-image: url(../images/csv.png); }
142 142 .icon-html { background-image: url(../images/html.png); }
143 143 .icon-txt { background-image: url(../images/txt.png); }
144 144 .icon-file { background-image: url(../images/file.png); }
145 145 .icon-folder { background-image: url(../images/folder.png); }
146 146 .icon-package { background-image: url(../images/package.png); }
147 147 .icon-home { background-image: url(../images/home.png); }
148 148 .icon-user { background-image: url(../images/user.png); }
149 149 .icon-mypage { background-image: url(../images/user_page.png); }
150 150 .icon-admin { background-image: url(../images/admin.png); }
151 151 .icon-projects { background-image: url(../images/projects.png); }
152 152 .icon-logout { background-image: url(../images/logout.png); }
153 153 .icon-help { background-image: url(../images/help.png); }
154 154 .icon-attachment { background-image: url(../images/attachment.png); }
155 155 .icon-index { background-image: url(../images/index.png); }
156 156 .icon-history { background-image: url(../images/history.png); }
157 157 .icon-feed { background-image: url(../images/feed.png); }
158 158 .icon-time { background-image: url(../images/time.png); }
159 159 .icon-stats { background-image: url(../images/stats.png); }
160 160
161 161 .icon22-projects { background-image: url(../images/22x22/projects.png); }
162 162 .icon22-users { background-image: url(../images/22x22/users.png); }
163 163 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
164 164 .icon22-role { background-image: url(../images/22x22/role.png); }
165 165 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
166 166 .icon22-options { background-image: url(../images/22x22/options.png); }
167 167 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
168 168 .icon22-authent { background-image: url(../images/22x22/authent.png); }
169 169 .icon22-info { background-image: url(../images/22x22/info.png); }
170 170 .icon22-comment { background-image: url(../images/22x22/comment.png); }
171 171 .icon22-package { background-image: url(../images/22x22/package.png); }
172 172 .icon22-settings { background-image: url(../images/22x22/settings.png); }
173 173
174 174 /**************** Content styles ****************/
175 175
176 176 html>body #content {
177 177 height: auto;
178 178 min-height: 500px;
179 179 }
180 180
181 181 #content{
182 182 width: auto;
183 183 height:500px;
184 184 font-size:0.9em;
185 185 padding:20px 10px 10px 20px;
186 186 margin-left: 120px;
187 187 border-left: 1px dashed #c0c0c0;
188 188
189 189 }
190 190
191 191 #content h2, #content div.wiki h1 {
192 192 display:block;
193 193 margin:0 0 16px 0;
194 194 font-size:1.7em;
195 195 font-weight:normal;
196 196 letter-spacing:-1px;
197 197 color:#606060;
198 198 background-color:inherit;
199 199 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
200 200 }
201 201
202 202 #content h2 a{font-weight:normal;}
203 203 #content h3{margin:0 0 12px 0; font-size:1.4em;color:#707070;font-family: Trebuchet MS,Georgia,"Times New Roman",serif;}
204 204 #content h4{font-size: 1em; margin-bottom: 12px; margin-top: 20px; font-weight: normal; border-bottom: dotted 1px #c0c0c0;}
205 205 #content a:hover,#subcontent a:hover{text-decoration:underline;}
206 206 #content ul,#content ol{margin:0 5px 16px 35px;}
207 207 #content dl{margin:0 5px 10px 25px;}
208 208 #content dt{font-weight:bold; margin-bottom:5px;}
209 209 #content dd{margin:0 0 10px 15px;}
210 210
211 211 #content .tabs{height: 2.6em;}
212 212 #content .tabs ul{margin:0;}
213 213 #content .tabs ul li{
214 214 float:left;
215 215 list-style-type:none;
216 216 white-space:nowrap;
217 217 margin-right:8px;
218 218 background:#fff;
219 219 }
220 220 #content .tabs ul li a{
221 221 display:block;
222 222 font-size: 0.9em;
223 223 text-decoration:none;
224 224 line-height:1em;
225 225 padding:4px;
226 226 border: 1px solid #c0c0c0;
227 227 }
228 228
229 229 #content .tabs ul li a.selected, #content .tabs ul li a:hover{
230 230 background-color: #80b0da;
231 231 border: 1px solid #80b0da;
232 232 color: #fff;
233 233 text-decoration:none;
234 234 }
235 235
236 236 /***********************************************/
237 237
238 238 form {display: inline;}
239 239 blockquote {padding-left: 6px; border-left: 2px solid #ccc;}
240 240 input, select {vertical-align: middle; margin-bottom: 4px;}
241 241
242 242 input.button-small {font-size: 0.8em;}
243 243 textarea.wiki-edit { width: 99.5%; }
244 244 .select-small {font-size: 0.8em;}
245 245 label {font-weight: bold; font-size: 1em; color: #505050;}
246 246 fieldset {border:1px solid #c0c0c0; padding: 6px;}
247 247 legend {color: #505050;}
248 248 .required {color: #bb0000;}
249 249 .odd {background-color:#f6f7f8;}
250 250 .even {background-color: #fff;}
251 251 hr { border:0; border-top: dotted 1px #fff; border-bottom: dotted 1px #c0c0c0; }
252 252 table p {margin:0; padding:0;}
253 253
254 254 .highlight { background-color: #FCFD8D;}
255 255
256 256 div.square {
257 257 border: 1px solid #999;
258 258 float: left;
259 259 margin: .4em .5em 0 0;
260 260 overflow: hidden;
261 261 width: .6em; height: .6em;
262 262 }
263 263
264 264 ul.documents {
265 265 list-style-type: none;
266 266 padding: 0;
267 267 margin: 0;
268 268 }
269 269
270 270 ul.documents li {
271 271 background-image: url(../images/32x32/file.png);
272 272 background-repeat: no-repeat;
273 273 background-position: 0 1px;
274 274 padding-left: 36px;
275 275 margin-bottom: 10px;
276 276 margin-left: -37px;
277 277 }
278 278
279 279 /********** Table used to display lists of things ***********/
280 280
281 281 table.list {
282 282 width:100%;
283 283 border-collapse: collapse;
284 284 border: 1px dotted #d0d0d0;
285 285 margin-bottom: 6px;
286 286 }
287 287
288 288 table.with-cells td {
289 289 border: 1px solid #d7d7d7;
290 290 }
291 291
292 292 table.list td {
293 293 padding:2px;
294 294 }
295 295
296 296 table.list thead th {
297 297 text-align: center;
298 298 background: #eee;
299 299 border: 1px solid #d7d7d7;
300 300 color: #777;
301 301 }
302 302
303 303 table.list tbody th {
304 304 font-weight: normal;
305 305 background: #eed;
306 306 border: 1px solid #d7d7d7;
307 307 }
308 308
309 309 /********** Validation error messages *************/
310 310 #errorExplanation {
311 311 width: 400px;
312 312 border: 0;
313 313 padding: 7px;
314 314 padding-bottom: 3px;
315 315 margin-bottom: 0px;
316 316 }
317 317
318 318 #errorExplanation h2 {
319 319 text-align: left;
320 320 font-weight: bold;
321 321 padding: 5px 5px 10px 26px;
322 322 font-size: 1em;
323 323 margin: -7px;
324 324 background: url(../images/alert.png) no-repeat 6px 6px;
325 325 }
326 326
327 327 #errorExplanation p {
328 328 color: #333;
329 329 margin-bottom: 0;
330 330 padding: 5px;
331 331 }
332 332
333 333 #errorExplanation ul li {
334 334 font-size: 1em;
335 335 list-style: none;
336 336 margin-left: -16px;
337 337 }
338 338
339 339 /*========== Drop down menu ==============*/
340 340 div.menu {
341 341 background-color: #FFFFFF;
342 342 border-style: solid;
343 343 border-width: 1px;
344 344 border-color: #7F9DB9;
345 345 position: absolute;
346 346 top: 0px;
347 347 left: 0px;
348 348 padding: 0;
349 349 visibility: hidden;
350 350 z-index: 101;
351 351 }
352 352
353 353 div.menu a.menuItem {
354 354 font-size: 10px;
355 355 font-weight: normal;
356 356 line-height: 2em;
357 357 color: #000000;
358 358 background-color: #FFFFFF;
359 359 cursor: default;
360 360 display: block;
361 361 padding: 0 1em;
362 362 margin: 0;
363 363 border: 0;
364 364 text-decoration: none;
365 365 white-space: nowrap;
366 366 }
367 367
368 368 div.menu a.menuItem:hover, div.menu a.menuItemHighlight {
369 369 background-color: #80b0da;
370 370 color: #ffffff;
371 371 }
372 372
373 373 div.menu a.menuItem span.menuItemText {}
374 374
375 375 div.menu a.menuItem span.menuItemArrow {
376 376 margin-right: -.75em;
377 377 }
378 378
379 379 /**************** Sidebar styles ****************/
380 380
381 381 #subcontent{
382 382 position: absolute;
383 383 left: 0px;
384 384 width:110px;
385 385 padding:20px 20px 10px 5px;
386 386 }
387 387
388 388 #subcontent h2{
389 389 display:block;
390 390 margin:0 0 5px 0;
391 391 font-size:1.0em;
392 392 font-weight:bold;
393 393 text-align:left;
394 394 color:#606060;
395 395 background-color:inherit;
396 396 font-family: Trebuchet MS,Georgia,"Times New Roman",serif;
397 397 }
398 398
399 399 #subcontent p{margin:0 0 16px 0; font-size:0.9em;}
400 400
401 401 /**************** Menublock styles ****************/
402 402
403 403 .menublock{margin:0 0 20px 8px; font-size:0.8em;}
404 404 .menublock li{list-style:none; display:block; padding:1px; margin-bottom:0px;}
405 405 .menublock li a{font-weight:bold; text-decoration:none;}
406 406 .menublock li a:hover{text-decoration:none;}
407 407 .menublock li ul{margin:0; font-size:1em; font-weight:normal;}
408 408 .menublock li ul li{margin-bottom:0;}
409 409 .menublock li ul a{font-weight:normal;}
410 410
411 411 /**************** Footer styles ****************/
412 412
413 413 #footer{
414 414 clear:both;
415 415 padding:5px 0;
416 416 margin:0;
417 417 font-size:0.9em;
418 418 color:#f0f0f0;
419 419 background:#467aa7;
420 420 }
421 421
422 422 #footer p{padding:0; margin:0; text-align:center;}
423 423 #footer a{color:#f0f0f0; background-color:inherit; font-weight:bold;}
424 424 #footer a:hover{color:#ffffff; background-color:inherit; text-decoration: underline;}
425 425
426 426 /**************** Misc classes and styles ****************/
427 427
428 428 .splitcontentleft{float:left; width:49%;}
429 429 .splitcontentright{float:right; width:49%;}
430 430 .clear{clear:both;}
431 431 .small{font-size:0.8em;line-height:1.4em;padding:0 0 0 0;}
432 432 .hide{display:none;}
433 433 .textcenter{text-align:center;}
434 434 .textright{text-align:right;}
435 435 .important{color:#f02025; background-color:inherit; font-weight:bold;}
436 436
437 437 .box{
438 438 margin:0 0 20px 0;
439 439 padding:10px;
440 440 border:1px solid #c0c0c0;
441 441 background-color:#fafbfc;
442 442 color:#505050;
443 443 line-height:1.5em;
444 444 }
445 445
446 446 a.close-icon {
447 447 display:block;
448 448 margin-top:3px;
449 449 overflow:hidden;
450 450 width:12px;
451 451 height:12px;
452 452 background-repeat: no-repeat;
453 453 cursor:pointer;
454 454 background-image:url('../images/close.png');
455 455 }
456 456
457 457 a.close-icon:hover {
458 458 background-image:url('../images/close_hl.png');
459 459 }
460 460
461 461 .rightbox{
462 462 background: #fafbfc;
463 463 border: 1px solid #c0c0c0;
464 464 float: right;
465 465 padding: 8px;
466 466 position: relative;
467 467 margin: 0 5px 5px;
468 468 }
469 469
470 470 .overlay{
471 471 position: absolute;
472 472 margin-left:0;
473 473 z-index: 50;
474 474 }
475 475
476 476 .layout-active {
477 477 background: #ECF3E1;
478 478 }
479 479
480 480 .block-receiver {
481 481 border:1px dashed #c0c0c0;
482 482 margin-bottom: 20px;
483 483 padding: 15px 0 15px 0;
484 484 }
485 485
486 486 .mypage-box {
487 487 margin:0 0 20px 0;
488 488 color:#505050;
489 489 line-height:1.5em;
490 490 }
491 491
492 492 .handle {
493 493 cursor: move;
494 494 }
495 495
496 496 .login {
497 497 width: 50%;
498 498 text-align: left;
499 499 }
500 500
501 501 img.calendar-trigger {
502 502 cursor: pointer;
503 503 vertical-align: middle;
504 504 margin-left: 4px;
505 505 }
506 506
507 507 #history p {
508 508 margin-left: 34px;
509 509 }
510 510
511 511 .progress {
512 512 border: 1px solid #D7D7D7;
513 513 border-collapse: collapse;
514 514 border-spacing: 0pt;
515 515 empty-cells: show;
516 516 padding: 3px;
517 517 width: 40em;
518 518 text-align: center;
519 519 }
520 520
521 521 .progress td { height: 1em; }
522 522 .progress .closed { background: #BAE0BA none repeat scroll 0%; }
523 523 .progress .open { background: #FFF none repeat scroll 0%; }
524 524
525 525 /***** Contextual links div *****/
526 526 .contextual {
527 527 float: right;
528 528 font-size: 0.8em;
529 529 line-height: 16px;
530 530 padding: 2px;
531 531 }
532 532
533 533 .contextual select, .contextual input {
534 534 font-size: 1em;
535 535 }
536 536
537 537 /***** Gantt chart *****/
538 538 .gantt_hdr {
539 539 position:absolute;
540 540 top:0;
541 541 height:16px;
542 542 border-top: 1px solid #c0c0c0;
543 543 border-bottom: 1px solid #c0c0c0;
544 544 border-right: 1px solid #c0c0c0;
545 545 text-align: center;
546 546 overflow: hidden;
547 547 }
548 548
549 549 .task {
550 550 position: absolute;
551 551 height:8px;
552 552 font-size:0.8em;
553 553 color:#888;
554 554 padding:0;
555 555 margin:0;
556 556 line-height:0.8em;
557 557 }
558 558
559 559 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
560 560 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
561 561 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
562 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
562 563
563 564 /***** Tooltips ******/
564 565 .tooltip{position:relative;z-index:24;}
565 566 .tooltip:hover{z-index:25;color:#000;}
566 567 .tooltip span.tip{display: none; text-align:left;}
567 568
568 569 div.tooltip:hover span.tip{
569 570 display:block;
570 571 position:absolute;
571 572 top:12px; left:24px; width:270px;
572 573 border:1px solid #555;
573 574 background-color:#fff;
574 575 padding: 4px;
575 576 font-size: 0.8em;
576 577 color:#505050;
577 578 }
578 579
579 580 /***** CSS FORM ******/
580 581 .tabular p{
581 582 margin: 0;
582 583 padding: 5px 0 8px 0;
583 584 padding-left: 180px; /*width of left column containing the label elements*/
584 585 height: 1%;
585 586 }
586 587
587 588 .tabular label{
588 589 font-weight: bold;
589 590 float: left;
590 591 margin-left: -180px; /*width of left column*/
591 592 width: 175px; /*width of labels. Should be smaller than left column to create some right
592 593 margin*/
593 594 }
594 595
595 596 .error {
596 597 color: #cc0000;
597 598 }
598 599
599 600 #settings .tabular p{ padding-left: 250px; }
600 601 #settings .tabular label{ margin-left: -250px; width: 245px; }
601 602
602 603 /*.threepxfix class below:
603 604 Targets IE6- ONLY. Adds 3 pixel indent for multi-line form contents.
604 605 to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepxtest.html
605 606 */
606 607
607 608 * html .threepxfix{
608 609 margin-left: 3px;
609 610 }
610 611
611 612 /***** Wiki sections ****/
612 613 #content div.wiki { font-size: 110%}
613 614
614 615 #content div.wiki h2, div.wiki h3 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; color:#606060; }
615 616 #content div.wiki h2 { font-size: 1.4em;}
616 617 #content div.wiki h3 { font-size: 1.2em;}
617 618
618 619 div.wiki table {
619 620 border: 1px solid #505050;
620 621 border-collapse: collapse;
621 622 }
622 623
623 624 div.wiki table, div.wiki td {
624 625 border: 1px solid #bbb;
625 626 padding: 4px;
626 627 }
627 628
628 629 div.wiki code {
629 630 font-size: 1.2em;
630 631 }
631 632
632 633 #preview .preview { background: #fafbfc url(../images/draft.png); }
General Comments 0
You need to be logged in to leave comments. Login now