##// END OF EJS Templates
Include subprojects versions on calendar and gantt (#1116)....
Jean-Philippe Lang -
r1349:6a3236daea38
parent child
Show More
@@ -1,431 +1,433
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class ProjectsController < ApplicationController
18 class ProjectsController < ApplicationController
19 layout 'base'
19 layout 'base'
20 menu_item :overview
20 menu_item :overview
21 menu_item :activity, :only => :activity
21 menu_item :activity, :only => :activity
22 menu_item :roadmap, :only => :roadmap
22 menu_item :roadmap, :only => :roadmap
23 menu_item :files, :only => [:list_files, :add_file]
23 menu_item :files, :only => [:list_files, :add_file]
24 menu_item :settings, :only => :settings
24 menu_item :settings, :only => :settings
25 menu_item :issues, :only => [:changelog]
25 menu_item :issues, :only => [:changelog]
26
26
27 before_filter :find_project, :except => [ :index, :list, :add, :activity ]
27 before_filter :find_project, :except => [ :index, :list, :add, :activity ]
28 before_filter :find_optional_project, :only => :activity
28 before_filter :find_optional_project, :only => :activity
29 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy, :activity ]
29 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy, :activity ]
30 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
30 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
31 accept_key_auth :activity, :calendar
31 accept_key_auth :activity, :calendar
32
32
33 helper :sort
33 helper :sort
34 include SortHelper
34 include SortHelper
35 helper :custom_fields
35 helper :custom_fields
36 include CustomFieldsHelper
36 include CustomFieldsHelper
37 helper :ifpdf
37 helper :ifpdf
38 include IfpdfHelper
38 include IfpdfHelper
39 helper :issues
39 helper :issues
40 helper IssuesHelper
40 helper IssuesHelper
41 helper :queries
41 helper :queries
42 include QueriesHelper
42 include QueriesHelper
43 helper :repositories
43 helper :repositories
44 include RepositoriesHelper
44 include RepositoriesHelper
45 include ProjectsHelper
45 include ProjectsHelper
46
46
47 def index
47 def index
48 list
48 list
49 render :action => 'list' unless request.xhr?
49 render :action => 'list' unless request.xhr?
50 end
50 end
51
51
52 # Lists visible projects
52 # Lists visible projects
53 def list
53 def list
54 projects = Project.find :all,
54 projects = Project.find :all,
55 :conditions => Project.visible_by(User.current),
55 :conditions => Project.visible_by(User.current),
56 :include => :parent
56 :include => :parent
57 @project_tree = projects.group_by {|p| p.parent || p}
57 @project_tree = projects.group_by {|p| p.parent || p}
58 @project_tree.each_key {|p| @project_tree[p] -= [p]}
58 @project_tree.each_key {|p| @project_tree[p] -= [p]}
59 end
59 end
60
60
61 # Add a new project
61 # Add a new project
62 def add
62 def add
63 @custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
63 @custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
64 @trackers = Tracker.all
64 @trackers = Tracker.all
65 @root_projects = Project.find(:all,
65 @root_projects = Project.find(:all,
66 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
66 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
67 :order => 'name')
67 :order => 'name')
68 @project = Project.new(params[:project])
68 @project = Project.new(params[:project])
69 if request.get?
69 if request.get?
70 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
70 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) }
71 @project.trackers = Tracker.all
71 @project.trackers = Tracker.all
72 @project.is_public = Setting.default_projects_public?
72 @project.is_public = Setting.default_projects_public?
73 @project.enabled_module_names = Redmine::AccessControl.available_project_modules
73 @project.enabled_module_names = Redmine::AccessControl.available_project_modules
74 else
74 else
75 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
75 @project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids]
76 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
76 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) }
77 @project.custom_values = @custom_values
77 @project.custom_values = @custom_values
78 @project.enabled_module_names = params[:enabled_modules]
78 @project.enabled_module_names = params[:enabled_modules]
79 if @project.save
79 if @project.save
80 flash[:notice] = l(:notice_successful_create)
80 flash[:notice] = l(:notice_successful_create)
81 redirect_to :controller => 'admin', :action => 'projects'
81 redirect_to :controller => 'admin', :action => 'projects'
82 end
82 end
83 end
83 end
84 end
84 end
85
85
86 # Show @project
86 # Show @project
87 def show
87 def show
88 @custom_values = @project.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
88 @custom_values = @project.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
89 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
89 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
90 @subprojects = @project.active_children
90 @subprojects = @project.active_children
91 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
91 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
92 @trackers = @project.rolled_up_trackers
92 @trackers = @project.rolled_up_trackers
93
93
94 cond = @project.project_condition(Setting.display_subprojects_issues?)
94 cond = @project.project_condition(Setting.display_subprojects_issues?)
95 Issue.visible_by(User.current) do
95 Issue.visible_by(User.current) do
96 @open_issues_by_tracker = Issue.count(:group => :tracker,
96 @open_issues_by_tracker = Issue.count(:group => :tracker,
97 :include => [:project, :status, :tracker],
97 :include => [:project, :status, :tracker],
98 :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
98 :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
99 @total_issues_by_tracker = Issue.count(:group => :tracker,
99 @total_issues_by_tracker = Issue.count(:group => :tracker,
100 :include => [:project, :status, :tracker],
100 :include => [:project, :status, :tracker],
101 :conditions => cond)
101 :conditions => cond)
102 end
102 end
103 TimeEntry.visible_by(User.current) do
103 TimeEntry.visible_by(User.current) do
104 @total_hours = TimeEntry.sum(:hours,
104 @total_hours = TimeEntry.sum(:hours,
105 :include => :project,
105 :include => :project,
106 :conditions => cond).to_f
106 :conditions => cond).to_f
107 end
107 end
108 @key = User.current.rss_key
108 @key = User.current.rss_key
109 end
109 end
110
110
111 def settings
111 def settings
112 @root_projects = Project.find(:all,
112 @root_projects = Project.find(:all,
113 :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
113 :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
114 :order => 'name')
114 :order => 'name')
115 @custom_fields = IssueCustomField.find(:all)
115 @custom_fields = IssueCustomField.find(:all)
116 @issue_category ||= IssueCategory.new
116 @issue_category ||= IssueCategory.new
117 @member ||= @project.members.new
117 @member ||= @project.members.new
118 @trackers = Tracker.all
118 @trackers = Tracker.all
119 @custom_values ||= ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
119 @custom_values ||= ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) }
120 @repository ||= @project.repository
120 @repository ||= @project.repository
121 @wiki ||= @project.wiki
121 @wiki ||= @project.wiki
122 end
122 end
123
123
124 # Edit @project
124 # Edit @project
125 def edit
125 def edit
126 if request.post?
126 if request.post?
127 if params[:custom_fields]
127 if params[:custom_fields]
128 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
128 @custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => params["custom_fields"][x.id.to_s]) }
129 @project.custom_values = @custom_values
129 @project.custom_values = @custom_values
130 end
130 end
131 @project.attributes = params[:project]
131 @project.attributes = params[:project]
132 if @project.save
132 if @project.save
133 flash[:notice] = l(:notice_successful_update)
133 flash[:notice] = l(:notice_successful_update)
134 redirect_to :action => 'settings', :id => @project
134 redirect_to :action => 'settings', :id => @project
135 else
135 else
136 settings
136 settings
137 render :action => 'settings'
137 render :action => 'settings'
138 end
138 end
139 end
139 end
140 end
140 end
141
141
142 def modules
142 def modules
143 @project.enabled_module_names = params[:enabled_modules]
143 @project.enabled_module_names = params[:enabled_modules]
144 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
144 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
145 end
145 end
146
146
147 def archive
147 def archive
148 @project.archive if request.post? && @project.active?
148 @project.archive if request.post? && @project.active?
149 redirect_to :controller => 'admin', :action => 'projects'
149 redirect_to :controller => 'admin', :action => 'projects'
150 end
150 end
151
151
152 def unarchive
152 def unarchive
153 @project.unarchive if request.post? && !@project.active?
153 @project.unarchive if request.post? && !@project.active?
154 redirect_to :controller => 'admin', :action => 'projects'
154 redirect_to :controller => 'admin', :action => 'projects'
155 end
155 end
156
156
157 # Delete @project
157 # Delete @project
158 def destroy
158 def destroy
159 @project_to_destroy = @project
159 @project_to_destroy = @project
160 if request.post? and params[:confirm]
160 if request.post? and params[:confirm]
161 @project_to_destroy.destroy
161 @project_to_destroy.destroy
162 redirect_to :controller => 'admin', :action => 'projects'
162 redirect_to :controller => 'admin', :action => 'projects'
163 end
163 end
164 # hide project in layout
164 # hide project in layout
165 @project = nil
165 @project = nil
166 end
166 end
167
167
168 # Add a new issue category to @project
168 # Add a new issue category to @project
169 def add_issue_category
169 def add_issue_category
170 @category = @project.issue_categories.build(params[:category])
170 @category = @project.issue_categories.build(params[:category])
171 if request.post? and @category.save
171 if request.post? and @category.save
172 respond_to do |format|
172 respond_to do |format|
173 format.html do
173 format.html do
174 flash[:notice] = l(:notice_successful_create)
174 flash[:notice] = l(:notice_successful_create)
175 redirect_to :action => 'settings', :tab => 'categories', :id => @project
175 redirect_to :action => 'settings', :tab => 'categories', :id => @project
176 end
176 end
177 format.js do
177 format.js do
178 # IE doesn't support the replace_html rjs method for select box options
178 # IE doesn't support the replace_html rjs method for select box options
179 render(:update) {|page| page.replace "issue_category_id",
179 render(:update) {|page| page.replace "issue_category_id",
180 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
180 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
181 }
181 }
182 end
182 end
183 end
183 end
184 end
184 end
185 end
185 end
186
186
187 # Add a new version to @project
187 # Add a new version to @project
188 def add_version
188 def add_version
189 @version = @project.versions.build(params[:version])
189 @version = @project.versions.build(params[:version])
190 if request.post? and @version.save
190 if request.post? and @version.save
191 flash[:notice] = l(:notice_successful_create)
191 flash[:notice] = l(:notice_successful_create)
192 redirect_to :action => 'settings', :tab => 'versions', :id => @project
192 redirect_to :action => 'settings', :tab => 'versions', :id => @project
193 end
193 end
194 end
194 end
195
195
196 def add_file
196 def add_file
197 if request.post?
197 if request.post?
198 @version = @project.versions.find_by_id(params[:version_id])
198 @version = @project.versions.find_by_id(params[:version_id])
199 attachments = attach_files(@version, params[:attachments])
199 attachments = attach_files(@version, params[:attachments])
200 Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
200 Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
201 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
201 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
202 end
202 end
203 @versions = @project.versions.sort
203 @versions = @project.versions.sort
204 end
204 end
205
205
206 def list_files
206 def list_files
207 sort_init "#{Attachment.table_name}.filename", "asc"
207 sort_init "#{Attachment.table_name}.filename", "asc"
208 sort_update
208 sort_update
209 @versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
209 @versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
210 render :layout => !request.xhr?
210 render :layout => !request.xhr?
211 end
211 end
212
212
213 # Show changelog for @project
213 # Show changelog for @project
214 def changelog
214 def changelog
215 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
215 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
216 retrieve_selected_tracker_ids(@trackers)
216 retrieve_selected_tracker_ids(@trackers)
217 @versions = @project.versions.sort
217 @versions = @project.versions.sort
218 end
218 end
219
219
220 def roadmap
220 def roadmap
221 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
221 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
222 retrieve_selected_tracker_ids(@trackers)
222 retrieve_selected_tracker_ids(@trackers)
223 @versions = @project.versions.sort
223 @versions = @project.versions.sort
224 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
224 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
225 end
225 end
226
226
227 def activity
227 def activity
228 @days = Setting.activity_days_default.to_i
228 @days = Setting.activity_days_default.to_i
229
229
230 if params[:from]
230 if params[:from]
231 begin; @date_to = params[:from].to_date; rescue; end
231 begin; @date_to = params[:from].to_date; rescue; end
232 end
232 end
233
233
234 @date_to ||= Date.today + 1
234 @date_to ||= Date.today + 1
235 @date_from = @date_to - @days
235 @date_from = @date_to - @days
236
236
237 @event_types = %w(issues news files documents changesets wiki_pages messages)
237 @event_types = %w(issues news files documents changesets wiki_pages messages)
238 if @project
238 if @project
239 @event_types.delete('wiki_pages') unless @project.wiki
239 @event_types.delete('wiki_pages') unless @project.wiki
240 @event_types.delete('changesets') unless @project.repository
240 @event_types.delete('changesets') unless @project.repository
241 @event_types.delete('messages') unless @project.boards.any?
241 @event_types.delete('messages') unless @project.boards.any?
242 # only show what the user is allowed to view
242 # only show what the user is allowed to view
243 @event_types = @event_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, @project)}
243 @event_types = @event_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, @project)}
244 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
244 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
245 end
245 end
246 @scope = @event_types.select {|t| params["show_#{t}"]}
246 @scope = @event_types.select {|t| params["show_#{t}"]}
247 # default events if none is specified in parameters
247 # default events if none is specified in parameters
248 @scope = (@event_types - %w(wiki_pages messages))if @scope.empty?
248 @scope = (@event_types - %w(wiki_pages messages))if @scope.empty?
249
249
250 @events = []
250 @events = []
251
251
252 if @scope.include?('issues')
252 if @scope.include?('issues')
253 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_issues, :project => @project, :with_subprojects => @with_subprojects))
253 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_issues, :project => @project, :with_subprojects => @with_subprojects))
254 cond.add(["#{Issue.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
254 cond.add(["#{Issue.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
255 @events += Issue.find(:all, :include => [:project, :author, :tracker], :conditions => cond.conditions)
255 @events += Issue.find(:all, :include => [:project, :author, :tracker], :conditions => cond.conditions)
256
256
257 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_issues, :project => @project, :with_subprojects => @with_subprojects))
257 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_issues, :project => @project, :with_subprojects => @with_subprojects))
258 cond.add(["#{Journal.table_name}.journalized_type = 'Issue' AND #{JournalDetail.table_name}.prop_key = 'status_id' AND #{Journal.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
258 cond.add(["#{Journal.table_name}.journalized_type = 'Issue' AND #{JournalDetail.table_name}.prop_key = 'status_id' AND #{Journal.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
259 @events += Journal.find(:all, :include => [{:issue => :project}, :details, :user], :conditions => cond.conditions)
259 @events += Journal.find(:all, :include => [{:issue => :project}, :details, :user], :conditions => cond.conditions)
260 end
260 end
261
261
262 if @scope.include?('news')
262 if @scope.include?('news')
263 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_news, :project => @project, :with_subprojects => @with_subprojects))
263 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_news, :project => @project, :with_subprojects => @with_subprojects))
264 cond.add(["#{News.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
264 cond.add(["#{News.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
265 @events += News.find(:all, :include => [:project, :author], :conditions => cond.conditions)
265 @events += News.find(:all, :include => [:project, :author], :conditions => cond.conditions)
266 end
266 end
267
267
268 if @scope.include?('files')
268 if @scope.include?('files')
269 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_files, :project => @project, :with_subprojects => @with_subprojects))
269 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_files, :project => @project, :with_subprojects => @with_subprojects))
270 cond.add(["#{Attachment.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
270 cond.add(["#{Attachment.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
271 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*",
271 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*",
272 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
272 :joins => "LEFT JOIN #{Version.table_name} ON #{Attachment.table_name}.container_type='Version' AND #{Version.table_name}.id = #{Attachment.table_name}.container_id " +
273 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id",
273 "LEFT JOIN #{Project.table_name} ON #{Version.table_name}.project_id = #{Project.table_name}.id",
274 :conditions => cond.conditions)
274 :conditions => cond.conditions)
275 end
275 end
276
276
277 if @scope.include?('documents')
277 if @scope.include?('documents')
278 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_documents, :project => @project, :with_subprojects => @with_subprojects))
278 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_documents, :project => @project, :with_subprojects => @with_subprojects))
279 cond.add(["#{Document.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
279 cond.add(["#{Document.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
280 @events += Document.find(:all, :include => :project, :conditions => cond.conditions)
280 @events += Document.find(:all, :include => :project, :conditions => cond.conditions)
281
281
282 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_documents, :project => @project, :with_subprojects => @with_subprojects))
282 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_documents, :project => @project, :with_subprojects => @with_subprojects))
283 cond.add(["#{Attachment.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
283 cond.add(["#{Attachment.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
284 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*",
284 @events += Attachment.find(:all, :select => "#{Attachment.table_name}.*",
285 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
285 :joins => "LEFT JOIN #{Document.table_name} ON #{Attachment.table_name}.container_type='Document' AND #{Document.table_name}.id = #{Attachment.table_name}.container_id " +
286 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id",
286 "LEFT JOIN #{Project.table_name} ON #{Document.table_name}.project_id = #{Project.table_name}.id",
287 :conditions => cond.conditions)
287 :conditions => cond.conditions)
288 end
288 end
289
289
290 if @scope.include?('wiki_pages')
290 if @scope.include?('wiki_pages')
291 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
291 select = "#{WikiContent.versioned_table_name}.updated_on, #{WikiContent.versioned_table_name}.comments, " +
292 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
292 "#{WikiContent.versioned_table_name}.#{WikiContent.version_column}, #{WikiPage.table_name}.title, " +
293 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
293 "#{WikiContent.versioned_table_name}.page_id, #{WikiContent.versioned_table_name}.author_id, " +
294 "#{WikiContent.versioned_table_name}.id"
294 "#{WikiContent.versioned_table_name}.id"
295 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
295 joins = "LEFT JOIN #{WikiPage.table_name} ON #{WikiPage.table_name}.id = #{WikiContent.versioned_table_name}.page_id " +
296 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
296 "LEFT JOIN #{Wiki.table_name} ON #{Wiki.table_name}.id = #{WikiPage.table_name}.wiki_id " +
297 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"
297 "LEFT JOIN #{Project.table_name} ON #{Project.table_name}.id = #{Wiki.table_name}.project_id"
298
298
299 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_wiki_pages, :project => @project, :with_subprojects => @with_subprojects))
299 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_wiki_pages, :project => @project, :with_subprojects => @with_subprojects))
300 cond.add(["#{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?", @date_from, @date_to])
300 cond.add(["#{WikiContent.versioned_table_name}.updated_on BETWEEN ? AND ?", @date_from, @date_to])
301 @events += WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => cond.conditions)
301 @events += WikiContent.versioned_class.find(:all, :select => select, :joins => joins, :conditions => cond.conditions)
302 end
302 end
303
303
304 if @scope.include?('changesets')
304 if @scope.include?('changesets')
305 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_changesets, :project => @project, :with_subprojects => @with_subprojects))
305 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_changesets, :project => @project, :with_subprojects => @with_subprojects))
306 cond.add(["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to])
306 cond.add(["#{Changeset.table_name}.committed_on BETWEEN ? AND ?", @date_from, @date_to])
307 @events += Changeset.find(:all, :include => {:repository => :project}, :conditions => cond.conditions)
307 @events += Changeset.find(:all, :include => {:repository => :project}, :conditions => cond.conditions)
308 end
308 end
309
309
310 if @scope.include?('messages')
310 if @scope.include?('messages')
311 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_messages, :project => @project, :with_subprojects => @with_subprojects))
311 cond = ARCondition.new(Project.allowed_to_condition(User.current, :view_messages, :project => @project, :with_subprojects => @with_subprojects))
312 cond.add(["#{Message.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
312 cond.add(["#{Message.table_name}.created_on BETWEEN ? AND ?", @date_from, @date_to])
313 @events += Message.find(:all, :include => [{:board => :project}, :author], :conditions => cond.conditions)
313 @events += Message.find(:all, :include => [{:board => :project}, :author], :conditions => cond.conditions)
314 end
314 end
315
315
316 @events_by_day = @events.group_by(&:event_date)
316 @events_by_day = @events.group_by(&:event_date)
317
317
318 respond_to do |format|
318 respond_to do |format|
319 format.html { render :layout => false if request.xhr? }
319 format.html { render :layout => false if request.xhr? }
320 format.atom { render_feed(@events, :title => "#{@project || Setting.app_title}: #{l(:label_activity)}") }
320 format.atom { render_feed(@events, :title => "#{@project || Setting.app_title}: #{l(:label_activity)}") }
321 end
321 end
322 end
322 end
323
323
324 def calendar
324 def calendar
325 @trackers = @project.rolled_up_trackers
325 @trackers = @project.rolled_up_trackers
326 retrieve_selected_tracker_ids(@trackers)
326 retrieve_selected_tracker_ids(@trackers)
327
327
328 if params[:year] and params[:year].to_i > 1900
328 if params[:year] and params[:year].to_i > 1900
329 @year = params[:year].to_i
329 @year = params[:year].to_i
330 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
330 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
331 @month = params[:month].to_i
331 @month = params[:month].to_i
332 end
332 end
333 end
333 end
334 @year ||= Date.today.year
334 @year ||= Date.today.year
335 @month ||= Date.today.month
335 @month ||= Date.today.month
336 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
336 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
337 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
337 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
338 events = []
338 events = []
339 @project.issues_with_subprojects(@with_subprojects) do
339 @project.issues_with_subprojects(@with_subprojects) do
340 events += Issue.find(:all,
340 events += Issue.find(:all,
341 :include => [:tracker, :status, :assigned_to, :priority, :project],
341 :include => [:tracker, :status, :assigned_to, :priority, :project],
342 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
342 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
343 ) unless @selected_tracker_ids.empty?
343 ) unless @selected_tracker_ids.empty?
344 events += Version.find(:all, :include => :project,
345 :conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
344 end
346 end
345 events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
346 @calendar.events = events
347 @calendar.events = events
347
348
348 render :layout => false if request.xhr?
349 render :layout => false if request.xhr?
349 end
350 end
350
351
351 def gantt
352 def gantt
352 @trackers = @project.rolled_up_trackers
353 @trackers = @project.rolled_up_trackers
353 retrieve_selected_tracker_ids(@trackers)
354 retrieve_selected_tracker_ids(@trackers)
354
355
355 if params[:year] and params[:year].to_i >0
356 if params[:year] and params[:year].to_i >0
356 @year_from = params[:year].to_i
357 @year_from = params[:year].to_i
357 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
358 if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
358 @month_from = params[:month].to_i
359 @month_from = params[:month].to_i
359 else
360 else
360 @month_from = 1
361 @month_from = 1
361 end
362 end
362 else
363 else
363 @month_from ||= Date.today.month
364 @month_from ||= Date.today.month
364 @year_from ||= Date.today.year
365 @year_from ||= Date.today.year
365 end
366 end
366
367
367 zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
368 zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
368 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
369 @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
369 months = (params[:months] || User.current.pref[:gantt_months]).to_i
370 months = (params[:months] || User.current.pref[:gantt_months]).to_i
370 @months = (months > 0 && months < 25) ? months : 6
371 @months = (months > 0 && months < 25) ? months : 6
371
372
372 # Save gantt paramters as user preference (zoom and months count)
373 # Save gantt paramters as user preference (zoom and months count)
373 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
374 if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
374 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
375 User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
375 User.current.preference.save
376 User.current.preference.save
376 end
377 end
377
378
378 @date_from = Date.civil(@year_from, @month_from, 1)
379 @date_from = Date.civil(@year_from, @month_from, 1)
379 @date_to = (@date_from >> @months) - 1
380 @date_to = (@date_from >> @months) - 1
380 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
381 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
381
382
382 @events = []
383 @events = []
383 @project.issues_with_subprojects(@with_subprojects) do
384 @project.issues_with_subprojects(@with_subprojects) do
384 @events += Issue.find(:all,
385 @events += Issue.find(:all,
385 :order => "start_date, due_date",
386 :order => "start_date, due_date",
386 :include => [:tracker, :status, :assigned_to, :priority, :project],
387 :include => [:tracker, :status, :assigned_to, :priority, :project],
387 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
388 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
388 ) unless @selected_tracker_ids.empty?
389 ) unless @selected_tracker_ids.empty?
390 @events += Version.find(:all, :include => :project,
391 :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
389 end
392 end
390 @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
391 @events.sort! {|x,y| x.start_date <=> y.start_date }
393 @events.sort! {|x,y| x.start_date <=> y.start_date }
392
394
393 if params[:format]=='pdf'
395 if params[:format]=='pdf'
394 @options_for_rfpdf ||= {}
396 @options_for_rfpdf ||= {}
395 @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
397 @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
396 render :template => "projects/gantt.rfpdf", :layout => false
398 render :template => "projects/gantt.rfpdf", :layout => false
397 elsif params[:format]=='png' && respond_to?('gantt_image')
399 elsif params[:format]=='png' && respond_to?('gantt_image')
398 image = gantt_image(@events, @date_from, @months, @zoom)
400 image = gantt_image(@events, @date_from, @months, @zoom)
399 image.format = 'PNG'
401 image.format = 'PNG'
400 send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
402 send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
401 else
403 else
402 render :template => "projects/gantt.rhtml"
404 render :template => "projects/gantt.rhtml"
403 end
405 end
404 end
406 end
405
407
406 private
408 private
407 # Find project of id params[:id]
409 # Find project of id params[:id]
408 # if not found, redirect to project list
410 # if not found, redirect to project list
409 # Used as a before_filter
411 # Used as a before_filter
410 def find_project
412 def find_project
411 @project = Project.find(params[:id])
413 @project = Project.find(params[:id])
412 rescue ActiveRecord::RecordNotFound
414 rescue ActiveRecord::RecordNotFound
413 render_404
415 render_404
414 end
416 end
415
417
416 def find_optional_project
418 def find_optional_project
417 return true unless params[:id]
419 return true unless params[:id]
418 @project = Project.find(params[:id])
420 @project = Project.find(params[:id])
419 authorize
421 authorize
420 rescue ActiveRecord::RecordNotFound
422 rescue ActiveRecord::RecordNotFound
421 render_404
423 render_404
422 end
424 end
423
425
424 def retrieve_selected_tracker_ids(selectable_trackers)
426 def retrieve_selected_tracker_ids(selectable_trackers)
425 if ids = params[:tracker_ids]
427 if ids = params[:tracker_ids]
426 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
428 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
427 else
429 else
428 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
430 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
429 end
431 end
430 end
432 end
431 end
433 end
@@ -1,254 +1,256
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Project < ActiveRecord::Base
18 class Project < ActiveRecord::Base
19 # Project statuses
19 # Project statuses
20 STATUS_ACTIVE = 1
20 STATUS_ACTIVE = 1
21 STATUS_ARCHIVED = 9
21 STATUS_ARCHIVED = 9
22
22
23 has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
23 has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
24 has_many :users, :through => :members
24 has_many :users, :through => :members
25 has_many :custom_values, :dependent => :delete_all, :as => :customized
25 has_many :custom_values, :dependent => :delete_all, :as => :customized
26 has_many :enabled_modules, :dependent => :delete_all
26 has_many :enabled_modules, :dependent => :delete_all
27 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
27 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
28 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
28 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
29 has_many :issue_changes, :through => :issues, :source => :journals
29 has_many :issue_changes, :through => :issues, :source => :journals
30 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
30 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
31 has_many :time_entries, :dependent => :delete_all
31 has_many :time_entries, :dependent => :delete_all
32 has_many :queries, :dependent => :delete_all
32 has_many :queries, :dependent => :delete_all
33 has_many :documents, :dependent => :destroy
33 has_many :documents, :dependent => :destroy
34 has_many :news, :dependent => :delete_all, :include => :author
34 has_many :news, :dependent => :delete_all, :include => :author
35 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
35 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
36 has_many :boards, :dependent => :destroy, :order => "position ASC"
36 has_many :boards, :dependent => :destroy, :order => "position ASC"
37 has_one :repository, :dependent => :destroy
37 has_one :repository, :dependent => :destroy
38 has_many :changesets, :through => :repository
38 has_many :changesets, :through => :repository
39 has_one :wiki, :dependent => :destroy
39 has_one :wiki, :dependent => :destroy
40 # Custom field for the project issues
40 # Custom field for the project issues
41 has_and_belongs_to_many :custom_fields,
41 has_and_belongs_to_many :custom_fields,
42 :class_name => 'IssueCustomField',
42 :class_name => 'IssueCustomField',
43 :order => "#{CustomField.table_name}.position",
43 :order => "#{CustomField.table_name}.position",
44 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
44 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
45 :association_foreign_key => 'custom_field_id'
45 :association_foreign_key => 'custom_field_id'
46
46
47 acts_as_tree :order => "name", :counter_cache => true
47 acts_as_tree :order => "name", :counter_cache => true
48
48
49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id'
49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id'
50 acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
50 acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
51 :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}}
51 :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}}
52
52
53 attr_protected :status, :enabled_module_names
53 attr_protected :status, :enabled_module_names
54
54
55 validates_presence_of :name, :identifier
55 validates_presence_of :name, :identifier
56 validates_uniqueness_of :name, :identifier
56 validates_uniqueness_of :name, :identifier
57 validates_associated :custom_values, :on => :update
57 validates_associated :custom_values, :on => :update
58 validates_associated :repository, :wiki
58 validates_associated :repository, :wiki
59 validates_length_of :name, :maximum => 30
59 validates_length_of :name, :maximum => 30
60 validates_length_of :homepage, :maximum => 60
60 validates_length_of :homepage, :maximum => 60
61 validates_length_of :identifier, :in => 3..20
61 validates_length_of :identifier, :in => 3..20
62 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
62 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
63
63
64 before_destroy :delete_all_members
64 before_destroy :delete_all_members
65
65
66 def identifier=(identifier)
66 def identifier=(identifier)
67 super unless identifier_frozen?
67 super unless identifier_frozen?
68 end
68 end
69
69
70 def identifier_frozen?
70 def identifier_frozen?
71 errors[:identifier].nil? && !(new_record? || identifier.blank?)
71 errors[:identifier].nil? && !(new_record? || identifier.blank?)
72 end
72 end
73
73
74 def issues_with_subprojects(include_subprojects=false)
74 def issues_with_subprojects(include_subprojects=false)
75 conditions = nil
75 conditions = nil
76 if include_subprojects && !active_children.empty?
76 if include_subprojects && !active_children.empty?
77 ids = [id] + active_children.collect {|c| c.id}
77 ids = [id] + active_children.collect {|c| c.id}
78 conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
78 conditions = ["#{Project.table_name}.id IN (#{ids.join(',')})"]
79 end
79 end
80 conditions ||= ["#{Issue.table_name}.project_id = ?", id]
80 conditions ||= ["#{Project.table_name}.id = ?", id]
81 # Quick and dirty fix for Rails 2 compatibility
81 # Quick and dirty fix for Rails 2 compatibility
82 Issue.send(:with_scope, :find => { :conditions => conditions }) do
82 Issue.send(:with_scope, :find => { :conditions => conditions }) do
83 yield
83 Version.send(:with_scope, :find => { :conditions => conditions }) do
84 yield
85 end
84 end
86 end
85 end
87 end
86
88
87 # returns latest created projects
89 # returns latest created projects
88 # non public projects will be returned only if user is a member of those
90 # non public projects will be returned only if user is a member of those
89 def self.latest(user=nil, count=5)
91 def self.latest(user=nil, count=5)
90 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
92 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
91 end
93 end
92
94
93 def self.visible_by(user=nil)
95 def self.visible_by(user=nil)
94 if user && user.admin?
96 if user && user.admin?
95 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
97 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
96 elsif user && user.memberships.any?
98 elsif user && user.memberships.any?
97 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
99 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
98 else
100 else
99 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
101 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
100 end
102 end
101 end
103 end
102
104
103 def self.allowed_to_condition(user, permission, options={})
105 def self.allowed_to_condition(user, permission, options={})
104 statements = []
106 statements = []
105 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
107 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
106 if options[:project]
108 if options[:project]
107 project_statement = "#{Project.table_name}.id = #{options[:project].id}"
109 project_statement = "#{Project.table_name}.id = #{options[:project].id}"
108 project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
110 project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
109 base_statement = "(#{project_statement}) AND (#{base_statement})"
111 base_statement = "(#{project_statement}) AND (#{base_statement})"
110 end
112 end
111 if user.admin?
113 if user.admin?
112 # no restriction
114 # no restriction
113 elsif user.logged?
115 elsif user.logged?
114 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
116 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
115 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
117 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
116 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
118 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
117 elsif Role.anonymous.allowed_to?(permission)
119 elsif Role.anonymous.allowed_to?(permission)
118 # anonymous user allowed on public project
120 # anonymous user allowed on public project
119 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
121 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
120 else
122 else
121 # anonymous user is not authorized
123 # anonymous user is not authorized
122 statements << "1=0"
124 statements << "1=0"
123 end
125 end
124 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
126 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
125 end
127 end
126
128
127 def project_condition(with_subprojects)
129 def project_condition(with_subprojects)
128 cond = "#{Project.table_name}.id = #{id}"
130 cond = "#{Project.table_name}.id = #{id}"
129 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
131 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
130 cond
132 cond
131 end
133 end
132
134
133 def self.find(*args)
135 def self.find(*args)
134 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
136 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
135 project = find_by_identifier(*args)
137 project = find_by_identifier(*args)
136 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
138 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
137 project
139 project
138 else
140 else
139 super
141 super
140 end
142 end
141 end
143 end
142
144
143 def to_param
145 def to_param
144 identifier
146 identifier
145 end
147 end
146
148
147 def active?
149 def active?
148 self.status == STATUS_ACTIVE
150 self.status == STATUS_ACTIVE
149 end
151 end
150
152
151 def archive
153 def archive
152 # Archive subprojects if any
154 # Archive subprojects if any
153 children.each do |subproject|
155 children.each do |subproject|
154 subproject.archive
156 subproject.archive
155 end
157 end
156 update_attribute :status, STATUS_ARCHIVED
158 update_attribute :status, STATUS_ARCHIVED
157 end
159 end
158
160
159 def unarchive
161 def unarchive
160 return false if parent && !parent.active?
162 return false if parent && !parent.active?
161 update_attribute :status, STATUS_ACTIVE
163 update_attribute :status, STATUS_ACTIVE
162 end
164 end
163
165
164 def active_children
166 def active_children
165 children.select {|child| child.active?}
167 children.select {|child| child.active?}
166 end
168 end
167
169
168 # Returns an array of the trackers used by the project and its sub projects
170 # Returns an array of the trackers used by the project and its sub projects
169 def rolled_up_trackers
171 def rolled_up_trackers
170 @rolled_up_trackers ||=
172 @rolled_up_trackers ||=
171 Tracker.find(:all, :include => :projects,
173 Tracker.find(:all, :include => :projects,
172 :select => "DISTINCT #{Tracker.table_name}.*",
174 :select => "DISTINCT #{Tracker.table_name}.*",
173 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
175 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
174 :order => "#{Tracker.table_name}.position")
176 :order => "#{Tracker.table_name}.position")
175 end
177 end
176
178
177 # Deletes all project's members
179 # Deletes all project's members
178 def delete_all_members
180 def delete_all_members
179 Member.delete_all(['project_id = ?', id])
181 Member.delete_all(['project_id = ?', id])
180 end
182 end
181
183
182 # Users issues can be assigned to
184 # Users issues can be assigned to
183 def assignable_users
185 def assignable_users
184 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
186 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
185 end
187 end
186
188
187 # Returns the mail adresses of users that should be always notified on project events
189 # Returns the mail adresses of users that should be always notified on project events
188 def recipients
190 def recipients
189 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
191 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
190 end
192 end
191
193
192 # Returns an array of all custom fields enabled for project issues
194 # Returns an array of all custom fields enabled for project issues
193 # (explictly associated custom fields and custom fields enabled for all projects)
195 # (explictly associated custom fields and custom fields enabled for all projects)
194 def custom_fields_for_issues(tracker)
196 def custom_fields_for_issues(tracker)
195 all_custom_fields.select {|c| tracker.custom_fields.include? c }
197 all_custom_fields.select {|c| tracker.custom_fields.include? c }
196 end
198 end
197
199
198 def all_custom_fields
200 def all_custom_fields
199 @all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
201 @all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
200 end
202 end
201
203
202 def <=>(project)
204 def <=>(project)
203 name.downcase <=> project.name.downcase
205 name.downcase <=> project.name.downcase
204 end
206 end
205
207
206 def to_s
208 def to_s
207 name
209 name
208 end
210 end
209
211
210 # Returns a short description of the projects (first lines)
212 # Returns a short description of the projects (first lines)
211 def short_description(length = 255)
213 def short_description(length = 255)
212 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
214 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
213 end
215 end
214
216
215 def allows_to?(action)
217 def allows_to?(action)
216 if action.is_a? Hash
218 if action.is_a? Hash
217 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
219 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
218 else
220 else
219 allowed_permissions.include? action
221 allowed_permissions.include? action
220 end
222 end
221 end
223 end
222
224
223 def module_enabled?(module_name)
225 def module_enabled?(module_name)
224 module_name = module_name.to_s
226 module_name = module_name.to_s
225 enabled_modules.detect {|m| m.name == module_name}
227 enabled_modules.detect {|m| m.name == module_name}
226 end
228 end
227
229
228 def enabled_module_names=(module_names)
230 def enabled_module_names=(module_names)
229 enabled_modules.clear
231 enabled_modules.clear
230 module_names = [] unless module_names && module_names.is_a?(Array)
232 module_names = [] unless module_names && module_names.is_a?(Array)
231 module_names.each do |name|
233 module_names.each do |name|
232 enabled_modules << EnabledModule.new(:name => name.to_s)
234 enabled_modules << EnabledModule.new(:name => name.to_s)
233 end
235 end
234 end
236 end
235
237
236 protected
238 protected
237 def validate
239 def validate
238 errors.add(parent_id, " must be a root project") if parent and parent.parent
240 errors.add(parent_id, " must be a root project") if parent and parent.parent
239 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
241 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
240 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
242 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
241 end
243 end
242
244
243 private
245 private
244 def allowed_permissions
246 def allowed_permissions
245 @allowed_permissions ||= begin
247 @allowed_permissions ||= begin
246 module_names = enabled_modules.collect {|m| m.name}
248 module_names = enabled_modules.collect {|m| m.name}
247 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
249 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
248 end
250 end
249 end
251 end
250
252
251 def allowed_actions
253 def allowed_actions
252 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
254 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
253 end
255 end
254 end
256 end
@@ -1,36 +1,39
1 <table class="cal">
1 <table class="cal">
2 <thead>
2 <thead>
3 <tr><td></td><% 7.times do |i| %><th><%= day_name( (calendar.first_wday+i)%7 ) %></th><% end %></tr>
3 <tr><td></td><% 7.times do |i| %><th><%= day_name( (calendar.first_wday+i)%7 ) %></th><% end %></tr>
4 </thead>
4 </thead>
5 <tbody>
5 <tbody>
6 <tr>
6 <tr>
7 <% day = calendar.startdt
7 <% day = calendar.startdt
8 while day <= calendar.enddt %>
8 while day <= calendar.enddt %>
9 <%= "<th>#{day.cweek}</th>" if day.cwday == calendar.first_wday %>
9 <%= "<th>#{day.cweek}</th>" if day.cwday == calendar.first_wday %>
10 <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>">
10 <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>">
11 <p class="day-num"><%= day.day %></p>
11 <p class="day-num"><%= day.day %></p>
12 <% calendar.events_on(day).each do |i| %>
12 <% calendar.events_on(day).each do |i| %>
13 <% if i.is_a? Issue %>
13 <% if i.is_a? Issue %>
14 <div class="tooltip">
14 <div class="tooltip">
15 <%= if day == i.start_date && day == i.due_date
15 <%= if day == i.start_date && day == i.due_date
16 image_tag('arrow_bw.png')
16 image_tag('arrow_bw.png')
17 elsif day == i.start_date
17 elsif day == i.start_date
18 image_tag('arrow_from.png')
18 image_tag('arrow_from.png')
19 elsif day == i.due_date
19 elsif day == i.due_date
20 image_tag('arrow_to.png')
20 image_tag('arrow_to.png')
21 end %>
21 end %>
22 <%= h("#{i.project.name} -") unless @project && @project == i.project %>
22 <%= h("#{i.project} -") unless @project && @project == i.project %>
23 <%= link_to_issue i %>: <%= h(truncate(i.subject, 30)) %>
23 <%= link_to_issue i %>: <%= h(truncate(i.subject, 30)) %>
24 <span class="tip"><%= render_issue_tooltip i %></span>
24 <span class="tip"><%= render_issue_tooltip i %></span>
25 </div>
25 </div>
26 <% else %>
26 <% else %>
27 <%= link_to_version i, :class => "icon icon-package" %>
27 <span class="icon icon-package">
28 <%= h("#{i.project} -") unless @project && @project == i.project %>
29 <%= link_to_version i%>
30 </span>
28 <% end %>
31 <% end %>
29 <% end %>
32 <% end %>
30 </td>
33 </td>
31 <%= '</tr><tr>' if day.cwday==calendar.last_wday and day!=calendar.enddt %>
34 <%= '</tr><tr>' if day.cwday==calendar.last_wday and day!=calendar.enddt %>
32 <% day = day + 1
35 <% day = day + 1
33 end %>
36 end %>
34 </tr>
37 </tr>
35 </tbody>
38 </tbody>
36 </table>
39 </table>
@@ -1,246 +1,250
1 <% zoom = 1
1 <% zoom = 1
2 @zoom.times { zoom = zoom * 2 }
2 @zoom.times { zoom = zoom * 2 }
3
3
4 subject_width = 330
4 subject_width = 330
5 header_heigth = 18
5 header_heigth = 18
6
6
7 headers_height = header_heigth
7 headers_height = header_heigth
8 show_weeks = false
8 show_weeks = false
9 show_days = false
9 show_days = false
10
10
11 if @zoom >1
11 if @zoom >1
12 show_weeks = true
12 show_weeks = true
13 headers_height = 2*header_heigth
13 headers_height = 2*header_heigth
14 if @zoom > 2
14 if @zoom > 2
15 show_days = true
15 show_days = true
16 headers_height = 3*header_heigth
16 headers_height = 3*header_heigth
17 end
17 end
18 end
18 end
19
19
20 g_width = (@date_to - @date_from + 1)*zoom
20 g_width = (@date_to - @date_from + 1)*zoom
21 g_height = [(20 * @events.length + 6)+150, 206].max
21 g_height = [(20 * @events.length + 6)+150, 206].max
22 t_height = g_height + headers_height
22 t_height = g_height + headers_height
23 %>
23 %>
24
24
25 <div class="contextual">
25 <div class="contextual">
26 </div>
26 </div>
27
27
28 <h2><%= l(:label_gantt) %></h2>
28 <h2><%= l(:label_gantt) %></h2>
29
29
30 <% form_tag(params.merge(:month => nil, :year => nil, :months => nil)) do %>
30 <% form_tag(params.merge(:month => nil, :year => nil, :months => nil)) do %>
31 <table width="100%">
31 <table width="100%">
32 <tr>
32 <tr>
33 <td align="left">
33 <td align="left">
34 <input type="text" name="months" size="2" value="<%= @months %>" />
34 <input type="text" name="months" size="2" value="<%= @months %>" />
35 <%= l(:label_months_from) %>
35 <%= l(:label_months_from) %>
36 <%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
36 <%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
37 <%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
37 <%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
38 <%= hidden_field_tag 'zoom', @zoom %>
38 <%= hidden_field_tag 'zoom', @zoom %>
39 <%= submit_tag l(:button_submit), :class => "button-small" %>
39 <%= submit_tag l(:button_submit), :class => "button-small" %>
40 </td>
40 </td>
41
41
42 <td align="right">
42 <td align="right">
43 <%= if @zoom < 4
43 <%= if @zoom < 4
44 link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
44 link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
45 else
45 else
46 image_tag 'zoom_in_g.png'
46 image_tag 'zoom_in_g.png'
47 end %>
47 end %>
48 <%= if @zoom > 1
48 <%= if @zoom > 1
49 link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
49 link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
50 else
50 else
51 image_tag 'zoom_out_g.png'
51 image_tag 'zoom_out_g.png'
52 end %>
52 end %>
53 </td>
53 </td>
54 </tr>
54 </tr>
55 </table>
55 </table>
56 <% end %>
56 <% end %>
57
57
58 <table width="100%" style="border:0; border-collapse: collapse;">
58 <table width="100%" style="border:0; border-collapse: collapse;">
59 <tr>
59 <tr>
60 <td style="width:<%= subject_width %>px;">
60 <td style="width:<%= subject_width %>px;">
61
61
62 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
62 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
63 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
63 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
64 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
64 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
65 <%
65 <%
66 #
66 #
67 # Tasks subjects
67 # Tasks subjects
68 #
68 #
69 top = headers_height + 8
69 top = headers_height + 8
70 @events.each do |i| %>
70 @events.each do |i| %>
71 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
71 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
72 <% if i.is_a? Issue %>
72 <% if i.is_a? Issue %>
73 <%= h("#{i.project.name} -") unless @project && @project == i.project %>
73 <%= h("#{i.project} -") unless @project && @project == i.project %>
74 <%= link_to_issue i %>: <%=h i.subject %>
74 <%= link_to_issue i %>: <%=h i.subject %>
75 <% else %>
75 <% else %>
76 <%= link_to_version i, :class => "icon icon-package" %>
76 <span class="icon icon-package">
77 <%= h("#{i.project} -") unless @project && @project == i.project %>
78 <%= link_to_version i %>
79 </span>
77 <% end %>
80 <% end %>
78 </small></div>
81 </small></div>
79 <% top = top + 20
82 <% top = top + 20
80 end %>
83 end %>
81 </div>
84 </div>
82 </td>
85 </td>
83 <td>
86 <td>
84
87
85 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
88 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
86 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
89 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
87 <%
90 <%
88 #
91 #
89 # Months headers
92 # Months headers
90 #
93 #
91 month_f = @date_from
94 month_f = @date_from
92 left = 0
95 left = 0
93 height = (show_weeks ? header_heigth : header_heigth + g_height)
96 height = (show_weeks ? header_heigth : header_heigth + g_height)
94 @months.times do
97 @months.times do
95 width = ((month_f >> 1) - month_f) * zoom - 1
98 width = ((month_f >> 1) - month_f) * zoom - 1
96 %>
99 %>
97 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
100 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
98 <%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
101 <%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
99 </div>
102 </div>
100 <%
103 <%
101 left = left + width + 1
104 left = left + width + 1
102 month_f = month_f >> 1
105 month_f = month_f >> 1
103 end %>
106 end %>
104
107
105 <%
108 <%
106 #
109 #
107 # Weeks headers
110 # Weeks headers
108 #
111 #
109 if show_weeks
112 if show_weeks
110 left = 0
113 left = 0
111 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
114 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
112 if @date_from.cwday == 1
115 if @date_from.cwday == 1
113 # @date_from is monday
116 # @date_from is monday
114 week_f = @date_from
117 week_f = @date_from
115 else
118 else
116 # find next monday after @date_from
119 # find next monday after @date_from
117 week_f = @date_from + (7 - @date_from.cwday + 1)
120 week_f = @date_from + (7 - @date_from.cwday + 1)
118 width = (7 - @date_from.cwday + 1) * zoom-1
121 width = (7 - @date_from.cwday + 1) * zoom-1
119 %>
122 %>
120 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
123 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
121 <%
124 <%
122 left = left + width+1
125 left = left + width+1
123 end %>
126 end %>
124 <%
127 <%
125 while week_f <= @date_to
128 while week_f <= @date_to
126 width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
129 width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
127 %>
130 %>
128 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
131 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
129 <small><%= week_f.cweek if width >= 16 %></small>
132 <small><%= week_f.cweek if width >= 16 %></small>
130 </div>
133 </div>
131 <%
134 <%
132 left = left + width+1
135 left = left + width+1
133 week_f = week_f+7
136 week_f = week_f+7
134 end
137 end
135 end %>
138 end %>
136
139
137 <%
140 <%
138 #
141 #
139 # Days headers
142 # Days headers
140 #
143 #
141 if show_days
144 if show_days
142 left = 0
145 left = 0
143 height = g_height + header_heigth - 1
146 height = g_height + header_heigth - 1
144 wday = @date_from.cwday
147 wday = @date_from.cwday
145 (@date_to - @date_from + 1).to_i.times do
148 (@date_to - @date_from + 1).to_i.times do
146 width = zoom - 1
149 width = zoom - 1
147 %>
150 %>
148 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
151 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
149 <%= day_name(wday).first %>
152 <%= day_name(wday).first %>
150 </div>
153 </div>
151 <%
154 <%
152 left = left + width+1
155 left = left + width+1
153 wday = wday + 1
156 wday = wday + 1
154 wday = 1 if wday > 7
157 wday = 1 if wday > 7
155 end
158 end
156 end %>
159 end %>
157
160
158 <%
161 <%
159 #
162 #
160 # Tasks
163 # Tasks
161 #
164 #
162 top = headers_height + 10
165 top = headers_height + 10
163 @events.each do |i|
166 @events.each do |i|
164 if i.is_a? Issue
167 if i.is_a? Issue
165 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
168 i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
166 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
169 i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
167
170
168 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
171 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
169 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
172 i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
170 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
173 i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
171
174
172 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
175 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
173
176
174 i_left = ((i_start_date - @date_from)*zoom).floor
177 i_left = ((i_start_date - @date_from)*zoom).floor
175 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
178 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
176 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
179 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
177 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
180 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
178 %>
181 %>
179 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
182 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
180 <% if l_width > 0 %>
183 <% if l_width > 0 %>
181 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
184 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
182 <% end %>
185 <% end %>
183 <% if d_width > 0 %>
186 <% if d_width > 0 %>
184 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
187 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
185 <% end %>
188 <% end %>
186 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
189 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
187 <%= i.status.name %>
190 <%= i.status.name %>
188 <%= (i.done_ratio).to_i %>%
191 <%= (i.done_ratio).to_i %>%
189 </div>
192 </div>
190 <% # === tooltip === %>
193 <% # === tooltip === %>
191 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
194 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
192 <span class="tip">
195 <span class="tip">
193 <%= render_issue_tooltip i %>
196 <%= render_issue_tooltip i %>
194 </span></div>
197 </span></div>
195 <% else
198 <% else
196 i_left = ((i.start_date - @date_from)*zoom).floor
199 i_left = ((i.start_date - @date_from)*zoom).floor
197 %>
200 %>
198 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
201 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
199 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
202 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
200 <strong><%= i.name %></strong>
203 <%= h("#{i.project} -") unless @project && @project == i.project %>
204 <strong><%=h i %></strong>
201 </div>
205 </div>
202 <% end %>
206 <% end %>
203 <% top = top + 20
207 <% top = top + 20
204 end %>
208 end %>
205
209
206 <%
210 <%
207 #
211 #
208 # Today red line (excluded from cache)
212 # Today red line (excluded from cache)
209 #
213 #
210 if Date.today >= @date_from and Date.today <= @date_to %>
214 if Date.today >= @date_from and Date.today <= @date_to %>
211 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
215 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
212 <% end %>
216 <% end %>
213
217
214 </div>
218 </div>
215 </td>
219 </td>
216 </tr>
220 </tr>
217 </table>
221 </table>
218
222
219 <table width="100%">
223 <table width="100%">
220 <tr>
224 <tr>
221 <td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
225 <td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
222 <td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
226 <td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
223 </tr>
227 </tr>
224 </table>
228 </table>
225
229
226 <p class="other-formats">
230 <p class="other-formats">
227 <%= l(:label_export_to) %>
231 <%= l(:label_export_to) %>
228 <span><%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'pdf'}, :class => 'pdf' %></span>
232 <span><%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'pdf'}, :class => 'pdf' %></span>
229 <%= content_tag('span', link_to('PNG', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'png'}, :class => 'image')) if respond_to?('gantt_image') %>
233 <%= content_tag('span', link_to('PNG', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'png'}, :class => 'image')) if respond_to?('gantt_image') %>
230 </p>
234 </p>
231
235
232 <% content_for :sidebar do %>
236 <% content_for :sidebar do %>
233 <h3><%= l(:label_gantt) %></h3>
237 <h3><%= l(:label_gantt) %></h3>
234 <% form_tag(params.merge(:tracker_ids => nil, :with_subprojects => nil)) do %>
238 <% form_tag(params.merge(:tracker_ids => nil, :with_subprojects => nil)) do %>
235 <% @trackers.each do |tracker| %>
239 <% @trackers.each do |tracker| %>
236 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
240 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
237 <% end %>
241 <% end %>
238 <% if @project.active_children.any? %>
242 <% if @project.active_children.any? %>
239 <br /><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label>
243 <br /><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label>
240 <%= hidden_field_tag 'with_subprojects', 0 %>
244 <%= hidden_field_tag 'with_subprojects', 0 %>
241 <% end %>
245 <% end %>
242 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
246 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
243 <% end %>
247 <% end %>
244 <% end %>
248 <% end %>
245
249
246 <% html_title(l(:label_gantt)) -%>
250 <% html_title(l(:label_gantt)) -%>
General Comments 0
You need to be logged in to leave comments. Login now