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