##// END OF EJS Templates
Replaces TimeEntry.visible_by with a visible scope....
Jean-Philippe Lang -
r5029:0786b9ef99b0
parent child
Show More
@@ -1,270 +1,269
1 1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 ProjectsController < ApplicationController
19 19 menu_item :overview
20 20 menu_item :roadmap, :only => :roadmap
21 21 menu_item :settings, :only => :settings
22 22
23 23 before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
24 24 before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
25 25 before_filter :authorize_global, :only => [:new, :create]
26 26 before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
27 27 accept_key_auth :index, :show, :create, :update, :destroy
28 28
29 29 after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
30 30 if controller.request.post?
31 31 controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
32 32 end
33 33 end
34 34
35 35 helper :sort
36 36 include SortHelper
37 37 helper :custom_fields
38 38 include CustomFieldsHelper
39 39 helper :issues
40 40 helper :queries
41 41 include QueriesHelper
42 42 helper :repositories
43 43 include RepositoriesHelper
44 44 include ProjectsHelper
45 45
46 46 # Lists visible projects
47 47 def index
48 48 respond_to do |format|
49 49 format.html {
50 50 @projects = Project.visible.find(:all, :order => 'lft')
51 51 }
52 52 format.api {
53 53 @offset, @limit = api_offset_and_limit
54 54 @project_count = Project.visible.count
55 55 @projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
56 56 }
57 57 format.atom {
58 58 projects = Project.visible.find(:all, :order => 'created_on DESC',
59 59 :limit => Setting.feeds_limit.to_i)
60 60 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
61 61 }
62 62 end
63 63 end
64 64
65 65 def new
66 66 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
67 67 @trackers = Tracker.all
68 68 @project = Project.new(params[:project])
69 69 end
70 70
71 71 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
72 72 def create
73 73 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
74 74 @trackers = Tracker.all
75 75 @project = Project.new
76 76 @project.safe_attributes = params[:project]
77 77
78 78 if validate_parent_id && @project.save
79 79 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
80 80 # Add current user as a project member if he is not admin
81 81 unless User.current.admin?
82 82 r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
83 83 m = Member.new(:user => User.current, :roles => [r])
84 84 @project.members << m
85 85 end
86 86 respond_to do |format|
87 87 format.html {
88 88 flash[:notice] = l(:notice_successful_create)
89 89 redirect_to :controller => 'projects', :action => 'settings', :id => @project
90 90 }
91 91 format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
92 92 end
93 93 else
94 94 respond_to do |format|
95 95 format.html { render :action => 'new' }
96 96 format.api { render_validation_errors(@project) }
97 97 end
98 98 end
99 99
100 100 end
101 101
102 102 def copy
103 103 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
104 104 @trackers = Tracker.all
105 105 @root_projects = Project.find(:all,
106 106 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
107 107 :order => 'name')
108 108 @source_project = Project.find(params[:id])
109 109 if request.get?
110 110 @project = Project.copy_from(@source_project)
111 111 if @project
112 112 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
113 113 else
114 114 redirect_to :controller => 'admin', :action => 'projects'
115 115 end
116 116 else
117 117 Mailer.with_deliveries(params[:notifications] == '1') do
118 118 @project = Project.new
119 119 @project.safe_attributes = params[:project]
120 120 @project.enabled_module_names = params[:enabled_modules]
121 121 if validate_parent_id && @project.copy(@source_project, :only => params[:only])
122 122 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
123 123 flash[:notice] = l(:notice_successful_create)
124 124 redirect_to :controller => 'projects', :action => 'settings', :id => @project
125 125 elsif !@project.new_record?
126 126 # Project was created
127 127 # But some objects were not copied due to validation failures
128 128 # (eg. issues from disabled trackers)
129 129 # TODO: inform about that
130 130 redirect_to :controller => 'projects', :action => 'settings', :id => @project
131 131 end
132 132 end
133 133 end
134 134 rescue ActiveRecord::RecordNotFound
135 135 redirect_to :controller => 'admin', :action => 'projects'
136 136 end
137 137
138 138 # Show @project
139 139 def show
140 140 if params[:jump]
141 141 # try to redirect to the requested menu item
142 142 redirect_to_project_menu_item(@project, params[:jump]) && return
143 143 end
144 144
145 145 @users_by_role = @project.users_by_role
146 146 @subprojects = @project.children.visible
147 147 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
148 148 @trackers = @project.rolled_up_trackers
149 149
150 150 cond = @project.project_condition(Setting.display_subprojects_issues?)
151 151
152 152 @open_issues_by_tracker = Issue.visible.count(:group => :tracker,
153 153 :include => [:project, :status, :tracker],
154 154 :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
155 155 @total_issues_by_tracker = Issue.visible.count(:group => :tracker,
156 156 :include => [:project, :status, :tracker],
157 157 :conditions => cond)
158 158
159 TimeEntry.visible_by(User.current) do
160 @total_hours = TimeEntry.sum(:hours,
161 :include => :project,
162 :conditions => cond).to_f
159 if User.current.allowed_to?(:view_time_entries, @project)
160 @total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
163 161 end
162
164 163 @key = User.current.rss_key
165 164
166 165 respond_to do |format|
167 166 format.html
168 167 format.api
169 168 end
170 169 end
171 170
172 171 def settings
173 172 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
174 173 @issue_category ||= IssueCategory.new
175 174 @member ||= @project.members.new
176 175 @trackers = Tracker.all
177 176 @repository ||= @project.repository
178 177 @wiki ||= @project.wiki
179 178 end
180 179
181 180 def edit
182 181 end
183 182
184 183 # TODO: convert to PUT only
185 184 verify :method => [:post, :put], :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
186 185 def update
187 186 @project.safe_attributes = params[:project]
188 187 if validate_parent_id && @project.save
189 188 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
190 189 respond_to do |format|
191 190 format.html {
192 191 flash[:notice] = l(:notice_successful_update)
193 192 redirect_to :action => 'settings', :id => @project
194 193 }
195 194 format.api { head :ok }
196 195 end
197 196 else
198 197 respond_to do |format|
199 198 format.html {
200 199 settings
201 200 render :action => 'settings'
202 201 }
203 202 format.api { render_validation_errors(@project) }
204 203 end
205 204 end
206 205 end
207 206
208 207 verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
209 208 def modules
210 209 @project.enabled_module_names = params[:enabled_module_names]
211 210 flash[:notice] = l(:notice_successful_update)
212 211 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
213 212 end
214 213
215 214 def archive
216 215 if request.post?
217 216 unless @project.archive
218 217 flash[:error] = l(:error_can_not_archive_project)
219 218 end
220 219 end
221 220 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
222 221 end
223 222
224 223 def unarchive
225 224 @project.unarchive if request.post? && !@project.active?
226 225 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
227 226 end
228 227
229 228 # Delete @project
230 229 def destroy
231 230 @project_to_destroy = @project
232 231 if request.get?
233 232 # display confirmation view
234 233 else
235 234 if api_request? || params[:confirm]
236 235 @project_to_destroy.destroy
237 236 respond_to do |format|
238 237 format.html { redirect_to :controller => 'admin', :action => 'projects' }
239 238 format.api { head :ok }
240 239 end
241 240 end
242 241 end
243 242 # hide project in layout
244 243 @project = nil
245 244 end
246 245
247 246 private
248 247 def find_optional_project
249 248 return true unless params[:id]
250 249 @project = Project.find(params[:id])
251 250 authorize
252 251 rescue ActiveRecord::RecordNotFound
253 252 render_404
254 253 end
255 254
256 255 # Validates parent_id param according to user's permissions
257 256 # TODO: move it to Project model in a validation that depends on User.current
258 257 def validate_parent_id
259 258 return true if User.current.admin?
260 259 parent_id = params[:project] && params[:project][:parent_id]
261 260 if parent_id || @project.new_record?
262 261 parent = parent_id.blank? ? nil : Project.find_by_id(parent_id.to_i)
263 262 unless @project.allowed_parents.include?(parent)
264 263 @project.errors.add :parent_id, :invalid
265 264 return false
266 265 end
267 266 end
268 267 true
269 268 end
270 269 end
@@ -1,272 +1,268
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2010 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 TimelogController < ApplicationController
19 19 menu_item :issues
20 20 before_filter :find_project, :only => [:new, :create]
21 21 before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
22 22 before_filter :authorize, :except => [:index]
23 23 before_filter :find_optional_project, :only => [:index]
24 24 accept_key_auth :index, :show, :create, :update, :destroy
25 25
26 26 helper :sort
27 27 include SortHelper
28 28 helper :issues
29 29 include TimelogHelper
30 30 helper :custom_fields
31 31 include CustomFieldsHelper
32 32
33 33 def index
34 34 sort_init 'spent_on', 'desc'
35 35 sort_update 'spent_on' => 'spent_on',
36 36 'user' => 'user_id',
37 37 'activity' => 'activity_id',
38 38 'project' => "#{Project.table_name}.name",
39 39 'issue' => 'issue_id',
40 40 'hours' => 'hours'
41 41
42 42 cond = ARCondition.new
43 if @project.nil?
44 cond << Project.allowed_to_condition(User.current, :view_time_entries)
45 elsif @issue.nil?
46 cond << @project.project_condition(Setting.display_subprojects_issues?)
47 else
43 if @issue
48 44 cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
45 elsif @project
46 cond << @project.project_condition(Setting.display_subprojects_issues?)
49 47 end
50 48
51 49 retrieve_date_range
52 50 cond << ['spent_on BETWEEN ? AND ?', @from, @to]
53 51
54 TimeEntry.visible_by(User.current) do
55 52 respond_to do |format|
56 53 format.html {
57 54 # Paginate results
58 @entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
55 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
59 56 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
60 @entries = TimeEntry.find(:all,
57 @entries = TimeEntry.visible.find(:all,
61 58 :include => [:project, :activity, :user, {:issue => :tracker}],
62 59 :conditions => cond.conditions,
63 60 :order => sort_clause,
64 61 :limit => @entry_pages.items_per_page,
65 62 :offset => @entry_pages.current.offset)
66 @total_hours = TimeEntry.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
63 @total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
67 64
68 65 render :layout => !request.xhr?
69 66 }
70 67 format.api {
71 @entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
68 @entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
72 69 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
73 @entries = TimeEntry.find(:all,
70 @entries = TimeEntry.visible.find(:all,
74 71 :include => [:project, :activity, :user, {:issue => :tracker}],
75 72 :conditions => cond.conditions,
76 73 :order => sort_clause,
77 74 :limit => @entry_pages.items_per_page,
78 75 :offset => @entry_pages.current.offset)
79 76 }
80 77 format.atom {
81 entries = TimeEntry.find(:all,
78 entries = TimeEntry.visible.find(:all,
82 79 :include => [:project, :activity, :user, {:issue => :tracker}],
83 80 :conditions => cond.conditions,
84 81 :order => "#{TimeEntry.table_name}.created_on DESC",
85 82 :limit => Setting.feeds_limit.to_i)
86 83 render_feed(entries, :title => l(:label_spent_time))
87 84 }
88 85 format.csv {
89 86 # Export all entries
90 @entries = TimeEntry.find(:all,
87 @entries = TimeEntry.visible.find(:all,
91 88 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
92 89 :conditions => cond.conditions,
93 90 :order => sort_clause)
94 91 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
95 92 }
96 93 end
97 94 end
98 end
99 95
100 96 def show
101 97 respond_to do |format|
102 98 # TODO: Implement html response
103 99 format.html { render :nothing => true, :status => 406 }
104 100 format.api
105 101 end
106 102 end
107 103
108 104 def new
109 105 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
110 106 @time_entry.attributes = params[:time_entry]
111 107
112 108 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
113 109 render :action => 'edit'
114 110 end
115 111
116 112 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
117 113 def create
118 114 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
119 115 @time_entry.attributes = params[:time_entry]
120 116
121 117 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
122 118
123 119 if @time_entry.save
124 120 respond_to do |format|
125 121 format.html {
126 122 flash[:notice] = l(:notice_successful_update)
127 123 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
128 124 }
129 125 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
130 126 end
131 127 else
132 128 respond_to do |format|
133 129 format.html { render :action => 'edit' }
134 130 format.api { render_validation_errors(@time_entry) }
135 131 end
136 132 end
137 133 end
138 134
139 135 def edit
140 136 @time_entry.attributes = params[:time_entry]
141 137
142 138 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
143 139 end
144 140
145 141 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
146 142 def update
147 143 @time_entry.attributes = params[:time_entry]
148 144
149 145 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
150 146
151 147 if @time_entry.save
152 148 respond_to do |format|
153 149 format.html {
154 150 flash[:notice] = l(:notice_successful_update)
155 151 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
156 152 }
157 153 format.api { head :ok }
158 154 end
159 155 else
160 156 respond_to do |format|
161 157 format.html { render :action => 'edit' }
162 158 format.api { render_validation_errors(@time_entry) }
163 159 end
164 160 end
165 161 end
166 162
167 163 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
168 164 def destroy
169 165 if @time_entry.destroy && @time_entry.destroyed?
170 166 respond_to do |format|
171 167 format.html {
172 168 flash[:notice] = l(:notice_successful_delete)
173 169 redirect_to :back
174 170 }
175 171 format.api { head :ok }
176 172 end
177 173 else
178 174 respond_to do |format|
179 175 format.html {
180 176 flash[:error] = l(:notice_unable_delete_time_entry)
181 177 redirect_to :back
182 178 }
183 179 format.api { render_validation_errors(@time_entry) }
184 180 end
185 181 end
186 182 rescue ::ActionController::RedirectBackError
187 183 redirect_to :action => 'index', :project_id => @time_entry.project
188 184 end
189 185
190 186 private
191 187 def find_time_entry
192 188 @time_entry = TimeEntry.find(params[:id])
193 189 unless @time_entry.editable_by?(User.current)
194 190 render_403
195 191 return false
196 192 end
197 193 @project = @time_entry.project
198 194 rescue ActiveRecord::RecordNotFound
199 195 render_404
200 196 end
201 197
202 198 def find_project
203 199 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
204 200 @issue = Issue.find(issue_id)
205 201 @project = @issue.project
206 202 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
207 203 @project = Project.find(project_id)
208 204 else
209 205 render_404
210 206 return false
211 207 end
212 208 rescue ActiveRecord::RecordNotFound
213 209 render_404
214 210 end
215 211
216 212 def find_optional_project
217 213 if !params[:issue_id].blank?
218 214 @issue = Issue.find(params[:issue_id])
219 215 @project = @issue.project
220 216 elsif !params[:project_id].blank?
221 217 @project = Project.find(params[:project_id])
222 218 end
223 219 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
224 220 end
225 221
226 222 # Retrieves the date range based on predefined ranges or specific from/to param dates
227 223 def retrieve_date_range
228 224 @free_period = false
229 225 @from, @to = nil, nil
230 226
231 227 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
232 228 case params[:period].to_s
233 229 when 'today'
234 230 @from = @to = Date.today
235 231 when 'yesterday'
236 232 @from = @to = Date.today - 1
237 233 when 'current_week'
238 234 @from = Date.today - (Date.today.cwday - 1)%7
239 235 @to = @from + 6
240 236 when 'last_week'
241 237 @from = Date.today - 7 - (Date.today.cwday - 1)%7
242 238 @to = @from + 6
243 239 when '7_days'
244 240 @from = Date.today - 7
245 241 @to = Date.today
246 242 when 'current_month'
247 243 @from = Date.civil(Date.today.year, Date.today.month, 1)
248 244 @to = (@from >> 1) - 1
249 245 when 'last_month'
250 246 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
251 247 @to = (@from >> 1) - 1
252 248 when '30_days'
253 249 @from = Date.today - 30
254 250 @to = Date.today
255 251 when 'current_year'
256 252 @from = Date.civil(Date.today.year, 1, 1)
257 253 @to = Date.civil(Date.today.year, 12, 31)
258 254 end
259 255 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
260 256 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
261 257 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
262 258 @free_period = true
263 259 else
264 260 # default
265 261 end
266 262
267 263 @from, @to = @to, @from if @from && @to && @from > @to
268 264 @from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today)
269 265 @to ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
270 266 end
271 267
272 268 end
@@ -1,103 +1,110
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 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 TimeEntry < ActiveRecord::Base
19 19 # could have used polymorphic association
20 20 # project association here allows easy loading of time entries at project level with one database trip
21 21 belongs_to :project
22 22 belongs_to :issue
23 23 belongs_to :user
24 24 belongs_to :activity, :class_name => 'TimeEntryActivity', :foreign_key => 'activity_id'
25 25
26 26 attr_protected :project_id, :user_id, :tyear, :tmonth, :tweek
27 27
28 28 acts_as_customizable
29 29 acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
30 30 :url => Proc.new {|o| {:controller => 'timelog', :action => 'index', :project_id => o.project, :issue_id => o.issue}},
31 31 :author => :user,
32 32 :description => :comments
33 33
34 34 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
35 35 :author_key => :user_id,
36 36 :find_options => {:include => :project}
37 37
38 38 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
39 39 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
40 40 validates_length_of :comments, :maximum => 255, :allow_nil => true
41 41
42 named_scope :visible, lambda {|*args| {
43 :include => :project,
44 :conditions => Project.allowed_to_condition(args.first || User.current, :view_time_entries)
45 }}
46
42 47 def after_initialize
43 48 if new_record? && self.activity.nil?
44 49 if default_activity = TimeEntryActivity.default
45 50 self.activity_id = default_activity.id
46 51 end
47 52 self.hours = nil if hours == 0
48 53 end
49 54 end
50 55
51 56 def before_validation
52 57 self.project = issue.project if issue && project.nil?
53 58 end
54 59
55 60 def validate
56 61 errors.add :hours, :invalid if hours && (hours < 0 || hours >= 1000)
57 62 errors.add :project_id, :invalid if project.nil?
58 63 errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project)
59 64 end
60 65
61 66 def hours=(h)
62 67 write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
63 68 end
64 69
65 70 # tyear, tmonth, tweek assigned where setting spent_on attributes
66 71 # these attributes make time aggregations easier
67 72 def spent_on=(date)
68 73 super
69 74 if spent_on.is_a?(Time)
70 75 self.spent_on = spent_on.to_date
71 76 end
72 77 self.tyear = spent_on ? spent_on.year : nil
73 78 self.tmonth = spent_on ? spent_on.month : nil
74 79 self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil
75 80 end
76 81
77 82 # Returns true if the time entry can be edited by usr, otherwise false
78 83 def editable_by?(usr)
79 84 (usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project)
80 85 end
81 86
87 # TODO: remove this method in 1.3.0
82 88 def self.visible_by(usr)
89 ActiveSupport::Deprecation.warn "TimeEntry.visible_by is deprecated and will be removed in Redmine 1.3.0. Use the visible scope instead."
83 90 with_scope(:find => { :conditions => Project.allowed_to_condition(usr, :view_time_entries) }) do
84 91 yield
85 92 end
86 93 end
87 94
88 95 def self.earilest_date_for_project(project=nil)
89 96 finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries))
90 97 if project
91 98 finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)]
92 99 end
93 100 TimeEntry.minimum(:spent_on, :include => :project, :conditions => finder_conditions.conditions)
94 101 end
95 102
96 103 def self.latest_date_for_project(project=nil)
97 104 finder_conditions = ARCondition.new(Project.allowed_to_condition(User.current, :view_time_entries))
98 105 if project
99 106 finder_conditions << ["project_id IN (?)", project.hierarchy.collect(&:id)]
100 107 end
101 108 TimeEntry.maximum(:spent_on, :include => :project, :conditions => finder_conditions.conditions)
102 109 end
103 110 end
@@ -1,80 +1,80
1 1 <div class="contextual">
2 2 <% if User.current.allowed_to?(:add_subprojects, @project) %>
3 3 <%= link_to l(:label_subproject_new), {:controller => 'projects', :action => 'new', :parent_id => @project}, :class => 'icon icon-add' %>
4 4 <% end %>
5 5 </div>
6 6
7 7 <h2><%=l(:label_overview)%></h2>
8 8
9 9 <div class="splitcontentleft">
10 10 <div class="wiki">
11 11 <%= textilizable @project.description %>
12 12 </div>
13 13 <ul>
14 14 <% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= auto_link(h(@project.homepage)) %></li><% end %>
15 15 <% if @subprojects.any? %>
16 16 <li><%=l(:label_subproject_plural)%>:
17 17 <%= @subprojects.collect{|p| link_to(h(p), :action => 'show', :id => p)}.join(", ") %></li>
18 18 <% end %>
19 19 <% @project.visible_custom_field_values.each do |custom_value| %>
20 20 <% if !custom_value.value.blank? %>
21 21 <li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
22 22 <% end %>
23 23 <% end %>
24 24 </ul>
25 25
26 26 <% if User.current.allowed_to?(:view_issues, @project) %>
27 27 <div class="issues box">
28 28 <h3><%=l(:label_issue_tracking)%></h3>
29 29 <ul>
30 30 <% for tracker in @trackers %>
31 31 <li><%= link_to tracker.name, :controller => 'issues', :action => 'index', :project_id => @project,
32 32 :set_filter => 1,
33 33 "tracker_id" => tracker.id %>:
34 34 <%= l(:label_x_open_issues_abbr_on_total, :count => @open_issues_by_tracker[tracker].to_i,
35 35 :total => @total_issues_by_tracker[tracker].to_i) %>
36 36 </li>
37 37 <% end %>
38 38 </ul>
39 39 <p>
40 40 <%= link_to l(:label_issue_view_all), :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 %>
41 41 <% if User.current.allowed_to?(:view_calendar, @project, :global => true) %>
42 42 | <%= link_to(l(:label_calendar), :controller => 'calendars', :action => 'show', :project_id => @project) %>
43 43 <% end %>
44 44 <% if User.current.allowed_to?(:view_gantt, @project, :global => true) %>
45 45 | <%= link_to(l(:label_gantt), :controller => 'gantts', :action => 'show', :project_id => @project) %>
46 46 <% end %>
47 47 </p>
48 48 </div>
49 49 <% end %>
50 50 <%= call_hook(:view_projects_show_left, :project => @project) %>
51 51 </div>
52 52
53 53 <div class="splitcontentright">
54 54 <%= render :partial => 'members_box' %>
55 55
56 56 <% if @news.any? && authorize_for('news', 'index') %>
57 57 <div class="news box">
58 58 <h3><%=l(:label_news_latest)%></h3>
59 59 <%= render :partial => 'news/news', :collection => @news %>
60 60 <p><%= link_to l(:label_news_view_all), :controller => 'news', :action => 'index', :project_id => @project %></p>
61 61 </div>
62 62 <% end %>
63 63 <%= call_hook(:view_projects_show_right, :project => @project) %>
64 64 </div>
65 65
66 66 <% content_for :sidebar do %>
67 <% if @total_hours && User.current.allowed_to?(:view_time_entries, @project) %>
67 <% if @total_hours.present? %>
68 68 <h3><%= l(:label_spent_time) %></h3>
69 69 <p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p>
70 70 <p><%= link_to(l(:label_details), {:controller => 'timelog', :action => 'index', :project_id => @project}) %> |
71 71 <%= link_to(l(:label_report), {:controller => 'time_entry_reports', :action => 'report', :project_id => @project}) %></p>
72 72 <% end %>
73 73 <%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %>
74 74 <% end %>
75 75
76 76 <% content_for :header_tags do %>
77 77 <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :key => User.current.rss_key}) %>
78 78 <% end %>
79 79
80 80 <% html_title(l(:label_overview)) -%>
General Comments 0
You need to be logged in to leave comments. Login now