diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2008-01-06 17:06:14 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2008-01-06 17:06:14 +0000 |
commit | 976a31e941e61542075866563e4c0740106c5d70 (patch) | |
tree | f9e9ef35a84f4f37f5db76de1db6f7442503abb8 | |
parent | 4a729036bf0a92b8da4481d1313512c5b885770a (diff) | |
download | redmine-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
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
|