##// END OF EJS Templates
Add an icon to each event on the activity view....
Jean-Philippe Lang -
r1327:6d2a89142af2
parent child
Show More
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -1,65 +1,66
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 class Journal < ActiveRecord::Base
18 class Journal < ActiveRecord::Base
19 belongs_to :journalized, :polymorphic => true
19 belongs_to :journalized, :polymorphic => true
20 # added as a quick fix to allow eager loading of the polymorphic association
20 # added as a quick fix to allow eager loading of the polymorphic association
21 # since always associated to an issue, for now
21 # since always associated to an issue, for now
22 belongs_to :issue, :foreign_key => :journalized_id
22 belongs_to :issue, :foreign_key => :journalized_id
23
23
24 belongs_to :user
24 belongs_to :user
25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
26 attr_accessor :indice
26 attr_accessor :indice
27
27
28 acts_as_searchable :columns => 'notes',
28 acts_as_searchable :columns => 'notes',
29 :include => :issue,
29 :include => :issue,
30 :project_key => "#{Issue.table_name}.project_id",
30 :project_key => "#{Issue.table_name}.project_id",
31 :date_column => "#{Issue.table_name}.created_on"
31 :date_column => "#{Issue.table_name}.created_on"
32
32
33 acts_as_event :title => Proc.new {|o| "#{o.issue.tracker.name} ##{o.issue.id}: #{o.issue.subject}" + ((s = o.new_status) ? " (#{s})" : '') },
33 acts_as_event :title => Proc.new {|o| "#{o.issue.tracker.name} ##{o.issue.id}: #{o.issue.subject}" + ((s = o.new_status) ? " (#{s})" : '') },
34 :description => :notes,
34 :description => :notes,
35 :author => :user,
35 :author => :user,
36 :type => Proc.new {|o| (s = o.new_status) && s.is_closed? ? 'issue-closed' : 'issue-edit' },
36 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
37 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
37
38
38 def save
39 def save
39 # Do not save an empty journal
40 # Do not save an empty journal
40 (details.empty? && notes.blank?) ? false : super
41 (details.empty? && notes.blank?) ? false : super
41 end
42 end
42
43
43 # Returns the new status if the journal contains a status change, otherwise nil
44 # Returns the new status if the journal contains a status change, otherwise nil
44 def new_status
45 def new_status
45 c = details.detect {|detail| detail.prop_key == 'status_id'}
46 c = details.detect {|detail| detail.prop_key == 'status_id'}
46 (c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil
47 (c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil
47 end
48 end
48
49
49 def new_value_for(prop)
50 def new_value_for(prop)
50 c = details.detect {|detail| detail.prop_key == prop}
51 c = details.detect {|detail| detail.prop_key == prop}
51 c ? c.value : nil
52 c ? c.value : nil
52 end
53 end
53
54
54 def editable_by?(usr)
55 def editable_by?(usr)
55 usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
56 usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
56 end
57 end
57
58
58 def project
59 def project
59 journalized.respond_to?(:project) ? journalized.project : nil
60 journalized.respond_to?(:project) ? journalized.project : nil
60 end
61 end
61
62
62 def attachments
63 def attachments
63 journalized.respond_to?(:attachments) ? journalized.attachments : nil
64 journalized.respond_to?(:attachments) ? journalized.attachments : nil
64 end
65 end
65 end
66 end
@@ -1,67 +1,68
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Message < ActiveRecord::Base
18 class Message < ActiveRecord::Base
19 belongs_to :board
19 belongs_to :board
20 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
20 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
21 acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
21 acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
22 has_many :attachments, :as => :container, :dependent => :destroy
22 has_many :attachments, :as => :container, :dependent => :destroy
23 belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
23 belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
24
24
25 acts_as_searchable :columns => ['subject', 'content'],
25 acts_as_searchable :columns => ['subject', 'content'],
26 :include => :board,
26 :include => :board,
27 :project_key => 'project_id',
27 :project_key => 'project_id',
28 :date_column => 'created_on'
28 :date_column => 'created_on'
29 acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
29 acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
30 :description => :content,
30 :description => :content,
31 :type => Proc.new {|o| o.parent_id.nil? ? 'message' : 'reply'},
31 :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id, :id => o.id}}
32 :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id, :id => o.id}}
32
33
33 attr_protected :locked, :sticky
34 attr_protected :locked, :sticky
34 validates_presence_of :subject, :content
35 validates_presence_of :subject, :content
35 validates_length_of :subject, :maximum => 255
36 validates_length_of :subject, :maximum => 255
36
37
37 def validate_on_create
38 def validate_on_create
38 # Can not reply to a locked topic
39 # Can not reply to a locked topic
39 errors.add_to_base 'Topic is locked' if root.locked? && self != root
40 errors.add_to_base 'Topic is locked' if root.locked? && self != root
40 end
41 end
41
42
42 def after_create
43 def after_create
43 board.update_attribute(:last_message_id, self.id)
44 board.update_attribute(:last_message_id, self.id)
44 board.increment! :messages_count
45 board.increment! :messages_count
45 if parent
46 if parent
46 parent.reload.update_attribute(:last_reply_id, self.id)
47 parent.reload.update_attribute(:last_reply_id, self.id)
47 else
48 else
48 board.increment! :topics_count
49 board.increment! :topics_count
49 end
50 end
50 end
51 end
51
52
52 def after_destroy
53 def after_destroy
53 # The following line is required so that the previous counter
54 # The following line is required so that the previous counter
54 # updates (due to children removal) are not overwritten
55 # updates (due to children removal) are not overwritten
55 board.reload
56 board.reload
56 board.decrement! :messages_count
57 board.decrement! :messages_count
57 board.decrement! :topics_count unless parent
58 board.decrement! :topics_count unless parent
58 end
59 end
59
60
60 def sticky?
61 def sticky?
61 sticky == 1
62 sticky == 1
62 end
63 end
63
64
64 def project
65 def project
65 board.project
66 board.project
66 end
67 end
67 end
68 end
@@ -1,77 +1,78
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 'zlib'
18 require 'zlib'
19
19
20 class WikiContent < ActiveRecord::Base
20 class WikiContent < ActiveRecord::Base
21 set_locking_column :version
21 set_locking_column :version
22 belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
22 belongs_to :page, :class_name => 'WikiPage', :foreign_key => 'page_id'
23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
23 belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
24 validates_presence_of :text
24 validates_presence_of :text
25
25
26 acts_as_versioned
26 acts_as_versioned
27 class Version
27 class Version
28 belongs_to :page, :class_name => '::WikiPage', :foreign_key => 'page_id'
28 belongs_to :page, :class_name => '::WikiPage', :foreign_key => 'page_id'
29 belongs_to :author, :class_name => '::User', :foreign_key => 'author_id'
29 belongs_to :author, :class_name => '::User', :foreign_key => 'author_id'
30 attr_protected :data
30 attr_protected :data
31
31
32 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
32 acts_as_event :title => Proc.new {|o| "#{l(:label_wiki_edit)}: #{o.page.title} (##{o.version})"},
33 :description => :comments,
33 :description => :comments,
34 :datetime => :updated_on,
34 :datetime => :updated_on,
35 :type => 'wiki-page',
35 :url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
36 :url => Proc.new {|o| {:controller => 'wiki', :id => o.page.wiki.project_id, :page => o.page.title, :version => o.version}}
36
37
37 def text=(plain)
38 def text=(plain)
38 case Setting.wiki_compression
39 case Setting.wiki_compression
39 when 'gzip'
40 when 'gzip'
40 begin
41 begin
41 self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
42 self.data = Zlib::Deflate.deflate(plain, Zlib::BEST_COMPRESSION)
42 self.compression = 'gzip'
43 self.compression = 'gzip'
43 rescue
44 rescue
44 self.data = plain
45 self.data = plain
45 self.compression = ''
46 self.compression = ''
46 end
47 end
47 else
48 else
48 self.data = plain
49 self.data = plain
49 self.compression = ''
50 self.compression = ''
50 end
51 end
51 plain
52 plain
52 end
53 end
53
54
54 def text
55 def text
55 @text ||= case compression
56 @text ||= case compression
56 when 'gzip'
57 when 'gzip'
57 Zlib::Inflate.inflate(data)
58 Zlib::Inflate.inflate(data)
58 else
59 else
59 # uncompressed data
60 # uncompressed data
60 data
61 data
61 end
62 end
62 end
63 end
63
64
64 def project
65 def project
65 page.project
66 page.project
66 end
67 end
67
68
68 # Returns the previous version or nil
69 # Returns the previous version or nil
69 def previous
70 def previous
70 @previous ||= WikiContent::Version.find(:first,
71 @previous ||= WikiContent::Version.find(:first,
71 :order => 'version DESC',
72 :order => 'version DESC',
72 :include => :author,
73 :include => :author,
73 :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
74 :conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
74 end
75 end
75 end
76 end
76
77
77 end
78 end
@@ -1,58 +1,58
1 <h2><%= l(:label_activity) %></h2>
1 <h2><%= l(:label_activity) %></h2>
2 <p class="subtitle"><%= "#{l(:label_date_from)} #{format_date(@date_to - @days)} #{l(:label_date_to).downcase} #{format_date(@date_to-1)}" %></p>
2 <p class="subtitle"><%= "#{l(:label_date_from)} #{format_date(@date_to - @days)} #{l(:label_date_to).downcase} #{format_date(@date_to-1)}" %></p>
3
3
4 <div id="activity">
4 <div id="activity">
5 <% @events_by_day.keys.sort.reverse.each do |day| %>
5 <% @events_by_day.keys.sort.reverse.each do |day| %>
6 <h3><%= format_activity_day(day) %></h3>
6 <h3><%= format_activity_day(day) %></h3>
7 <dl>
7 <dl>
8 <% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
8 <% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
9 <dt class="<%= e.class.name.downcase %>"><span class="time"><%= format_time(e.event_datetime, false) %></span>
9 <dt class="<%= e.event_type %>"><span class="time"><%= format_time(e.event_datetime, false) %></span>
10 <%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %> <%= link_to h(truncate(e.event_title, 100)), e.event_url %></dt>
10 <%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %> <%= link_to h(truncate(e.event_title, 100)), e.event_url %></dt>
11 <dd><% unless e.event_description.blank? -%>
11 <dd><% unless e.event_description.blank? -%>
12 <span class="description"><%= format_activity_description(e.event_description) %></span><br />
12 <span class="description"><%= format_activity_description(e.event_description) %></span><br />
13 <% end %>
13 <% end %>
14 <span class="author"><%= e.event_author if e.respond_to?(:event_author) %></span></dd>
14 <span class="author"><%= e.event_author if e.respond_to?(:event_author) %></span></dd>
15 <% end -%>
15 <% end -%>
16 </dl>
16 </dl>
17 <% end -%>
17 <% end -%>
18 </div>
18 </div>
19
19
20 <%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %>
20 <%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %>
21
21
22 <div style="float:left;">
22 <div style="float:left;">
23 <%= link_to_remote(('&#171; ' + l(:label_previous)),
23 <%= link_to_remote(('&#171; ' + l(:label_previous)),
24 {:update => "content", :url => params.merge(:from => @date_to - @days), :complete => 'window.scrollTo(0,0)'},
24 {:update => "content", :url => params.merge(:from => @date_to - @days), :complete => 'window.scrollTo(0,0)'},
25 {:href => url_for(params.merge(:from => @date_to - @days)),
25 {:href => url_for(params.merge(:from => @date_to - @days)),
26 :title => "#{l(:label_date_from)} #{format_date(@date_to - 2*@days)} #{l(:label_date_to).downcase} #{format_date(@date_to - @days - 1)}"}) %>
26 :title => "#{l(:label_date_from)} #{format_date(@date_to - 2*@days)} #{l(:label_date_to).downcase} #{format_date(@date_to - @days - 1)}"}) %>
27 </div>
27 </div>
28 <div style="float:right;">
28 <div style="float:right;">
29 <%= link_to_remote((l(:label_next) + ' &#187;'),
29 <%= link_to_remote((l(:label_next) + ' &#187;'),
30 {:update => "content", :url => params.merge(:from => @date_to + @days), :complete => 'window.scrollTo(0,0)'},
30 {:update => "content", :url => params.merge(:from => @date_to + @days), :complete => 'window.scrollTo(0,0)'},
31 {:href => url_for(params.merge(:from => @date_to + @days)),
31 {:href => url_for(params.merge(:from => @date_to + @days)),
32 :title => "#{l(:label_date_from)} #{format_date(@date_to)} #{l(:label_date_to).downcase} #{format_date(@date_to + @days - 1)}"}) unless @date_to >= Date.today %>
32 :title => "#{l(:label_date_from)} #{format_date(@date_to)} #{l(:label_date_to).downcase} #{format_date(@date_to + @days - 1)}"}) unless @date_to >= Date.today %>
33 </div>
33 </div>
34 &nbsp;
34 &nbsp;
35 <p class="other-formats">
35 <p class="other-formats">
36 <%= l(:label_export_to) %>
36 <%= l(:label_export_to) %>
37 <%= link_to 'Atom', params.merge(:format => :atom, :key => User.current.rss_key).delete_if{|k,v|k=="commit"}, :class => 'feed' %>
37 <%= link_to 'Atom', params.merge(:format => :atom, :key => User.current.rss_key).delete_if{|k,v|k=="commit"}, :class => 'feed' %>
38 </p>
38 </p>
39
39
40 <% content_for :header_tags do %>
40 <% content_for :header_tags do %>
41 <%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :year => nil, :month => nil, :key => User.current.rss_key)) %>
41 <%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :year => nil, :month => nil, :key => User.current.rss_key)) %>
42 <% end %>
42 <% end %>
43
43
44 <% content_for :sidebar do %>
44 <% content_for :sidebar do %>
45 <% form_tag({}, :method => :get) do %>
45 <% form_tag({}, :method => :get) do %>
46 <h3><%= l(:label_activity) %></h3>
46 <h3><%= l(:label_activity) %></h3>
47 <p><% @event_types.each do |t| %>
47 <p><% @event_types.each do |t| %>
48 <label><%= check_box_tag "show_#{t}", 1, @scope.include?(t) %> <%= l("label_#{t.singularize}_plural")%></label><br />
48 <label><%= check_box_tag "show_#{t}", 1, @scope.include?(t) %> <%= l("label_#{t.singularize}_plural")%></label><br />
49 <% end %></p>
49 <% end %></p>
50 <% if @project && @project.active_children.any? %>
50 <% if @project && @project.active_children.any? %>
51 <p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
51 <p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
52 <%= hidden_field_tag 'with_subprojects', 0 %>
52 <%= hidden_field_tag 'with_subprojects', 0 %>
53 <% end %>
53 <% end %>
54 <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
54 <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
55 <% end %>
55 <% end %>
56 <% end %>
56 <% end %>
57
57
58 <% html_title(l(:label_activity)) -%>
58 <% html_title(l(:label_activity)) -%>
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,580 +1,590
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
2
2
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
4 h1 {margin:0; padding:0; font-size: 24px;}
4 h1 {margin:0; padding:0; font-size: 24px;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
8
8
9 /***** Layout *****/
9 /***** Layout *****/
10 #wrapper {background: white;}
10 #wrapper {background: white;}
11
11
12 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
12 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
13 #top-menu ul {margin: 0; padding: 0;}
13 #top-menu ul {margin: 0; padding: 0;}
14 #top-menu li {
14 #top-menu li {
15 float:left;
15 float:left;
16 list-style-type:none;
16 list-style-type:none;
17 margin: 0px 0px 0px 0px;
17 margin: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
19 white-space:nowrap;
19 white-space:nowrap;
20 }
20 }
21 #top-menu a {color: #fff; padding-right: 8px; font-weight: bold;}
21 #top-menu a {color: #fff; padding-right: 8px; font-weight: bold;}
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
23
23
24 #account {float:right;}
24 #account {float:right;}
25
25
26 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
26 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
27 #header a {color:#f8f8f8;}
27 #header a {color:#f8f8f8;}
28 #quick-search {float:right;}
28 #quick-search {float:right;}
29
29
30 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
30 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
31 #main-menu ul {margin: 0; padding: 0;}
31 #main-menu ul {margin: 0; padding: 0;}
32 #main-menu li {
32 #main-menu li {
33 float:left;
33 float:left;
34 list-style-type:none;
34 list-style-type:none;
35 margin: 0px 2px 0px 0px;
35 margin: 0px 2px 0px 0px;
36 padding: 0px 0px 0px 0px;
36 padding: 0px 0px 0px 0px;
37 white-space:nowrap;
37 white-space:nowrap;
38 }
38 }
39 #main-menu li a {
39 #main-menu li a {
40 display: block;
40 display: block;
41 color: #fff;
41 color: #fff;
42 text-decoration: none;
42 text-decoration: none;
43 font-weight: bold;
43 font-weight: bold;
44 margin: 0;
44 margin: 0;
45 padding: 4px 10px 4px 10px;
45 padding: 4px 10px 4px 10px;
46 }
46 }
47 #main-menu li a:hover {background:#759FCF; color:#fff;}
47 #main-menu li a:hover {background:#759FCF; color:#fff;}
48 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
48 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
49
49
50 #main {background-color:#EEEEEE;}
50 #main {background-color:#EEEEEE;}
51
51
52 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
52 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
53 * html #sidebar{ width: 17%; }
53 * html #sidebar{ width: 17%; }
54 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
54 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
55 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
55 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
56 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
56 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
57
57
58 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;}
58 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;}
59 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
59 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
60 html>body #content { height: auto; min-height: 600px; overflow: auto; }
60 html>body #content { height: auto; min-height: 600px; overflow: auto; }
61
61
62 #main.nosidebar #sidebar{ display: none; }
62 #main.nosidebar #sidebar{ display: none; }
63 #main.nosidebar #content{ width: auto; border-right: 0; }
63 #main.nosidebar #content{ width: auto; border-right: 0; }
64
64
65 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
65 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
66
66
67 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
67 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
68 #login-form table td {padding: 6px;}
68 #login-form table td {padding: 6px;}
69 #login-form label {font-weight: bold;}
69 #login-form label {font-weight: bold;}
70
70
71 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
71 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
72
72
73 /***** Links *****/
73 /***** Links *****/
74 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
74 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
75 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
75 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
76 a img{ border: 0; }
76 a img{ border: 0; }
77
77
78 a.issue.closed, .issue.closed a { text-decoration: line-through; }
78 a.issue.closed, .issue.closed a { text-decoration: line-through; }
79
79
80 /***** Tables *****/
80 /***** Tables *****/
81 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
81 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
82 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
82 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
83 table.list td { overflow: hidden; vertical-align: top;}
83 table.list td { overflow: hidden; vertical-align: top;}
84 table.list td.id { width: 2%; text-align: center;}
84 table.list td.id { width: 2%; text-align: center;}
85 table.list td.checkbox { width: 15px; padding: 0px;}
85 table.list td.checkbox { width: 15px; padding: 0px;}
86
86
87 table.list.issues { margin-top: 10px; }
87 table.list.issues { margin-top: 10px; }
88 tr.issue { text-align: center; white-space: nowrap; }
88 tr.issue { text-align: center; white-space: nowrap; }
89 tr.issue td.subject, tr.issue td.category { white-space: normal; }
89 tr.issue td.subject, tr.issue td.category { white-space: normal; }
90 tr.issue td.subject { text-align: left; }
90 tr.issue td.subject { text-align: left; }
91 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
91 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
92
92
93 tr.entry { border: 1px solid #f8f8f8; }
93 tr.entry { border: 1px solid #f8f8f8; }
94 tr.entry td { white-space: nowrap; }
94 tr.entry td { white-space: nowrap; }
95 tr.entry td.filename { width: 30%; }
95 tr.entry td.filename { width: 30%; }
96 tr.entry td.size { text-align: right; font-size: 90%; }
96 tr.entry td.size { text-align: right; font-size: 90%; }
97 tr.entry td.revision, tr.entry td.author { text-align: center; }
97 tr.entry td.revision, tr.entry td.author { text-align: center; }
98 tr.entry td.age { text-align: right; }
98 tr.entry td.age { text-align: right; }
99
99
100 tr.changeset td.author { text-align: center; width: 15%; }
100 tr.changeset td.author { text-align: center; width: 15%; }
101 tr.changeset td.committed_on { text-align: center; width: 15%; }
101 tr.changeset td.committed_on { text-align: center; width: 15%; }
102
102
103 tr.message { height: 2.6em; }
103 tr.message { height: 2.6em; }
104 tr.message td.last_message { font-size: 80%; }
104 tr.message td.last_message { font-size: 80%; }
105 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
105 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
106 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
106 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
107
107
108 tr.user td { width:13%; }
108 tr.user td { width:13%; }
109 tr.user td.email { width:18%; }
109 tr.user td.email { width:18%; }
110 tr.user td { white-space: nowrap; }
110 tr.user td { white-space: nowrap; }
111 tr.user.locked, tr.user.registered { color: #aaa; }
111 tr.user.locked, tr.user.registered { color: #aaa; }
112 tr.user.locked a, tr.user.registered a { color: #aaa; }
112 tr.user.locked a, tr.user.registered a { color: #aaa; }
113
113
114 tr.time-entry { text-align: center; white-space: nowrap; }
114 tr.time-entry { text-align: center; white-space: nowrap; }
115 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
115 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
116 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
116 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
117 td.hours .hours-dec { font-size: 0.9em; }
117 td.hours .hours-dec { font-size: 0.9em; }
118
118
119 table.list tbody tr:hover { background-color:#ffffdd; }
119 table.list tbody tr:hover { background-color:#ffffdd; }
120 table td {padding:2px;}
120 table td {padding:2px;}
121 table p {margin:0;}
121 table p {margin:0;}
122 .odd {background-color:#f6f7f8;}
122 .odd {background-color:#f6f7f8;}
123 .even {background-color: #fff;}
123 .even {background-color: #fff;}
124
124
125 .highlight { background-color: #FCFD8D;}
125 .highlight { background-color: #FCFD8D;}
126 .highlight.token-1 { background-color: #faa;}
126 .highlight.token-1 { background-color: #faa;}
127 .highlight.token-2 { background-color: #afa;}
127 .highlight.token-2 { background-color: #afa;}
128 .highlight.token-3 { background-color: #aaf;}
128 .highlight.token-3 { background-color: #aaf;}
129
129
130 .box{
130 .box{
131 padding:6px;
131 padding:6px;
132 margin-bottom: 10px;
132 margin-bottom: 10px;
133 background-color:#f6f6f6;
133 background-color:#f6f6f6;
134 color:#505050;
134 color:#505050;
135 line-height:1.5em;
135 line-height:1.5em;
136 border: 1px solid #e4e4e4;
136 border: 1px solid #e4e4e4;
137 }
137 }
138
138
139 div.square {
139 div.square {
140 border: 1px solid #999;
140 border: 1px solid #999;
141 float: left;
141 float: left;
142 margin: .3em .4em 0 .4em;
142 margin: .3em .4em 0 .4em;
143 overflow: hidden;
143 overflow: hidden;
144 width: .6em; height: .6em;
144 width: .6em; height: .6em;
145 }
145 }
146 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
146 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
147 .contextual input {font-size:0.9em;}
147 .contextual input {font-size:0.9em;}
148
148
149 .splitcontentleft{float:left; width:49%;}
149 .splitcontentleft{float:left; width:49%;}
150 .splitcontentright{float:right; width:49%;}
150 .splitcontentright{float:right; width:49%;}
151 form {display: inline;}
151 form {display: inline;}
152 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
152 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
153 fieldset {border: 1px solid #e4e4e4; margin:0;}
153 fieldset {border: 1px solid #e4e4e4; margin:0;}
154 legend {color: #484848;}
154 legend {color: #484848;}
155 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
155 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
156 textarea.wiki-edit { width: 99%; }
156 textarea.wiki-edit { width: 99%; }
157 li p {margin-top: 0;}
157 li p {margin-top: 0;}
158 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
158 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
159 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
159 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
160 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
160 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
161
161
162 fieldset#filters { padding: 0.7em; }
162 fieldset#filters { padding: 0.7em; }
163 fieldset#filters p { margin: 0.8em 0 0.8em 0; }
163 fieldset#filters p { margin: 0.8em 0 0.8em 0; }
164 fieldset#filters .buttons { text-align: right; font-size: 0.9em; }
164 fieldset#filters .buttons { text-align: right; font-size: 0.9em; }
165
165
166 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
166 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
167 div#issue-changesets .changeset { padding: 4px;}
167 div#issue-changesets .changeset { padding: 4px;}
168 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
168 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
169 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
169 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
170
170
171 div#activity dl { margin-left: 2em; }
171 div#activity dl { margin-left: 2em; }
172 div#activity dd { margin-bottom: 1em; }
172 div#activity dd { margin-bottom: 1em; padding-left: 18px; }
173 div#activity dt { margin-bottom: 1px; }
173 div#activity dt { margin-bottom: 1px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
174 div#activity dt .time { color: #777; font-size: 80%; }
174 div#activity dt .time { color: #777; font-size: 80%; }
175 div#activity dd .description { font-style: italic; }
175 div#activity dd .description { font-style: italic; }
176 div#activity span.project:after { content: " -"; }
176 div#activity span.project:after { content: " -"; }
177 div#activity dt.issue { background-image: url(../images/ticket.png); }
178 div#activity dt.issue-edit { background-image: url(../images/ticket_edit.png); }
179 div#activity dt.issue-closed { background-image: url(../images/ticket_checked.png); }
180 div#activity dt.changeset { background-image: url(../images/changeset.png); }
181 div#activity dt.news { background-image: url(../images/news.png); }
182 div#activity dt.message { background-image: url(../images/message.png); }
183 div#activity dt.reply { background-image: url(../images/comments.png); }
184 div#activity dt.wiki-page { background-image: url(../images/wiki_edit.png); }
185 div#activity dt.attachment { background-image: url(../images/attachment.png); }
186 div#activity dt.document { background-image: url(../images/document.png); }
177
187
178 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
188 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
179 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
189 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
180 div#roadmap .wiki h1:first-child { display: none; }
190 div#roadmap .wiki h1:first-child { display: none; }
181 div#roadmap .wiki h1 { font-size: 120%; }
191 div#roadmap .wiki h1 { font-size: 120%; }
182 div#roadmap .wiki h2 { font-size: 110%; }
192 div#roadmap .wiki h2 { font-size: 110%; }
183
193
184 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
194 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
185 div#version-summary fieldset { margin-bottom: 1em; }
195 div#version-summary fieldset { margin-bottom: 1em; }
186 div#version-summary .total-hours { text-align: right; }
196 div#version-summary .total-hours { text-align: right; }
187
197
188 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
198 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
189 table#time-report tbody tr { font-style: italic; color: #777; }
199 table#time-report tbody tr { font-style: italic; color: #777; }
190 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
200 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
191 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
201 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
192 table#time-report .hours-dec { font-size: 0.9em; }
202 table#time-report .hours-dec { font-size: 0.9em; }
193
203
194 .total-hours { font-size: 110%; font-weight: bold; }
204 .total-hours { font-size: 110%; font-weight: bold; }
195 .total-hours span.hours-int { font-size: 120%; }
205 .total-hours span.hours-int { font-size: 120%; }
196
206
197 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
207 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
198 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
208 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
199
209
200 .pagination {font-size: 90%}
210 .pagination {font-size: 90%}
201 p.pagination {margin-top:8px;}
211 p.pagination {margin-top:8px;}
202
212
203 /***** Tabular forms ******/
213 /***** Tabular forms ******/
204 .tabular p{
214 .tabular p{
205 margin: 0;
215 margin: 0;
206 padding: 5px 0 8px 0;
216 padding: 5px 0 8px 0;
207 padding-left: 180px; /*width of left column containing the label elements*/
217 padding-left: 180px; /*width of left column containing the label elements*/
208 height: 1%;
218 height: 1%;
209 clear:left;
219 clear:left;
210 }
220 }
211
221
212 .tabular label{
222 .tabular label{
213 font-weight: bold;
223 font-weight: bold;
214 float: left;
224 float: left;
215 text-align: right;
225 text-align: right;
216 margin-left: -180px; /*width of left column*/
226 margin-left: -180px; /*width of left column*/
217 width: 175px; /*width of labels. Should be smaller than left column to create some right
227 width: 175px; /*width of labels. Should be smaller than left column to create some right
218 margin*/
228 margin*/
219 }
229 }
220
230
221 .tabular label.floating{
231 .tabular label.floating{
222 font-weight: normal;
232 font-weight: normal;
223 margin-left: 0px;
233 margin-left: 0px;
224 text-align: left;
234 text-align: left;
225 width: 200px;
235 width: 200px;
226 }
236 }
227
237
228 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
238 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
229
239
230 .tabular.settings p{ padding-left: 300px; }
240 .tabular.settings p{ padding-left: 300px; }
231 .tabular.settings label{ margin-left: -300px; width: 295px; }
241 .tabular.settings label{ margin-left: -300px; width: 295px; }
232
242
233 .required {color: #bb0000;}
243 .required {color: #bb0000;}
234 .summary {font-style: italic;}
244 .summary {font-style: italic;}
235
245
236 #attachments_fields input[type=text] {margin-left: 8px; }
246 #attachments_fields input[type=text] {margin-left: 8px; }
237
247
238 div.attachments p { margin:4px 0 2px 0; }
248 div.attachments p { margin:4px 0 2px 0; }
239 div.attachments img { vertical-align: middle; }
249 div.attachments img { vertical-align: middle; }
240 div.attachments span.author { font-size: 0.9em; color: #888; }
250 div.attachments span.author { font-size: 0.9em; color: #888; }
241
251
242 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
252 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
243 .other-formats span + span:before { content: "| "; }
253 .other-formats span + span:before { content: "| "; }
244
254
245 a.feed { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
255 a.feed { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
246
256
247 /***** Flash & error messages ****/
257 /***** Flash & error messages ****/
248 #errorExplanation, div.flash, .nodata {
258 #errorExplanation, div.flash, .nodata {
249 padding: 4px 4px 4px 30px;
259 padding: 4px 4px 4px 30px;
250 margin-bottom: 12px;
260 margin-bottom: 12px;
251 font-size: 1.1em;
261 font-size: 1.1em;
252 border: 2px solid;
262 border: 2px solid;
253 }
263 }
254
264
255 div.flash {margin-top: 8px;}
265 div.flash {margin-top: 8px;}
256
266
257 div.flash.error, #errorExplanation {
267 div.flash.error, #errorExplanation {
258 background: url(../images/false.png) 8px 5px no-repeat;
268 background: url(../images/false.png) 8px 5px no-repeat;
259 background-color: #ffe3e3;
269 background-color: #ffe3e3;
260 border-color: #dd0000;
270 border-color: #dd0000;
261 color: #550000;
271 color: #550000;
262 }
272 }
263
273
264 div.flash.notice {
274 div.flash.notice {
265 background: url(../images/true.png) 8px 5px no-repeat;
275 background: url(../images/true.png) 8px 5px no-repeat;
266 background-color: #dfffdf;
276 background-color: #dfffdf;
267 border-color: #9fcf9f;
277 border-color: #9fcf9f;
268 color: #005f00;
278 color: #005f00;
269 }
279 }
270
280
271 .nodata {
281 .nodata {
272 text-align: center;
282 text-align: center;
273 background-color: #FFEBC1;
283 background-color: #FFEBC1;
274 border-color: #FDBF3B;
284 border-color: #FDBF3B;
275 color: #A6750C;
285 color: #A6750C;
276 }
286 }
277
287
278 #errorExplanation ul { font-size: 0.9em;}
288 #errorExplanation ul { font-size: 0.9em;}
279
289
280 /***** Ajax indicator ******/
290 /***** Ajax indicator ******/
281 #ajax-indicator {
291 #ajax-indicator {
282 position: absolute; /* fixed not supported by IE */
292 position: absolute; /* fixed not supported by IE */
283 background-color:#eee;
293 background-color:#eee;
284 border: 1px solid #bbb;
294 border: 1px solid #bbb;
285 top:35%;
295 top:35%;
286 left:40%;
296 left:40%;
287 width:20%;
297 width:20%;
288 font-weight:bold;
298 font-weight:bold;
289 text-align:center;
299 text-align:center;
290 padding:0.6em;
300 padding:0.6em;
291 z-index:100;
301 z-index:100;
292 filter:alpha(opacity=50);
302 filter:alpha(opacity=50);
293 opacity: 0.5;
303 opacity: 0.5;
294 }
304 }
295
305
296 html>body #ajax-indicator { position: fixed; }
306 html>body #ajax-indicator { position: fixed; }
297
307
298 #ajax-indicator span {
308 #ajax-indicator span {
299 background-position: 0% 40%;
309 background-position: 0% 40%;
300 background-repeat: no-repeat;
310 background-repeat: no-repeat;
301 background-image: url(../images/loading.gif);
311 background-image: url(../images/loading.gif);
302 padding-left: 26px;
312 padding-left: 26px;
303 vertical-align: bottom;
313 vertical-align: bottom;
304 }
314 }
305
315
306 /***** Calendar *****/
316 /***** Calendar *****/
307 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
317 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
308 table.cal thead th {width: 14%;}
318 table.cal thead th {width: 14%;}
309 table.cal tbody tr {height: 100px;}
319 table.cal tbody tr {height: 100px;}
310 table.cal th { background-color:#EEEEEE; padding: 4px; }
320 table.cal th { background-color:#EEEEEE; padding: 4px; }
311 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
321 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
312 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
322 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
313 table.cal td.odd p.day-num {color: #bbb;}
323 table.cal td.odd p.day-num {color: #bbb;}
314 table.cal td.today {background:#ffffdd;}
324 table.cal td.today {background:#ffffdd;}
315 table.cal td.today p.day-num {font-weight: bold;}
325 table.cal td.today p.day-num {font-weight: bold;}
316
326
317 /***** Tooltips ******/
327 /***** Tooltips ******/
318 .tooltip{position:relative;z-index:24;}
328 .tooltip{position:relative;z-index:24;}
319 .tooltip:hover{z-index:25;color:#000;}
329 .tooltip:hover{z-index:25;color:#000;}
320 .tooltip span.tip{display: none; text-align:left;}
330 .tooltip span.tip{display: none; text-align:left;}
321
331
322 div.tooltip:hover span.tip{
332 div.tooltip:hover span.tip{
323 display:block;
333 display:block;
324 position:absolute;
334 position:absolute;
325 top:12px; left:24px; width:270px;
335 top:12px; left:24px; width:270px;
326 border:1px solid #555;
336 border:1px solid #555;
327 background-color:#fff;
337 background-color:#fff;
328 padding: 4px;
338 padding: 4px;
329 font-size: 0.8em;
339 font-size: 0.8em;
330 color:#505050;
340 color:#505050;
331 }
341 }
332
342
333 /***** Progress bar *****/
343 /***** Progress bar *****/
334 table.progress {
344 table.progress {
335 border: 1px solid #D7D7D7;
345 border: 1px solid #D7D7D7;
336 border-collapse: collapse;
346 border-collapse: collapse;
337 border-spacing: 0pt;
347 border-spacing: 0pt;
338 empty-cells: show;
348 empty-cells: show;
339 text-align: center;
349 text-align: center;
340 float:left;
350 float:left;
341 margin: 1px 6px 1px 0px;
351 margin: 1px 6px 1px 0px;
342 }
352 }
343
353
344 table.progress td { height: 0.9em; }
354 table.progress td { height: 0.9em; }
345 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
355 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
346 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
356 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
347 table.progress td.open { background: #FFF none repeat scroll 0%; }
357 table.progress td.open { background: #FFF none repeat scroll 0%; }
348 p.pourcent {font-size: 80%;}
358 p.pourcent {font-size: 80%;}
349 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
359 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
350
360
351 /***** Tabs *****/
361 /***** Tabs *****/
352 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
362 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
353 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
363 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
354 #content .tabs>ul { bottom:-1px; } /* others */
364 #content .tabs>ul { bottom:-1px; } /* others */
355 #content .tabs ul li {
365 #content .tabs ul li {
356 float:left;
366 float:left;
357 list-style-type:none;
367 list-style-type:none;
358 white-space:nowrap;
368 white-space:nowrap;
359 margin-right:8px;
369 margin-right:8px;
360 background:#fff;
370 background:#fff;
361 }
371 }
362 #content .tabs ul li a{
372 #content .tabs ul li a{
363 display:block;
373 display:block;
364 font-size: 0.9em;
374 font-size: 0.9em;
365 text-decoration:none;
375 text-decoration:none;
366 line-height:1.3em;
376 line-height:1.3em;
367 padding:4px 6px 4px 6px;
377 padding:4px 6px 4px 6px;
368 border: 1px solid #ccc;
378 border: 1px solid #ccc;
369 border-bottom: 1px solid #bbbbbb;
379 border-bottom: 1px solid #bbbbbb;
370 background-color: #eeeeee;
380 background-color: #eeeeee;
371 color:#777;
381 color:#777;
372 font-weight:bold;
382 font-weight:bold;
373 }
383 }
374
384
375 #content .tabs ul li a:hover {
385 #content .tabs ul li a:hover {
376 background-color: #ffffdd;
386 background-color: #ffffdd;
377 text-decoration:none;
387 text-decoration:none;
378 }
388 }
379
389
380 #content .tabs ul li a.selected {
390 #content .tabs ul li a.selected {
381 background-color: #fff;
391 background-color: #fff;
382 border: 1px solid #bbbbbb;
392 border: 1px solid #bbbbbb;
383 border-bottom: 1px solid #fff;
393 border-bottom: 1px solid #fff;
384 }
394 }
385
395
386 #content .tabs ul li a.selected:hover {
396 #content .tabs ul li a.selected:hover {
387 background-color: #fff;
397 background-color: #fff;
388 }
398 }
389
399
390 /***** Diff *****/
400 /***** Diff *****/
391 .diff_out { background: #fcc; }
401 .diff_out { background: #fcc; }
392 .diff_in { background: #cfc; }
402 .diff_in { background: #cfc; }
393
403
394 /***** Wiki *****/
404 /***** Wiki *****/
395 div.wiki table {
405 div.wiki table {
396 border: 1px solid #505050;
406 border: 1px solid #505050;
397 border-collapse: collapse;
407 border-collapse: collapse;
398 margin-bottom: 1em;
408 margin-bottom: 1em;
399 }
409 }
400
410
401 div.wiki table, div.wiki td, div.wiki th {
411 div.wiki table, div.wiki td, div.wiki th {
402 border: 1px solid #bbb;
412 border: 1px solid #bbb;
403 padding: 4px;
413 padding: 4px;
404 }
414 }
405
415
406 div.wiki .external {
416 div.wiki .external {
407 background-position: 0% 60%;
417 background-position: 0% 60%;
408 background-repeat: no-repeat;
418 background-repeat: no-repeat;
409 padding-left: 12px;
419 padding-left: 12px;
410 background-image: url(../images/external.png);
420 background-image: url(../images/external.png);
411 }
421 }
412
422
413 div.wiki a.new {
423 div.wiki a.new {
414 color: #b73535;
424 color: #b73535;
415 }
425 }
416
426
417 div.wiki pre {
427 div.wiki pre {
418 margin: 1em 1em 1em 1.6em;
428 margin: 1em 1em 1em 1.6em;
419 padding: 2px;
429 padding: 2px;
420 background-color: #fafafa;
430 background-color: #fafafa;
421 border: 1px solid #dadada;
431 border: 1px solid #dadada;
422 width:95%;
432 width:95%;
423 overflow-x: auto;
433 overflow-x: auto;
424 }
434 }
425
435
426 div.wiki div.toc {
436 div.wiki div.toc {
427 background-color: #ffffdd;
437 background-color: #ffffdd;
428 border: 1px solid #e4e4e4;
438 border: 1px solid #e4e4e4;
429 padding: 4px;
439 padding: 4px;
430 line-height: 1.2em;
440 line-height: 1.2em;
431 margin-bottom: 12px;
441 margin-bottom: 12px;
432 margin-right: 12px;
442 margin-right: 12px;
433 display: table
443 display: table
434 }
444 }
435 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
445 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
436
446
437 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
447 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
438 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
448 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
439
449
440 div.wiki div.toc a {
450 div.wiki div.toc a {
441 display: block;
451 display: block;
442 font-size: 0.9em;
452 font-size: 0.9em;
443 font-weight: normal;
453 font-weight: normal;
444 text-decoration: none;
454 text-decoration: none;
445 color: #606060;
455 color: #606060;
446 }
456 }
447 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
457 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
448
458
449 div.wiki div.toc a.heading2 { margin-left: 6px; }
459 div.wiki div.toc a.heading2 { margin-left: 6px; }
450 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
460 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
451
461
452 /***** My page layout *****/
462 /***** My page layout *****/
453 .block-receiver {
463 .block-receiver {
454 border:1px dashed #c0c0c0;
464 border:1px dashed #c0c0c0;
455 margin-bottom: 20px;
465 margin-bottom: 20px;
456 padding: 15px 0 15px 0;
466 padding: 15px 0 15px 0;
457 }
467 }
458
468
459 .mypage-box {
469 .mypage-box {
460 margin:0 0 20px 0;
470 margin:0 0 20px 0;
461 color:#505050;
471 color:#505050;
462 line-height:1.5em;
472 line-height:1.5em;
463 }
473 }
464
474
465 .handle {
475 .handle {
466 cursor: move;
476 cursor: move;
467 }
477 }
468
478
469 a.close-icon {
479 a.close-icon {
470 display:block;
480 display:block;
471 margin-top:3px;
481 margin-top:3px;
472 overflow:hidden;
482 overflow:hidden;
473 width:12px;
483 width:12px;
474 height:12px;
484 height:12px;
475 background-repeat: no-repeat;
485 background-repeat: no-repeat;
476 cursor:pointer;
486 cursor:pointer;
477 background-image:url('../images/close.png');
487 background-image:url('../images/close.png');
478 }
488 }
479
489
480 a.close-icon:hover {
490 a.close-icon:hover {
481 background-image:url('../images/close_hl.png');
491 background-image:url('../images/close_hl.png');
482 }
492 }
483
493
484 /***** Gantt chart *****/
494 /***** Gantt chart *****/
485 .gantt_hdr {
495 .gantt_hdr {
486 position:absolute;
496 position:absolute;
487 top:0;
497 top:0;
488 height:16px;
498 height:16px;
489 border-top: 1px solid #c0c0c0;
499 border-top: 1px solid #c0c0c0;
490 border-bottom: 1px solid #c0c0c0;
500 border-bottom: 1px solid #c0c0c0;
491 border-right: 1px solid #c0c0c0;
501 border-right: 1px solid #c0c0c0;
492 text-align: center;
502 text-align: center;
493 overflow: hidden;
503 overflow: hidden;
494 }
504 }
495
505
496 .task {
506 .task {
497 position: absolute;
507 position: absolute;
498 height:8px;
508 height:8px;
499 font-size:0.8em;
509 font-size:0.8em;
500 color:#888;
510 color:#888;
501 padding:0;
511 padding:0;
502 margin:0;
512 margin:0;
503 line-height:0.8em;
513 line-height:0.8em;
504 }
514 }
505
515
506 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
516 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
507 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
517 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
508 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
518 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
509 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
519 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
510
520
511 /***** Icons *****/
521 /***** Icons *****/
512 .icon {
522 .icon {
513 background-position: 0% 40%;
523 background-position: 0% 40%;
514 background-repeat: no-repeat;
524 background-repeat: no-repeat;
515 padding-left: 20px;
525 padding-left: 20px;
516 padding-top: 2px;
526 padding-top: 2px;
517 padding-bottom: 3px;
527 padding-bottom: 3px;
518 }
528 }
519
529
520 .icon22 {
530 .icon22 {
521 background-position: 0% 40%;
531 background-position: 0% 40%;
522 background-repeat: no-repeat;
532 background-repeat: no-repeat;
523 padding-left: 26px;
533 padding-left: 26px;
524 line-height: 22px;
534 line-height: 22px;
525 vertical-align: middle;
535 vertical-align: middle;
526 }
536 }
527
537
528 .icon-add { background-image: url(../images/add.png); }
538 .icon-add { background-image: url(../images/add.png); }
529 .icon-edit { background-image: url(../images/edit.png); }
539 .icon-edit { background-image: url(../images/edit.png); }
530 .icon-copy { background-image: url(../images/copy.png); }
540 .icon-copy { background-image: url(../images/copy.png); }
531 .icon-del { background-image: url(../images/delete.png); }
541 .icon-del { background-image: url(../images/delete.png); }
532 .icon-move { background-image: url(../images/move.png); }
542 .icon-move { background-image: url(../images/move.png); }
533 .icon-save { background-image: url(../images/save.png); }
543 .icon-save { background-image: url(../images/save.png); }
534 .icon-cancel { background-image: url(../images/cancel.png); }
544 .icon-cancel { background-image: url(../images/cancel.png); }
535 .icon-file { background-image: url(../images/file.png); }
545 .icon-file { background-image: url(../images/file.png); }
536 .icon-folder { background-image: url(../images/folder.png); }
546 .icon-folder { background-image: url(../images/folder.png); }
537 .open .icon-folder { background-image: url(../images/folder_open.png); }
547 .open .icon-folder { background-image: url(../images/folder_open.png); }
538 .icon-package { background-image: url(../images/package.png); }
548 .icon-package { background-image: url(../images/package.png); }
539 .icon-home { background-image: url(../images/home.png); }
549 .icon-home { background-image: url(../images/home.png); }
540 .icon-user { background-image: url(../images/user.png); }
550 .icon-user { background-image: url(../images/user.png); }
541 .icon-mypage { background-image: url(../images/user_page.png); }
551 .icon-mypage { background-image: url(../images/user_page.png); }
542 .icon-admin { background-image: url(../images/admin.png); }
552 .icon-admin { background-image: url(../images/admin.png); }
543 .icon-projects { background-image: url(../images/projects.png); }
553 .icon-projects { background-image: url(../images/projects.png); }
544 .icon-logout { background-image: url(../images/logout.png); }
554 .icon-logout { background-image: url(../images/logout.png); }
545 .icon-help { background-image: url(../images/help.png); }
555 .icon-help { background-image: url(../images/help.png); }
546 .icon-attachment { background-image: url(../images/attachment.png); }
556 .icon-attachment { background-image: url(../images/attachment.png); }
547 .icon-index { background-image: url(../images/index.png); }
557 .icon-index { background-image: url(../images/index.png); }
548 .icon-history { background-image: url(../images/history.png); }
558 .icon-history { background-image: url(../images/history.png); }
549 .icon-time { background-image: url(../images/time.png); }
559 .icon-time { background-image: url(../images/time.png); }
550 .icon-stats { background-image: url(../images/stats.png); }
560 .icon-stats { background-image: url(../images/stats.png); }
551 .icon-warning { background-image: url(../images/warning.png); }
561 .icon-warning { background-image: url(../images/warning.png); }
552 .icon-fav { background-image: url(../images/fav.png); }
562 .icon-fav { background-image: url(../images/fav.png); }
553 .icon-fav-off { background-image: url(../images/fav_off.png); }
563 .icon-fav-off { background-image: url(../images/fav_off.png); }
554 .icon-reload { background-image: url(../images/reload.png); }
564 .icon-reload { background-image: url(../images/reload.png); }
555 .icon-lock { background-image: url(../images/locked.png); }
565 .icon-lock { background-image: url(../images/locked.png); }
556 .icon-unlock { background-image: url(../images/unlock.png); }
566 .icon-unlock { background-image: url(../images/unlock.png); }
557 .icon-checked { background-image: url(../images/true.png); }
567 .icon-checked { background-image: url(../images/true.png); }
558 .icon-details { background-image: url(../images/zoom_in.png); }
568 .icon-details { background-image: url(../images/zoom_in.png); }
559 .icon-report { background-image: url(../images/report.png); }
569 .icon-report { background-image: url(../images/report.png); }
560
570
561 .icon22-projects { background-image: url(../images/22x22/projects.png); }
571 .icon22-projects { background-image: url(../images/22x22/projects.png); }
562 .icon22-users { background-image: url(../images/22x22/users.png); }
572 .icon22-users { background-image: url(../images/22x22/users.png); }
563 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
573 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
564 .icon22-role { background-image: url(../images/22x22/role.png); }
574 .icon22-role { background-image: url(../images/22x22/role.png); }
565 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
575 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
566 .icon22-options { background-image: url(../images/22x22/options.png); }
576 .icon22-options { background-image: url(../images/22x22/options.png); }
567 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
577 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
568 .icon22-authent { background-image: url(../images/22x22/authent.png); }
578 .icon22-authent { background-image: url(../images/22x22/authent.png); }
569 .icon22-info { background-image: url(../images/22x22/info.png); }
579 .icon22-info { background-image: url(../images/22x22/info.png); }
570 .icon22-comment { background-image: url(../images/22x22/comment.png); }
580 .icon22-comment { background-image: url(../images/22x22/comment.png); }
571 .icon22-package { background-image: url(../images/22x22/package.png); }
581 .icon22-package { background-image: url(../images/22x22/package.png); }
572 .icon22-settings { background-image: url(../images/22x22/settings.png); }
582 .icon22-settings { background-image: url(../images/22x22/settings.png); }
573 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
583 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
574
584
575 /***** Media print specific styles *****/
585 /***** Media print specific styles *****/
576 @media print {
586 @media print {
577 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
587 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
578 #main { background: #fff; }
588 #main { background: #fff; }
579 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
589 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
580 }
590 }
@@ -1,268 +1,268
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 File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'projects_controller'
19 require 'projects_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class ProjectsController; def rescue_action(e) raise e end; end
22 class ProjectsController; def rescue_action(e) raise e end; end
23
23
24 class ProjectsControllerTest < Test::Unit::TestCase
24 class ProjectsControllerTest < Test::Unit::TestCase
25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
27
27
28 def setup
28 def setup
29 @controller = ProjectsController.new
29 @controller = ProjectsController.new
30 @request = ActionController::TestRequest.new
30 @request = ActionController::TestRequest.new
31 @response = ActionController::TestResponse.new
31 @response = ActionController::TestResponse.new
32 end
32 end
33
33
34 def test_index
34 def test_index
35 get :index
35 get :index
36 assert_response :success
36 assert_response :success
37 assert_template 'list'
37 assert_template 'list'
38 end
38 end
39
39
40 def test_list
40 def test_list
41 get :list
41 get :list
42 assert_response :success
42 assert_response :success
43 assert_template 'list'
43 assert_template 'list'
44 assert_not_nil assigns(:project_tree)
44 assert_not_nil assigns(:project_tree)
45 # Root project as hash key
45 # Root project as hash key
46 assert assigns(:project_tree).has_key?(Project.find(1))
46 assert assigns(:project_tree).has_key?(Project.find(1))
47 # Subproject in corresponding value
47 # Subproject in corresponding value
48 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
48 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
49 end
49 end
50
50
51 def test_show_by_id
51 def test_show_by_id
52 get :show, :id => 1
52 get :show, :id => 1
53 assert_response :success
53 assert_response :success
54 assert_template 'show'
54 assert_template 'show'
55 assert_not_nil assigns(:project)
55 assert_not_nil assigns(:project)
56 end
56 end
57
57
58 def test_show_by_identifier
58 def test_show_by_identifier
59 get :show, :id => 'ecookbook'
59 get :show, :id => 'ecookbook'
60 assert_response :success
60 assert_response :success
61 assert_template 'show'
61 assert_template 'show'
62 assert_not_nil assigns(:project)
62 assert_not_nil assigns(:project)
63 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
63 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
64 end
64 end
65
65
66 def test_settings
66 def test_settings
67 @request.session[:user_id] = 2 # manager
67 @request.session[:user_id] = 2 # manager
68 get :settings, :id => 1
68 get :settings, :id => 1
69 assert_response :success
69 assert_response :success
70 assert_template 'settings'
70 assert_template 'settings'
71 end
71 end
72
72
73 def test_edit
73 def test_edit
74 @request.session[:user_id] = 2 # manager
74 @request.session[:user_id] = 2 # manager
75 post :edit, :id => 1, :project => {:name => 'Test changed name',
75 post :edit, :id => 1, :project => {:name => 'Test changed name',
76 :custom_field_ids => ['']}
76 :custom_field_ids => ['']}
77 assert_redirected_to 'projects/settings/ecookbook'
77 assert_redirected_to 'projects/settings/ecookbook'
78 project = Project.find(1)
78 project = Project.find(1)
79 assert_equal 'Test changed name', project.name
79 assert_equal 'Test changed name', project.name
80 end
80 end
81
81
82 def test_get_destroy
82 def test_get_destroy
83 @request.session[:user_id] = 1 # admin
83 @request.session[:user_id] = 1 # admin
84 get :destroy, :id => 1
84 get :destroy, :id => 1
85 assert_response :success
85 assert_response :success
86 assert_template 'destroy'
86 assert_template 'destroy'
87 assert_not_nil Project.find_by_id(1)
87 assert_not_nil Project.find_by_id(1)
88 end
88 end
89
89
90 def test_post_destroy
90 def test_post_destroy
91 @request.session[:user_id] = 1 # admin
91 @request.session[:user_id] = 1 # admin
92 post :destroy, :id => 1, :confirm => 1
92 post :destroy, :id => 1, :confirm => 1
93 assert_redirected_to 'admin/projects'
93 assert_redirected_to 'admin/projects'
94 assert_nil Project.find_by_id(1)
94 assert_nil Project.find_by_id(1)
95 end
95 end
96
96
97 def test_list_files
97 def test_list_files
98 get :list_files, :id => 1
98 get :list_files, :id => 1
99 assert_response :success
99 assert_response :success
100 assert_template 'list_files'
100 assert_template 'list_files'
101 assert_not_nil assigns(:versions)
101 assert_not_nil assigns(:versions)
102 end
102 end
103
103
104 def test_changelog
104 def test_changelog
105 get :changelog, :id => 1
105 get :changelog, :id => 1
106 assert_response :success
106 assert_response :success
107 assert_template 'changelog'
107 assert_template 'changelog'
108 assert_not_nil assigns(:versions)
108 assert_not_nil assigns(:versions)
109 end
109 end
110
110
111 def test_roadmap
111 def test_roadmap
112 get :roadmap, :id => 1
112 get :roadmap, :id => 1
113 assert_response :success
113 assert_response :success
114 assert_template 'roadmap'
114 assert_template 'roadmap'
115 assert_not_nil assigns(:versions)
115 assert_not_nil assigns(:versions)
116 # Version with no date set appears
116 # Version with no date set appears
117 assert assigns(:versions).include?(Version.find(3))
117 assert assigns(:versions).include?(Version.find(3))
118 # Completed version doesn't appear
118 # Completed version doesn't appear
119 assert !assigns(:versions).include?(Version.find(1))
119 assert !assigns(:versions).include?(Version.find(1))
120 end
120 end
121
121
122 def test_roadmap_with_completed_versions
122 def test_roadmap_with_completed_versions
123 get :roadmap, :id => 1, :completed => 1
123 get :roadmap, :id => 1, :completed => 1
124 assert_response :success
124 assert_response :success
125 assert_template 'roadmap'
125 assert_template 'roadmap'
126 assert_not_nil assigns(:versions)
126 assert_not_nil assigns(:versions)
127 # Version with no date set appears
127 # Version with no date set appears
128 assert assigns(:versions).include?(Version.find(3))
128 assert assigns(:versions).include?(Version.find(3))
129 # Completed version appears
129 # Completed version appears
130 assert assigns(:versions).include?(Version.find(1))
130 assert assigns(:versions).include?(Version.find(1))
131 end
131 end
132
132
133 def test_project_activity
133 def test_project_activity
134 get :activity, :id => 1, :with_subprojects => 0
134 get :activity, :id => 1, :with_subprojects => 0
135 assert_response :success
135 assert_response :success
136 assert_template 'activity'
136 assert_template 'activity'
137 assert_not_nil assigns(:events_by_day)
137 assert_not_nil assigns(:events_by_day)
138 assert_not_nil assigns(:events)
138 assert_not_nil assigns(:events)
139
139
140 # subproject issue not included by default
140 # subproject issue not included by default
141 assert !assigns(:events).include?(Issue.find(5))
141 assert !assigns(:events).include?(Issue.find(5))
142
142
143 assert_tag :tag => "h3",
143 assert_tag :tag => "h3",
144 :content => /#{2.days.ago.to_date.day}/,
144 :content => /#{2.days.ago.to_date.day}/,
145 :sibling => { :tag => "dl",
145 :sibling => { :tag => "dl",
146 :child => { :tag => "dt",
146 :child => { :tag => "dt",
147 :attributes => { :class => 'journal' },
147 :attributes => { :class => 'issue-edit' },
148 :child => { :tag => "a",
148 :child => { :tag => "a",
149 :content => /(#{IssueStatus.find(2).name})/,
149 :content => /(#{IssueStatus.find(2).name})/,
150 }
150 }
151 }
151 }
152 }
152 }
153
153
154 get :activity, :id => 1, :from => 3.days.ago.to_date
154 get :activity, :id => 1, :from => 3.days.ago.to_date
155 assert_response :success
155 assert_response :success
156 assert_template 'activity'
156 assert_template 'activity'
157 assert_not_nil assigns(:events_by_day)
157 assert_not_nil assigns(:events_by_day)
158
158
159 assert_tag :tag => "h3",
159 assert_tag :tag => "h3",
160 :content => /#{3.day.ago.to_date.day}/,
160 :content => /#{3.day.ago.to_date.day}/,
161 :sibling => { :tag => "dl",
161 :sibling => { :tag => "dl",
162 :child => { :tag => "dt",
162 :child => { :tag => "dt",
163 :attributes => { :class => 'issue' },
163 :attributes => { :class => 'issue' },
164 :child => { :tag => "a",
164 :child => { :tag => "a",
165 :content => /#{Issue.find(1).subject}/,
165 :content => /#{Issue.find(1).subject}/,
166 }
166 }
167 }
167 }
168 }
168 }
169 end
169 end
170
170
171 def test_activity_with_subprojects
171 def test_activity_with_subprojects
172 get :activity, :id => 1, :with_subprojects => 1
172 get :activity, :id => 1, :with_subprojects => 1
173 assert_response :success
173 assert_response :success
174 assert_template 'activity'
174 assert_template 'activity'
175 assert_not_nil assigns(:events)
175 assert_not_nil assigns(:events)
176
176
177 assert assigns(:events).include?(Issue.find(1))
177 assert assigns(:events).include?(Issue.find(1))
178 assert !assigns(:events).include?(Issue.find(4))
178 assert !assigns(:events).include?(Issue.find(4))
179 # subproject issue
179 # subproject issue
180 assert assigns(:events).include?(Issue.find(5))
180 assert assigns(:events).include?(Issue.find(5))
181 end
181 end
182
182
183 def test_global_activity_anonymous
183 def test_global_activity_anonymous
184 get :activity
184 get :activity
185 assert_response :success
185 assert_response :success
186 assert_template 'activity'
186 assert_template 'activity'
187 assert_not_nil assigns(:events)
187 assert_not_nil assigns(:events)
188
188
189 assert assigns(:events).include?(Issue.find(1))
189 assert assigns(:events).include?(Issue.find(1))
190 # Issue of a private project
190 # Issue of a private project
191 assert !assigns(:events).include?(Issue.find(4))
191 assert !assigns(:events).include?(Issue.find(4))
192 end
192 end
193
193
194 def test_global_activity_logged_user
194 def test_global_activity_logged_user
195 @request.session[:user_id] = 2 # manager
195 @request.session[:user_id] = 2 # manager
196 get :activity
196 get :activity
197 assert_response :success
197 assert_response :success
198 assert_template 'activity'
198 assert_template 'activity'
199 assert_not_nil assigns(:events)
199 assert_not_nil assigns(:events)
200
200
201 assert assigns(:events).include?(Issue.find(1))
201 assert assigns(:events).include?(Issue.find(1))
202 # Issue of a private project the user belongs to
202 # Issue of a private project the user belongs to
203 assert assigns(:events).include?(Issue.find(4))
203 assert assigns(:events).include?(Issue.find(4))
204 end
204 end
205
205
206
206
207 def test_global_activity_with_all_types
207 def test_global_activity_with_all_types
208 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
208 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
209 assert_response :success
209 assert_response :success
210 assert_template 'activity'
210 assert_template 'activity'
211 assert_not_nil assigns(:events)
211 assert_not_nil assigns(:events)
212
212
213 assert assigns(:events).include?(Issue.find(1))
213 assert assigns(:events).include?(Issue.find(1))
214 assert !assigns(:events).include?(Issue.find(4))
214 assert !assigns(:events).include?(Issue.find(4))
215 assert assigns(:events).include?(Message.find(5))
215 assert assigns(:events).include?(Message.find(5))
216 end
216 end
217
217
218 def test_calendar
218 def test_calendar
219 get :calendar, :id => 1
219 get :calendar, :id => 1
220 assert_response :success
220 assert_response :success
221 assert_template 'calendar'
221 assert_template 'calendar'
222 assert_not_nil assigns(:calendar)
222 assert_not_nil assigns(:calendar)
223 end
223 end
224
224
225 def test_calendar_with_subprojects
225 def test_calendar_with_subprojects
226 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
226 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
227 assert_response :success
227 assert_response :success
228 assert_template 'calendar'
228 assert_template 'calendar'
229 assert_not_nil assigns(:calendar)
229 assert_not_nil assigns(:calendar)
230 end
230 end
231
231
232 def test_gantt
232 def test_gantt
233 get :gantt, :id => 1
233 get :gantt, :id => 1
234 assert_response :success
234 assert_response :success
235 assert_template 'gantt.rhtml'
235 assert_template 'gantt.rhtml'
236 assert_not_nil assigns(:events)
236 assert_not_nil assigns(:events)
237 end
237 end
238
238
239 def test_gantt_with_subprojects
239 def test_gantt_with_subprojects
240 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
240 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
241 assert_response :success
241 assert_response :success
242 assert_template 'gantt.rhtml'
242 assert_template 'gantt.rhtml'
243 assert_not_nil assigns(:events)
243 assert_not_nil assigns(:events)
244 end
244 end
245
245
246 def test_gantt_export_to_pdf
246 def test_gantt_export_to_pdf
247 get :gantt, :id => 1, :format => 'pdf'
247 get :gantt, :id => 1, :format => 'pdf'
248 assert_response :success
248 assert_response :success
249 assert_template 'gantt.rfpdf'
249 assert_template 'gantt.rfpdf'
250 assert_equal 'application/pdf', @response.content_type
250 assert_equal 'application/pdf', @response.content_type
251 assert_not_nil assigns(:events)
251 assert_not_nil assigns(:events)
252 end
252 end
253
253
254 def test_archive
254 def test_archive
255 @request.session[:user_id] = 1 # admin
255 @request.session[:user_id] = 1 # admin
256 post :archive, :id => 1
256 post :archive, :id => 1
257 assert_redirected_to 'admin/projects'
257 assert_redirected_to 'admin/projects'
258 assert !Project.find(1).active?
258 assert !Project.find(1).active?
259 end
259 end
260
260
261 def test_unarchive
261 def test_unarchive
262 @request.session[:user_id] = 1 # admin
262 @request.session[:user_id] = 1 # admin
263 Project.find(1).archive
263 Project.find(1).archive
264 post :unarchive, :id => 1
264 post :unarchive, :id => 1
265 assert_redirected_to 'admin/projects'
265 assert_redirected_to 'admin/projects'
266 assert Project.find(1).active?
266 assert Project.find(1).active?
267 end
267 end
268 end
268 end
@@ -1,68 +1,75
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module Redmine
18 module Redmine
19 module Acts
19 module Acts
20 module Event
20 module Event
21 def self.included(base)
21 def self.included(base)
22 base.extend ClassMethods
22 base.extend ClassMethods
23 end
23 end
24
24
25 module ClassMethods
25 module ClassMethods
26 def acts_as_event(options = {})
26 def acts_as_event(options = {})
27 return if self.included_modules.include?(Redmine::Acts::Event::InstanceMethods)
27 return if self.included_modules.include?(Redmine::Acts::Event::InstanceMethods)
28 options[:datetime] ||= 'created_on'
28 options[:datetime] ||= :created_on
29 options[:title] ||= 'title'
29 options[:title] ||= :title
30 options[:description] ||= 'description'
30 options[:description] ||= :description
31 options[:author] ||= 'author'
31 options[:author] ||= :author
32 options[:url] ||= {:controller => 'welcome'}
32 options[:url] ||= {:controller => 'welcome'}
33 options[:type] ||= self.name.underscore.dasherize
33 cattr_accessor :event_options
34 cattr_accessor :event_options
34 self.event_options = options
35 self.event_options = options
35 send :include, Redmine::Acts::Event::InstanceMethods
36 send :include, Redmine::Acts::Event::InstanceMethods
36 end
37 end
37 end
38 end
38
39
39 module InstanceMethods
40 module InstanceMethods
40 def self.included(base)
41 def self.included(base)
41 base.extend ClassMethods
42 base.extend ClassMethods
42 end
43 end
43
44
44 %w(datetime title description author).each do |attr|
45 %w(datetime title description author type).each do |attr|
45 src = <<-END_SRC
46 src = <<-END_SRC
46 def event_#{attr}
47 def event_#{attr}
47 option = event_options[:#{attr}]
48 option = event_options[:#{attr}]
48 option.is_a?(Proc) ? option.call(self) : send(option)
49 if option.is_a?(Proc)
50 option.call(self)
51 elsif option.is_a?(Symbol)
52 send(option)
53 else
54 option
55 end
49 end
56 end
50 END_SRC
57 END_SRC
51 class_eval src, __FILE__, __LINE__
58 class_eval src, __FILE__, __LINE__
52 end
59 end
53
60
54 def event_date
61 def event_date
55 event_datetime.to_date
62 event_datetime.to_date
56 end
63 end
57
64
58 def event_url(options = {})
65 def event_url(options = {})
59 option = event_options[:url]
66 option = event_options[:url]
60 (option.is_a?(Proc) ? option.call(self) : send(option)).merge(options)
67 (option.is_a?(Proc) ? option.call(self) : send(option)).merge(options)
61 end
68 end
62
69
63 module ClassMethods
70 module ClassMethods
64 end
71 end
65 end
72 end
66 end
73 end
67 end
74 end
68 end
75 end
General Comments 0
You need to be logged in to leave comments. Login now