summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2008-01-06 17:06:14 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2008-01-06 17:06:14 +0000
commit976a31e941e61542075866563e4c0740106c5d70 (patch)
treef9e9ef35a84f4f37f5db76de1db6f7442503abb8
parent4a729036bf0a92b8da4481d1313512c5b885770a (diff)
downloadredmine-976a31e941e61542075866563e4c0740106c5d70.tar.gz
redmine-976a31e941e61542075866563e4c0740106c5d70.zip
Merged IssuesController change_status and add_note actions.
The 'Change status' specific form removed and now accessible from issue/show view with no additional request (click on 'Update' to show the form). The 'Change issue status' permission is removed. To change the status, the user just needs to have either 'Edit' or 'Add note' permissions and some workflow transitions allowed. git-svn-id: http://redmine.rubyforge.org/svn/trunk@1043 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/controllers/issues_controller.rb74
-rw-r--r--app/controllers/projects_controller.rb8
-rw-r--r--app/models/issue.rb7
-rw-r--r--app/models/issue_status.rb4
-rw-r--r--app/views/issues/_bulk_edit_form.rhtml2
-rw-r--r--app/views/issues/_update.rhtml42
-rw-r--r--app/views/issues/change_status.rhtml38
-rw-r--r--app/views/issues/context_menu.rhtml12
-rw-r--r--app/views/issues/show.rhtml30
-rw-r--r--app/views/issues/update.rhtml4
-rw-r--r--lang/bg.yml2
-rw-r--r--lang/cs.yml2
-rw-r--r--lang/de.yml2
-rw-r--r--lang/en.yml2
-rw-r--r--lang/es.yml2
-rw-r--r--lang/fr.yml2
-rw-r--r--lang/he.yml2
-rw-r--r--lang/it.yml2
-rw-r--r--lang/ja.yml2
-rw-r--r--lang/ko.yml2
-rw-r--r--lang/nl.yml2
-rw-r--r--lang/pl.yml2
-rw-r--r--lang/pt-br.yml2
-rw-r--r--lang/pt.yml2
-rw-r--r--lang/ro.yml2
-rw-r--r--lang/ru.yml2
-rw-r--r--lang/sr.yml2
-rw-r--r--lang/sv.yml2
-rw-r--r--lang/zh-tw.yml2
-rw-r--r--lang/zh.yml2
-rw-r--r--lib/redmine.rb5
-rw-r--r--lib/redmine/default_data/loader.rb3
-rw-r--r--test/fixtures/enumerations.yml9
-rw-r--r--test/fixtures/roles.yml5
-rw-r--r--test/fixtures/users.yml23
-rw-r--r--test/functional/issues_controller_test.rb122
-rw-r--r--test/integration/issues_test.rb4
-rw-r--r--test/test_helper.rb6
-rw-r--r--vendor/plugins/gloc-1.1.0/lib/gloc-rails.rb21
39 files changed, 326 insertions, 133 deletions
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 39a1e2d13..11112289e 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -21,7 +21,7 @@ class IssuesController < ApplicationController
before_filter :find_optional_project, :only => [:index, :changes]
accept_key_auth :index, :changes
- cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ]
+ cache_sweeper :issue_sweeper, :only => [ :edit, :update, :destroy ]
helper :projects
include ProjectsHelper
@@ -82,7 +82,8 @@ class IssuesController < ApplicationController
def show
@custom_values = @issue.custom_values.find(:all, :include => :custom_field, :order => "#{CustomField.table_name}.position")
@journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
- @status_options = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)
+ @status_options = @issue.new_statuses_allowed_to(User.current)
+ @activities = Enumeration::get_values('ACTI')
respond_to do |format|
format.html { render :template => 'issues/show.rhtml' }
format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
@@ -115,47 +116,42 @@ class IssuesController < ApplicationController
end
end
- def add_note
+ # Attributes that can be updated on workflow transition
+ # TODO: make it configurable (at least per role)
+ UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
+
+ def update
+ @status_options = @issue.new_statuses_allowed_to(User.current)
+ @activities = Enumeration::get_values('ACTI')
journal = @issue.init_journal(User.current, params[:notes])
- attachments = attach_files(@issue, params[:attachments])
- attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
- if journal.save
- flash[:notice] = l(:notice_successful_update)
- Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
- redirect_to :action => 'show', :id => @issue
- return
+ # User can change issue attributes only if a workflow transition is allowed
+ if !@status_options.empty? && params[:issue]
+ attrs = params[:issue].dup
+ attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) }
+ attrs.delete(:status_id) unless @status_options.detect {|s| s.id.to_s == attrs[:status_id].to_s}
+ @issue.attributes = attrs
end
- show
- end
-
- def change_status
- @status_options = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)
- @new_status = IssueStatus.find(params[:new_status_id])
- if params[:confirm]
- begin
- journal = @issue.init_journal(User.current, params[:notes])
- @issue.status = @new_status
- if @issue.update_attributes(params[:issue])
- attachments = attach_files(@issue, params[:attachments])
- attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
- # Log time
- if current_role.allowed_to?(:log_time)
- @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
- @time_entry.attributes = params[:time_entry]
- @time_entry.save
- end
-
+ if request.post?
+ attachments = attach_files(@issue, params[:attachments])
+ attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
+ if @issue.save
+ # Log spend time
+ if current_role.allowed_to?(:log_time)
+ @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
+ @time_entry.attributes = params[:time_entry]
+ @time_entry.save
+ end
+ if !journal.new_record?
+ # Only send notification if something was actually changed
flash[:notice] = l(:notice_successful_update)
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
- redirect_to :action => 'show', :id => @issue
end
- rescue ActiveRecord::StaleObjectError
- # Optimistic locking exception
- flash[:error] = l(:notice_locking_conflict)
+ redirect_to(params[:back_to] || {:action => 'show', :id => @issue})
end
- end
- @assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user }
- @activities = Enumeration::get_values('ACTI')
+ end
+ rescue ActiveRecord::StaleObjectError
+ # Optimistic locking exception
+ flash.now[:error] = l(:notice_locking_conflict)
end
def destroy
@@ -177,11 +173,11 @@ class IssuesController < ApplicationController
def context_menu
@priorities = Enumeration.get_values('IPRI').reverse
@statuses = IssueStatus.find(:all, :order => 'position')
- @allowed_statuses = @issue.status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)
+ @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@assignables = @issue.assignable_users
@assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@can = {:edit => User.current.allowed_to?(:edit_issues, @project),
- :change_status => User.current.allowed_to?(:change_issue_status, @project),
+ :assign => (@allowed_statuses.any? || User.current.allowed_to?(:edit_issues, @project)),
:add => User.current.allowed_to?(:add_issues, @project),
:move => User.current.allowed_to?(:move_issues, @project),
:copy => (@project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a58eef719..2ae742dfa 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -252,11 +252,9 @@ class ProjectsController < ApplicationController
redirect_to :controller => 'issues', :action => 'index', :project_id => @project
return
end
- if current_role && User.current.allowed_to?(:change_issue_status, @project)
- # Find potential statuses the user could be allowed to switch issues to
- @available_statuses = Workflow.find(:all, :include => :new_status,
- :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
- end
+ # Find potential statuses the user could be allowed to switch issues to
+ @available_statuses = Workflow.find(:all, :include => :new_status,
+ :conditions => {:role_id => current_role.id}).collect(&:new_status).compact.uniq
render :update do |page|
page.hide 'query_form'
page.replace_html 'bulk-edit', :partial => 'issues/bulk_edit_form'
diff --git a/app/models/issue.rb b/app/models/issue.rb
index f7b01ea6a..419c6cdc7 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -180,6 +180,13 @@ class Issue < ActiveRecord::Base
project.assignable_users
end
+ # Returns an array of status that user is able to apply
+ def new_statuses_allowed_to(user)
+ statuses = status.find_new_statuses_allowed_to(user.role_for_project(project), tracker)
+ statuses << status unless statuses.empty?
+ statuses.uniq.sort
+ end
+
# Returns the mail adresses of users that should be notified for the issue
def recipients
recipients = project.recipients
diff --git a/app/models/issue_status.rb b/app/models/issue_status.rb
index a5d228405..ddff9c005 100644
--- a/app/models/issue_status.rb
+++ b/app/models/issue_status.rb
@@ -56,6 +56,10 @@ class IssueStatus < ActiveRecord::Base
false
end
+ def <=>(status)
+ position <=> status.position
+ end
+
def to_s; name end
private
diff --git a/app/views/issues/_bulk_edit_form.rhtml b/app/views/issues/_bulk_edit_form.rhtml
index bc3f62e6d..e9e1cef86 100644
--- a/app/views/issues/_bulk_edit_form.rhtml
+++ b/app/views/issues/_bulk_edit_form.rhtml
@@ -2,7 +2,7 @@
<fieldset class="box"><legend><%= l(:label_bulk_edit_selected_issues) %></legend>
<p>
-<% if @available_statuses %>
+<% if @available_statuses.any? %>
<label><%= l(:field_status) %>:
<%= select_tag('status_id', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %></label>
<% end %>
diff --git a/app/views/issues/_update.rhtml b/app/views/issues/_update.rhtml
new file mode 100644
index 000000000..3cf593806
--- /dev/null
+++ b/app/views/issues/_update.rhtml
@@ -0,0 +1,42 @@
+<% labelled_tabular_form_for(:issue, @issue, :url => {:action => 'update', :id => @issue}, :html => {:multipart => true}) do |f| %>
+
+<div class="box">
+<% unless @status_options.empty? %>
+<%= f.hidden_field :lock_version %>
+<fieldset><legend><%= l(:label_change_properties) %></legend>
+ <div class="splitcontentleft">
+ <p><%= f.select :status_id, (@status_options.collect {|p| [p.name, p.id]}), :required => true %></p>
+ <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
+ </div>
+ <div class="splitcontentright">
+ <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
+ <p><%= f.select :fixed_version_id, (@project.versions.sort.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
+ </div>
+</fieldset>
+<% end%>
+<% if authorize_for('timelog', 'edit') %>
+<fieldset><legend><%= l(:button_log_time) %></legend>
+ <% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
+ <div class="splitcontentleft">
+ <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p>
+ </div>
+ <div class="splitcontentright">
+ <p><%= time_entry.text_field :comments, :size => 40 %></p>
+ <p><%= time_entry.select :activity_id, (@activities.collect {|p| [p.name, p.id]}) %></p>
+ </div>
+ <% end %>
+</fieldset>
+<% end %>
+
+<fieldset><legend><%= l(:field_notes) %></legend>
+<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
+<%= wikitoolbar_for 'notes' %>
+
+<p id="attachments_p"><label><%=l(:label_attachment_new)%>
+<%= image_to_function "add.png", "addFileField();return false" %></label>
+<%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p>
+</fieldset>
+</div>
+
+<%= submit_tag l(:button_submit) %>
+<% end %>
diff --git a/app/views/issues/change_status.rhtml b/app/views/issues/change_status.rhtml
deleted file mode 100644
index a1e294556..000000000
--- a/app/views/issues/change_status.rhtml
+++ /dev/null
@@ -1,38 +0,0 @@
-<h2><%=l(:label_issue)%> #<%= @issue.id %>: <%=h @issue.subject %></h2>
-
-<%= error_messages_for 'issue' %>
-<% labelled_tabular_form_for(:issue, @issue, :url => {:action => 'change_status', :id => @issue}, :html => {:multipart => true}) do |f| %>
-
-<%= hidden_field_tag 'confirm', 1 %>
-<%= hidden_field_tag 'new_status_id', @new_status.id %>
-<%= f.hidden_field :lock_version %>
-
-<div class="box">
-<div class="splitcontentleft">
-<p><label><%=l(:label_issue_status_new)%></label> <%= @new_status.name %></p>
-<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
-<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
-<p><%= f.select :fixed_version_id, (@project.versions.sort.collect {|v| [v.name, v.id]}), { :include_blank => true } %></p>
-</div>
-<div class="splitcontentright">
-<% if authorize_for('timelog', 'edit') %>
-<% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
-<p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p>
-<p><%= time_entry.text_field :comments, :size => 40 %></p>
-<p><%= time_entry.select :activity_id, (@activities.collect {|p| [p.name, p.id]}) %></p>
-<% end %>
-<% end %>
-</div>
-
-<div class="clear"></div>
-
-<p><label for="notes"><%= l(:field_notes) %></label>
-<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %></p>
-
-<p id="attachments_p"><label><%=l(:label_attachment_new)%>
-<%= image_to_function "add.png", "addFileField();return false" %></label>
-<%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p>
-</div>
-
-<%= submit_tag l(:button_save) %>
-<% end %>
diff --git a/app/views/issues/context_menu.rhtml b/app/views/issues/context_menu.rhtml
index 3af49fb04..0f11bb943 100644
--- a/app/views/issues/context_menu.rhtml
+++ b/app/views/issues/context_menu.rhtml
@@ -6,8 +6,8 @@
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
<ul>
<% @statuses.each do |s| %>
- <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'change_status', :id => @issue, :new_status_id => s},
- :selected => (s == @issue.status), :disabled => !(@can[:change_status] && @allowed_statuses.include?(s)) %></li>
+ <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'update', :id => @issue, :issue => {:status_id => s}},
+ :selected => (s == @issue.status), :disabled => !(@allowed_statuses.include?(s)) %></li>
<% end %>
</ul>
</li>
@@ -24,11 +24,11 @@
<a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
<ul>
<% @assignables.each do |u| %>
- <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => u, :back_to => back_to}, :method => :post,
- :selected => (u == @issue.assigned_to), :disabled => !(@can[:edit] || @can[:change_status]) %></li>
+ <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'update', :id => @issue, :issue => {:assigned_to_id => u}, :back_to => back_to}, :method => :post,
+ :selected => (u == @issue.assigned_to), :disabled => !@can[:assign] %></li>
<% end %>
- <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'edit', :id => @issue, 'issue[assigned_to_id]' => '', :back_to => back_to}, :method => :post,
- :selected => @issue.assigned_to.nil?, :disabled => !(@can[:edit] || @can[:change_status]) %></li>
+ <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'update', :id => @issue, :issue => {:assigned_to_id => nil}, :back_to => back_to}, :method => :post,
+ :selected => @issue.assigned_to.nil?, :disabled => !@can[:assign] %></li>
</ul>
</li>
<li><%= context_menu_link l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue},
diff --git a/app/views/issues/show.rhtml b/app/views/issues/show.rhtml
index 91b638216..8cd44424c 100644
--- a/app/views/issues/show.rhtml
+++ b/app/views/issues/show.rhtml
@@ -1,5 +1,5 @@
<div class="contextual">
-<%= show_and_goto_link(l(:label_add_note), 'add-note', :class => 'icon icon-note') if authorize_for('issues', 'add_note') %>
+<%= show_and_goto_link(l(:button_update), 'update', :class => 'icon icon-note') if authorize_for('issues', 'update') %>
<%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit', :accesskey => accesskey(:edit) %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
<%= watcher_tag(@issue, User.current) %>
@@ -81,16 +81,6 @@ end %>
</div>
-<% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %>
- <% form_tag({:controller => 'issues', :action => 'change_status', :id => @issue}) do %>
- <p><%=l(:label_change_status)%> :
- <select name="new_status_id">
- <%= options_from_collection_for_select @status_options, "id", "name", @issue.status_id %>
- </select>
- <%= submit_tag l(:button_change) %></p>
- <% end %>
-<% end %>
-
<% if @journals.any? %>
<div id="history">
<h3><%=l(:label_history)%></h3>
@@ -98,18 +88,12 @@ end %>
</div>
<% end %>
-<% if authorize_for('issues', 'add_note') %>
- <a name="add-note-anchor"></a>
- <div id="add-note" class="box" style="display:none;">
- <h3><%= l(:label_add_note) %></h3>
- <% form_tag({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular", :multipart => true) do %>
- <p><label for="notes"><%=l(:field_notes)%></label>
- <%= text_area_tag 'notes', '', :cols => 60, :rows => 10, :class => 'wiki-edit' %></p>
- <%= wikitoolbar_for 'notes' %>
- <%= render :partial => 'attachments/form' %>
- <%= submit_tag l(:button_add) %>
- <%= toggle_link l(:button_cancel), 'add-note' %>
- <% end %>
+<% if authorize_for('issues', 'update') %>
+ <a name="update-anchor"></a>
+ <div id="update" style="display:none;">
+ <h3><%= l(:button_update) %></h3>
+ <%= render :partial => 'update' %>
+ <%= toggle_link l(:button_cancel), 'update' %>
</div>
<% end %>
diff --git a/app/views/issues/update.rhtml b/app/views/issues/update.rhtml
new file mode 100644
index 000000000..44e72da87
--- /dev/null
+++ b/app/views/issues/update.rhtml
@@ -0,0 +1,4 @@
+<h2><%= @issue.tracker.name %> #<%= @issue.id %>: <%=h @issue.subject %></h2>
+
+<%= error_messages_for 'issue' %>
+<%= render :partial => 'update' %>
diff --git a/lang/bg.yml b/lang/bg.yml
index eeb6ef80b..57e7fd22d 100644
--- a/lang/bg.yml
+++ b/lang/bg.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/cs.yml b/lang/cs.yml
index e99fe061b..5cbf1133d 100644
--- a/lang/cs.yml
+++ b/lang/cs.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/de.yml b/lang/de.yml
index cd4e222ae..ba3dcda5b 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/en.yml b/lang/en.yml
index 7a1a2622f..eae61d7bf 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -462,6 +462,7 @@ label_registration_manual_activation: manual account activation
label_registration_automatic_activation: automatic account activation
label_display_per_page: 'Per page: %s'
label_age: Age
+label_change_properties: Change properties
button_login: Login
button_submit: Submit
@@ -498,6 +499,7 @@ button_rename: Rename
button_change_password: Change password
button_copy: Copy
button_annotate: Annotate
+button_update: Update
status_active: active
status_registered: registered
diff --git a/lang/es.yml b/lang/es.yml
index bae295586..1a3af0f5c 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -560,3 +560,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/fr.yml b/lang/fr.yml
index d47feceb2..ff2bf792f 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -462,6 +462,7 @@ label_registration_manual_activation: activation manuelle du compte
label_registration_automatic_activation: activation automatique du compte
label_display_per_page: 'Par page: %s'
label_age: Age
+label_change_properties: Changer les propriétés
button_login: Connexion
button_submit: Soumettre
@@ -498,6 +499,7 @@ button_rename: Renommer
button_change_password: Changer de mot de passe
button_copy: Copier
button_annotate: Annoter
+button_update: Mettre à jour
status_active: actif
status_registered: enregistré
diff --git a/lang/he.yml b/lang/he.yml
index e2277dc3d..d110ee8bf 100644
--- a/lang/he.yml
+++ b/lang/he.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/it.yml b/lang/it.yml
index 709f41774..bb9440ffd 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/ja.yml b/lang/ja.yml
index 286fb5971..e4c13b34c 100644
--- a/lang/ja.yml
+++ b/lang/ja.yml
@@ -558,3 +558,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/ko.yml b/lang/ko.yml
index 52c9b5431..790598b8d 100644
--- a/lang/ko.yml
+++ b/lang/ko.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/nl.yml b/lang/nl.yml
index 43e1704a4..2e18a6b7c 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -558,3 +558,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/pl.yml b/lang/pl.yml
index 77ae6bb66..0ee0e3792 100644
--- a/lang/pl.yml
+++ b/lang/pl.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/pt-br.yml b/lang/pt-br.yml
index 55e8b3fae..21d21b793 100644
--- a/lang/pt-br.yml
+++ b/lang/pt-br.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/pt.yml b/lang/pt.yml
index 0a5661997..c7f05f8c8 100644
--- a/lang/pt.yml
+++ b/lang/pt.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/ro.yml b/lang/ro.yml
index 1381adba9..6ebc3b0e5 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/ru.yml b/lang/ru.yml
index b293dc309..569e96884 100644
--- a/lang/ru.yml
+++ b/lang/ru.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/sr.yml b/lang/sr.yml
index c842ec438..1d4f52539 100644
--- a/lang/sr.yml
+++ b/lang/sr.yml
@@ -558,3 +558,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/sv.yml b/lang/sv.yml
index 85fb19bcb..75f297c20 100644
--- a/lang/sv.yml
+++ b/lang/sv.yml
@@ -558,3 +558,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/zh-tw.yml b/lang/zh-tw.yml
index 719a2e868..5bf47d943 100644
--- a/lang/zh-tw.yml
+++ b/lang/zh-tw.yml
@@ -557,3 +557,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lang/zh.yml b/lang/zh.yml
index 05134751f..16f4c7195 100644
--- a/lang/zh.yml
+++ b/lang/zh.yml
@@ -560,3 +560,5 @@ notice_default_data_loaded: Default configuration successfully loaded.
text_load_default_configuration: Load the default configuration
text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
error_can_t_load_default_data: "Default configuration could not be loaded: %s"
+button_update: Update
+label_change_properties: Change properties
diff --git a/lib/redmine.rb b/lib/redmine.rb
index 9b29257bd..2dca9ed50 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -32,10 +32,9 @@ Redmine::AccessControl.map do |map|
:reports => :issue_report}, :public => true
map.permission :add_issues, {:projects => :add_issue}
map.permission :edit_issues, {:projects => :bulk_edit_issues,
- :issues => [:edit, :destroy_attachment]}
+ :issues => [:edit, :update, :destroy_attachment]}
map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
- map.permission :add_issue_notes, {:issues => :add_note}
- map.permission :change_issue_status, {:issues => :change_status}, :require => :loggedin
+ map.permission :add_issue_notes, {:issues => :update}
map.permission :move_issues, {:projects => :move_issues}, :require => :loggedin
map.permission :delete_issues, {:issues => :destroy}, :require => :member
# Queries
diff --git a/lib/redmine/default_data/loader.rb b/lib/redmine/default_data/loader.rb
index 0abb5572a..11bd2a0b4 100644
--- a/lib/redmine/default_data/loader.rb
+++ b/lib/redmine/default_data/loader.rb
@@ -53,7 +53,6 @@ module Redmine
:edit_issues,
:manage_issue_relations,
:add_issue_notes,
- :change_issue_status,
:save_queries,
:view_gantt,
:view_calendar,
@@ -74,7 +73,6 @@ module Redmine
:position => 3,
:permissions => [:add_issues,
:add_issue_notes,
- :change_issue_status,
:save_queries,
:view_gantt,
:view_calendar,
@@ -90,7 +88,6 @@ module Redmine
Role.non_member.update_attribute :permissions, [:add_issues,
:add_issue_notes,
- :change_issue_status,
:save_queries,
:view_gantt,
:view_calendar,
diff --git a/test/fixtures/enumerations.yml b/test/fixtures/enumerations.yml
index eeef99b5b..c90a997ee 100644
--- a/test/fixtures/enumerations.yml
+++ b/test/fixtures/enumerations.yml
@@ -31,3 +31,12 @@ enumerations_008:
name: Immediate
id: 8
opt: IPRI
+enumerations_009:
+ name: Design
+ id: 9
+ opt: ACTI
+enumerations_010:
+ name: Development
+ id: 10
+ opt: ACTI
+ \ No newline at end of file
diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml
index a089a98f9..c4d417a09 100644
--- a/test/fixtures/roles.yml
+++ b/test/fixtures/roles.yml
@@ -9,7 +9,6 @@ roles_004:
- :edit_issues
- :manage_issue_relations
- :add_issue_notes
- - :change_issue_status
- :move_issues
- :save_queries
- :view_gantt
@@ -34,6 +33,7 @@ roles_005:
builtin: 2
permissions: |
---
+ - :add_issue_notes
- :view_gantt
- :view_calendar
- :view_time_entries
@@ -58,7 +58,6 @@ roles_001:
- :edit_issues
- :manage_issue_relations
- :add_issue_notes
- - :change_issue_status
- :move_issues
- :delete_issues
- :manage_public_queries
@@ -99,7 +98,6 @@ roles_002:
- :edit_issues
- :manage_issue_relations
- :add_issue_notes
- - :change_issue_status
- :move_issues
- :delete_issues
- :manage_public_queries
@@ -137,7 +135,6 @@ roles_003:
- :edit_issues
- :manage_issue_relations
- :add_issue_notes
- - :change_issue_status
- :move_issues
- :delete_issues
- :manage_public_queries
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index df7123879..de3553173 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -14,6 +14,7 @@ users_004:
auth_source_id:
mail_notification: true
login: rhill
+ type: User
users_001:
created_on: 2006-07-19 19:12:21 +02:00
status: 1
@@ -29,6 +30,7 @@ users_001:
auth_source_id:
mail_notification: true
login: admin
+ type: User
users_002:
created_on: 2006-07-19 19:32:09 +02:00
status: 1
@@ -44,6 +46,7 @@ users_002:
auth_source_id:
mail_notification: true
login: jsmith
+ type: User
users_003:
created_on: 2006-07-19 19:33:19 +02:00
status: 1
@@ -59,6 +62,7 @@ users_003:
auth_source_id:
mail_notification: true
login: dlopper
+ type: User
users_005:
id: 5
created_on: 2006-07-19 19:33:19 +02:00
@@ -75,3 +79,22 @@ users_005:
auth_source_id:
mail_notification: true
login: dlopper2
+ type: User
+users_006:
+ id: 6
+ created_on: 2006-07-19 19:33:19 +02:00
+ status: 1
+ last_login_on:
+ language: ''
+ hashed_password: 1
+ updated_on: 2006-07-19 19:33:19 +02:00
+ admin: false
+ mail: ''
+ lastname: Anonymous
+ firstname: ''
+ auth_source_id:
+ mail_notification: false
+ login: ''
+ type: AnonymousUser
+
+ \ No newline at end of file
diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb
index 06d2f1029..d60e32200 100644
--- a/test/functional/issues_controller_test.rb
+++ b/test/functional/issues_controller_test.rb
@@ -32,7 +32,8 @@ class IssuesControllerTest < Test::Unit::TestCase
:issue_categories,
:enabled_modules,
:enumerations,
- :attachments
+ :attachments,
+ :workflows
def setup
@controller = IssuesController.new
@@ -94,13 +95,37 @@ class IssuesControllerTest < Test::Unit::TestCase
assert_equal 'application/atom+xml', @response.content_type
end
- def test_show
+ def test_show_by_anonymous
get :show, :id => 1
assert_response :success
assert_template 'show.rhtml'
assert_not_nil assigns(:issue)
+ assert_equal Issue.find(1), assigns(:issue)
+
+ # anonymous role is allowed to add a note
+ assert_tag :tag => 'form',
+ :descendant => { :tag => 'fieldset',
+ :child => { :tag => 'legend',
+ :content => /Notes/ } }
end
+ def test_show_by_manager
+ @request.session[:user_id] = 2
+ get :show, :id => 1
+ assert_response :success
+
+ assert_tag :tag => 'form',
+ :descendant => { :tag => 'fieldset',
+ :child => { :tag => 'legend',
+ :content => /Change properties/ } },
+ :descendant => { :tag => 'fieldset',
+ :child => { :tag => 'legend',
+ :content => /Log time/ } },
+ :descendant => { :tag => 'fieldset',
+ :child => { :tag => 'legend',
+ :content => /Notes/ } }
+ end
+
def test_get_edit
@request.session[:user_id] = 2
get :edit, :id => 1
@@ -129,21 +154,100 @@ class IssuesControllerTest < Test::Unit::TestCase
assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
end
- def test_post_change_status
+ def test_get_update
+ @request.session[:user_id] = 2
+ get :update, :id => 1
+ assert_response :success
+ assert_template 'update'
+ end
+
+ def test_update_with_status_and_assignee_change
issue = Issue.find(1)
assert_equal 1, issue.status_id
@request.session[:user_id] = 2
- post :change_status, :id => 1,
- :new_status_id => 2,
- :issue => { :assigned_to_id => 3 },
- :notes => 'Assigned to dlopper',
- :confirm => 1
+ post :update,
+ :id => 1,
+ :issue => { :status_id => 2, :assigned_to_id => 3 },
+ :notes => 'Assigned to dlopper'
assert_redirected_to 'issues/show/1'
issue.reload
assert_equal 2, issue.status_id
- j = issue.journals.find(:first, :order => 'created_on DESC')
+ j = issue.journals.find(:first, :order => 'id DESC')
assert_equal 'Assigned to dlopper', j.notes
assert_equal 2, j.details.size
+
+ mail = ActionMailer::Base.deliveries.last
+ assert mail.body.include?("Status changed from New to Assigned")
+ end
+
+ def test_update_with_note_only
+ notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
+ # anonymous user
+ post :update,
+ :id => 1,
+ :notes => notes
+ assert_redirected_to 'issues/show/1'
+ j = Issue.find(1).journals.find(:first, :order => 'id DESC')
+ assert_equal notes, j.notes
+ assert_equal 0, j.details.size
+ assert_equal User.anonymous, j.user
+
+ mail = ActionMailer::Base.deliveries.last
+ assert mail.body.include?(notes)
+ end
+
+ def test_update_with_note_and_spent_time
+ @request.session[:user_id] = 2
+ spent_hours_before = Issue.find(1).spent_hours
+ post :update,
+ :id => 1,
+ :notes => '2.5 hours added',
+ :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
+ assert_redirected_to 'issues/show/1'
+
+ issue = Issue.find(1)
+
+ j = issue.journals.find(:first, :order => 'id DESC')
+ assert_equal '2.5 hours added', j.notes
+ assert_equal 0, j.details.size
+
+ t = issue.time_entries.find(:first, :order => 'id DESC')
+ assert_not_nil t
+ assert_equal 2.5, t.hours
+ assert_equal spent_hours_before + 2.5, issue.spent_hours
+ end
+
+ def test_update_with_attachment_only
+ # anonymous user
+ post :update,
+ :id => 1,
+ :notes => '',
+ :attachments => [ test_uploaded_file('testfile.txt', 'text/plain') ]
+ assert_redirected_to 'issues/show/1'
+ j = Issue.find(1).journals.find(:first, :order => 'id DESC')
+ assert j.notes.blank?
+ assert_equal 1, j.details.size
+ assert_equal 'testfile.txt', j.details.first.value
+ assert_equal User.anonymous, j.user
+
+ mail = ActionMailer::Base.deliveries.last
+ assert mail.body.include?('testfile.txt')
+ end
+
+ def test_update_with_no_change
+ issue = Issue.find(1)
+ issue.journals.clear
+ ActionMailer::Base.deliveries.clear
+
+ post :update,
+ :id => 1,
+ :notes => ''
+ assert_redirected_to 'issues/show/1'
+
+ issue.reload
+ assert issue.journals.empty?
+ # No email should be sent
+ assert ActionMailer::Base.deliveries.empty?
end
def test_context_menu
diff --git a/test/integration/issues_test.rb b/test/integration/issues_test.rb
index 702fbc026..a9d3f9c74 100644
--- a/test/integration/issues_test.rb
+++ b/test/integration/issues_test.rb
@@ -38,7 +38,9 @@ class IssuesTest < ActionController::IntegrationTest
def test_issue_attachements
log_user('jsmith', 'jsmith')
- post "issues/add_note/1", { :notes => 'Some notes', 'attachments[]' => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/testfile.txt', 'text/plain') }
+ post 'issues/update/1',
+ :notes => 'Some notes',
+ :attachments => ([] << ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/testfile.txt', 'text/plain'))
assert_redirected_to "issues/show/1"
# make sure attachment was saved
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 542d4ce72..7c81c3607 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -53,6 +53,10 @@ class Test::Unit::TestCase
assert_redirected_to "my/page"
assert_equal login, User.find(session[:user_id]).login
end
+
+ def test_uploaded_file(name, mime)
+ ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + "/files/#{name}", mime)
+ end
end
@@ -70,4 +74,4 @@ class String
def read
self.to_s
end
-end \ No newline at end of file
+end
diff --git a/vendor/plugins/gloc-1.1.0/lib/gloc-rails.rb b/vendor/plugins/gloc-1.1.0/lib/gloc-rails.rb
index 8f201bcb8..aa65991b0 100644
--- a/vendor/plugins/gloc-1.1.0/lib/gloc-rails.rb
+++ b/vendor/plugins/gloc-1.1.0/lib/gloc-rails.rb
@@ -155,6 +155,27 @@ module ActiveRecord #:nodoc:
# end
# end
+ class Errors
+ include GLoc
+
+ def full_messages
+ full_messages = []
+
+ @errors.each_key do |attr|
+ @errors[attr].each do |msg|
+ next if msg.nil?
+
+ if attr == "base"
+ full_messages << (msg.is_a?(Symbol) ? l(msg) : msg)
+ else
+ full_messages << @base.class.human_attribute_name(attr) + " " + (msg.is_a?(Symbol) ? l(msg) : msg)
+ end
+ end
+ end
+ full_messages
+ end
+ end
+
module Validations #:nodoc:
module ClassMethods
# The default Rails version of this function creates an error message and then