2007-06-24 20:07:06 +04:00
# redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2007-03-23 15:22:31 +03:00
class TimelogController < ApplicationController
2008-01-19 14:53:43 +03:00
menu_item :issues
2008-08-31 20:34:54 +04:00
before_filter :find_project , :authorize , :only = > [ :edit , :destroy ]
before_filter :find_optional_project , :only = > [ :report , :details ]
2007-03-23 15:22:31 +03:00
2008-03-15 00:17:09 +03:00
verify :method = > :post , :only = > :destroy , :redirect_to = > { :action = > :details }
2007-03-23 15:22:31 +03:00
helper :sort
include SortHelper
2007-10-03 21:20:04 +04:00
helper :issues
2008-02-26 21:15:58 +03:00
include TimelogHelper
2008-04-09 21:45:39 +04:00
helper :custom_fields
include CustomFieldsHelper
2007-03-23 15:22:31 +03:00
2007-06-24 20:07:06 +04:00
def report
2008-02-27 23:50:19 +03:00
@available_criterias = { 'project' = > { :sql = > " #{ TimeEntry . table_name } .project_id " ,
:klass = > Project ,
:label = > :label_project } ,
'version' = > { :sql = > " #{ Issue . table_name } .fixed_version_id " ,
:klass = > Version ,
2007-06-24 20:07:06 +04:00
:label = > :label_version } ,
'category' = > { :sql = > " #{ Issue . table_name } .category_id " ,
2008-02-27 23:50:19 +03:00
:klass = > IssueCategory ,
2007-06-24 20:07:06 +04:00
:label = > :field_category } ,
'member' = > { :sql = > " #{ TimeEntry . table_name } .user_id " ,
2008-02-27 23:50:19 +03:00
:klass = > User ,
2007-06-24 20:07:06 +04:00
:label = > :label_member } ,
'tracker' = > { :sql = > " #{ Issue . table_name } .tracker_id " ,
2008-02-27 23:50:19 +03:00
:klass = > Tracker ,
2007-06-24 20:07:06 +04:00
:label = > :label_tracker } ,
'activity' = > { :sql = > " #{ TimeEntry . table_name } .activity_id " ,
2009-05-31 03:30:36 +04:00
:klass = > TimeEntryActivity ,
2008-04-02 02:42:10 +04:00
:label = > :label_activity } ,
'issue' = > { :sql = > " #{ TimeEntry . table_name } .issue_id " ,
:klass = > Issue ,
:label = > :label_issue }
2007-06-24 20:07:06 +04:00
}
2008-04-09 21:45:39 +04:00
# Add list and boolean custom fields as available criterias
2008-08-31 20:34:54 +04:00
custom_fields = ( @project . nil? ? IssueCustomField . for_all : @project . all_issue_custom_fields )
custom_fields . select { | cf | %w( list bool ) . include? cf . field_format } . each do | cf |
2008-07-22 23:07:43 +04:00
@available_criterias [ " cf_ #{ cf . id } " ] = { :sql = > " (SELECT c.value FROM #{ CustomValue . table_name } c WHERE c.custom_field_id = #{ cf . id } AND c.customized_type = 'Issue' AND c.customized_id = #{ Issue . table_name } .id) " ,
2008-04-09 21:45:39 +04:00
:format = > cf . field_format ,
:label = > cf . name }
2008-08-31 20:34:54 +04:00
end if @project
2008-04-09 21:45:39 +04:00
2008-07-22 23:06:13 +04:00
# Add list and boolean time entry custom fields
TimeEntryCustomField . find ( :all ) . select { | cf | %w( list bool ) . include? cf . field_format } . each do | cf |
2008-07-22 23:07:43 +04:00
@available_criterias [ " cf_ #{ cf . id } " ] = { :sql = > " (SELECT c.value FROM #{ CustomValue . table_name } c WHERE c.custom_field_id = #{ cf . id } AND c.customized_type = 'TimeEntry' AND c.customized_id = #{ TimeEntry . table_name } .id) " ,
2008-07-22 23:06:13 +04:00
:format = > cf . field_format ,
:label = > cf . name }
end
2009-10-22 02:34:57 +04:00
# Add list and boolean time entry activity custom fields
TimeEntryActivityCustomField . find ( :all ) . select { | cf | %w( list bool ) . include? cf . field_format } . each do | cf |
@available_criterias [ " cf_ #{ cf . id } " ] = { :sql = > " (SELECT c.value FROM #{ CustomValue . table_name } c WHERE c.custom_field_id = #{ cf . id } AND c.customized_type = 'Enumeration' AND c.customized_id = #{ TimeEntry . table_name } .activity_id) " ,
:format = > cf . field_format ,
:label = > cf . name }
end
2007-06-24 20:07:06 +04:00
@criterias = params [ :criterias ] || [ ]
@criterias = @criterias . select { | criteria | @available_criterias . has_key? criteria }
@criterias . uniq!
2008-02-27 23:50:19 +03:00
@criterias = @criterias [ 0 , 3 ]
2007-06-24 20:07:06 +04:00
2008-04-05 20:40:26 +04:00
@columns = ( params [ :columns ] && %w( year month week day ) . include? ( params [ :columns ] ) ) ? params [ :columns ] : 'month'
2007-06-24 20:07:06 +04:00
2008-04-01 23:40:40 +04:00
retrieve_date_range
2007-06-24 20:07:06 +04:00
unless @criterias . empty?
sql_select = @criterias . collect { | criteria | @available_criterias [ criteria ] [ :sql ] + " AS " + criteria } . join ( ', ' )
sql_group_by = @criterias . collect { | criteria | @available_criterias [ criteria ] [ :sql ] } . join ( ', ' )
2009-09-20 13:30:41 +04:00
sql_condition = ''
2007-06-24 20:07:06 +04:00
2009-09-20 13:30:41 +04:00
if @project . nil?
sql_condition = Project . allowed_to_condition ( User . current , :view_time_entries )
elsif @issue . nil?
sql_condition = @project . project_condition ( Setting . display_subprojects_issues? )
else
2010-03-13 17:56:49 +03:00
sql_condition = " #{ Issue . table_name } .root_id = #{ @issue . root_id } AND #{ Issue . table_name } .lft >= #{ @issue . lft } AND #{ Issue . table_name } .rgt <= #{ @issue . rgt } "
2009-09-20 13:30:41 +04:00
end
2008-04-05 20:40:26 +04:00
sql = " SELECT #{ sql_select } , tyear, tmonth, tweek, spent_on, SUM(hours) AS hours "
2008-02-27 23:50:19 +03:00
sql << " FROM #{ TimeEntry . table_name } "
sql << " LEFT JOIN #{ Issue . table_name } ON #{ TimeEntry . table_name } .issue_id = #{ Issue . table_name } .id "
sql << " LEFT JOIN #{ Project . table_name } ON #{ TimeEntry . table_name } .project_id = #{ Project . table_name } .id "
2008-08-31 20:34:54 +04:00
sql << " WHERE "
2009-09-20 13:30:41 +04:00
sql << " (%s) AND " % sql_condition
2009-11-04 20:35:20 +03:00
sql << " (spent_on BETWEEN '%s' AND '%s') " % [ ActiveRecord :: Base . connection . quoted_date ( @from ) , ActiveRecord :: Base . connection . quoted_date ( @to ) ]
2008-04-05 20:40:26 +04:00
sql << " GROUP BY #{ sql_group_by } , tyear, tmonth, tweek, spent_on "
2007-06-24 20:07:06 +04:00
@hours = ActiveRecord :: Base . connection . select_all ( sql )
@hours . each do | row |
case @columns
when 'year'
row [ 'year' ] = row [ 'tyear' ]
when 'month'
row [ 'month' ] = " #{ row [ 'tyear' ] } - #{ row [ 'tmonth' ] } "
when 'week'
row [ 'week' ] = " #{ row [ 'tyear' ] } - #{ row [ 'tweek' ] } "
2008-04-05 20:40:26 +04:00
when 'day'
row [ 'day' ] = " #{ row [ 'spent_on' ] } "
2007-06-24 20:07:06 +04:00
end
end
2008-02-27 23:50:19 +03:00
@total_hours = @hours . inject ( 0 ) { | s , k | s = s + k [ 'hours' ] . to_f }
2008-04-01 23:40:40 +04:00
@periods = [ ]
# Date#at_beginning_of_ not supported in Rails 1.2.x
date_from = @from . to_time
# 100 columns max
while date_from < = @to . to_time && @periods . length < 100
case @columns
when 'year'
@periods << " #{ date_from . year } "
date_from = ( date_from + 1 . year ) . at_beginning_of_year
when 'month'
@periods << " #{ date_from . year } - #{ date_from . month } "
date_from = ( date_from + 1 . month ) . at_beginning_of_month
when 'week'
@periods << " #{ date_from . year } - #{ date_from . to_date . cweek } "
date_from = ( date_from + 7 . day ) . at_beginning_of_week
2008-04-05 20:40:26 +04:00
when 'day'
@periods << " #{ date_from . to_date } "
date_from = date_from + 1 . day
2008-04-01 23:40:40 +04:00
end
2007-06-24 20:07:06 +04:00
end
end
2008-04-07 21:18:09 +04:00
respond_to do | format |
format . html { render :layout = > ! request . xhr? }
2009-11-04 16:22:26 +03:00
format . csv { send_data ( report_to_csv ( @criterias , @periods , @hours ) , :type = > 'text/csv; header=present' , :filename = > 'timelog.csv' ) }
2008-04-07 21:18:09 +04:00
end
2007-06-24 20:07:06 +04:00
end
2007-03-23 15:22:31 +03:00
def details
sort_init 'spent_on' , 'desc'
2008-12-24 13:03:13 +03:00
sort_update 'spent_on' = > 'spent_on' ,
'user' = > 'user_id' ,
'activity' = > 'activity_id' ,
'project' = > " #{ Project . table_name } .name " ,
'issue' = > 'issue_id' ,
'hours' = > 'hours'
2007-03-23 15:22:31 +03:00
2008-02-27 23:50:19 +03:00
cond = ARCondition . new
2008-08-31 20:34:54 +04:00
if @project . nil?
cond << Project . allowed_to_condition ( User . current , :view_time_entries )
elsif @issue . nil?
cond << @project . project_condition ( Setting . display_subprojects_issues? )
else
2010-03-13 17:56:49 +03:00
cond << " #{ Issue . table_name } .root_id = #{ @issue . root_id } AND #{ Issue . table_name } .lft >= #{ @issue . lft } AND #{ Issue . table_name } .rgt <= #{ @issue . rgt } "
2008-08-31 20:34:54 +04:00
end
2008-02-27 23:50:19 +03:00
2008-04-01 23:40:40 +04:00
retrieve_date_range
2008-04-05 20:40:26 +04:00
cond << [ 'spent_on BETWEEN ? AND ?' , @from , @to ]
2008-02-26 21:15:58 +03:00
2008-02-27 23:50:19 +03:00
TimeEntry . visible_by ( User . current ) do
respond_to do | format |
format . html {
# Paginate results
2010-03-13 17:56:49 +03:00
@entry_count = TimeEntry . count ( :include = > [ :project , :issue ] , :conditions = > cond . conditions )
2008-02-27 23:50:19 +03:00
@entry_pages = Paginator . new self , @entry_count , per_page_option , params [ 'page' ]
@entries = TimeEntry . find ( :all ,
:include = > [ :project , :activity , :user , { :issue = > :tracker } ] ,
:conditions = > cond . conditions ,
:order = > sort_clause ,
:limit = > @entry_pages . items_per_page ,
:offset = > @entry_pages . current . offset )
2010-03-13 17:56:49 +03:00
@total_hours = TimeEntry . sum ( :hours , :include = > [ :project , :issue ] , :conditions = > cond . conditions ) . to_f
2008-04-05 20:40:26 +04:00
2008-02-27 23:50:19 +03:00
render :layout = > ! request . xhr?
}
2008-06-18 00:01:15 +04:00
format . atom {
entries = TimeEntry . find ( :all ,
:include = > [ :project , :activity , :user , { :issue = > :tracker } ] ,
:conditions = > cond . conditions ,
:order = > " #{ TimeEntry . table_name } .created_on DESC " ,
:limit = > Setting . feeds_limit . to_i )
render_feed ( entries , :title = > l ( :label_spent_time ) )
}
2008-02-27 23:50:19 +03:00
format . csv {
# Export all entries
@entries = TimeEntry . find ( :all ,
:include = > [ :project , :activity , :user , { :issue = > [ :tracker , :assigned_to , :priority ] } ] ,
:conditions = > cond . conditions ,
:order = > sort_clause )
2009-11-04 16:22:26 +03:00
send_data ( entries_to_csv ( @entries ) , :type = > 'text/csv; header=present' , :filename = > 'timelog.csv' )
2008-02-27 23:50:19 +03:00
}
end
2008-02-26 21:15:58 +03:00
end
2007-03-23 15:22:31 +03:00
end
def edit
2009-12-18 17:22:18 +03:00
( render_403 ; return ) if @time_entry && ! @time_entry . editable_by? ( User . current )
2009-11-06 22:41:03 +03:00
@time_entry || = TimeEntry . new ( :project = > @project , :issue = > @issue , :user = > User . current , :spent_on = > User . current . today )
2007-03-23 15:22:31 +03:00
@time_entry . attributes = params [ :time_entry ]
2009-05-31 02:49:39 +04:00
call_hook ( :controller_timelog_edit_before_save , { :params = > params , :time_entry = > @time_entry } )
2007-03-23 15:22:31 +03:00
if request . post? and @time_entry . save
flash [ :notice ] = l ( :notice_successful_update )
2008-09-21 16:45:22 +04:00
redirect_back_or_default :action = > 'details' , :project_id = > @time_entry . project
2007-03-23 15:22:31 +03:00
return
end
end
2008-03-15 00:17:09 +03:00
def destroy
2009-12-18 17:22:18 +03:00
( render_404 ; return ) unless @time_entry
( render_403 ; return ) unless @time_entry . editable_by? ( User . current )
2010-06-20 23:30:51 +04:00
if @time_entry . destroy && @time_entry . destroyed?
flash [ :notice ] = l ( :notice_successful_delete )
else
flash [ :error ] = l ( :notice_unable_delete_time_entry )
end
2008-03-16 15:21:54 +03:00
redirect_to :back
2008-08-31 20:34:54 +04:00
rescue :: ActionController :: RedirectBackError
2008-03-15 00:17:09 +03:00
redirect_to :action = > 'details' , :project_id = > @time_entry . project
end
2007-03-23 15:22:31 +03:00
private
def find_project
if params [ :id ]
@time_entry = TimeEntry . find ( params [ :id ] )
@project = @time_entry . project
elsif params [ :issue_id ]
@issue = Issue . find ( params [ :issue_id ] )
@project = @issue . project
elsif params [ :project_id ]
@project = Project . find ( params [ :project_id ] )
else
render_404
return false
end
2008-03-15 00:17:09 +03:00
rescue ActiveRecord :: RecordNotFound
render_404
2007-03-23 15:22:31 +03:00
end
2008-04-01 23:40:40 +04:00
2008-08-31 20:34:54 +04:00
def find_optional_project
if ! params [ :issue_id ] . blank?
@issue = Issue . find ( params [ :issue_id ] )
@project = @issue . project
elsif ! params [ :project_id ] . blank?
@project = Project . find ( params [ :project_id ] )
end
deny_access unless User . current . allowed_to? ( :view_time_entries , @project , :global = > true )
end
2008-04-02 02:42:10 +04:00
# Retrieves the date range based on predefined ranges or specific from/to param dates
2008-04-01 23:40:40 +04:00
def retrieve_date_range
@free_period = false
@from , @to = nil , nil
if params [ :period_type ] == '1' || ( params [ :period_type ] . nil? && ! params [ :period ] . nil? )
case params [ :period ] . to_s
when 'today'
@from = @to = Date . today
when 'yesterday'
@from = @to = Date . today - 1
when 'current_week'
@from = Date . today - ( Date . today . cwday - 1 ) % 7
@to = @from + 6
when 'last_week'
@from = Date . today - 7 - ( Date . today . cwday - 1 ) % 7
@to = @from + 6
when '7_days'
@from = Date . today - 7
@to = Date . today
when 'current_month'
@from = Date . civil ( Date . today . year , Date . today . month , 1 )
@to = ( @from >> 1 ) - 1
when 'last_month'
@from = Date . civil ( Date . today . year , Date . today . month , 1 ) << 1
@to = ( @from >> 1 ) - 1
when '30_days'
@from = Date . today - 30
@to = Date . today
when 'current_year'
@from = Date . civil ( Date . today . year , 1 , 1 )
@to = Date . civil ( Date . today . year , 12 , 31 )
end
elsif params [ :period_type ] == '2' || ( params [ :period_type ] . nil? && ( ! params [ :from ] . nil? || ! params [ :to ] . nil? ) )
begin ; @from = params [ :from ] . to_s . to_date unless params [ :from ] . blank? ; rescue ; end
begin ; @to = params [ :to ] . to_s . to_date unless params [ :to ] . blank? ; rescue ; end
@free_period = true
else
# default
end
@from , @to = @to , @from if @from && @to && @from > @to
2008-08-31 20:34:54 +04:00
@from || = ( TimeEntry . minimum ( :spent_on , :include = > :project , :conditions = > Project . allowed_to_condition ( User . current , :view_time_entries ) ) || Date . today ) - 1
@to || = ( TimeEntry . maximum ( :spent_on , :include = > :project , :conditions = > Project . allowed_to_condition ( User . current , :view_time_entries ) ) || Date . today )
2008-04-01 23:40:40 +04:00
end
2007-03-23 15:22:31 +03:00
end