From 604548bb2f82bd2d8239f5767976764960f9c9aa Mon Sep 17 00:00:00 2001 From: Nicola Baisero Date: Sun, 15 Jul 2012 17:41:33 +0200 Subject: [PATCH 1/8] Redmine 2.0 compatibility --- .gitignore | 3 + Gemfile | 4 + Gemfile_hidden | 4 + Rakefile | 2 + .../custom_timesheets/no_projects.html.erb | 5 ++ app/views/issues/_edit.html.erb | 81 +++++++++++++++++ .../settings/_timesheet_settings.html.erb | 4 + app/views/timelog/_form.html.erb | 58 ++++++++++++ app/views/timelog/_list.html.erb | 90 +++++++++++++++++++ app/views/timelog/edit.html.erb | 6 ++ app/views/timelog/new.html.erb | 8 ++ init.rb | 4 +- lib/redmine_timesheet_extensions.rb | 3 + lib/redmine_timesheet_extensions/version.rb | 3 + .../custom_timesheets_controller_test.rb | 2 + lib/test/functional/routing_test.rb | 29 ++++++ redmine_timesheet_extensions.gemspec | 21 +++++ 17 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile_hidden create mode 100644 Rakefile create mode 100644 app/views/custom_timesheets/no_projects.html.erb create mode 100644 app/views/issues/_edit.html.erb create mode 100644 app/views/settings/_timesheet_settings.html.erb create mode 100644 app/views/timelog/_form.html.erb create mode 100644 app/views/timelog/_list.html.erb create mode 100644 app/views/timelog/edit.html.erb create mode 100644 app/views/timelog/new.html.erb create mode 100644 lib/redmine_timesheet_extensions.rb create mode 100644 lib/redmine_timesheet_extensions/version.rb create mode 100644 lib/test/functional/routing_test.rb create mode 100644 redmine_timesheet_extensions.gemspec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f30a35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +pkg/* +*.gem +.bundle diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..df3c3c0 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +# Specify your gem's dependencies in redmine_timesheet_extensions.gemspec +gemspec diff --git a/Gemfile_hidden b/Gemfile_hidden new file mode 100644 index 0000000..7de5e0d --- /dev/null +++ b/Gemfile_hidden @@ -0,0 +1,4 @@ +# Gemfile +source "http://rubygems.org" +#gem "dispatcher", "~> 0.0.1" +gemspec diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..14cfe0b --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +require 'bundler' +Bundler::GemHelper.install_tasks diff --git a/app/views/custom_timesheets/no_projects.html.erb b/app/views/custom_timesheets/no_projects.html.erb new file mode 100644 index 0000000..7635ec2 --- /dev/null +++ b/app/views/custom_timesheets/no_projects.html.erb @@ -0,0 +1,5 @@ +

<%= l(:timesheet_title)%>

+ +

+ <%= l(:label_no_data) %> +

diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb new file mode 100644 index 0000000..0cb18b1 --- /dev/null +++ b/app/views/issues/_edit.html.erb @@ -0,0 +1,81 @@ +<% +=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_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) %> + <%= 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', :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) %> + <%= link_to_remote l(:label_preview), + { :url => preview_edit_issue_path(: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/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.html.erb b/app/views/timelog/_list.html.erb new file mode 100644 index 0000000..947dbe9 --- /dev/null +++ b/app/views/timelog/_list.html.erb @@ -0,0 +1,90 @@ +<%= 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)) %> +<%= sort_header_tag('project', :caption => l(:label_project)) %> +<%= 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 + def hours_min_string(number) + str = number.to_s + str = "0#{number}" if number < 10 + str + end +%> + + + +<%= sort_header_tag('hours', :caption => l(:field_hours)) %> + + + + + + + + +<% entries.each do |entry| -%> + hascontextmenu"> + + + + + + + + + + + + + + + + + + + +<% 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) %><%= l(:field_billed_hours) %>
<%= check_box_tag("ids[]", entry.id, false, :id => nil) %><%= format_date(entry.spent_on) %><%= 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 -%> +<%= entry.comments %> +<% if entry.start_time.blank? %> +- +<% else %> +<%= hours_min_string entry.start_time.hour -%>:<%= hours_min_string entry.start_time.min -%> +<% end %> +<%= html_hours("%.2f" % entry.hours) %> + <%= entry.billed_hours.blank? ? "-" : entry.billed_hours %> + +<% if entry.editable_by?(User.current) -%> + <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry, :project_id => nil}, + :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 => :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/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/init.rb b/init.rb index 6e55d71..5275a51 100644 --- a/init.rb +++ b/init.rb @@ -29,7 +29,7 @@ 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' + requires_redmine :version_or_higher => '2.0.3' # Impostazioni timesheet plugin modificato @@ -58,4 +58,4 @@ 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 +end diff --git a/lib/redmine_timesheet_extensions.rb b/lib/redmine_timesheet_extensions.rb new file mode 100644 index 0000000..e34ff0d --- /dev/null +++ b/lib/redmine_timesheet_extensions.rb @@ -0,0 +1,3 @@ +module RedmineTimesheetExtensions + # Your code goes here... +end diff --git a/lib/redmine_timesheet_extensions/version.rb b/lib/redmine_timesheet_extensions/version.rb new file mode 100644 index 0000000..946b0ba --- /dev/null +++ b/lib/redmine_timesheet_extensions/version.rb @@ -0,0 +1,3 @@ +module RedmineTimesheetExtensions + VERSION = "0.0.1" +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/redmine_timesheet_extensions.gemspec b/redmine_timesheet_extensions.gemspec new file mode 100644 index 0000000..08badac --- /dev/null +++ b/redmine_timesheet_extensions.gemspec @@ -0,0 +1,21 @@ +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) +require "redmine_timesheet_extensions/version" + +Gem::Specification.new do |s| + s.name = "redmine_timesheet_extensions" + s.version = RedmineTimesheetExtensions::VERSION + s.platform = Gem::Platform::RUBY + s.authors = ["TODO: Write your name"] + s.email = ["TODO: Write your email address"] + s.homepage = "" + s.summary = %q{TODO: Write a gem summary} + s.description = %q{TODO: Write a gem description} + + s.rubyforge_project = "redmine_timesheet_extensions" + + s.files = `git ls-files`.split("\n") + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + s.require_paths = ["lib"] +end From 2f65c76305f33e9519b86455b362ccd81af188cc Mon Sep 17 00:00:00 2001 From: Nicola Baisero Date: Sun, 15 Jul 2012 17:42:35 +0200 Subject: [PATCH 2/8] Redmine 2.0 compatibility --- .gitignore | 3 - Gemfile | 4 - Gemfile_hidden | 4 - Rakefile | 2 - .../custom_timesheets_controller.rb | 4 +- app/helpers/custom_timesheets_helper.rb | 4 +- app/models/custom_timesheet.rb | 15 ++-- app/views/custom_timesheets/_form.html.erb | 51 +++++------ app/views/custom_timesheets/no_projects.rhtml | 5 -- app/views/custom_timesheets/report.html.erb | 50 +++++------ app/views/issues/_edit.rhtml | 85 ------------------- app/views/settings/_timesheet_settings.rhtml | 4 - app/views/timelog/_list.rhtml | 79 ----------------- app/views/timelog/edit.rhtml | 57 ------------- config/routes.rb | 11 ++- init.rb | 10 +-- lib/redmine_timesheet_extensions.rb | 3 - lib/redmine_timesheet_extensions/version.rb | 3 - lib/test/test_helper.rb | 5 +- lib/time_entry_patch.rb | 10 ++- redmine_timesheet_extensions.gemspec | 21 ----- 21 files changed, 79 insertions(+), 351 deletions(-) delete mode 100644 .gitignore delete mode 100644 Gemfile delete mode 100644 Gemfile_hidden delete mode 100644 Rakefile delete mode 100644 app/views/custom_timesheets/no_projects.rhtml delete mode 100644 app/views/issues/_edit.rhtml delete mode 100644 app/views/settings/_timesheet_settings.rhtml delete mode 100644 app/views/timelog/_list.rhtml delete mode 100644 app/views/timelog/edit.rhtml delete mode 100644 lib/redmine_timesheet_extensions.rb delete mode 100644 lib/redmine_timesheet_extensions/version.rb delete mode 100644 redmine_timesheet_extensions.gemspec diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 9f30a35..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -pkg/* -*.gem -.bundle diff --git a/Gemfile b/Gemfile deleted file mode 100644 index df3c3c0..0000000 --- a/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source "http://rubygems.org" - -# Specify your gem's dependencies in redmine_timesheet_extensions.gemspec -gemspec diff --git a/Gemfile_hidden b/Gemfile_hidden deleted file mode 100644 index 7de5e0d..0000000 --- a/Gemfile_hidden +++ /dev/null @@ -1,4 +0,0 @@ -# Gemfile -source "http://rubygems.org" -#gem "dispatcher", "~> 0.0.1" -gemspec diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 14cfe0b..0000000 --- a/Rakefile +++ /dev/null @@ -1,2 +0,0 @@ -require 'bundler' -Bundler::GemHelper.install_tasks 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/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.rhtml deleted file mode 100644 index 7635ec2..0000000 --- a/app/views/custom_timesheets/no_projects.rhtml +++ /dev/null @@ -1,5 +0,0 @@ -

<%= l(:timesheet_title)%>

- -

- <%= l(:label_no_data) %> -

diff --git a/app/views/custom_timesheets/report.html.erb b/app/views/custom_timesheets/report.html.erb index 4edd791..ecf20f4 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' do check_box_tag('ids[]', time_entry.id, false, { :class => 'checkbox' }) end) %> + <%= raw(table_col true, 'spent_on' do format_date(time_entry.spent_on) end) %> + <%= raw(table_col(true, 'tweek') { time_entry.tweek.to_s }) %> + <%= raw(table_col true, 'user_id' do time_entry.user.name end) %> + <%= raw(table_col true, 'activity_id' do time_entry.activity.name end) %> + <%= raw(table_col true, 'project_id' do time_entry.project.name end) %> <% 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' do time_entry.comments end) %> + <%= raw(table_col true, 'hours' do number_with_precision(time_entry.hours, :precision => @precision) end) %> + <%= raw(table_col true, 'billed_hours' do '' + number_with_precision(time_entry.billed_hours, :precision => @precision) + '' unless time_entry.billed_hours.blank? end) %> <% if selected?('id') %> <% @timesheet.time_entries.each do |time_entry| %> hascontextmenu"> - <%= raw(table_col true, 'id' do check_box_tag('ids[]', time_entry.id, false, { :class => 'checkbox' }) end) %> - <%= raw(table_col true, 'spent_on' do format_date(time_entry.spent_on) 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' do time_entry.user.name end) %> - <%= raw(table_col true, 'activity_id' do time_entry.activity.name end) %> - <%= raw(table_col true, 'project_id' do time_entry.project.name end) %> + <%= 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 %> - <%= raw(table_col false, 'comments' do time_entry.comments end) %> - <%= raw(table_col true, 'hours' do number_with_precision(time_entry.hours, :precision => @precision) end) %> - <%= raw(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.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.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/_list.rhtml b/app/views/timelog/_list.rhtml deleted file mode 100644 index 9c764b8..0000000 --- a/app/views/timelog/_list.rhtml +++ /dev/null @@ -1,79 +0,0 @@ - - - -<%= 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)) %> -<%= sort_header_tag('project', :caption => l(:label_project)) %> -<%= 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 - def hours_min_string(number) - str = number.to_s - str = "0#{number}" if number < 10 - str - end -%> - - - - - -<%= sort_header_tag('hours', :caption => l(:field_hours)) %> - - - - - - - - -<% entries.each do |entry| -%> -"> - - - - - - - - - - - - - - - - - - -<% end -%> - -
<%= l(:field_comments) %><%= l(:field_start_hour) %><%= l(:field_billed_hours) %>
<%= format_date(entry.spent_on) %><%=h entry.user %><%=h entry.activity %><%=h entry.project %> -<% if entry.issue -%> -<%= entry.issue.visible? ? link_to_issue(entry.issue, :truncate => 50) : "##{entry.issue.id}" -%> -<% end -%> -<%=h entry.comments %> -<% if entry.start_time.blank? %> -- -<% else %> -<%= hours_min_string entry.start_time.hour -%>:<%= hours_min_string entry.start_time.min -%> -<% end %> -<%= html_hours("%.2f" % entry.hours) %> - <%= entry.billed_hours.blank? ? "-" : entry.billed_hours %> - -<% if entry.editable_by?(User.current) -%> - <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry, :project_id => nil}, - :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, - :title => l(:button_delete) %> -<% end -%> -
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/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 5275a51..20912ba 100644 --- a/init.rb +++ b/init.rb @@ -8,8 +8,7 @@ FCSV = CSV end -require 'dispatcher' -Dispatcher.to_prepare :redmine_timesheet_extensions do +Rails.configuration.to_prepare do # Needed for the compatibility check begin require_dependency 'time_entry_activity' @@ -29,7 +28,7 @@ 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 => '2.0.3' + requires_redmine :version => '2.0.3' # Impostazioni timesheet plugin modificato @@ -39,7 +38,7 @@ menu(:top_menu, :timesheet, - {:controller => 'custom_timesheet_controller', :action => 'index'}, + {:controller => 'custom_timesheets', :action => 'index'}, :caption => :timesheet_title, :if => Proc.new { User.current.allowed_to?(:see_project_timesheets, nil, :global => true) || @@ -54,8 +53,7 @@ # Hooks (attualmente non ce ne sono) # Aggiunta comportamento ai modelli esistenti (Patch) -require 'dispatcher' require 'time_entry_patch' -Dispatcher.to_prepare do +Rails.configuration.to_prepare do TimeEntry.send(:include, TimeEntryPatch) unless TimeEntry.included_modules.include?(TimeEntryPatch) end diff --git a/lib/redmine_timesheet_extensions.rb b/lib/redmine_timesheet_extensions.rb deleted file mode 100644 index e34ff0d..0000000 --- a/lib/redmine_timesheet_extensions.rb +++ /dev/null @@ -1,3 +0,0 @@ -module RedmineTimesheetExtensions - # Your code goes here... -end diff --git a/lib/redmine_timesheet_extensions/version.rb b/lib/redmine_timesheet_extensions/version.rb deleted file mode 100644 index 946b0ba..0000000 --- a/lib/redmine_timesheet_extensions/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module RedmineTimesheetExtensions - VERSION = "0.0.1" -end 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..276c6c7 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 @@ -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 diff --git a/redmine_timesheet_extensions.gemspec b/redmine_timesheet_extensions.gemspec deleted file mode 100644 index 08badac..0000000 --- a/redmine_timesheet_extensions.gemspec +++ /dev/null @@ -1,21 +0,0 @@ -# -*- encoding: utf-8 -*- -$:.push File.expand_path("../lib", __FILE__) -require "redmine_timesheet_extensions/version" - -Gem::Specification.new do |s| - s.name = "redmine_timesheet_extensions" - s.version = RedmineTimesheetExtensions::VERSION - s.platform = Gem::Platform::RUBY - s.authors = ["TODO: Write your name"] - s.email = ["TODO: Write your email address"] - s.homepage = "" - s.summary = %q{TODO: Write a gem summary} - s.description = %q{TODO: Write a gem description} - - s.rubyforge_project = "redmine_timesheet_extensions" - - s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } - s.require_paths = ["lib"] -end From cf54abb3cfea93586a354332b55aa104ada199ca Mon Sep 17 00:00:00 2001 From: Nicola Baisero Date: Thu, 27 Sep 2012 00:23:25 +0200 Subject: [PATCH 3/8] Risolto problema salvataggio orario di inizio: il tempo veniva sempre considerato un GMT e non nel timezone locale --- lib/time_entry_patch.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/time_entry_patch.rb b/lib/time_entry_patch.rb index 276c6c7..302b4dd 100644 --- a/lib/time_entry_patch.rb +++ b/lib/time_entry_patch.rb @@ -81,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 From 142667ea786b5769686f17a0ebea0a9e40749769 Mon Sep 17 00:00:00 2001 From: saadro Date: Sat, 26 Jan 2013 19:56:49 +1100 Subject: [PATCH 4/8] Redmine 2.2.1 compatibility Created branch to include mods that make the plugin compatible with Redmine v2.2.1. (My first commit on GH :)) --- app/views/custom_timesheets/report.html.erb | 16 +-- app/views/issues/_edit.html.erb | 20 ++-- init.rb | 118 ++++++++++---------- 3 files changed, 77 insertions(+), 77 deletions(-) diff --git a/app/views/custom_timesheets/report.html.erb b/app/views/custom_timesheets/report.html.erb index ecf20f4..ae0de40 100644 --- a/app/views/custom_timesheets/report.html.erb +++ b/app/views/custom_timesheets/report.html.erb @@ -32,12 +32,12 @@
<% if time_entry.issue %> @@ -50,9 +50,9 @@ <% end %> <% if time_entry.editable_by?(User.current) -%> diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb index 0cb18b1..dfc99b4 100644 --- a/app/views/issues/_edit.html.erb +++ b/app/views/issues/_edit.html.erb @@ -1,22 +1,22 @@ -<% +<% =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). +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 concettualmente questi metodi sono helper e andrebbero quindi definiti - # nel file helper + # TODO conceptually these methods are helpers and should therefore be defined + # in the file helper - # Aggiunge uno "0" davanti al numero passato se è minore di 10 + # 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 - # Restituisce un array di interi da 0 a 55 andando di 5 in 5 + # 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} @@ -69,13 +69,13 @@ XXX questa logica serve per vedere come integrare i nuovi campi della registrazi <%= f.hidden_field :lock_version %> <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %> <%= submit_tag l(:button_submit) %> - <%= link_to_remote l(:label_preview), + <%= link_to l(:label_preview), { :url => preview_edit_issue_path(:project_id => @project, :id => @issue), :method => 'post', :update => 'preview', :with => 'Form.serialize("issue-form")', :complete => "Element.scrollTo('preview')" - }, :accesskey => accesskey(:preview) %> + }, :accesskey => accesskey(:preview), :remote => true %> <% end %>
diff --git a/init.rb b/init.rb index 20912ba..1d9315b 100644 --- a/init.rb +++ b/init.rb @@ -1,59 +1,59 @@ -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.0.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_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 +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 From f3099229caefe4f00362b4a00dbe7f200e99b59b Mon Sep 17 00:00:00 2001 From: saadro Date: Sat, 26 Jan 2013 20:14:11 +1100 Subject: [PATCH 5/8] Fixed Internet Explorer issue Fixed hour calculation bug that occurred with certain versions of Internet Explorer --- app/views/common/_auto_completion.html.erb | 324 ++++++++++----------- 1 file changed, 158 insertions(+), 166 deletions(-) diff --git a/app/views/common/_auto_completion.html.erb b/app/views/common/_auto_completion.html.erb index 3e50373..37768d2 100644 --- a/app/views/common/_auto_completion.html.erb +++ b/app/views/common/_auto_completion.html.erb @@ -1,166 +1,158 @@ - -<% -=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 +%> + + + From 5b86d56e91cdb615ca84f5f08eff3a7a9e62a61d Mon Sep 17 00:00:00 2001 From: saadro Date: Sat, 26 Jan 2013 20:29:43 +1100 Subject: [PATCH 6/8] Allow users to enter hours only by defaulting start time Allow users to enter hours only by defaulting the start time --- app/views/common/_auto_completion.html.erb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/views/common/_auto_completion.html.erb b/app/views/common/_auto_completion.html.erb index 37768d2..6c56fbb 100644 --- a/app/views/common/_auto_completion.html.erb +++ b/app/views/common/_auto_completion.html.erb @@ -26,6 +26,20 @@ timeEntryStartMinute.onchange = TimeEntryStartAfterUpdate; timeEntryEndHour.onchange = TimeEntryEndAfterUpdate; timeEntryEndMinute.onchange = TimeEntryEndAfterUpdate; + + setDefaultStartTime(); + + /* + * Defaults the start time hours and minutes to 00:00. + */ + function setDefaultStartTime() { + var startHourIndex = document.getElementById('time_entry_start_hour').selectedIndex; + var startMinuteIndex = document.getElementById('time_entry_start_minute').selectedIndex; + if (startHourIndex == 0 || startMinuteIndex == 0) { + document.getElementById('time_entry_start_hour').selectedIndex = 1; + document.getElementById('time_entry_start_minute').selectedIndex = 1; + } + } /* * Funzione chiamata in seguito all'aggiornamento della casella delle ore. @@ -38,6 +52,10 @@ var startMinuteIndex = document.getElementById('time_entry_start_minute').selectedIndex; var endHourIndex = document.getElementById('time_entry_end_hour').selectedIndex; var endMinuteIndex = document.getElementById('time_entry_end_minute').selectedIndex; + + // if hours are set but start time is not, set default start time as this is a mandatory field + setDefaultStartTime(); + if (startHourIndex > 0 && startMinuteIndex > 0) { setEndTime(); } else if (endHourIndex > 0 && endMinuteIndex > 0) { From 1adf771a3dad3a41c8f64709ae46e311ab97c889 Mon Sep 17 00:00:00 2001 From: saadro Date: Sat, 26 Jan 2013 20:41:30 +1100 Subject: [PATCH 7/8] Introduced a Now button, minutes more accurate Introduced a Now button, also the minutes are now accurate to the minute --- app/views/common/_auto_completion.html.erb | 30 +++++++++++- app/views/common/_custom_fields.html.erb | 56 +++++++++++----------- 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/app/views/common/_auto_completion.html.erb b/app/views/common/_auto_completion.html.erb index 6c56fbb..d1f80b6 100644 --- a/app/views/common/_auto_completion.html.erb +++ b/app/views/common/_auto_completion.html.erb @@ -40,7 +40,35 @@ document.getElementById('time_entry_start_minute').selectedIndex = 1; } } - + + /* + * Set the time of these time controls to the current time + */ + function setNowTime(hourComboId, minuteComboId) + { + var currentTime = new Date(); + var hours = currentTime.getHours(); + var minutes = currentTime.getMinutes(); + var hourCombo = document.getElementById(hourComboId); + var minuteCombo = document.getElementById(minuteComboId); + for(var i = 0; i < hourCombo.options.length; i++) + { + if (hourCombo.options[i].value == hours) + { + hourCombo.selectedIndex = i; + break; + } + } + for(var i = 0; i < minuteCombo.options.length; i++) + { + if (minuteCombo.options[i].value == minutes) + { + minuteCombo.selectedIndex = i; + break; + } + } + } + /* * Funzione chiamata in seguito all'aggiornamento della casella delle ore. * Se l'ora di inizio è valorizzata imposta l'ora di fine aggiungendo il numero di ore specificato all'ora di inizio. 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 From d9093c3e482e1c4a42542880a4f2c44e6eebd781 Mon Sep 17 00:00:00 2001 From: Ross Saad Date: Wed, 13 Feb 2013 23:10:08 +1100 Subject: [PATCH 8/8] Redmine 2.2.1 compatibility (additional) Included additional changes to edit page that was preventing ticket Preview and Notes update. --- app/views/issues/_edit.html.erb | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb index dfc99b4..7d5938d 100644 --- a/app/views/issues/_edit.html.erb +++ b/app/views/issues/_edit.html.erb @@ -25,8 +25,6 @@ XXX This logic is to see how to integrate the new fields of the record %> <%= labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %> - - <%= error_messages_for 'issue', 'time_entry' %> <%= render :partial => 'conflict' if @conflict %>
@@ -58,24 +56,25 @@ XXX This logic is to see how to integrate the new fields of the record <% end %>
<%= l(:field_notes) %> - <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %> - <%= wikitoolbar_for '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} %>

+
+ +
<%= 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) %> - <%= link_to l(:label_preview), - { :url => preview_edit_issue_path(:project_id => @project, :id => @issue), - :method => 'post', - :update => 'preview', - :with => 'Form.serialize("issue-form")', - :complete => "Element.scrollTo('preview')" - }, :accesskey => accesskey(:preview), :remote => true %> + <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %> <% end %>