##// END OF EJS Templates
Replaces find(:all) calls....
Jean-Philippe Lang -
r10687:5b21efd4a432
parent child
Show More
@@ -1,78 +1,78
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class CustomFieldsController < ApplicationController
19 19 layout 'admin'
20 20
21 21 before_filter :require_admin
22 22 before_filter :build_new_custom_field, :only => [:new, :create]
23 23 before_filter :find_custom_field, :only => [:edit, :update, :destroy]
24 24
25 25 def index
26 @custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
26 @custom_fields_by_type = CustomField.all.group_by {|f| f.class.name }
27 27 @tab = params[:tab] || 'IssueCustomField'
28 28 end
29 29
30 30 def new
31 31 end
32 32
33 33 def create
34 34 if request.post? and @custom_field.save
35 35 flash[:notice] = l(:notice_successful_create)
36 36 call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
37 37 redirect_to :action => 'index', :tab => @custom_field.class.name
38 38 else
39 39 render :action => 'new'
40 40 end
41 41 end
42 42
43 43 def edit
44 44 end
45 45
46 46 def update
47 47 if request.put? and @custom_field.update_attributes(params[:custom_field])
48 48 flash[:notice] = l(:notice_successful_update)
49 49 call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
50 50 redirect_to :action => 'index', :tab => @custom_field.class.name
51 51 else
52 52 render :action => 'edit'
53 53 end
54 54 end
55 55
56 56 def destroy
57 57 @custom_field.destroy
58 58 redirect_to :action => 'index', :tab => @custom_field.class.name
59 59 rescue
60 60 flash[:error] = l(:error_can_not_delete_custom_field)
61 61 redirect_to :action => 'index'
62 62 end
63 63
64 64 private
65 65
66 66 def build_new_custom_field
67 67 @custom_field = CustomField.new_subclass_instance(params[:type], params[:custom_field])
68 68 if @custom_field.nil?
69 69 render_404
70 70 end
71 71 end
72 72
73 73 def find_custom_field
74 74 @custom_field = CustomField.find(params[:id])
75 75 rescue ActiveRecord::RecordNotFound
76 76 render_404
77 77 end
78 78 end
@@ -1,94 +1,94
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class DocumentsController < ApplicationController
19 19 default_search_scope :documents
20 20 model_object Document
21 21 before_filter :find_project_by_project_id, :only => [:index, :new, :create]
22 22 before_filter :find_model_object, :except => [:index, :new, :create]
23 23 before_filter :find_project_from_association, :except => [:index, :new, :create]
24 24 before_filter :authorize
25 25
26 26 helper :attachments
27 27
28 28 def index
29 29 @sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category'
30 30 documents = @project.documents.find :all, :include => [:attachments, :category]
31 31 case @sort_by
32 32 when 'date'
33 33 @grouped = documents.group_by {|d| d.updated_on.to_date }
34 34 when 'title'
35 35 @grouped = documents.group_by {|d| d.title.first.upcase}
36 36 when 'author'
37 37 @grouped = documents.select{|d| d.attachments.any?}.group_by {|d| d.attachments.last.author}
38 38 else
39 39 @grouped = documents.group_by(&:category)
40 40 end
41 41 @document = @project.documents.build
42 42 render :layout => false if request.xhr?
43 43 end
44 44
45 45 def show
46 @attachments = @document.attachments.find(:all, :order => "created_on DESC")
46 @attachments = @document.attachments.all
47 47 end
48 48
49 49 def new
50 50 @document = @project.documents.build
51 51 @document.safe_attributes = params[:document]
52 52 end
53 53
54 54 def create
55 55 @document = @project.documents.build
56 56 @document.safe_attributes = params[:document]
57 57 @document.save_attachments(params[:attachments])
58 58 if @document.save
59 59 render_attachment_warning_if_needed(@document)
60 60 flash[:notice] = l(:notice_successful_create)
61 61 redirect_to :action => 'index', :project_id => @project
62 62 else
63 63 render :action => 'new'
64 64 end
65 65 end
66 66
67 67 def edit
68 68 end
69 69
70 70 def update
71 71 @document.safe_attributes = params[:document]
72 72 if request.put? and @document.save
73 73 flash[:notice] = l(:notice_successful_update)
74 74 redirect_to :action => 'show', :id => @document
75 75 else
76 76 render :action => 'edit'
77 77 end
78 78 end
79 79
80 80 def destroy
81 81 @document.destroy if request.delete?
82 82 redirect_to :controller => 'documents', :action => 'index', :project_id => @project
83 83 end
84 84
85 85 def add_attachment
86 86 attachments = Attachment.attach_files(@document, params[:attachments])
87 87 render_attachment_warning_if_needed(@document)
88 88
89 89 if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
90 90 Mailer.attachments_added(attachments[:files]).deliver
91 91 end
92 92 redirect_to :action => 'show', :id => @document
93 93 end
94 94 end
@@ -1,139 +1,141
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class MessagesController < ApplicationController
19 19 menu_item :boards
20 20 default_search_scope :messages
21 21 before_filter :find_board, :only => [:new, :preview]
22 22 before_filter :find_message, :except => [:new, :preview]
23 23 before_filter :authorize, :except => [:preview, :edit, :destroy]
24 24
25 25 helper :boards
26 26 helper :watchers
27 27 helper :attachments
28 28 include AttachmentsHelper
29 29
30 30 REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
31 31
32 32 # Show a topic and its replies
33 33 def show
34 34 page = params[:page]
35 35 # Find the page of the requested reply
36 36 if params[:r] && page.nil?
37 37 offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
38 38 page = 1 + offset / REPLIES_PER_PAGE
39 39 end
40 40
41 41 @reply_count = @topic.children.count
42 42 @reply_pages = Paginator.new self, @reply_count, REPLIES_PER_PAGE, page
43 @replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}],
44 :order => "#{Message.table_name}.created_on ASC",
45 :limit => @reply_pages.items_per_page,
46 :offset => @reply_pages.current.offset)
43 @replies = @topic.children.
44 includes(:author, :attachments, {:board => :project}).
45 reorder("#{Message.table_name}.created_on ASC").
46 limit(@reply_pages.items_per_page).
47 offset(@reply_pages.current.offset).
48 all
47 49
48 50 @reply = Message.new(:subject => "RE: #{@message.subject}")
49 51 render :action => "show", :layout => false if request.xhr?
50 52 end
51 53
52 54 # Create a new topic
53 55 def new
54 56 @message = Message.new
55 57 @message.author = User.current
56 58 @message.board = @board
57 59 @message.safe_attributes = params[:message]
58 60 if request.post?
59 61 @message.save_attachments(params[:attachments])
60 62 if @message.save
61 63 call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
62 64 render_attachment_warning_if_needed(@message)
63 65 redirect_to board_message_path(@board, @message)
64 66 end
65 67 end
66 68 end
67 69
68 70 # Reply to a topic
69 71 def reply
70 72 @reply = Message.new
71 73 @reply.author = User.current
72 74 @reply.board = @board
73 75 @reply.safe_attributes = params[:reply]
74 76 @topic.children << @reply
75 77 if !@reply.new_record?
76 78 call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
77 79 attachments = Attachment.attach_files(@reply, params[:attachments])
78 80 render_attachment_warning_if_needed(@reply)
79 81 end
80 82 redirect_to board_message_path(@board, @topic, :r => @reply)
81 83 end
82 84
83 85 # Edit a message
84 86 def edit
85 87 (render_403; return false) unless @message.editable_by?(User.current)
86 88 @message.safe_attributes = params[:message]
87 89 if request.post? && @message.save
88 90 attachments = Attachment.attach_files(@message, params[:attachments])
89 91 render_attachment_warning_if_needed(@message)
90 92 flash[:notice] = l(:notice_successful_update)
91 93 @message.reload
92 94 redirect_to board_message_path(@message.board, @message.root, :r => (@message.parent_id && @message.id))
93 95 end
94 96 end
95 97
96 98 # Delete a messages
97 99 def destroy
98 100 (render_403; return false) unless @message.destroyable_by?(User.current)
99 101 r = @message.to_param
100 102 @message.destroy
101 103 if @message.parent
102 104 redirect_to board_message_path(@board, @message.parent, :r => r)
103 105 else
104 106 redirect_to project_board_path(@project, @board)
105 107 end
106 108 end
107 109
108 110 def quote
109 111 @subject = @message.subject
110 112 @subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
111 113
112 114 @content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
113 115 @content << @message.content.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
114 116 end
115 117
116 118 def preview
117 119 message = @board.messages.find_by_id(params[:id])
118 120 @attachements = message.attachments if message
119 121 @text = (params[:message] || params[:reply])[:content]
120 122 @previewed = message
121 123 render :partial => 'common/preview'
122 124 end
123 125
124 126 private
125 127 def find_message
126 128 find_board
127 129 @message = @board.messages.find(params[:id], :include => :parent)
128 130 @topic = @message.root
129 131 rescue ActiveRecord::RecordNotFound
130 132 render_404
131 133 end
132 134
133 135 def find_board
134 136 @board = Board.find(params[:board_id], :include => :project)
135 137 @project = @board.project
136 138 rescue ActiveRecord::RecordNotFound
137 139 render_404
138 140 end
139 141 end
@@ -1,261 +1,260
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class ProjectsController < ApplicationController
19 19 menu_item :overview
20 20 menu_item :roadmap, :only => :roadmap
21 21 menu_item :settings, :only => :settings
22 22
23 23 before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
24 24 before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
25 25 before_filter :authorize_global, :only => [:new, :create]
26 26 before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
27 27 accept_rss_auth :index
28 28 accept_api_auth :index, :show, :create, :update, :destroy
29 29
30 30 after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
31 31 if controller.request.post?
32 32 controller.send :expire_action, :controller => 'welcome', :action => 'robots'
33 33 end
34 34 end
35 35
36 36 helper :sort
37 37 include SortHelper
38 38 helper :custom_fields
39 39 include CustomFieldsHelper
40 40 helper :issues
41 41 helper :queries
42 42 include QueriesHelper
43 43 helper :repositories
44 44 include RepositoriesHelper
45 45 include ProjectsHelper
46 46
47 47 # Lists visible projects
48 48 def index
49 49 respond_to do |format|
50 50 format.html {
51 51 scope = Project
52 52 unless params[:closed]
53 53 scope = scope.active
54 54 end
55 55 @projects = scope.visible.order('lft').all
56 56 }
57 57 format.api {
58 58 @offset, @limit = api_offset_and_limit
59 59 @project_count = Project.visible.count
60 @projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
60 @projects = Project.visible.offset(@offset).limit(@limit).order('lft').all
61 61 }
62 62 format.atom {
63 projects = Project.visible.find(:all, :order => 'created_on DESC',
64 :limit => Setting.feeds_limit.to_i)
63 projects = Project.visible.order('created_on DESC').limit(Setting.feeds_limit.to_i).all
65 64 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
66 65 }
67 66 end
68 67 end
69 68
70 69 def new
71 70 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
72 71 @trackers = Tracker.sorted.all
73 72 @project = Project.new
74 73 @project.safe_attributes = params[:project]
75 74 end
76 75
77 76 def create
78 77 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
79 78 @trackers = Tracker.sorted.all
80 79 @project = Project.new
81 80 @project.safe_attributes = params[:project]
82 81
83 82 if validate_parent_id && @project.save
84 83 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
85 84 # Add current user as a project member if he is not admin
86 85 unless User.current.admin?
87 86 r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
88 87 m = Member.new(:user => User.current, :roles => [r])
89 88 @project.members << m
90 89 end
91 90 respond_to do |format|
92 91 format.html {
93 92 flash[:notice] = l(:notice_successful_create)
94 93 redirect_to(params[:continue] ?
95 94 {:controller => 'projects', :action => 'new', :project => {:parent_id => @project.parent_id}.reject {|k,v| v.nil?}} :
96 95 {:controller => 'projects', :action => 'settings', :id => @project}
97 96 )
98 97 }
99 98 format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => @project.id) }
100 99 end
101 100 else
102 101 respond_to do |format|
103 102 format.html { render :action => 'new' }
104 103 format.api { render_validation_errors(@project) }
105 104 end
106 105 end
107 106
108 107 end
109 108
110 109 def copy
111 110 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
112 111 @trackers = Tracker.sorted.all
113 112 @source_project = Project.find(params[:id])
114 113 if request.get?
115 114 @project = Project.copy_from(@source_project)
116 115 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
117 116 else
118 117 Mailer.with_deliveries(params[:notifications] == '1') do
119 118 @project = Project.new
120 119 @project.safe_attributes = params[:project]
121 120 if validate_parent_id && @project.copy(@source_project, :only => params[:only])
122 121 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
123 122 flash[:notice] = l(:notice_successful_create)
124 123 redirect_to :controller => 'projects', :action => 'settings', :id => @project
125 124 elsif !@project.new_record?
126 125 # Project was created
127 126 # But some objects were not copied due to validation failures
128 127 # (eg. issues from disabled trackers)
129 128 # TODO: inform about that
130 129 redirect_to :controller => 'projects', :action => 'settings', :id => @project
131 130 end
132 131 end
133 132 end
134 133 rescue ActiveRecord::RecordNotFound
135 134 # source_project not found
136 135 render_404
137 136 end
138 137
139 138 # Show @project
140 139 def show
141 140 if params[:jump]
142 141 # try to redirect to the requested menu item
143 142 redirect_to_project_menu_item(@project, params[:jump]) && return
144 143 end
145 144
146 145 @users_by_role = @project.users_by_role
147 146 @subprojects = @project.children.visible.all
148 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
147 @news = @project.news.limit(5).includes(:author, :project).reorder("#{News.table_name}.created_on DESC").all
149 148 @trackers = @project.rolled_up_trackers
150 149
151 150 cond = @project.project_condition(Setting.display_subprojects_issues?)
152 151
153 152 @open_issues_by_tracker = Issue.visible.open.where(cond).count(:group => :tracker)
154 153 @total_issues_by_tracker = Issue.visible.where(cond).count(:group => :tracker)
155 154
156 155 if User.current.allowed_to?(:view_time_entries, @project)
157 156 @total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
158 157 end
159 158
160 159 @key = User.current.rss_key
161 160
162 161 respond_to do |format|
163 162 format.html
164 163 format.api
165 164 end
166 165 end
167 166
168 167 def settings
169 168 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
170 169 @issue_category ||= IssueCategory.new
171 170 @member ||= @project.members.new
172 171 @trackers = Tracker.sorted.all
173 172 @wiki ||= @project.wiki
174 173 end
175 174
176 175 def edit
177 176 end
178 177
179 178 def update
180 179 @project.safe_attributes = params[:project]
181 180 if validate_parent_id && @project.save
182 181 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
183 182 respond_to do |format|
184 183 format.html {
185 184 flash[:notice] = l(:notice_successful_update)
186 185 redirect_to :action => 'settings', :id => @project
187 186 }
188 187 format.api { render_api_ok }
189 188 end
190 189 else
191 190 respond_to do |format|
192 191 format.html {
193 192 settings
194 193 render :action => 'settings'
195 194 }
196 195 format.api { render_validation_errors(@project) }
197 196 end
198 197 end
199 198 end
200 199
201 200 def modules
202 201 @project.enabled_module_names = params[:enabled_module_names]
203 202 flash[:notice] = l(:notice_successful_update)
204 203 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
205 204 end
206 205
207 206 def archive
208 207 if request.post?
209 208 unless @project.archive
210 209 flash[:error] = l(:error_can_not_archive_project)
211 210 end
212 211 end
213 212 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
214 213 end
215 214
216 215 def unarchive
217 216 @project.unarchive if request.post? && !@project.active?
218 217 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
219 218 end
220 219
221 220 def close
222 221 @project.close
223 222 redirect_to project_path(@project)
224 223 end
225 224
226 225 def reopen
227 226 @project.reopen
228 227 redirect_to project_path(@project)
229 228 end
230 229
231 230 # Delete @project
232 231 def destroy
233 232 @project_to_destroy = @project
234 233 if api_request? || params[:confirm]
235 234 @project_to_destroy.destroy
236 235 respond_to do |format|
237 236 format.html { redirect_to :controller => 'admin', :action => 'projects' }
238 237 format.api { render_api_ok }
239 238 end
240 239 end
241 240 # hide project in layout
242 241 @project = nil
243 242 end
244 243
245 244 private
246 245
247 246 # Validates parent_id param according to user's permissions
248 247 # TODO: move it to Project model in a validation that depends on User.current
249 248 def validate_parent_id
250 249 return true if User.current.admin?
251 250 parent_id = params[:project] && params[:project][:parent_id]
252 251 if parent_id || @project.new_record?
253 252 parent = parent_id.blank? ? nil : Project.find_by_id(parent_id.to_i)
254 253 unless @project.allowed_parents.include?(parent)
255 254 @project.errors.add :parent_id, :invalid
256 255 return false
257 256 end
258 257 end
259 258 true
260 259 end
261 260 end
@@ -1,95 +1,95
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class ReportsController < ApplicationController
19 19 menu_item :issues
20 20 before_filter :find_project, :authorize, :find_issue_statuses
21 21
22 22 def issue_report
23 23 @trackers = @project.trackers
24 24 @versions = @project.shared_versions.sort
25 25 @priorities = IssuePriority.all.reverse
26 26 @categories = @project.issue_categories
27 27 @assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
28 28 @authors = @project.users.sort
29 29 @subprojects = @project.descendants.visible
30 30
31 31 @issues_by_tracker = Issue.by_tracker(@project)
32 32 @issues_by_version = Issue.by_version(@project)
33 33 @issues_by_priority = Issue.by_priority(@project)
34 34 @issues_by_category = Issue.by_category(@project)
35 35 @issues_by_assigned_to = Issue.by_assigned_to(@project)
36 36 @issues_by_author = Issue.by_author(@project)
37 37 @issues_by_subproject = Issue.by_subproject(@project) || []
38 38
39 39 render :template => "reports/issue_report"
40 40 end
41 41
42 42 def issue_report_details
43 43 case params[:detail]
44 44 when "tracker"
45 45 @field = "tracker_id"
46 46 @rows = @project.trackers
47 47 @data = Issue.by_tracker(@project)
48 48 @report_title = l(:field_tracker)
49 49 when "version"
50 50 @field = "fixed_version_id"
51 51 @rows = @project.shared_versions.sort
52 52 @data = Issue.by_version(@project)
53 53 @report_title = l(:field_version)
54 54 when "priority"
55 55 @field = "priority_id"
56 56 @rows = IssuePriority.all.reverse
57 57 @data = Issue.by_priority(@project)
58 58 @report_title = l(:field_priority)
59 59 when "category"
60 60 @field = "category_id"
61 61 @rows = @project.issue_categories
62 62 @data = Issue.by_category(@project)
63 63 @report_title = l(:field_category)
64 64 when "assigned_to"
65 65 @field = "assigned_to_id"
66 66 @rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort
67 67 @data = Issue.by_assigned_to(@project)
68 68 @report_title = l(:field_assigned_to)
69 69 when "author"
70 70 @field = "author_id"
71 71 @rows = @project.users.sort
72 72 @data = Issue.by_author(@project)
73 73 @report_title = l(:field_author)
74 74 when "subproject"
75 75 @field = "project_id"
76 76 @rows = @project.descendants.visible
77 77 @data = Issue.by_subproject(@project) || []
78 78 @report_title = l(:field_subproject)
79 79 end
80 80
81 81 respond_to do |format|
82 82 if @field
83 83 format.html {}
84 84 else
85 85 format.html { redirect_to :action => 'issue_report', :id => @project }
86 86 end
87 87 end
88 88 end
89 89
90 90 private
91 91
92 92 def find_issue_statuses
93 @statuses = IssueStatus.find(:all, :order => 'position')
93 @statuses = IssueStatus.sorted.all
94 94 end
95 95 end
@@ -1,433 +1,434
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'SVG/Graph/Bar'
19 19 require 'SVG/Graph/BarHorizontal'
20 20 require 'digest/sha1'
21 21 require 'redmine/scm/adapters/abstract_adapter'
22 22
23 23 class ChangesetNotFound < Exception; end
24 24 class InvalidRevisionParam < Exception; end
25 25
26 26 class RepositoriesController < ApplicationController
27 27 menu_item :repository
28 28 menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
29 29 default_search_scope :changesets
30 30
31 31 before_filter :find_project_by_project_id, :only => [:new, :create]
32 32 before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
33 33 before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
34 34 before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
35 35 before_filter :authorize
36 36 accept_rss_auth :revisions
37 37
38 38 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
39 39
40 40 def new
41 41 scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
42 42 @repository = Repository.factory(scm)
43 43 @repository.is_default = @project.repository.nil?
44 44 @repository.project = @project
45 45 end
46 46
47 47 def create
48 48 attrs = pickup_extra_info
49 49 @repository = Repository.factory(params[:repository_scm])
50 50 @repository.safe_attributes = params[:repository]
51 51 if attrs[:attrs_extra].keys.any?
52 52 @repository.merge_extra_info(attrs[:attrs_extra])
53 53 end
54 54 @repository.project = @project
55 55 if request.post? && @repository.save
56 56 redirect_to settings_project_path(@project, :tab => 'repositories')
57 57 else
58 58 render :action => 'new'
59 59 end
60 60 end
61 61
62 62 def edit
63 63 end
64 64
65 65 def update
66 66 attrs = pickup_extra_info
67 67 @repository.safe_attributes = attrs[:attrs]
68 68 if attrs[:attrs_extra].keys.any?
69 69 @repository.merge_extra_info(attrs[:attrs_extra])
70 70 end
71 71 @repository.project = @project
72 72 if request.put? && @repository.save
73 73 redirect_to settings_project_path(@project, :tab => 'repositories')
74 74 else
75 75 render :action => 'edit'
76 76 end
77 77 end
78 78
79 79 def pickup_extra_info
80 80 p = {}
81 81 p_extra = {}
82 82 params[:repository].each do |k, v|
83 83 if k =~ /^extra_/
84 84 p_extra[k] = v
85 85 else
86 86 p[k] = v
87 87 end
88 88 end
89 89 {:attrs => p, :attrs_extra => p_extra}
90 90 end
91 91 private :pickup_extra_info
92 92
93 93 def committers
94 94 @committers = @repository.committers
95 95 @users = @project.users
96 96 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
97 97 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
98 98 @users.compact!
99 99 @users.sort!
100 100 if request.post? && params[:committers].is_a?(Hash)
101 101 # Build a hash with repository usernames as keys and corresponding user ids as values
102 102 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
103 103 flash[:notice] = l(:notice_successful_update)
104 104 redirect_to settings_project_path(@project, :tab => 'repositories')
105 105 end
106 106 end
107 107
108 108 def destroy
109 109 @repository.destroy if request.delete?
110 110 redirect_to settings_project_path(@project, :tab => 'repositories')
111 111 end
112 112
113 113 def show
114 114 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
115 115
116 116 @entries = @repository.entries(@path, @rev)
117 117 @changeset = @repository.find_changeset_by_name(@rev)
118 118 if request.xhr?
119 119 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
120 120 else
121 121 (show_error_not_found; return) unless @entries
122 122 @changesets = @repository.latest_changesets(@path, @rev)
123 123 @properties = @repository.properties(@path, @rev)
124 124 @repositories = @project.repositories
125 125 render :action => 'show'
126 126 end
127 127 end
128 128
129 129 alias_method :browse, :show
130 130
131 131 def changes
132 132 @entry = @repository.entry(@path, @rev)
133 133 (show_error_not_found; return) unless @entry
134 134 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
135 135 @properties = @repository.properties(@path, @rev)
136 136 @changeset = @repository.find_changeset_by_name(@rev)
137 137 end
138 138
139 139 def revisions
140 140 @changeset_count = @repository.changesets.count
141 141 @changeset_pages = Paginator.new self, @changeset_count,
142 142 per_page_option,
143 143 params['page']
144 @changesets = @repository.changesets.find(:all,
145 :limit => @changeset_pages.items_per_page,
146 :offset => @changeset_pages.current.offset,
147 :include => [:user, :repository, :parents])
144 @changesets = @repository.changesets.
145 limit(@changeset_pages.items_per_page).
146 offset(@changeset_pages.current.offset).
147 includes(:user, :repository, :parents).
148 all
148 149
149 150 respond_to do |format|
150 151 format.html { render :layout => false if request.xhr? }
151 152 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
152 153 end
153 154 end
154 155
155 156 def raw
156 157 entry_and_raw(true)
157 158 end
158 159
159 160 def entry
160 161 entry_and_raw(false)
161 162 end
162 163
163 164 def entry_and_raw(is_raw)
164 165 @entry = @repository.entry(@path, @rev)
165 166 (show_error_not_found; return) unless @entry
166 167
167 168 # If the entry is a dir, show the browser
168 169 (show; return) if @entry.is_dir?
169 170
170 171 @content = @repository.cat(@path, @rev)
171 172 (show_error_not_found; return) unless @content
172 173 if is_raw ||
173 174 (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
174 175 ! is_entry_text_data?(@content, @path)
175 176 # Force the download
176 177 send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
177 178 send_type = Redmine::MimeType.of(@path)
178 179 send_opt[:type] = send_type.to_s if send_type
179 180 send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
180 181 send_data @content, send_opt
181 182 else
182 183 # Prevent empty lines when displaying a file with Windows style eol
183 184 # TODO: UTF-16
184 185 # Is this needs? AttachmentsController reads file simply.
185 186 @content.gsub!("\r\n", "\n")
186 187 @changeset = @repository.find_changeset_by_name(@rev)
187 188 end
188 189 end
189 190 private :entry_and_raw
190 191
191 192 def is_entry_text_data?(ent, path)
192 193 # UTF-16 contains "\x00".
193 194 # It is very strict that file contains less than 30% of ascii symbols
194 195 # in non Western Europe.
195 196 return true if Redmine::MimeType.is_type?('text', path)
196 197 # Ruby 1.8.6 has a bug of integer divisions.
197 198 # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
198 199 return false if ent.is_binary_data?
199 200 true
200 201 end
201 202 private :is_entry_text_data?
202 203
203 204 def annotate
204 205 @entry = @repository.entry(@path, @rev)
205 206 (show_error_not_found; return) unless @entry
206 207
207 208 @annotate = @repository.scm.annotate(@path, @rev)
208 209 if @annotate.nil? || @annotate.empty?
209 210 (render_error l(:error_scm_annotate); return)
210 211 end
211 212 ann_buf_size = 0
212 213 @annotate.lines.each do |buf|
213 214 ann_buf_size += buf.size
214 215 end
215 216 if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
216 217 (render_error l(:error_scm_annotate_big_text_file); return)
217 218 end
218 219 @changeset = @repository.find_changeset_by_name(@rev)
219 220 end
220 221
221 222 def revision
222 223 respond_to do |format|
223 224 format.html
224 225 format.js {render :layout => false}
225 226 end
226 227 end
227 228
228 229 # Adds a related issue to a changeset
229 230 # POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
230 231 def add_related_issue
231 232 @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
232 233 if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
233 234 @issue = nil
234 235 end
235 236
236 237 if @issue
237 238 @changeset.issues << @issue
238 239 end
239 240 end
240 241
241 242 # Removes a related issue from a changeset
242 243 # DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
243 244 def remove_related_issue
244 245 @issue = Issue.visible.find_by_id(params[:issue_id])
245 246 if @issue
246 247 @changeset.issues.delete(@issue)
247 248 end
248 249 end
249 250
250 251 def diff
251 252 if params[:format] == 'diff'
252 253 @diff = @repository.diff(@path, @rev, @rev_to)
253 254 (show_error_not_found; return) unless @diff
254 255 filename = "changeset_r#{@rev}"
255 256 filename << "_r#{@rev_to}" if @rev_to
256 257 send_data @diff.join, :filename => "#{filename}.diff",
257 258 :type => 'text/x-patch',
258 259 :disposition => 'attachment'
259 260 else
260 261 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
261 262 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
262 263
263 264 # Save diff type as user preference
264 265 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
265 266 User.current.pref[:diff_type] = @diff_type
266 267 User.current.preference.save
267 268 end
268 269 @cache_key = "repositories/diff/#{@repository.id}/" +
269 270 Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
270 271 unless read_fragment(@cache_key)
271 272 @diff = @repository.diff(@path, @rev, @rev_to)
272 273 show_error_not_found unless @diff
273 274 end
274 275
275 276 @changeset = @repository.find_changeset_by_name(@rev)
276 277 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
277 278 @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
278 279 end
279 280 end
280 281
281 282 def stats
282 283 end
283 284
284 285 def graph
285 286 data = nil
286 287 case params[:graph]
287 288 when "commits_per_month"
288 289 data = graph_commits_per_month(@repository)
289 290 when "commits_per_author"
290 291 data = graph_commits_per_author(@repository)
291 292 end
292 293 if data
293 294 headers["Content-Type"] = "image/svg+xml"
294 295 send_data(data, :type => "image/svg+xml", :disposition => "inline")
295 296 else
296 297 render_404
297 298 end
298 299 end
299 300
300 301 private
301 302
302 303 def find_repository
303 304 @repository = Repository.find(params[:id])
304 305 @project = @repository.project
305 306 rescue ActiveRecord::RecordNotFound
306 307 render_404
307 308 end
308 309
309 310 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
310 311
311 312 def find_project_repository
312 313 @project = Project.find(params[:id])
313 314 if params[:repository_id].present?
314 315 @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
315 316 else
316 317 @repository = @project.repository
317 318 end
318 319 (render_404; return false) unless @repository
319 320 @path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
320 321 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
321 322 @rev_to = params[:rev_to]
322 323
323 324 unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
324 325 if @repository.branches.blank?
325 326 raise InvalidRevisionParam
326 327 end
327 328 end
328 329 rescue ActiveRecord::RecordNotFound
329 330 render_404
330 331 rescue InvalidRevisionParam
331 332 show_error_not_found
332 333 end
333 334
334 335 def find_changeset
335 336 if @rev.present?
336 337 @changeset = @repository.find_changeset_by_name(@rev)
337 338 end
338 339 show_error_not_found unless @changeset
339 340 end
340 341
341 342 def show_error_not_found
342 343 render_error :message => l(:error_scm_not_found), :status => 404
343 344 end
344 345
345 346 # Handler for Redmine::Scm::Adapters::CommandFailed exception
346 347 def show_error_command_failed(exception)
347 348 render_error l(:error_scm_command_failed, exception.message)
348 349 end
349 350
350 351 def graph_commits_per_month(repository)
351 352 @date_to = Date.today
352 353 @date_from = @date_to << 11
353 354 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
354 355 commits_by_day = Changeset.count(
355 356 :all, :group => :commit_date,
356 357 :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
357 358 commits_by_month = [0] * 12
358 359 commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
359 360
360 361 changes_by_day = Change.count(
361 362 :all, :group => :commit_date, :include => :changeset,
362 363 :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
363 364 changes_by_month = [0] * 12
364 365 changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
365 366
366 367 fields = []
367 368 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
368 369
369 370 graph = SVG::Graph::Bar.new(
370 371 :height => 300,
371 372 :width => 800,
372 373 :fields => fields.reverse,
373 374 :stack => :side,
374 375 :scale_integers => true,
375 376 :step_x_labels => 2,
376 377 :show_data_values => false,
377 378 :graph_title => l(:label_commits_per_month),
378 379 :show_graph_title => true
379 380 )
380 381
381 382 graph.add_data(
382 383 :data => commits_by_month[0..11].reverse,
383 384 :title => l(:label_revision_plural)
384 385 )
385 386
386 387 graph.add_data(
387 388 :data => changes_by_month[0..11].reverse,
388 389 :title => l(:label_change_plural)
389 390 )
390 391
391 392 graph.burn
392 393 end
393 394
394 395 def graph_commits_per_author(repository)
395 396 commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
396 397 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
397 398
398 399 changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
399 400 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
400 401
401 402 fields = commits_by_author.collect {|r| r.first}
402 403 commits_data = commits_by_author.collect {|r| r.last}
403 404 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
404 405
405 406 fields = fields + [""]*(10 - fields.length) if fields.length<10
406 407 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
407 408 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
408 409
409 410 # Remove email adress in usernames
410 411 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
411 412
412 413 graph = SVG::Graph::BarHorizontal.new(
413 414 :height => 400,
414 415 :width => 800,
415 416 :fields => fields,
416 417 :stack => :side,
417 418 :scale_integers => true,
418 419 :show_data_values => false,
419 420 :rotate_y_labels => false,
420 421 :graph_title => l(:label_commits_per_author),
421 422 :show_graph_title => true
422 423 )
423 424 graph.add_data(
424 425 :data => commits_data,
425 426 :title => l(:label_revision_plural)
426 427 )
427 428 graph.add_data(
428 429 :data => changes_data,
429 430 :title => l(:label_change_plural)
430 431 )
431 432 graph.burn
432 433 end
433 434 end
@@ -1,101 +1,101
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class TrackersController < ApplicationController
19 19 layout 'admin'
20 20
21 21 before_filter :require_admin, :except => :index
22 22 before_filter :require_admin_or_api_request, :only => :index
23 23 accept_api_auth :index
24 24
25 25 def index
26 26 respond_to do |format|
27 27 format.html {
28 28 @tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
29 29 render :action => "index", :layout => false if request.xhr?
30 30 }
31 31 format.api {
32 32 @trackers = Tracker.sorted.all
33 33 }
34 34 end
35 35 end
36 36
37 37 def new
38 38 @tracker ||= Tracker.new(params[:tracker])
39 39 @trackers = Tracker.find :all, :order => 'position'
40 @projects = Project.find(:all)
40 @projects = Project.all
41 41 end
42 42
43 43 def create
44 44 @tracker = Tracker.new(params[:tracker])
45 45 if request.post? and @tracker.save
46 46 # workflow copy
47 47 if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
48 48 @tracker.workflow_rules.copy(copy_from)
49 49 end
50 50 flash[:notice] = l(:notice_successful_create)
51 51 redirect_to :action => 'index'
52 52 return
53 53 end
54 54 new
55 55 render :action => 'new'
56 56 end
57 57
58 58 def edit
59 59 @tracker ||= Tracker.find(params[:id])
60 @projects = Project.find(:all)
60 @projects = Project.all
61 61 end
62 62
63 63 def update
64 64 @tracker = Tracker.find(params[:id])
65 65 if request.put? and @tracker.update_attributes(params[:tracker])
66 66 flash[:notice] = l(:notice_successful_update)
67 67 redirect_to :action => 'index'
68 68 return
69 69 end
70 70 edit
71 71 render :action => 'edit'
72 72 end
73 73
74 74 def destroy
75 75 @tracker = Tracker.find(params[:id])
76 76 unless @tracker.issues.empty?
77 77 flash[:error] = l(:error_can_not_delete_tracker)
78 78 else
79 79 @tracker.destroy
80 80 end
81 81 redirect_to :action => 'index'
82 82 end
83 83
84 84 def fields
85 85 if request.post? && params[:trackers]
86 86 params[:trackers].each do |tracker_id, tracker_params|
87 87 tracker = Tracker.find_by_id(tracker_id)
88 88 if tracker
89 89 tracker.core_fields = tracker_params[:core_fields]
90 90 tracker.custom_field_ids = tracker_params[:custom_field_ids]
91 91 tracker.save
92 92 end
93 93 end
94 94 flash[:notice] = l(:notice_successful_update)
95 95 redirect_to :action => 'fields'
96 96 return
97 97 end
98 98 @trackers = Tracker.sorted.all
99 99 @custom_fields = IssueCustomField.all.sort
100 100 end
101 101 end
@@ -1,214 +1,214
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class UsersController < ApplicationController
19 19 layout 'admin'
20 20
21 21 before_filter :require_admin, :except => :show
22 22 before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
23 23 accept_api_auth :index, :show, :create, :update, :destroy
24 24
25 25 helper :sort
26 26 include SortHelper
27 27 helper :custom_fields
28 28 include CustomFieldsHelper
29 29
30 30 def index
31 31 sort_init 'login', 'asc'
32 32 sort_update %w(login firstname lastname mail admin created_on last_login_on)
33 33
34 34 case params[:format]
35 35 when 'xml', 'json'
36 36 @offset, @limit = api_offset_and_limit
37 37 else
38 38 @limit = per_page_option
39 39 end
40 40
41 41 @status = params[:status] || 1
42 42
43 43 scope = User.logged.status(@status)
44 44 scope = scope.like(params[:name]) if params[:name].present?
45 45 scope = scope.in_group(params[:group_id]) if params[:group_id].present?
46 46
47 47 @user_count = scope.count
48 48 @user_pages = Paginator.new self, @user_count, @limit, params['page']
49 49 @offset ||= @user_pages.current.offset
50 50 @users = scope.find :all,
51 51 :order => sort_clause,
52 52 :limit => @limit,
53 53 :offset => @offset
54 54
55 55 respond_to do |format|
56 56 format.html {
57 57 @groups = Group.all.sort
58 58 render :layout => !request.xhr?
59 59 }
60 60 format.api
61 61 end
62 62 end
63 63
64 64 def show
65 65 # show projects based on current user visibility
66 66 @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
67 67
68 68 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
69 69 @events_by_day = events.group_by(&:event_date)
70 70
71 71 unless User.current.admin?
72 72 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
73 73 render_404
74 74 return
75 75 end
76 76 end
77 77
78 78 respond_to do |format|
79 79 format.html { render :layout => 'base' }
80 80 format.api
81 81 end
82 82 end
83 83
84 84 def new
85 85 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
86 @auth_sources = AuthSource.find(:all)
86 @auth_sources = AuthSource.all
87 87 end
88 88
89 89 def create
90 90 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
91 91 @user.safe_attributes = params[:user]
92 92 @user.admin = params[:user][:admin] || false
93 93 @user.login = params[:user][:login]
94 94 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
95 95
96 96 if @user.save
97 97 @user.pref.attributes = params[:pref]
98 98 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
99 99 @user.pref.save
100 100 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
101 101
102 102 Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
103 103
104 104 respond_to do |format|
105 105 format.html {
106 106 flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
107 107 redirect_to(params[:continue] ?
108 108 {:controller => 'users', :action => 'new'} :
109 109 {:controller => 'users', :action => 'edit', :id => @user}
110 110 )
111 111 }
112 112 format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
113 113 end
114 114 else
115 @auth_sources = AuthSource.find(:all)
115 @auth_sources = AuthSource.all
116 116 # Clear password input
117 117 @user.password = @user.password_confirmation = nil
118 118
119 119 respond_to do |format|
120 120 format.html { render :action => 'new' }
121 121 format.api { render_validation_errors(@user) }
122 122 end
123 123 end
124 124 end
125 125
126 126 def edit
127 @auth_sources = AuthSource.find(:all)
127 @auth_sources = AuthSource.all
128 128 @membership ||= Member.new
129 129 end
130 130
131 131 def update
132 132 @user.admin = params[:user][:admin] if params[:user][:admin]
133 133 @user.login = params[:user][:login] if params[:user][:login]
134 134 if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
135 135 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
136 136 end
137 137 @user.safe_attributes = params[:user]
138 138 # Was the account actived ? (do it before User#save clears the change)
139 139 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
140 140 # TODO: Similar to My#account
141 141 @user.pref.attributes = params[:pref]
142 142 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
143 143
144 144 if @user.save
145 145 @user.pref.save
146 146 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
147 147
148 148 if was_activated
149 149 Mailer.account_activated(@user).deliver
150 150 elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
151 151 Mailer.account_information(@user, params[:user][:password]).deliver
152 152 end
153 153
154 154 respond_to do |format|
155 155 format.html {
156 156 flash[:notice] = l(:notice_successful_update)
157 157 redirect_to_referer_or edit_user_path(@user)
158 158 }
159 159 format.api { render_api_ok }
160 160 end
161 161 else
162 @auth_sources = AuthSource.find(:all)
162 @auth_sources = AuthSource.all
163 163 @membership ||= Member.new
164 164 # Clear password input
165 165 @user.password = @user.password_confirmation = nil
166 166
167 167 respond_to do |format|
168 168 format.html { render :action => :edit }
169 169 format.api { render_validation_errors(@user) }
170 170 end
171 171 end
172 172 end
173 173
174 174 def destroy
175 175 @user.destroy
176 176 respond_to do |format|
177 177 format.html { redirect_back_or_default(users_url) }
178 178 format.api { render_api_ok }
179 179 end
180 180 end
181 181
182 182 def edit_membership
183 183 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
184 184 @membership.save
185 185 respond_to do |format|
186 186 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
187 187 format.js
188 188 end
189 189 end
190 190
191 191 def destroy_membership
192 192 @membership = Member.find(params[:membership_id])
193 193 if @membership.deletable?
194 194 @membership.destroy
195 195 end
196 196 respond_to do |format|
197 197 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
198 198 format.js
199 199 end
200 200 end
201 201
202 202 private
203 203
204 204 def find_user
205 205 if params[:id] == 'current'
206 206 require_login || return
207 207 @user = User.current
208 208 else
209 209 @user = User.find(params[:id])
210 210 end
211 211 rescue ActiveRecord::RecordNotFound
212 212 render_404
213 213 end
214 214 end
@@ -1,181 +1,182
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class VersionsController < ApplicationController
19 19 menu_item :roadmap
20 20 model_object Version
21 21 before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
22 22 before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
23 23 before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed]
24 24 before_filter :authorize
25 25
26 26 accept_api_auth :index, :show, :create, :update, :destroy
27 27
28 28 helper :custom_fields
29 29 helper :projects
30 30
31 31 def index
32 32 respond_to do |format|
33 33 format.html {
34 @trackers = @project.trackers.find(:all, :order => 'position')
34 @trackers = @project.trackers.sorted.all
35 35 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
36 36 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
37 37 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
38 38
39 39 @versions = @project.shared_versions || []
40 40 @versions += @project.rolled_up_versions.visible if @with_subprojects
41 41 @versions = @versions.uniq.sort
42 42 unless params[:completed]
43 43 @completed_versions = @versions.select {|version| version.closed? || version.completed? }
44 44 @versions -= @completed_versions
45 45 end
46 46
47 47 @issues_by_version = {}
48 48 if @selected_tracker_ids.any? && @versions.any?
49 49 issues = Issue.visible.all(
50 50 :include => [:project, :status, :tracker, :priority, :fixed_version],
51 51 :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
52 52 :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
53 53 )
54 54 @issues_by_version = issues.group_by(&:fixed_version)
55 55 end
56 56 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
57 57 }
58 58 format.api {
59 59 @versions = @project.shared_versions.all
60 60 }
61 61 end
62 62 end
63 63
64 64 def show
65 65 respond_to do |format|
66 66 format.html {
67 @issues = @version.fixed_issues.visible.find(:all,
68 :include => [:status, :tracker, :priority],
69 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
67 @issues = @version.fixed_issues.visible.
68 includes(:status, :tracker, :priority).
69 reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
70 all
70 71 }
71 72 format.api
72 73 end
73 74 end
74 75
75 76 def new
76 77 @version = @project.versions.build
77 78 @version.safe_attributes = params[:version]
78 79
79 80 respond_to do |format|
80 81 format.html
81 82 format.js
82 83 end
83 84 end
84 85
85 86 def create
86 87 @version = @project.versions.build
87 88 if params[:version]
88 89 attributes = params[:version].dup
89 90 attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
90 91 @version.safe_attributes = attributes
91 92 end
92 93
93 94 if request.post?
94 95 if @version.save
95 96 respond_to do |format|
96 97 format.html do
97 98 flash[:notice] = l(:notice_successful_create)
98 99 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
99 100 end
100 101 format.js
101 102 format.api do
102 103 render :action => 'show', :status => :created, :location => version_url(@version)
103 104 end
104 105 end
105 106 else
106 107 respond_to do |format|
107 108 format.html { render :action => 'new' }
108 109 format.js { render :action => 'new' }
109 110 format.api { render_validation_errors(@version) }
110 111 end
111 112 end
112 113 end
113 114 end
114 115
115 116 def edit
116 117 end
117 118
118 119 def update
119 120 if request.put? && params[:version]
120 121 attributes = params[:version].dup
121 122 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
122 123 @version.safe_attributes = attributes
123 124 if @version.save
124 125 respond_to do |format|
125 126 format.html {
126 127 flash[:notice] = l(:notice_successful_update)
127 128 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
128 129 }
129 130 format.api { render_api_ok }
130 131 end
131 132 else
132 133 respond_to do |format|
133 134 format.html { render :action => 'edit' }
134 135 format.api { render_validation_errors(@version) }
135 136 end
136 137 end
137 138 end
138 139 end
139 140
140 141 def close_completed
141 142 if request.put?
142 143 @project.close_completed_versions
143 144 end
144 145 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
145 146 end
146 147
147 148 def destroy
148 149 if @version.fixed_issues.empty?
149 150 @version.destroy
150 151 respond_to do |format|
151 152 format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
152 153 format.api { render_api_ok }
153 154 end
154 155 else
155 156 respond_to do |format|
156 157 format.html {
157 158 flash[:error] = l(:notice_unable_delete_version)
158 159 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
159 160 }
160 161 format.api { head :unprocessable_entity }
161 162 end
162 163 end
163 164 end
164 165
165 166 def status_by
166 167 respond_to do |format|
167 168 format.html { render :action => 'show' }
168 169 format.js
169 170 end
170 171 end
171 172
172 173 private
173 174
174 175 def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
175 176 if ids = params[:tracker_ids]
176 177 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
177 178 else
178 179 @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s }
179 180 end
180 181 end
181 182 end
@@ -1,95 +1,95
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class WatchersController < ApplicationController
19 19 before_filter :find_project
20 20 before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
21 21 before_filter :authorize, :only => [:new, :destroy]
22 22
23 23 def watch
24 24 if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
25 25 render_403
26 26 else
27 27 set_watcher(User.current, true)
28 28 end
29 29 end
30 30
31 31 def unwatch
32 32 set_watcher(User.current, false)
33 33 end
34 34
35 35 def new
36 36 end
37 37
38 38 def create
39 39 if params[:watcher].is_a?(Hash) && request.post?
40 40 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
41 41 user_ids.each do |user_id|
42 42 Watcher.create(:watchable => @watched, :user_id => user_id)
43 43 end
44 44 end
45 45 respond_to do |format|
46 46 format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
47 47 format.js
48 48 end
49 49 end
50 50
51 51 def append
52 52 if params[:watcher].is_a?(Hash)
53 53 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
54 54 @users = User.active.find_all_by_id(user_ids)
55 55 end
56 56 end
57 57
58 58 def destroy
59 59 @watched.set_watcher(User.find(params[:user_id]), false) if request.post?
60 60 respond_to do |format|
61 61 format.html { redirect_to :back }
62 62 format.js
63 63 end
64 64 end
65 65
66 66 def autocomplete_for_user
67 @users = User.active.like(params[:q]).find(:all, :limit => 100)
67 @users = User.active.like(params[:q]).limit(100).all
68 68 if @watched
69 69 @users -= @watched.watcher_users
70 70 end
71 71 render :layout => false
72 72 end
73 73
74 74 private
75 75 def find_project
76 76 if params[:object_type] && params[:object_id]
77 77 klass = Object.const_get(params[:object_type].camelcase)
78 78 return false unless klass.respond_to?('watched_by')
79 79 @watched = klass.find(params[:object_id])
80 80 @project = @watched.project
81 81 elsif params[:project_id]
82 82 @project = Project.visible.find_by_param(params[:project_id])
83 83 end
84 84 rescue
85 85 render_404
86 86 end
87 87
88 88 def set_watcher(user, watching)
89 89 @watched.set_watcher(user, watching)
90 90 respond_to do |format|
91 91 format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
92 92 format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
93 93 end
94 94 end
95 95 end
General Comments 0
You need to be logged in to leave comments. Login now