annotate app/controllers/.svn/text-base/timelog_controller.rb.svn-base @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children 1d32c0a0efbf
rev   line source
Chris@0 1 # redMine - project management software
Chris@0 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@0 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@0 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@0 18 class TimelogController < ApplicationController
Chris@0 19 menu_item :issues
Chris@0 20 before_filter :find_project, :authorize, :only => [:edit, :destroy]
Chris@0 21 before_filter :find_optional_project, :only => [:report, :details]
Chris@0 22 before_filter :load_available_criterias, :only => [:report]
Chris@0 23
Chris@0 24 verify :method => :post, :only => :destroy, :redirect_to => { :action => :details }
Chris@0 25
Chris@0 26 helper :sort
Chris@0 27 include SortHelper
Chris@0 28 helper :issues
Chris@0 29 include TimelogHelper
Chris@0 30 helper :custom_fields
Chris@0 31 include CustomFieldsHelper
Chris@0 32
Chris@0 33 def report
Chris@0 34 @criterias = params[:criterias] || []
Chris@0 35 @criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria}
Chris@0 36 @criterias.uniq!
Chris@0 37 @criterias = @criterias[0,3]
Chris@0 38
Chris@0 39 @columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month'
Chris@0 40
Chris@0 41 retrieve_date_range
Chris@0 42
Chris@0 43 unless @criterias.empty?
Chris@0 44 sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ')
Chris@0 45 sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ')
Chris@0 46 sql_condition = ''
Chris@0 47
Chris@0 48 if @project.nil?
Chris@0 49 sql_condition = Project.allowed_to_condition(User.current, :view_time_entries)
Chris@0 50 elsif @issue.nil?
Chris@0 51 sql_condition = @project.project_condition(Setting.display_subprojects_issues?)
Chris@0 52 else
Chris@0 53 sql_condition = "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
Chris@0 54 end
Chris@0 55
Chris@0 56 sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours"
Chris@0 57 sql << " FROM #{TimeEntry.table_name}"
Chris@0 58 sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id"
Chris@0 59 sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id"
Chris@0 60 sql << " WHERE"
Chris@0 61 sql << " (%s) AND" % sql_condition
Chris@0 62 sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
Chris@0 63 sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
Chris@0 64
Chris@0 65 @hours = ActiveRecord::Base.connection.select_all(sql)
Chris@0 66
Chris@0 67 @hours.each do |row|
Chris@0 68 case @columns
Chris@0 69 when 'year'
Chris@0 70 row['year'] = row['tyear']
Chris@0 71 when 'month'
Chris@0 72 row['month'] = "#{row['tyear']}-#{row['tmonth']}"
Chris@0 73 when 'week'
Chris@0 74 row['week'] = "#{row['tyear']}-#{row['tweek']}"
Chris@0 75 when 'day'
Chris@0 76 row['day'] = "#{row['spent_on']}"
Chris@0 77 end
Chris@0 78 end
Chris@0 79
Chris@0 80 @total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}
Chris@0 81
Chris@0 82 @periods = []
Chris@0 83 # Date#at_beginning_of_ not supported in Rails 1.2.x
Chris@0 84 date_from = @from.to_time
Chris@0 85 # 100 columns max
Chris@0 86 while date_from <= @to.to_time && @periods.length < 100
Chris@0 87 case @columns
Chris@0 88 when 'year'
Chris@0 89 @periods << "#{date_from.year}"
Chris@0 90 date_from = (date_from + 1.year).at_beginning_of_year
Chris@0 91 when 'month'
Chris@0 92 @periods << "#{date_from.year}-#{date_from.month}"
Chris@0 93 date_from = (date_from + 1.month).at_beginning_of_month
Chris@0 94 when 'week'
Chris@0 95 @periods << "#{date_from.year}-#{date_from.to_date.cweek}"
Chris@0 96 date_from = (date_from + 7.day).at_beginning_of_week
Chris@0 97 when 'day'
Chris@0 98 @periods << "#{date_from.to_date}"
Chris@0 99 date_from = date_from + 1.day
Chris@0 100 end
Chris@0 101 end
Chris@0 102 end
Chris@0 103
Chris@0 104 respond_to do |format|
Chris@0 105 format.html { render :layout => !request.xhr? }
Chris@0 106 format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
Chris@0 107 end
Chris@0 108 end
Chris@0 109
Chris@0 110 def details
Chris@0 111 sort_init 'spent_on', 'desc'
Chris@0 112 sort_update 'spent_on' => 'spent_on',
Chris@0 113 'user' => 'user_id',
Chris@0 114 'activity' => 'activity_id',
Chris@0 115 'project' => "#{Project.table_name}.name",
Chris@0 116 'issue' => 'issue_id',
Chris@0 117 'hours' => 'hours'
Chris@0 118
Chris@0 119 cond = ARCondition.new
Chris@0 120 if @project.nil?
Chris@0 121 cond << Project.allowed_to_condition(User.current, :view_time_entries)
Chris@0 122 elsif @issue.nil?
Chris@0 123 cond << @project.project_condition(Setting.display_subprojects_issues?)
Chris@0 124 else
Chris@0 125 cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
Chris@0 126 end
Chris@0 127
Chris@0 128 retrieve_date_range
Chris@0 129 cond << ['spent_on BETWEEN ? AND ?', @from, @to]
Chris@0 130
Chris@0 131 TimeEntry.visible_by(User.current) do
Chris@0 132 respond_to do |format|
Chris@0 133 format.html {
Chris@0 134 # Paginate results
Chris@0 135 @entry_count = TimeEntry.count(:include => [:project, :issue], :conditions => cond.conditions)
Chris@0 136 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
Chris@0 137 @entries = TimeEntry.find(:all,
Chris@0 138 :include => [:project, :activity, :user, {:issue => :tracker}],
Chris@0 139 :conditions => cond.conditions,
Chris@0 140 :order => sort_clause,
Chris@0 141 :limit => @entry_pages.items_per_page,
Chris@0 142 :offset => @entry_pages.current.offset)
Chris@0 143 @total_hours = TimeEntry.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
Chris@0 144
Chris@0 145 render :layout => !request.xhr?
Chris@0 146 }
Chris@0 147 format.atom {
Chris@0 148 entries = TimeEntry.find(:all,
Chris@0 149 :include => [:project, :activity, :user, {:issue => :tracker}],
Chris@0 150 :conditions => cond.conditions,
Chris@0 151 :order => "#{TimeEntry.table_name}.created_on DESC",
Chris@0 152 :limit => Setting.feeds_limit.to_i)
Chris@0 153 render_feed(entries, :title => l(:label_spent_time))
Chris@0 154 }
Chris@0 155 format.csv {
Chris@0 156 # Export all entries
Chris@0 157 @entries = TimeEntry.find(:all,
Chris@0 158 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
Chris@0 159 :conditions => cond.conditions,
Chris@0 160 :order => sort_clause)
Chris@0 161 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
Chris@0 162 }
Chris@0 163 end
Chris@0 164 end
Chris@0 165 end
Chris@0 166
Chris@0 167 def edit
Chris@0 168 (render_403; return) if @time_entry && !@time_entry.editable_by?(User.current)
Chris@0 169 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
Chris@0 170 @time_entry.attributes = params[:time_entry]
Chris@0 171
Chris@0 172 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
Chris@0 173
Chris@0 174 if request.post? and @time_entry.save
Chris@0 175 flash[:notice] = l(:notice_successful_update)
Chris@0 176 redirect_back_or_default :action => 'details', :project_id => @time_entry.project
Chris@0 177 return
Chris@0 178 end
Chris@0 179 end
Chris@0 180
Chris@0 181 def destroy
Chris@0 182 (render_404; return) unless @time_entry
Chris@0 183 (render_403; return) unless @time_entry.editable_by?(User.current)
Chris@0 184 if @time_entry.destroy && @time_entry.destroyed?
Chris@0 185 flash[:notice] = l(:notice_successful_delete)
Chris@0 186 else
Chris@0 187 flash[:error] = l(:notice_unable_delete_time_entry)
Chris@0 188 end
Chris@0 189 redirect_to :back
Chris@0 190 rescue ::ActionController::RedirectBackError
Chris@0 191 redirect_to :action => 'details', :project_id => @time_entry.project
Chris@0 192 end
Chris@0 193
Chris@0 194 private
Chris@0 195 def find_project
Chris@0 196 if params[:id]
Chris@0 197 @time_entry = TimeEntry.find(params[:id])
Chris@0 198 @project = @time_entry.project
Chris@0 199 elsif params[:issue_id]
Chris@0 200 @issue = Issue.find(params[:issue_id])
Chris@0 201 @project = @issue.project
Chris@0 202 elsif params[:project_id]
Chris@0 203 @project = Project.find(params[:project_id])
Chris@0 204 else
Chris@0 205 render_404
Chris@0 206 return false
Chris@0 207 end
Chris@0 208 rescue ActiveRecord::RecordNotFound
Chris@0 209 render_404
Chris@0 210 end
Chris@0 211
Chris@0 212 def find_optional_project
Chris@0 213 if !params[:issue_id].blank?
Chris@0 214 @issue = Issue.find(params[:issue_id])
Chris@0 215 @project = @issue.project
Chris@0 216 elsif !params[:project_id].blank?
Chris@0 217 @project = Project.find(params[:project_id])
Chris@0 218 end
Chris@0 219 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
Chris@0 220 end
Chris@0 221
Chris@0 222 # Retrieves the date range based on predefined ranges or specific from/to param dates
Chris@0 223 def retrieve_date_range
Chris@0 224 @free_period = false
Chris@0 225 @from, @to = nil, nil
Chris@0 226
Chris@0 227 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
Chris@0 228 case params[:period].to_s
Chris@0 229 when 'today'
Chris@0 230 @from = @to = Date.today
Chris@0 231 when 'yesterday'
Chris@0 232 @from = @to = Date.today - 1
Chris@0 233 when 'current_week'
Chris@0 234 @from = Date.today - (Date.today.cwday - 1)%7
Chris@0 235 @to = @from + 6
Chris@0 236 when 'last_week'
Chris@0 237 @from = Date.today - 7 - (Date.today.cwday - 1)%7
Chris@0 238 @to = @from + 6
Chris@0 239 when '7_days'
Chris@0 240 @from = Date.today - 7
Chris@0 241 @to = Date.today
Chris@0 242 when 'current_month'
Chris@0 243 @from = Date.civil(Date.today.year, Date.today.month, 1)
Chris@0 244 @to = (@from >> 1) - 1
Chris@0 245 when 'last_month'
Chris@0 246 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
Chris@0 247 @to = (@from >> 1) - 1
Chris@0 248 when '30_days'
Chris@0 249 @from = Date.today - 30
Chris@0 250 @to = Date.today
Chris@0 251 when 'current_year'
Chris@0 252 @from = Date.civil(Date.today.year, 1, 1)
Chris@0 253 @to = Date.civil(Date.today.year, 12, 31)
Chris@0 254 end
Chris@0 255 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
Chris@0 256 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
Chris@0 257 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
Chris@0 258 @free_period = true
Chris@0 259 else
Chris@0 260 # default
Chris@0 261 end
Chris@0 262
Chris@0 263 @from, @to = @to, @from if @from && @to && @from > @to
Chris@0 264 @from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1
Chris@0 265 @to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today)
Chris@0 266 end
Chris@0 267
Chris@0 268 def load_available_criterias
Chris@0 269 @available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
Chris@0 270 :klass => Project,
Chris@0 271 :label => :label_project},
Chris@0 272 'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
Chris@0 273 :klass => Version,
Chris@0 274 :label => :label_version},
Chris@0 275 'category' => {:sql => "#{Issue.table_name}.category_id",
Chris@0 276 :klass => IssueCategory,
Chris@0 277 :label => :field_category},
Chris@0 278 'member' => {:sql => "#{TimeEntry.table_name}.user_id",
Chris@0 279 :klass => User,
Chris@0 280 :label => :label_member},
Chris@0 281 'tracker' => {:sql => "#{Issue.table_name}.tracker_id",
Chris@0 282 :klass => Tracker,
Chris@0 283 :label => :label_tracker},
Chris@0 284 'activity' => {:sql => "#{TimeEntry.table_name}.activity_id",
Chris@0 285 :klass => TimeEntryActivity,
Chris@0 286 :label => :label_activity},
Chris@0 287 'issue' => {:sql => "#{TimeEntry.table_name}.issue_id",
Chris@0 288 :klass => Issue,
Chris@0 289 :label => :label_issue}
Chris@0 290 }
Chris@0 291
Chris@0 292 # Add list and boolean custom fields as available criterias
Chris@0 293 custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
Chris@0 294 custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
Chris@0 295 @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)",
Chris@0 296 :format => cf.field_format,
Chris@0 297 :label => cf.name}
Chris@0 298 end if @project
Chris@0 299
Chris@0 300 # Add list and boolean time entry custom fields
Chris@0 301 TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
Chris@0 302 @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)",
Chris@0 303 :format => cf.field_format,
Chris@0 304 :label => cf.name}
Chris@0 305 end
Chris@0 306
Chris@0 307 # Add list and boolean time entry activity custom fields
Chris@0 308 TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
Chris@0 309 @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)",
Chris@0 310 :format => cf.field_format,
Chris@0 311 :label => cf.name}
Chris@0 312 end
Chris@0 313
Chris@0 314 call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project })
Chris@0 315 @available_criterias
Chris@0 316 end
Chris@0 317 end