Project

General

Profile

« Previous | Next » 

Revision 873

Added the hability to copy an issue.
It can be done from the 'issue/show' view or from the context menu on the issue list.
The Copy functionality is of course only available if the user is allowed to create an issue.
It copies the issue attributes and the custom fields values.

View differences:

trunk/app/controllers/issues_controller.rb
174 174
    @assignables << @issue.assigned_to if @issue.assigned_to && [email protected]?(@issue.assigned_to)
175 175
    @can = {:edit => User.current.allowed_to?(:edit_issues, @project),
176 176
            :change_status => User.current.allowed_to?(:change_issue_status, @project),
177
            :add => User.current.allowed_to?(:add_issues, @project),
177 178
            :move => User.current.allowed_to?(:move_issues, @project),
178 179
            :delete => User.current.allowed_to?(:delete_issues, @project)}
179 180
    render :layout => false
trunk/app/controllers/projects_controller.rb
192 192
  end
193 193

  
194 194
  # Add a new issue to @project
195
  # The new issue will be created from an existing one if copy_from parameter is given
195 196
  def add_issue
196
    @tracker = Tracker.find(params[:tracker_id])
197
    @priorities = Enumeration::get_values('IPRI')
197
    @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
198
    @issue.project = @project
199
    @issue.author = User.current
200
    @issue.tracker ||= Tracker.find(params[:tracker_id])
198 201
    
199 202
    default_status = IssueStatus.default
200 203
    unless default_status
201
      flash.now[:error] = 'No default issue status defined. Please check your configuration.'
204
      flash.now[:error] = 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
202 205
      render :nothing => true, :layout => true
203 206
      return
204
    end
205
    @issue = Issue.new(:project => @project, :tracker => @tracker)    
207
    end    
206 208
    @issue.status = default_status
207 209
    @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
210
    
208 211
    if request.get?
209
      @issue.start_date = Date.today
210
      @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
212
      @issue.start_date ||= Date.today
213
      @custom_values = @issue.custom_values.empty? ?
214
        @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } :
215
        @issue.custom_values
211 216
    else
212
      @issue.attributes = params[:issue]
213
      
214 217
      requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
218
      # Check that the user is allowed to apply the requested status
215 219
      @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
216
      
217
      @issue.author_id = self.logged_in_user.id if self.logged_in_user
218
      # Multiple file upload
219
      @attachments = []
220
      params[:attachments].each { |a|
221
        @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
222
      } if params[:attachments] and params[:attachments].is_a? Array
223
      @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
220
      @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
224 221
      @issue.custom_values = @custom_values
225 222
      if @issue.save
226
        @attachments.each(&:save)
223
        if params[:attachments] && params[:attachments].is_a?(Array)
224
          # Save attachments
225
          params[:attachments].each {|a| Attachment.create(:container => @issue, :file => a, :author => User.current) unless a.size == 0}
226
        end
227 227
        flash[:notice] = l(:notice_successful_create)
228 228
        Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
229 229
        redirect_to :action => 'list_issues', :id => @project
230
        return
230 231
      end		
231 232
    end	
233
    @priorities = Enumeration::get_values('IPRI')
232 234
  end
233 235

  
234 236
  # Show filtered/sorted issues list of @project
trunk/app/models/issue.rb
54 54
    end
55 55
  end
56 56
  
57
  def copy_from(arg)
58
    issue = arg.is_a?(Issue) ? arg : Issue.find(arg)
59
    self.attributes = issue.attributes.dup
60
    self.custom_values = issue.custom_values.collect {|v| v.clone}
61
    self
62
  end
63
  
57 64
  def priority_id=(pid)
58 65
    self.priority = nil
59 66
    write_attribute(:priority_id, pid)
trunk/app/views/issues/context_menu.rhtml
31 31
		                              :selected => @issue.assigned_to.nil?, :disabled => !(@can[:edit] || @can[:change_status]) %></li>
32 32
		</ul>
33 33
	</li>
34
	<li><%= context_menu_link l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue},
35
	        :class => 'icon-copy', :disabled => !@can[:add] %></li>
34 36
	<li><%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id },
35 37
	                          :class => 'icon-move', :disabled => !@can[:move]  %>
36 38
    <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue},
trunk/app/views/issues/show.rhtml
3 3
<%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit', :accesskey => accesskey(:edit) %>
4 4
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
5 5
<%= watcher_tag(@issue, User.current) %>
6
<%= link_to_if_authorized l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue }, :class => 'icon icon-copy' %>
6 7
<%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %>
7 8
<%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
8 9
</div>
trunk/app/views/projects/add_issue.rhtml
1
<h2><%=l(:label_issue_new)%>: <%= @tracker.name %></h2>
1
<h2><%=l(:label_issue_new)%>: <%= @issue.tracker %></h2>
2 2

  
3 3
<% labelled_tabular_form_for :issue, @issue, 
4 4
                             :url => {:action => 'add_issue'},
5 5
                             :html => {:multipart => true, :id => 'issue-form'} do |f| %>
6
    <%= hidden_field_tag 'tracker_id', @tracker.id %>
7
    <%= render :partial => 'issues/form', :locals => {:f => f} %>    
6
    <%= f.hidden_field :tracker_id %>
7
    <%= render :partial => 'issues/form', :locals => {:f => f} %>
8 8
    <%= submit_tag l(:button_create) %>
9 9
    <%= link_to_remote l(:label_preview), 
10 10
                       { :url => { :controller => 'issues', :action => 'preview', :id => @issue },
trunk/lang/en.yml
476 476
button_reset: Reset
477 477
button_rename: Rename
478 478
button_change_password: Change password
479
button_copy: Copy
479 480

  
480 481
status_active: active
481 482
status_registered: registered
trunk/lang/fr.yml
476 476
button_reset: Réinitialiser
477 477
button_rename: Renommer
478 478
button_change_password: Changer de mot de passe
479
button_copy: Copier
479 480

  
480 481
status_active: actif
481 482
status_registered: enregistré
trunk/public/stylesheets/application.css
421 421

  
422 422
.icon-add { background-image: url(../images/add.png); }
423 423
.icon-edit { background-image: url(../images/edit.png); }
424
.icon-copy { background-image: url(../images/copy.png); }
424 425
.icon-del { background-image: url(../images/delete.png); }
425 426
.icon-move { background-image: url(../images/move.png); }
426 427
.icon-save { background-image: url(../images/save.png); }
trunk/test/functional/projects_controller_test.rb
22 22
class ProjectsController; def rescue_action(e) raise e end; end
23 23

  
24 24
class ProjectsControllerTest < Test::Unit::TestCase
25
  fixtures :projects, :users, :roles, :enabled_modules, :enumerations
25
  fixtures :projects, :users, :roles, :members, :issues, :enabled_modules, :enumerations
26 26

  
27 27
  def setup
28 28
    @controller = ProjectsController.new
......
143 143
    assert_redirected_to 'admin/projects'
144 144
    assert Project.find(1).active?
145 145
  end
146
  
147
  def test_add_issue
148
    @request.session[:user_id] = 2
149
    get :add_issue, :id => 1, :tracker_id => 1
150
    assert_response :success
151
    assert_template 'add_issue'
152
    post :add_issue, :id => 1, :issue => {:tracker_id => 1, :subject => 'This is the test_add_issue issue', :description => 'This is the description', :priority_id => 5}
153
    assert_redirected_to 'projects/list_issues'
154
    assert Issue.find_by_subject('This is the test_add_issue issue')
155
  end
156
  
157
  def test_copy_issue
158
    @request.session[:user_id] = 2
159
    get :add_issue, :id => 1, :copy_from => 1
160
    assert_template 'add_issue'
161
    assert_not_nil assigns(:issue)
162
    orig = Issue.find(1)
163
    assert_equal orig.subject, assigns(:issue).subject
164
  end
146 165
end
trunk/test/unit/issue_test.rb
18 18
require File.dirname(__FILE__) + '/../test_helper'
19 19

  
20 20
class IssueTest < Test::Unit::TestCase
21
  fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues
21
  fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values
22 22

  
23 23
  def test_category_based_assignment
24 24
    issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1)
25 25
    assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
26 26
  end
27 27
  
28
  def test_copy
29
    issue = Issue.new.copy_from(1)
30
    assert issue.save
31
    issue.reload
32
    orig = Issue.find(1)
33
    assert_equal orig.subject, issue.subject
34
    assert_equal orig.tracker, issue.tracker
35
    assert_equal orig.custom_values.first.value, issue.custom_values.first.value
36
  end
37
  
28 38
  def test_close_duplicates
29 39
    # Create 3 issues
30 40
    issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'Duplicates test', :description => 'Duplicates test')

Also available in: Unified diff