Project

General

Profile

Feature #7996 » bulk_time_entry_edit.diff

Adam Soltys, 2011-03-29 21:11

View differences:

app/controllers/context_menus_controller.rb
40 40
    
41 41
    render :layout => false
42 42
  end
43
  
43

  
44
  def time_entries
45
    @time_entries = TimeEntry.all(:conditions => {:id => params[:ids]}, :include => :project)
46
    
47
    @projects = @time_entries.collect(&:project).compact.uniq
48
    @project = @projects.first if @projects.size == 1
49
    
50
    @activities = TimeEntryActivity.shared.active
51

  
52
    @can = {:edit => User.current.allowed_to?(:log_time, @projects),
53
            :update => User.current.allowed_to?(:log_time, @projects),
54
            :delete => User.current.allowed_to?(:log_time, @projects)
55
            }
56

  
57
    @back = back_url
58

  
59
    render :layout => false
60
  end  
44 61
end
app/controllers/timelog_controller.rb
18 18
class TimelogController < ApplicationController
19 19
  menu_item :issues
20 20
  before_filter :find_project, :only => [:new, :create]
21
  before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
21
  before_filter :find_time_entry, :only => [:show, :edit, :update]
22
  before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
22 23
  before_filter :authorize, :except => [:index]
23 24
  before_filter :find_optional_project, :only => [:index]
24 25
  accept_key_auth :index, :show, :create, :update, :destroy
......
160 161
    end    
161 162
  end
162 163

  
164
  def bulk_edit
165
    @available_activities = TimeEntryActivity.shared.active
166
    @custom_fields = TimeEntry.first.available_custom_fields
167
  end
168

  
169
  def bulk_update
170
    attributes = parse_params_for_bulk_time_entry_attributes(params)
171

  
172
    unsaved_time_entry_ids = []
173
    @time_entries.each do |time_entry|
174
      time_entry.reload
175
      time_entry.attributes = attributes
176
      call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
177
      unless time_entry.save
178
        # Keep unsaved time_entry ids to display them in flash error
179
        unsaved_time_entry_ids << time_entry.id
180
      end
181
    end
182
    set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
183
    redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
184
  end
185

  
163 186
  verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
164 187
  def destroy
165
    if @time_entry.destroy && @time_entry.destroyed?
166
      respond_to do |format|
167
        format.html {
168
          flash[:notice] = l(:notice_successful_delete)
169
          redirect_to :back
170
        }
171
        format.api  { head :ok }
172
      end
173
    else
174
      respond_to do |format|
175
        format.html {
176
          flash[:error] = l(:notice_unable_delete_time_entry)
177
          redirect_to :back
178
        }
179
        format.api  { render_validation_errors(@time_entry) }
188
    @time_entries.each do |t| 
189
      begin
190
        unless t.destroy && t.destroyed?
191
          respond_to do |format|
192
            format.html {
193
              flash[:error] = l(:notice_unable_delete_time_entry)
194
              redirect_to :back
195
            }
196
            format.api  { render_validation_errors(t) }
197
          end
198
        end
199
      rescue ::ActionController::RedirectBackError
200
        redirect_to :action => 'index', :project_id => @projects.first
180 201
      end
181 202
    end
182
  rescue ::ActionController::RedirectBackError
183
    redirect_to :action => 'index', :project_id => @time_entry.project
203

  
204
    respond_to do |format|
205
      format.html {
206
        flash[:notice] = l(:notice_successful_delete)
207
        redirect_back_or_default(:action => 'index', :project_id => @projects.first)
208
      }
209
      format.api  { head :ok }
210
    end
184 211
  end
185 212

  
186 213
private
......
195 222
    render_404
196 223
  end
197 224

  
225
  def find_time_entries
226
    @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
227
    raise ActiveRecord::RecordNotFound if @time_entries.empty?
228
    @projects = @time_entries.collect(&:project).compact.uniq
229
    @project = @projects.first if @projects.size == 1
230
  rescue ActiveRecord::RecordNotFound
231
    render_404
232
  end
233

  
234
  def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
235
    if unsaved_time_entry_ids.empty?
236
      flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
237
    else
238
      flash[:error] = l(:notice_failed_to_save_time_entries,
239
                        :count => unsaved_time_entry_ids.size,
240
                        :total => time_entries.size,
241
                        :ids => '#' + unsaved_time_entry_ids.join(', #'))
242
    end
243
  end
244

  
198 245
  def find_project
199 246
    if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
200 247
      @issue = Issue.find(issue_id)
......
265 312
    @to   ||= (TimeEntry.latest_date_for_project(@project) || Date.today)
266 313
  end
267 314

  
315
  def parse_params_for_bulk_time_entry_attributes(params)
316
    attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
317
    attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
318
    attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
319
    attributes
320
  end
268 321
end
app/models/project.rb
431 431
  def all_issue_custom_fields
432 432
    @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort
433 433
  end
434

  
435
# Returns an array of all custom fields enabled for project time entries
436
  # (explictly associated custom fields and custom fields enabled for all projects)
437
  def all_time_entry_custom_fields
438
    @all_time_entry_custom_fields ||= (TimeEntryCustomField.for_all + time_entry_custom_fields).uniq.sort
439
  end
434 440
  
435 441
  def project
436 442
    self
app/views/context_menus/time_entries.html.erb
1
<ul>
2
  <%= call_hook(:view_time_entries_context_menu_start, {:time_entries => @time_entries, :can => @can, :back => @back }) %>
3

  
4
<% if !@time_entry.nil? -%>
5
	<li><%= context_menu_link l(:button_edit), {:controller => 'timelog', :action => 'edit', :id => @time_entry},
6
	        :class => 'icon-edit', :disabled => !@can[:edit] %></li>
7
<% else %>
8
	<li><%= context_menu_link l(:button_edit), {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id)},
9
	        :class => 'icon-edit', :disabled => !@can[:edit] %></li>
10
<% end %>
11
	<% if @activities.present? -%>
12
	<li class="folder">			
13
		<a href="#" class="submenu"><%= l(:field_activity) %></a>
14
		<ul>
15
		<% @activities.each do |u| -%>
16
		    <li><%= context_menu_link u.name, {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => u}, :back_url => @back}, :method => :post,
17
		                              :selected => (@time_entry && u == @time_entry.activity), :disabled => !@can[:update] %></li>
18
		<% end -%>
19
		    <li><%= context_menu_link l(:label_nobody), {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => 'none'}, :back_url => @back}, :method => :post,
20
		                              :selected => (@time_entry && @time_entry.activity.nil?), :disabled => !@can[:update] %></li>
21
		</ul>
22
	</li>
23
	<% end %>
24

  
25
  <li>
26
    <%= context_menu_link l(:button_delete), 
27
      {:controller => 'timelog', :action => 'destroy', :ids => @time_entries.collect(&:id), :back_url => @back},
28
      :method => :delete, :confirm => l(:text_time_entries_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %>
29
  </li>
30

  
31
  <%= call_hook(:view_time_entries_context_menu_end, {:time_entries => @time_entries, :can => @can, :back => @back }) %>
32
</ul>
app/views/timelog/_list.rhtml
1
<% content_for :header_tags do %>
2
    <%= javascript_include_tag 'context_menu' %>
3
    <%= stylesheet_link_tag 'context_menu' %>
4
    <%= stylesheet_link_tag 'context_menu_rtl' if l(:direction) == 'rtl' %>
5
<% end %>
6

  
7
<% form_tag({}) do -%>	
8
<%= hidden_field_tag 'back_url', url_for(params) %>
1 9
<table class="list time-entries">
2 10
<thead>
3 11
<tr>
12
<th class="checkbox hide-when-print">
13
  <%= link_to image_tag('toggle_check.png'), 
14
    {}, 
15
    :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;', 
16
    :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
17
</th>
4 18
<%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>
5 19
<%= sort_header_tag('user', :caption => l(:label_member)) %>
6 20
<%= sort_header_tag('activity', :caption => l(:label_activity)) %>
......
13 27
</thead>
14 28
<tbody>
15 29
<% entries.each do |entry| -%>
16
<tr class="time-entry <%= cycle("odd", "even") %>">
30
<tr class="time-entry <%= cycle("odd", "even") %> hascontextmenu">
31
<td class="checkbox hide-when-print"><%= check_box_tag("ids[]", entry.id, false, :id => nil) %></td>
17 32
<td class="spent_on"><%= format_date(entry.spent_on) %></td>
18 33
<td class="user"><%=h entry.user %></td>
19 34
<td class="activity"><%=h entry.activity %></td>
......
39 54
<% end -%>
40 55
</tbody>
41 56
</table>
57
<% end -%>
58

  
59
<%= context_menu time_entries_context_menu_path %>
app/views/timelog/bulk_edit.rhtml
1
<h2><%= l(:label_bulk_edit_selected_time_entries) %></h2>
2

  
3
<ul><%= @time_entries.collect {|i| content_tag('li', link_to(h("#{i.spent_on.strftime("%Y-%m-%d")} -- #{i.project}:  #{l(:label_f_hour_plural, :value => i.hours)}"), { :action => 'edit', :id => i }))} %></ul>
4

  
5
<% form_tag(:action => 'bulk_update') do %>
6
<%= @time_entries.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
7
<div class="box tabular">
8
  <fieldset class="attributes">
9
  <legend><%= l(:label_change_properties) %></legend>
10
  <div>
11
    <p>
12
      <label><%= l(:field_issue) %></label>
13
      <%= text_field :time_entry, :issue_id, :size => 6 %>
14
    </p>
15

  
16
    <p>
17
      <label><%= l(:field_spent_on) %></label>
18
      <%= text_field :time_entry, :spent_on, :size => 10 %><%= calendar_for('time_entry_spent_on') %>
19
    </p>
20

  
21
    <p>
22
      <label><%= l(:field_hours) %></label>
23
      <%= text_field :time_entry, :hours, :size => 6 %>
24
    </p>
25

  
26
    <% if @available_activities.any? %>
27
    <p>
28
      <label><%= l(:field_activity) %></label>
29
      <%= select_tag('time_entry[activity_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_activities, :id, :name)) %>
30
    </p>
31
    <% end %>
32

  
33
    <p>
34
      <label><%= l(:field_comments) %></label>
35
      <%= text_field(:time_entry, :comments, :size => 100) %>
36
    </p>
37

  
38
    <% @custom_fields.each do |custom_field| %>
39
      <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('time_entry', custom_field) %></p>
40
    <% end %>
41

  
42
    <%= call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries }) %>
43
  </div>
44

  
45
  </fieldset>
46
</div>
47

  
48
<p><%= submit_tag l(:button_submit) %></p>
49
<% end %>
config/locales/bg.yml
943 943

  
944 944
  button_expand_all: Expand all
945 945
  button_collapse_all: Collapse all
946
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
947
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/bs.yml
957 957
  label_news_comment_added: Comment added to a news
958 958
  button_expand_all: Expand all
959 959
  button_collapse_all: Collapse all
960
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
961
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/ca.yml
946 946
  label_news_comment_added: Comment added to a news
947 947
  button_expand_all: Expand all
948 948
  button_collapse_all: Collapse all
949
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
950
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/cs.yml
947 947
  label_news_comment_added: Comment added to a news
948 948
  button_expand_all: Expand all
949 949
  button_collapse_all: Collapse all
950
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
951
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/da.yml
959 959
  label_news_comment_added: Comment added to a news
960 960
  button_expand_all: Expand all
961 961
  button_collapse_all: Collapse all
962
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
963
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/de.yml
960 960
  label_news_comment_added: Comment added to a news
961 961
  button_expand_all: Expand all
962 962
  button_collapse_all: Collapse all
963
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
964
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/el.yml
943 943
  label_news_comment_added: Comment added to a news
944 944
  button_expand_all: Expand all
945 945
  button_collapse_all: Collapse all
946
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
947
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/en-GB.yml
947 947
  label_news_comment_added: Comment added to a news
948 948
  button_expand_all: Expand all
949 949
  button_collapse_all: Collapse all
950
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
951
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/en.yml
735 735
  label_default_columns: Default columns
736 736
  label_no_change_option: (No change)
737 737
  label_bulk_edit_selected_issues: Bulk edit selected issues
738
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
738 739
  label_theme: Theme
739 740
  label_default: Default
740 741
  label_search_titles_only: Search titles only
......
891 892
  text_status_changed_by_changeset: "Applied in changeset %{value}."
892 893
  text_time_logged_by_changeset: "Applied in changeset %{value}."
893 894
  text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
895
  text_time_entries_destroy_confirmation: 'Are you sure you want to delete the selected time entr(y/ies) ?'
894 896
  text_select_project_modules: 'Select modules to enable for this project:'
895 897
  text_default_administrator_account_changed: Default administrator account changed
896 898
  text_file_repository_writable: Attachments directory writable
config/locales/es.yml
980 980
  label_news_comment_added: Comment added to a news
981 981
  button_expand_all: Expand all
982 982
  button_collapse_all: Collapse all
983
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
984
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/eu.yml
947 947
  label_news_comment_added: Comment added to a news
948 948
  button_expand_all: Expand all
949 949
  button_collapse_all: Collapse all
950
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
951
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/fa.yml
946 946
  label_news_comment_added: Comment added to a news
947 947
  button_expand_all: Expand all
948 948
  button_collapse_all: Collapse all
949
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
950
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/fi.yml
964 964
  label_news_comment_added: Comment added to a news
965 965
  button_expand_all: Expand all
966 966
  button_collapse_all: Collapse all
967
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
968
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/fr.yml
961 961
  field_member_of_group: Groupe de l'assigné
962 962
  field_assigned_to_role: Rôle de l'assigné
963 963
  setting_emails_header: En-tête des emails
964
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
965
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/gl.yml
955 955
  label_news_comment_added: Comment added to a news
956 956
  button_expand_all: Expand all
957 957
  button_collapse_all: Collapse all
958
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
959
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/he.yml
948 948
  label_news_comment_added: Comment added to a news
949 949
  button_expand_all: Expand all
950 950
  button_collapse_all: Collapse all
951
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
952
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/hr.yml
950 950
  label_news_comment_added: Comment added to a news
951 951
  button_expand_all: Expand all
952 952
  button_collapse_all: Collapse all
953
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
954
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/hu.yml
962 962
  label_news_comment_added: Comment added to a news
963 963
  button_expand_all: Expand all
964 964
  button_collapse_all: Collapse all
965
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
966
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/id.yml
951 951
  label_news_comment_added: Comment added to a news
952 952
  button_expand_all: Expand all
953 953
  button_collapse_all: Collapse all
954
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
955
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/it.yml
944 944
  label_news_comment_added: Comment added to a news
945 945
  button_expand_all: Expand all
946 946
  button_collapse_all: Collapse all
947
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
948
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/ja.yml
964 964
  label_news_comment_added: Comment added to a news
965 965
  button_expand_all: Expand all
966 966
  button_collapse_all: Collapse all
967
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
968
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/ko.yml
995 995
  label_news_comment_added: Comment added to a news
996 996
  button_expand_all: Expand all
997 997
  button_collapse_all: Collapse all
998
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
999
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/lt.yml
1003 1003
  label_news_comment_added: Comment added to a news
1004 1004
  button_expand_all: Expand all
1005 1005
  button_collapse_all: Collapse all
1006
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
1007
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/lv.yml
938 938
  label_news_comment_added: Comment added to a news
939 939
  button_expand_all: Expand all
940 940
  button_collapse_all: Collapse all
941
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
942
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/mk.yml
943 943
  label_news_comment_added: Comment added to a news
944 944
  button_expand_all: Expand all
945 945
  button_collapse_all: Collapse all
946
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
947
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/mn.yml
944 944
  label_news_comment_added: Comment added to a news
945 945
  button_expand_all: Expand all
946 946
  button_collapse_all: Collapse all
947
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
948
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/nl.yml
925 925
  label_news_comment_added: Commentaar toegevoegd aan een nieuwsitem
926 926
  button_expand_all: Expand all
927 927
  button_collapse_all: Collapse all
928
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
929
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/no.yml
930 930
  label_news_comment_added: Comment added to a news
931 931
  button_expand_all: Expand all
932 932
  button_collapse_all: Collapse all
933
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
934
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/pl.yml
960 960
  label_news_comment_added: Comment added to a news
961 961
  button_expand_all: Expand all
962 962
  button_collapse_all: Collapse all
963
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
964
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/pt-BR.yml
966 966
  label_news_comment_added: Notícia recebeu um comentário
967 967
  button_expand_all: Expand all
968 968
  button_collapse_all: Collapse all
969
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
970
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/pt.yml
947 947
  label_news_comment_added: Comment added to a news
948 948
  button_expand_all: Expand all
949 949
  button_collapse_all: Collapse all
950
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
951
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/ro.yml
936 936
  label_news_comment_added: Comment added to a news
937 937
  button_expand_all: Expand all
938 938
  button_collapse_all: Collapse all
939
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
940
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/ru.yml
1056 1056
  label_news_comment_added: Comment added to a news
1057 1057
  button_expand_all: Expand all
1058 1058
  button_collapse_all: Collapse all
1059
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
1060
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/sk.yml
938 938
  label_news_comment_added: Comment added to a news
939 939
  button_expand_all: Expand all
940 940
  button_collapse_all: Collapse all
941
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
942
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/sl.yml
939 939
  label_news_comment_added: Comment added to a news
940 940
  button_expand_all: Expand all
941 941
  button_collapse_all: Collapse all
942
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
943
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/sr-YU.yml
943 943
  label_news_comment_added: Comment added to a news
944 944
  button_expand_all: Expand all
945 945
  button_collapse_all: Collapse all
946
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
947
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/sr.yml
944 944
  label_news_comment_added: Comment added to a news
945 945
  button_expand_all: Expand all
946 946
  button_collapse_all: Collapse all
947
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
948
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/sv.yml
984 984
  label_news_comment_added: Comment added to a news
985 985
  button_expand_all: Expand all
986 986
  button_collapse_all: Collapse all
987
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
988
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/th.yml
940 940
  label_news_comment_added: Comment added to a news
941 941
  button_expand_all: Expand all
942 942
  button_collapse_all: Collapse all
943
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
944
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/tr.yml
962 962
  label_news_comment_added: Comment added to a news
963 963
  button_expand_all: Expand all
964 964
  button_collapse_all: Collapse all
965
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
966
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/uk.yml
939 939
  label_news_comment_added: Comment added to a news
940 940
  button_expand_all: Expand all
941 941
  button_collapse_all: Collapse all
942
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
943
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/vi.yml
994 994
  label_news_comment_added: Comment added to a news
995 995
  button_expand_all: Expand all
996 996
  button_collapse_all: Collapse all
997
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
998
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/zh-TW.yml
1025 1025
  label_news_comment_added: Comment added to a news
1026 1026
  button_expand_all: Expand all
1027 1027
  button_collapse_all: Collapse all
1028
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
1029
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/locales/zh.yml
957 957
  label_news_comment_added: Comment added to a news
958 958
  button_expand_all: Expand all
959 959
  button_collapse_all: Collapse all
960
  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
961
  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies) ?
config/routes.rb
21 21
    time_report.connect 'projects/:project_id/time_entries/report.:format'
22 22
  end
23 23

  
24
  map.bulk_edit_time_entry 'time_entries/bulk_edit', :controller => 'timelog', :action => 'bulk_edit', :conditions => { :method => :get }
25
  map.bulk_update_time_entry 'time_entries/bulk_edit', :controller => 'timelog', :action => 'bulk_update', :conditions => { :method => :post }
26
  map.time_entries_context_menu '/time_entries/context_menu', :controller => 'context_menus', :action => 'time_entries'
27

  
24 28
  # TODO: wasteful since this is also nested under issues, projects, and projects/issues
25 29
  map.resources :time_entries, :controller => 'timelog'
26 30
  
......
101 105
  map.resources :issues, :path_prefix => '/projects/:project_id', :collection => { :create => :post } do |issues|
102 106
    issues.resources :time_entries, :controller => 'timelog'
103 107
  end
108
  # map.time_entries_context_menu '/time_entries/context_menu', :controller => 'context_menus', :action => 'time_entries'
104 109

  
105 110
  map.with_options  :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
106 111
    relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
lib/redmine.rb
84 84
  end
85 85
  
86 86
  map.project_module :time_tracking do |map|
87
    map.permission :log_time, {:timelog => [:new, :create, :edit, :update]}, :require => :loggedin
87
    map.permission :log_time, {:timelog => [:new, :create, :edit, :update, :bulk_edit, :bulk_update]}, :require => :loggedin
88 88
    map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report]
89
    map.permission :edit_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy]}, :require => :member
90
    map.permission :edit_own_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
89
    map.permission :edit_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
90
    map.permission :edit_own_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
91 91
    map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
92 92
  end
93 93
  
test/fixtures/custom_fields.yml
129 129
  field_format: date
130 130
  default_value: ""
131 131
  editable: true
132
custom_fields_010: 
133
  name: Overtime
134
  min_length: 0
135
  regexp: ""
136
  is_for_all: false
137
  is_filter: false
138
  type: TimeEntryCustomField
139
  max_length: 0
140
  possible_values: ""
141
  id: 10
142
  is_required: false
143
  field_format: bool
144
  default_value: 0
145
  editable: true
test/fixtures/time_entries.yml
55 55
  hours: 7.65
56 56
  user_id: 1
57 57
  tyear: 2007
58
time_entries_005: 
59
  created_on: 2011-03-22 12:20:48 +02:00
60
  tweek: 12
61
  tmonth: 3
62
  project_id: 5
63
  comments: Time spent on a subproject
64
  updated_on: 2011-03-22 12:20:48 +02:00
65
  activity_id: 10
66
  spent_on: 2011-03-22
67
  issue_id: 
68
  id: 5
69
  hours: 7.65
70
  user_id: 1
71
  tyear: 2011
58 72
  
test/functional/timelog_controller_test.rb
131 131
    assert_equal 2, entry.issue_id
132 132
    assert_equal 2, entry.user_id
133 133
  end
134

  
135
  def test_get_bulk_edit
136
    @request.session[:user_id] = 2
137
    get :bulk_edit, :ids => [1, 2]
138
    assert_response :success
139
    assert_template 'bulk_edit'
140
    
141
    # System wide custom field
142
    assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
143
  end
144

  
145
  def test_get_bulk_edit_on_different_projects
146
    @request.session[:user_id] = 2
147
    get :bulk_edit, :ids => [1, 2, 6]
148
    assert_response :success
149
    assert_template 'bulk_edit'
150
  end
151

  
152
  def test_bulk_update
153
    @request.session[:user_id] = 2
154
    # update time entry activity
155
    post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
156
                                     
157
    assert_response 302
158
    # check that the issues were updated
159
    assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
160
  end
161

  
162
  def test_bulk_update_on_different_projects
163
    @request.session[:user_id] = 2
164
    # update time entry activity
165
    post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
166
    
167
    assert_response 302
168
    # check that the issues were updated
169
    assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
170
  end
171

  
172
  def test_bulk_update_on_different_projects_without_rights
173
    @request.session[:user_id] = 3
174
    user = User.find(3)
175
    action = { :controller => "timelog", :action => "bulk_update" }
176
    assert user.allowed_to?(action, TimeEntry.find(1).project)
177
    assert ! user.allowed_to?(action, TimeEntry.find(5).project)
178
    post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
179
    assert_response 403
180
  end
181

  
182
  def test_bulk_update_custom_field
183
    @request.session[:user_id] = 2
184
    post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
185
                                     
186
    assert_response 302
187
    assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
188
  end
189

  
190
  def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
191
    @request.session[:user_id] = 2
192
    post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
193

  
194
    assert_response :redirect
195
    assert_redirected_to '/time_entries'
196
  end
197

  
198
  def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
199
    @request.session[:user_id] = 2
200
    post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
201

  
202
    assert_response :redirect
203
    assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
204
  end
134 205
  
135 206
  def test_destroy
136 207
    @request.session[:user_id] = 2
......
236 307
    get :index, :format => 'csv'
237 308
    assert_response :success
238 309
    assert_equal 'text/csv', @response.content_type
239
    assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment\n")
240
    assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\"\n")
310
    assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
311
    assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
241 312
  end
242 313
  
243 314
  def test_index_csv_export
......
245 316
    get :index, :project_id => 1, :format => 'csv'
246 317
    assert_response :success
247 318
    assert_equal 'text/csv', @response.content_type
248
    assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment\n")
249
    assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\"\n")
319
    assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
320
    assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
250 321
  end
251 322
end
(1-1/3)