diff --git a/app/controllers/custom_timesheets_controller.rb b/app/controllers/custom_timesheets_controller.rb index 1482083..2ecfb94 100644 --- a/app/controllers/custom_timesheets_controller.rb +++ b/app/controllers/custom_timesheets_controller.rb @@ -14,8 +14,6 @@ class CustomTimesheetsController < ApplicationController SessionKey = 'timesheet_filter' - verify :method => :delete, :only => :reset, :render => {:nothing => true, :status => :method_not_allowed } - def index load_filters_from_session @timesheet ||= CustomTimesheet.new @@ -105,7 +103,7 @@ def allowed_projects if User.current.admin? return Project.find(:all, :order => 'name ASC') else - return Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name ASC') + return Project.find(:all, :conditions => Project.visible_condition(User.current), :order => 'name ASC') end end diff --git a/app/helpers/custom_timesheets_helper.rb b/app/helpers/custom_timesheets_helper.rb index a9ba571..1a45cab 100644 --- a/app/helpers/custom_timesheets_helper.rb +++ b/app/helpers/custom_timesheets_helper.rb @@ -5,7 +5,7 @@ def showing_users(users) def permalink_to_timesheet(timesheet) link_to(l(:timesheet_permalink), - :controller => 'custom_timesheet', + :controller => 'custom_timesheets', :action => 'report', :timesheet => timesheet.to_param) end @@ -13,7 +13,7 @@ def permalink_to_timesheet(timesheet) def link_to_csv_export(timesheet) link_to('CSV', { - :controller => 'custom_timesheet', + :controller => 'custom_timesheets', :action => 'report', :format => 'csv', :timesheet => timesheet.to_param diff --git a/app/models/custom_timesheet.rb b/app/models/custom_timesheet.rb index 389db61..e1b5c0e 100644 --- a/app/models/custom_timesheet.rb +++ b/app/models/custom_timesheet.rb @@ -54,6 +54,7 @@ def attributes # TODO una time_entry contiene anche @user, @project, @activity, @issue: vedere se togliere queste cose quando si castrano gli attributi. sempre nello stesso posto, @attributes_cache può dare problemi? dup_entry = entry.dup dup_entry.attributes = filtered_attributes.dup + dup_entry.attributes['id'] = entry.id @time_entries.push(dup_entry) end @@ -248,15 +249,15 @@ def to_param end def to_csv - returning '' do |out| - FCSV.generate out do |csv| - csv << csv_header - - @time_entries.each do |t| - csv << time_entry_to_csv(t) - end + result = FCSV.generate do |csv| + csv << csv_header + + @time_entries.each do |t| + csv << time_entry_to_csv(t) end end + + result end def self.viewable_users diff --git a/app/views/common/_auto_completion.html.erb b/app/views/common/_auto_completion.html.erb index 3e50373..d1f80b6 100644 --- a/app/views/common/_auto_completion.html.erb +++ b/app/views/common/_auto_completion.html.erb @@ -1,166 +1,204 @@ - -<% -=begin - Logica per l'autocompletamento dei campi temporali - Informazioni - ----------------------------------------------------------------- - - In questo file è realizzata la logica in javascript per completare i campi - del tempo in base ad informazioni parzialmente inserite. Specificando che - l'ora di inizio deve sempre essere specificata, l'auto completamento viene - fatto secondo le seguenti regole: - - il campo "Ore" viene completato se è specificata l'ora di fine - - l'ora di fine viene completata se è specificato il campo "Ore" -=end -%> - - - + +<% +=begin + Logica per l'autocompletamento dei campi temporali - Informazioni + ----------------------------------------------------------------- + + In questo file è realizzata la logica in javascript per completare i campi + del tempo in base ad informazioni parzialmente inserite. Specificando che + l'ora di inizio deve sempre essere specificata, l'auto completamento viene + fatto secondo le seguenti regole: + - il campo "Ore" viene completato se è specificata l'ora di fine + - l'ora di fine viene completata se è specificato il campo "Ore" +=end +%> + + + diff --git a/app/views/common/_custom_fields.html.erb b/app/views/common/_custom_fields.html.erb index d38d668..1c977e1 100644 --- a/app/views/common/_custom_fields.html.erb +++ b/app/views/common/_custom_fields.html.erb @@ -1,28 +1,30 @@ -<% - # TODO concettualmente questi metodi sono helper e andrebbero quindi definiti - # nel file helper - - # Aggiunge uno "0" davanti al numero passato se è minore di 10 - def hours_min_string(number) - str = number.to_s - str = "0#{number}" if number < 10 - str - end - - # Restituisce un array di interi da 0 a 55 andando di 5 in 5 - def minutes - mins = Array.new - (0..3).each { |i| mins << 15 * i} - mins - end -%> - -

- <%= time_entry.select :start_hour, (0..23).to_a.collect {|a| [hours_min_string(a), a]}, { :include_blank => true, :required => true } -%>:<%= time_entry.select :start_minute, minutes.collect {|m| [hours_min_string(m), m]}, { :include_blank => true } -%> -

-

- <%= time_entry.select :end_hour, (0..23).to_a.collect {|a| [hours_min_string(a), a]}, { :include_blank => true, :required => true } -%>:<%= time_entry.select :end_minute, minutes.collect {|m| [hours_min_string(m), m]}, { :include_blank => true } -%> -

-

<%= time_entry.text_field :billed_hours, :size => 6 %>

- +<% + # TODO concettualmente questi metodi sono helper e andrebbero quindi definiti + # nel file helper + + # Aggiunge uno "0" davanti al numero passato se è minore di 10 + def hours_min_string(number) + str = number.to_s + str = "0#{number}" if number < 10 + str + end + + # Restituisce un array di interi da 0 a 55 andando di 5 in 5 + def minutes + mins = Array.new + (0..59).each { |i| mins << i} + mins + end +%> + +

+ <%= time_entry.select :start_hour, (0..23).to_a.collect {|a| [hours_min_string(a), a]}, { :include_blank => true, :required => false } -%>:<%= time_entry.select :start_minute, minutes.collect {|m| [hours_min_string(m), m]}, { :include_blank => true } -%> + +

+

+ <%= time_entry.select :end_hour, (0..23).to_a.collect {|a| [hours_min_string(a), a]}, { :include_blank => true, :required => false } -%>:<%= time_entry.select :end_minute, minutes.collect {|m| [hours_min_string(m), m]}, { :include_blank => true } -%> + +

+

<%= time_entry.text_field :billed_hours, :size => 6 %>

+ <%= render :partial => "common/auto_completion" %> \ No newline at end of file diff --git a/app/views/custom_timesheets/_form.html.erb b/app/views/custom_timesheets/_form.html.erb index 4de7e7b..d18b996 100644 --- a/app/views/custom_timesheets/_form.html.erb +++ b/app/views/custom_timesheets/_form.html.erb @@ -1,5 +1,5 @@
- <% form_for :timesheet, :url =>{:action => 'report'} do |f| %> + <%= form_for :timesheet, :url => {:action => 'report'} do |f| %> <%# Scelta del periodo %>
@@ -7,47 +7,48 @@ <%= l :t_filters %>
+

<%= l(:timesheet_period) %>

- <%= radio_button_tag 'timesheet[period_type]', '3', @timesheet.period_type == CustomTimesheet::ValidPeriodType[:month] %> - <%= select_tag 'timesheet[year]', options_for_select([Time.now.year, Time.now.year - 1], (@timesheet.year.blank? ? Time.new.year : @timesheet.year)) %> - <%= select_tag 'timesheet[month]', options_for_select(l('date.month_names'), (@timesheet.month.blank? ? 0 : l('date.month_names')[@timesheet.month])) %> + <%= raw(radio_button_tag 'timesheet[period_type]', '3', @timesheet.period_type == CustomTimesheet::ValidPeriodType[:month]) %> + <%= raw(select_tag 'timesheet[year]', options_for_select([Time.now.year, Time.now.year - 1], (@timesheet.year.blank? ? Time.new.year : @timesheet.year))) %> + <%= raw(select_tag 'timesheet[month]', options_for_select(l('date.month_names'), (@timesheet.month.blank? ? 0 : l('date.month_names')[@timesheet.month]))) %>

- <%= radio_button_tag 'timesheet[period_type]', '1', @timesheet.period_type == CustomTimesheet::ValidPeriodType[:default] %> - <%= select_tag 'timesheet[period]', options_for_period_select((params[:timesheet].nil? ? nil : params[:timesheet][:period])), - :onfocus => '$("timesheet_period_type_1").checked = true;' %> + <%= raw(radio_button_tag 'timesheet[period_type]', '1', @timesheet.period_type == CustomTimesheet::ValidPeriodType[:default]) %> + <%= raw(select_tag 'timesheet[period]', options_for_period_select((params[:timesheet].nil? ? nil : params[:timesheet][:period])), + :onfocus => '$("timesheet_period_type_1").checked = true;') %>

- <%= radio_button_tag 'timesheet[period_type]', '2', @timesheet.period_type == CustomTimesheet::ValidPeriodType[:free_period] %> + <%= raw(radio_button_tag 'timesheet[period_type]', '2', @timesheet.period_type == CustomTimesheet::ValidPeriodType[:free_period]) %>
- <%= f.text_field "date_from", :size => 10 %><%= calendar_for('timesheet_date_from') %>
+ <%= raw(f.text_field "date_from", :size => 10) %><%= calendar_for('timesheet_date_from') %>

- <%= f.text_field "date_to", :size => 10 %><%= calendar_for('timesheet_date_to') %>

+ <%= raw(f.text_field "date_to", :size => 10) %><%= calendar_for('timesheet_date_to') %>


- <%= select_tag("timesheet[sort]", options_for_select(CustomTimesheet::SortOptions.invert.reject { |key, val| val == :hours || val == :billed_hours }, @timesheet.sort)) %> + <%= raw(select_tag("timesheet[sort]", options_for_select(CustomTimesheet::SortOptions.invert.reject { |key, val| val == :hours || val == :billed_hours }, @timesheet.sort))) %>

- <%= project_options(@timesheet) %> + <%= raw(project_options(@timesheet)) %>

- <%= select_tag 'timesheet[activities][]', activity_options(@timesheet, @activities), { :multiple => true, :size => @list_size} %> + <%= raw(select_tag 'timesheet[activities][]', activity_options(@timesheet, @activities), { :multiple => true, :size => @list_size}) %>

- <%= select_tag 'timesheet[users][]', user_options(@timesheet), { :multiple => true, :size => @list_size} %> + <%= raw(select_tag 'timesheet[users][]', user_options(@timesheet), { :multiple => true, :size => @list_size}) %>
@@ -59,19 +60,19 @@ <%= l :t_fields_to_include %>
- <%= complete_checkbox(:id, 'ID') %> - <%= complete_checkbox(:project_id, l(:field_project)) %> - <%= complete_checkbox(:user_id, l(:field_user)) %> - <%= complete_checkbox(:issue_id, l(:field_issue)) %> - <%= complete_checkbox(:comments, l(:t_comment)) %> + <%= raw(complete_checkbox(:id, 'ID')) %> + <%= raw(complete_checkbox(:project_id, l(:field_project))) %> + <%= raw(complete_checkbox(:user_id, l(:field_user))) %> + <%= raw(complete_checkbox(:issue_id, l(:field_issue))) %> + <%= raw(complete_checkbox(:comments, l(:t_comment))) %>
- <%= complete_checkbox(:activity_id, l(:field_activity)) %> - <%= complete_checkbox(:spent_on, l(:field_spent_on)) %> - <%= complete_checkbox(:tweek, l(:timesheet_week_of_year)) %> - <%= complete_checkbox(:hours, l(:field_hours)) %> - <%= complete_checkbox(:billed_hours, l(:timesheet_billed_hours)) %> + <%= raw(complete_checkbox(:activity_id, l(:field_activity))) %> + <%= raw(complete_checkbox(:spent_on, l(:field_spent_on))) %> + <%= raw(complete_checkbox(:tweek, l(:timesheet_week_of_year))) %> + <%= raw(complete_checkbox(:hours, l(:field_hours))) %> + <%= raw(complete_checkbox(:billed_hours, l(:timesheet_billed_hours))) %>
@@ -84,6 +85,6 @@
<% end %> - <%= button_to(l(:button_reset), {:controller => 'custom_timesheet', :action => 'reset'}, :method => 'delete') %> + <%= button_to(l(:button_reset), {:controller => 'custom_timesheets', :action => 'reset'}, :method => 'delete') %>
diff --git a/app/views/custom_timesheets/no_projects.rhtml b/app/views/custom_timesheets/no_projects.html.erb similarity index 100% rename from app/views/custom_timesheets/no_projects.rhtml rename to app/views/custom_timesheets/no_projects.html.erb diff --git a/app/views/custom_timesheets/report.html.erb b/app/views/custom_timesheets/report.html.erb index 4edd791..ae0de40 100644 --- a/app/views/custom_timesheets/report.html.erb +++ b/app/views/custom_timesheets/report.html.erb @@ -11,33 +11,33 @@ <% if !@timesheet.time_entries.blank? && @timesheet.time_entries.length > 0 %> <%# TODO preparare il totale nel controller %> -

<%= l(:label_spent_time) %> (<%= @timesheet.total_hours -%> <%= h(l(:field_hours)) -%>, +

<%= l(:label_spent_time) %> (<%= @timesheet.total_hours -%> <%= l(:field_hours) -%>, <%= @timesheet.total_billed_hours -%> <%= l(:timesheet_billed_hours) -%>)

- <%= table_header '5', link_to(image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "table")); return false;', - :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}", :class => 'toggle-all'), 'id' %> - <%= table_header '8', l(:label_date), 'spent_on' %> - <%= table_header '5', l(:timesheet_week_of_year), 'tweek' %> - <%= table_header '10', l(:label_member), 'user_id' %> - <%= table_header '10', l(:label_activity), 'activity_id' %> - <%= table_header '15', l(:label_project), 'project_id' %> - <%= table_header '10', l(:label_issue), 'issue_id' %> - <%= table_header '20', l(:field_comments), 'comments' %> - <%= table_header '6', l(:field_hours), 'hours' %> - <%= table_header '11', l(:timesheet_billed_hours), 'billed_hours' %> - <%= table_header '5', '', 'id' %> + <%= raw(table_header '5', link_to(image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "table")); return false;', + :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}", :class => 'toggle-all'), 'id') %> + <%= raw(table_header '8', l(:label_date), 'spent_on') %> + <%= raw(table_header '5', l(:timesheet_week_of_year), 'tweek') %> + <%= raw(table_header '10', l(:label_member), 'user_id') %> + <%= raw(table_header '10', l(:label_activity), 'activity_id') %> + <%= raw(table_header '15', l(:label_project), 'project_id') %> + <%= raw(table_header '10', l(:label_issue), 'issue_id') %> + <%= raw(table_header '20', l(:field_comments), 'comments') %> + <%= raw(table_header '6', l(:field_hours), 'hours') %> + <%= raw(table_header '11', l(:timesheet_billed_hours), 'billed_hours') %> + <%= raw(table_header '5', '', 'id') %> <% @timesheet.time_entries.each do |time_entry| %> hascontextmenu"> - <%= table_col true, 'id' do check_box_tag('ids[]', time_entry.id, false, { :class => 'checkbox' }) end %> - <%= table_col true, 'spent_on' do format_date(time_entry.spent_on) end %> - <%= table_col(true, 'tweek') { time_entry.tweek.to_s } %> - <%= table_col true, 'user_id' do time_entry.user.name end %> - <%= table_col true, 'activity_id' do time_entry.activity.name end %> - <%= table_col true, 'project_id' do time_entry.project.name end %> + <%= raw(table_col(true, 'id') { check_box_tag('ids[]', time_entry.id, false, { :class => 'checkbox' }) }) %> + <%= raw(table_col(true, 'spent_on') { format_date(time_entry.spent_on) }) %> + <%= raw(table_col(true, 'tweek') { time_entry.tweek.to_s }) %> + <%= raw(table_col(true, 'user_id') { time_entry.user.name }) %> + <%= raw(table_col(true, 'activity_id') { time_entry.activity.name }) %> + <%= raw(table_col(true, 'project_id') { time_entry.project.name }) %> <% if selected?('issue_id') %> <% end %> - <%= table_col false, 'comments' do time_entry.comments end %> - <%= table_col true, 'hours' do number_with_precision(time_entry.hours, :precision => @precision) end %> - <%= table_col true, 'billed_hours' do '' + number_with_precision(time_entry.billed_hours, :precision => @precision) + '' unless time_entry.billed_hours.blank? end %> + <%= raw(table_col(false, 'comments') { time_entry.comments }) %> + <%= raw(table_col(true, 'hours') { number_with_precision(time_entry.hours, :precision => @precision) }) %> + <%= raw(table_col(true, 'billed_hours') { '' + number_with_precision(time_entry.billed_hours, :precision => @precision) + '' unless time_entry.billed_hours.blank? }) %> <% if selected?('id') %>
<% if time_entry.issue %> @@ -50,15 +50,15 @@ <% end %> <% if time_entry.editable_by?(User.current) -%> - <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => time_entry}, + <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => time_entry.id}, :title => l(:button_edit) %> - <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => time_entry}, + <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => time_entry.id}, :confirm => l(:text_are_you_sure), :method => :post, :title => l(:button_delete) %> @@ -81,4 +81,4 @@ <% end %> -<%= javascript_tag "new ContextMenu('#{url_for(:controller => 'timesheet', :action => 'context_menu')}')" %> \ No newline at end of file +<%= javascript_tag "new ContextMenu('#{url_for(:controller => 'custom_timesheets', :action => 'context_menu')}')" %> \ No newline at end of file diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb new file mode 100644 index 0000000..7d5938d --- /dev/null +++ b/app/views/issues/_edit.html.erb @@ -0,0 +1,80 @@ +<% +=begin +XXX This logic is to see how to integrate the new fields of the record + of time in the update of an issue. to be deleted (if needed, in partial common + the other with a view to edit). +=end +%> +<% + # TODO conceptually these methods are helpers and should therefore be defined + # in the file helper + + # Adds a "0" before the number if the past is less than 10 + def hours_min_string(number) + str = number.to_s + str = "0#{number}" if number < 10 + str + end + + # Returns an array of integers from 0 to 55 going from 5 to 5 + def minutes + mins = Array.new + (0..3).each { |i| mins << 15 * i} + mins + end +%> + +<%= labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %> + <%= error_messages_for 'issue', 'time_entry' %> + <%= render :partial => 'conflict' if @conflict %> +
+ <% if @edit_allowed || !@allowed_statuses.empty? %> +
<%= l(:label_change_properties) %> +
+ <%= render :partial => 'form', :locals => {:f => f} %> +
+
+ <% end %> + <% if User.current.allowed_to?(:log_time, @project) %> +
<%= l(:button_log_time) %> + <%= labelled_fields_for :time_entry, @time_entry do |time_entry| %> +
+

<%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %>

+
+
+

<%= time_entry.select :activity_id, activity_collection_for_select_options %>

+
+
+ <%= render :partial => 'common/custom_fields', :locals => {:time_entry => time_entry} %> +
+

<%= time_entry.text_field :comments, :size => 60 %>

+ <% @time_entry.custom_field_values.each do |value| %> +

<%= custom_field_tag_with_label :time_entry, value %>

+ <% end %> + <% end %> +
+ <% end %> + +
<%= l(:field_notes) %> + <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> + <%= wikitoolbar_for 'issue_notes' %> + + <% if @issue.safe_attribute? 'private_notes' %> + + <% end %> + + <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> +
+ +
<%= l(:label_attachment_plural) %> +

<%= render :partial => 'attachments/form', :locals => {:container => @issue} %>

+
+
+ + <%= f.hidden_field :lock_version %> + <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %> + <%= submit_tag l(:button_submit) %> + <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %> +<% end %> + +
diff --git a/app/views/issues/_edit.rhtml b/app/views/issues/_edit.rhtml deleted file mode 100644 index 02f3254..0000000 --- a/app/views/issues/_edit.rhtml +++ /dev/null @@ -1,85 +0,0 @@ -<% -=begin -XXX questa logica serve per vedere come integrare i nuovi campi della registrazione - del tempo nell'update di una issue. da cancellare (deve stare nel partial comune - con l'altra vista di edit). -=end -%> -<% - # TODO concettualmente questi metodi sono helper e andrebbero quindi definiti - # nel file helper - - # Aggiunge uno "0" davanti al numero passato se è minore di 10 - def hours_min_string(number) - str = number.to_s - str = "0#{number}" if number < 10 - str - end - - # Restituisce un array di interi da 0 a 55 andando di 5 in 5 - def minutes - mins = Array.new - (0..3).each { |i| mins << 15 * i} - mins - end -%> - - -<% labelled_tabular_form_for :issue, @issue, - :url => {:action => 'update', :id => @issue}, - :html => {:id => 'issue-form', - :class => nil, - :method => :put, - :multipart => true} do |f| %> - <%= error_messages_for 'issue', 'time_entry' %> -
- <% if @edit_allowed || !@allowed_statuses.empty? %> -
<%= l(:label_change_properties) %> - <% if !@issue.new_record? && !@issue.errors.any? && @edit_allowed %> - (<%= link_to l(:label_more), {}, :onclick => 'Effect.toggle("issue_descr_fields", "appear", {duration:0.3}); return false;' %>) - <% end %> - - <%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %> -
- <% end %> - <% if authorize_for('timelog', 'edit') %> -
<%= l(:button_log_time) %> - <% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %> -
-

<%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %>

-
-
-

<%= time_entry.select :activity_id, activity_collection_for_select_options %>

-
-
- <%= render :partial => 'common/custom_fields', :locals => {:time_entry => time_entry} %> -
-

<%= time_entry.text_field :comments, :size => 60 %>

- <% @time_entry.custom_field_values.each do |value| %> -

<%= custom_field_tag_with_label :time_entry, value %>

- <% end %> - <% end %> -
- <% end %> - -
<%= l(:field_notes) %> - <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %> - <%= wikitoolbar_for 'notes' %> - <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> - -

<%=l(:label_attachment_plural)%>
<%= render :partial => 'attachments/form' %>

-
-
- - <%= f.hidden_field :lock_version %> - <%= submit_tag l(:button_submit) %> - <%= link_to_remote l(:label_preview), - { :url => { :controller => 'issues', :action => 'preview', :project_id => @project, :id => @issue }, - :method => 'post', - :update => 'preview', - :with => 'Form.serialize("issue-form")', - :complete => "Element.scrollTo('preview')" - }, :accesskey => accesskey(:preview) %> -<% end %> - -
diff --git a/app/views/settings/_timesheet_settings.html.erb b/app/views/settings/_timesheet_settings.html.erb new file mode 100644 index 0000000..1280ae1 --- /dev/null +++ b/app/views/settings/_timesheet_settings.html.erb @@ -0,0 +1,4 @@ +

<%= raw(text_field_tag 'settings[list_size]', @settings['list_size']) %>

+ +

<%= raw(text_field_tag 'settings[precision]', @settings['precision']) %>

+ diff --git a/app/views/settings/_timesheet_settings.rhtml b/app/views/settings/_timesheet_settings.rhtml deleted file mode 100644 index 5751b92..0000000 --- a/app/views/settings/_timesheet_settings.rhtml +++ /dev/null @@ -1,4 +0,0 @@ -

<%= text_field_tag 'settings[list_size]', @settings['list_size'] %>

- -

<%= text_field_tag 'settings[precision]', @settings['precision'] %>

- diff --git a/app/views/timelog/_form.html.erb b/app/views/timelog/_form.html.erb new file mode 100644 index 0000000..a60181e --- /dev/null +++ b/app/views/timelog/_form.html.erb @@ -0,0 +1,58 @@ +<% + # TODO concettualmente questi metodi sono helper e andrebbero quindi definiti + # nel file helper + + # Aggiunge uno "0" davanti al numero passato se è minore di 10 + def hours_min_string(number) + str = number.to_s + str = "0#{number}" if number < 10 + str + end + + # Restituisce un array di interi da 0 a 55 andando di 5 in 5 + def minutes + mins = Array.new + (0..3).each { |i| mins << 15 * i} + mins + end +%> + +<%= error_messages_for 'time_entry' %> +<%= back_url_hidden_field_tag %> + +
+ <% if @time_entry.new_record? %> + <% if params[:project_id] %> + <%= f.hidden_field :project_id %> + <% else %> +

<%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project), :required => true %>

+ <% end %> + <% end %> +

<%= f.text_field :issue_id, :size => 6 %> <%= "#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}" if @time_entry.issue %>

+

<%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %>

+

<%= f.text_field :hours, :size => 6, :required => true %>

+

<%= f.text_field :comments, :size => 100 %>

+

<%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %>

+ <% @time_entry.custom_field_values.each do |value| %> +

<%= custom_field_tag_with_label :time_entry, value %>

+ <% end %> + + <%# CPRS-MO %> + +
+ <%= render :partial => 'common/custom_fields', :locals => {:time_entry => f} %> +
+ + <% + # FIXME + # questa vista sovrascrive una vista esistente e si trova in un plugin: + # lo hook non può essere chiamato da qui. non si sa cosa succede se + # un altro componente usa lo hook (se questo è definito perché la vista + # è stata già sovrascritta, se invece non è definito oppure se avvengono + # situazioni inconsistenti). + # + # call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) + %> + + <%# ECPRS-MO %> +
\ No newline at end of file diff --git a/app/views/timelog/_list.rhtml b/app/views/timelog/_list.html.erb similarity index 71% rename from app/views/timelog/_list.rhtml rename to app/views/timelog/_list.html.erb index 9c764b8..947dbe9 100644 --- a/app/views/timelog/_list.rhtml +++ b/app/views/timelog/_list.html.erb @@ -1,6 +1,16 @@ +<%= form_tag({}) do -%> +<%= hidden_field_tag 'back_url', url_for(params) %> + +
+ <%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %> <%= sort_header_tag('user', :caption => l(:label_member)) %> <%= sort_header_tag('activity', :caption => l(:label_activity)) %> @@ -8,8 +18,6 @@ <%= sort_header_tag('issue', :caption => l(:label_issue), :default_order => 'desc') %> - - <% # Ripetere il metodo qui non è DRY perché l'ho già definito nello hook per # la form dell'inserimento. FIXME @@ -19,8 +27,6 @@ str end %> - - @@ -34,17 +40,18 @@ <% entries.each do |entry| -%> -"> + hascontextmenu"> + - - - + + + - + @@ -77,3 +84,7 @@ <% end -%>
+ <%= link_to image_tag('toggle_check.png'), + {}, + :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;', + :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %> +<%= l(:field_comments) %> <%= l(:field_start_hour) %>
<%= check_box_tag("ids[]", entry.id, false, :id => nil) %> <%= format_date(entry.spent_on) %><%=h entry.user %><%=h entry.activity %><%=h entry.project %><%= link_to_user(entry.user) %><%= entry.activity %><%= link_to_project(entry.project) %> <% if entry.issue -%> <%= entry.issue.visible? ? link_to_issue(entry.issue, :truncate => 50) : "##{entry.issue.id}" -%> <% end -%> <%=h entry.comments %><%= entry.comments %> <% if entry.start_time.blank? %> @@ -69,7 +76,7 @@ :title => l(:button_edit) %> <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry, :project_id => nil}, :confirm => l(:text_are_you_sure), - :method => :post, + :method => :delete, :title => l(:button_delete) %> <% end -%>
+
+<% end -%> + +<%= context_menu time_entries_context_menu_path %> diff --git a/app/views/timelog/edit.html.erb b/app/views/timelog/edit.html.erb new file mode 100644 index 0000000..675520c --- /dev/null +++ b/app/views/timelog/edit.html.erb @@ -0,0 +1,6 @@ +

<%= l(:label_spent_time) %>

+ +<%= labelled_form_for @time_entry, :url => project_time_entry_path(@time_entry.project, @time_entry) do |f| %> + <%= render :partial => 'form', :locals => {:f => f} %> + <%= submit_tag l(:button_save) %> +<% end %> \ No newline at end of file diff --git a/app/views/timelog/edit.rhtml b/app/views/timelog/edit.rhtml deleted file mode 100644 index a9a2e74..0000000 --- a/app/views/timelog/edit.rhtml +++ /dev/null @@ -1,57 +0,0 @@ -<% - # TODO concettualmente questi metodi sono helper e andrebbero quindi definiti - # nel file helper - - # Aggiunge uno "0" davanti al numero passato se è minore di 10 - def hours_min_string(number) - str = number.to_s - str = "0#{number}" if number < 10 - str - end - - # Restituisce un array di interi da 0 a 55 andando di 5 in 5 - def minutes - mins = Array.new - (0..3).each { |i| mins << 15 * i} - mins - end -%> - -

<%= l(:label_spent_time) %>

- -<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :id => @time_entry, :project_id => @time_entry.project} do |f| %> - <%= error_messages_for 'time_entry' %> - <%= back_url_hidden_field_tag %> - -
-

<%= f.text_field :issue_id, :size => 6 %> <%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %>

-

<%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %>

-

<%= f.text_field :hours, :size => 6, :required => true %>

-

<%= f.text_field :comments, :size => 100 %>

-

<%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %>

- <% @time_entry.custom_field_values.each do |value| %> -

<%= custom_field_tag_with_label :time_entry, value %>

- <% end %> - - <%# CPRS-MO %> - -
- <%= render :partial => 'common/custom_fields', :locals => {:time_entry => f} %> -
- - <% - # FIXME - # questa vista sovrascrive una vista esistente e si trova in un plugin: - # lo hook non può essere chiamato da qui. non si sa cosa succede se - # un altro componente usa lo hook (se questo è definito perché la vista - # è stata già sovrascritta, se invece non è definito oppure se avvengono - # situazioni inconsistenti). - # - # call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) - %> - - <%# ECPRS-MO %> -
- - <%= submit_tag l(:button_save) %> -<% end %> diff --git a/app/views/timelog/new.html.erb b/app/views/timelog/new.html.erb new file mode 100644 index 0000000..95253e8 --- /dev/null +++ b/app/views/timelog/new.html.erb @@ -0,0 +1,8 @@ +

<%= l(:label_spent_time) %>

+ +<%= labelled_form_for @time_entry, :url => time_entries_path do |f| %> + <%= hidden_field_tag 'project_id', params[:project_id] if params[:project_id] %> + <%= render :partial => 'form', :locals => {:f => f} %> + <%= submit_tag l(:button_create) %> + <%= submit_tag l(:button_create_and_continue), :name => 'continue' %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index f782d68..dfdb618 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,6 @@ -ActionController::Routing::Routes.draw do |map| - - map.connect '/custom_timesheet_controller', :controller => 'custom_timesheets', :action => 'index' - - map.connect 'custom_timesheet/report.:format', :controller => 'custom_timesheets', :action => 'report' - map.connect 'custom_timesheet/reset', :controller => 'custom_timesheets', :action => 'reset', :conditions => { :method => :delete } +RedmineApp::Application.routes.draw do + match 'custom_timesheet_controller' => 'custom_timesheets#index' + match 'custom_timesheet/report(.:format)' => 'custom_timesheets#report' + match 'custom_timesheet/reset' => 'custom_timesheets#reset', :via => :delete + match 'custom_timesheet/context_menu' => 'custom_timesheets#context_menu' end \ No newline at end of file diff --git a/init.rb b/init.rb index 6e55d71..1d9315b 100644 --- a/init.rb +++ b/init.rb @@ -1,61 +1,59 @@ -require 'redmine' - -# Taken from lib/redmine.rb -if RUBY_VERSION < '1.9' - require 'faster_csv' -else - require 'csv' - FCSV = CSV -end - -require 'dispatcher' -Dispatcher.to_prepare :redmine_timesheet_extensions do - # Needed for the compatibility check - begin - require_dependency 'time_entry_activity' - rescue LoadError - # TimeEntryActivity is not available - end -end - - - - - -# TODO: tradurre stringhe in inglese -Redmine::Plugin.register :redmine_timesheet_extensions do - name 'Timesheet Extensions Plugin' - author 'Nicola Baisero' - description 'Comprende una serie di estensioni per la gestione del tempo impiegato su attività e progetti.' - version '0.1.0' - - requires_redmine :version_or_higher => '0.9.3' - - - # Impostazioni timesheet plugin modificato - - settings :default => {'list_size' => '5', 'precision' => '2'}, :partial => 'settings/timesheet_settings' - permission :see_project_timesheets, { }, :require => :member - - menu(:top_menu, - :timesheet, - {:controller => 'custom_timesheet_controller', :action => 'index'}, - :caption => :timesheet_title, - :if => Proc.new { - User.current.allowed_to?(:see_project_timesheets, nil, :global => true) || - User.current.allowed_to?(:view_time_entries, nil, :global => true) || - User.current.admin? - }) -end - - - - -# Hooks (attualmente non ce ne sono) - -# Aggiunta comportamento ai modelli esistenti (Patch) -require 'dispatcher' -require 'time_entry_patch' -Dispatcher.to_prepare do - TimeEntry.send(:include, TimeEntryPatch) unless TimeEntry.included_modules.include?(TimeEntryPatch) -end \ No newline at end of file +require 'redmine' + +# Taken from lib/redmine.rb +if RUBY_VERSION < '1.9' + require 'faster_csv' +else + require 'csv' + FCSV = CSV +end + +Rails.configuration.to_prepare do + # Needed for the compatibility check + begin + require_dependency 'time_entry_activity' + rescue LoadError + # TimeEntryActivity is not available + end +end + + + + + +# TODO: tradurre stringhe in inglese +Redmine::Plugin.register :redmine_timesheet_extensions do + name 'Timesheet Extensions Plugin' + author 'Nicola Baisero' + description 'Comprende una serie di estensioni per la gestione del tempo impiegato su attività e progetti.' + version '0.1.0' + + requires_redmine :version => '2.2.1' + + + # Impostazioni timesheet plugin modificato + + settings :default => {'list_size' => '5', 'precision' => '2'}, :partial => 'settings/timesheet_settings' + permission :see_project_timesheets, { }, :require => :member + + menu(:top_menu, + :timesheet, + {:controller => 'custom_timesheets', :action => 'index'}, + :caption => :timesheet_title, + :if => Proc.new { + User.current.allowed_to?(:see_project_timesheets, nil, :global => true) || + User.current.allowed_to?(:view_time_entries, nil, :global => true) || + User.current.admin? + }) +end + + + + +# Hooks (attualmente non ce ne sono) + +# Aggiunta comportamento ai modelli esistenti (Patch) +require 'time_entry_patch' +Rails.configuration.to_prepare do + TimeEntry.send(:include, TimeEntryPatch) unless TimeEntry.included_modules.include?(TimeEntryPatch) +end diff --git a/lib/test/functional/custom_timesheets_controller_test.rb b/lib/test/functional/custom_timesheets_controller_test.rb index 840ed37..69b926b 100644 --- a/lib/test/functional/custom_timesheets_controller_test.rb +++ b/lib/test/functional/custom_timesheets_controller_test.rb @@ -1,3 +1,5 @@ +require File.expand_path('../../test_helper', __FILE__) + class CustomTimesheetsControllerTest < ActionController::TestCase test "the truth" do assert true diff --git a/lib/test/functional/routing_test.rb b/lib/test/functional/routing_test.rb new file mode 100644 index 0000000..630b6ae --- /dev/null +++ b/lib/test/functional/routing_test.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../test_helper', __FILE__) + +class RoutingTest < ActionController::TestCase + test "route to index" do + assert_routing 'custom_timesheet_controller', { :controller => "custom_timesheets", :action => "index" } + end + test "route to report with format" do + assert_routing 'custom_timesheet/report.csv', { :controller => "custom_timesheets", :action => 'report', :format => 'csv' } + end + test "route to report" do + assert_routing 'custom_timesheet/report', { :controller => "custom_timesheets", :action => 'report' } + end + + test "generates report with format" do + assert_generates 'custom_timesheet/report.csv', {:controller => "custom_timesheets", :action => "report", :format => 'csv'} + end + test "generates report" do + assert_generates 'custom_timesheet/report', {:controller => "custom_timesheets", :action => "report"} + end + + + + + test "recognizes timelog edit" do + # edit_time_entry GET;;;/time_entries/:id/edit(.:format);;;timelog#edit +# assert_recognizes({ :controller => "timelog", :action => "edit", :id => "1", :project_id => nil }, "/time_entries/1/edit") + assert_routing({:path => 'time_entries', :method => :get}, { :controller => "timelog", :action => "edit", :id => "1", :project_id => nil }) + end +end \ No newline at end of file diff --git a/lib/test/test_helper.rb b/lib/test/test_helper.rb index bd1ed0c..3344b99 100644 --- a/lib/test/test_helper.rb +++ b/lib/test/test_helper.rb @@ -1,5 +1,2 @@ # Load the normal Rails helper -require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper') - -# Ensure that we are using the temporary fixture path -Engines::Testing.set_fixture_path +require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper') \ No newline at end of file diff --git a/lib/time_entry_patch.rb b/lib/time_entry_patch.rb index 358c561..302b4dd 100644 --- a/lib/time_entry_patch.rb +++ b/lib/time_entry_patch.rb @@ -4,8 +4,6 @@ =end require_dependency 'time_entry' - - module TimeEntryPatch def self.included(base) base.extend ClassMethods @@ -20,7 +18,9 @@ def self.included(base) before_save :set_time_and_duration validates_numericality_of :billed_hours, :allow_nil => true - + + add_custom_self_attributes 'start_hour', 'start_minute', 'end_hour', 'end_minute', 'billed_hours' + # I getter per l'ora di inizio devo metterli per forza qui, né nel modulo # TimeEntryPatch né nel modulo InstanceMethods funzionano correttamente, # nemmeno con il modificatore public. @@ -34,6 +34,7 @@ def start_minute return @start_minute unless @start_minute.blank? return start_time.min unless start_time.blank? end + end end @@ -80,7 +81,7 @@ def time_validations def set_time_and_duration #logger.info "set_time_and_duration" - self.start_time = Time.gm spent_on.year, spent_on.month, spent_on.day, start_hour, start_minute, 0 + self.start_time = Time.local spent_on.year, spent_on.month, spent_on.day, start_hour, start_minute, 0 if !@end_hour.blank? && !@end_minute.blank? && @end_time > @begin_time logger.info "Start & end time: " + @begin_time.to_s + " " + @end_time.to_s @@ -111,5 +112,8 @@ def minutes_to_float_hours(mins) end module ClassMethods + def add_custom_self_attributes(*args) + args.each {|arg| safe_attributes[0][0] << arg} + end end end