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