annotate .svn/pristine/3d/3d39eaf1a13ab4e59f2f962003d5f13e35d876e3.svn-base @ 1327:287f201c2802 redmine-2.2-integration

Add italic
author Chris Cannam <chris.cannam@soundsoftware.ac.uk>
date Wed, 19 Jun 2013 20:56:22 +0100
parents 038ba2d95de8
children
rev   line source
Chris@1296 1 # Redmine - project management software
Chris@1296 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
Chris@1296 3 #
Chris@1296 4 # This program is free software; you can redistribute it and/or
Chris@1296 5 # modify it under the terms of the GNU General Public License
Chris@1296 6 # as published by the Free Software Foundation; either version 2
Chris@1296 7 # of the License, or (at your option) any later version.
Chris@1296 8 #
Chris@1296 9 # This program is distributed in the hope that it will be useful,
Chris@1296 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@1296 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@1296 12 # GNU General Public License for more details.
Chris@1296 13 #
Chris@1296 14 # You should have received a copy of the GNU General Public License
Chris@1296 15 # along with this program; if not, write to the Free Software
Chris@1296 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@1296 17
Chris@1296 18 class TimelogController < ApplicationController
Chris@1296 19 menu_item :issues
Chris@1296 20
Chris@1296 21 before_filter :find_project_for_new_time_entry, :only => [:create]
Chris@1296 22 before_filter :find_time_entry, :only => [:show, :edit, :update]
Chris@1296 23 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
Chris@1296 24 before_filter :authorize, :except => [:new, :index, :report]
Chris@1296 25
Chris@1296 26 before_filter :find_optional_project, :only => [:index, :report]
Chris@1296 27 before_filter :find_optional_project_for_new_time_entry, :only => [:new]
Chris@1296 28 before_filter :authorize_global, :only => [:new, :index, :report]
Chris@1296 29
Chris@1296 30 accept_rss_auth :index
Chris@1296 31 accept_api_auth :index, :show, :create, :update, :destroy
Chris@1296 32
Chris@1296 33 helper :sort
Chris@1296 34 include SortHelper
Chris@1296 35 helper :issues
Chris@1296 36 include TimelogHelper
Chris@1296 37 helper :custom_fields
Chris@1296 38 include CustomFieldsHelper
Chris@1296 39
Chris@1296 40 def index
Chris@1296 41 sort_init 'spent_on', 'desc'
Chris@1296 42 sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
Chris@1296 43 'user' => 'user_id',
Chris@1296 44 'activity' => 'activity_id',
Chris@1296 45 'project' => "#{Project.table_name}.name",
Chris@1296 46 'issue' => 'issue_id',
Chris@1296 47 'hours' => 'hours'
Chris@1296 48
Chris@1296 49 retrieve_date_range
Chris@1296 50
Chris@1296 51 scope = TimeEntry.visible.spent_between(@from, @to)
Chris@1296 52 if @issue
Chris@1296 53 scope = scope.on_issue(@issue)
Chris@1296 54 elsif @project
Chris@1296 55 scope = scope.on_project(@project, Setting.display_subprojects_issues?)
Chris@1296 56 end
Chris@1296 57
Chris@1296 58 respond_to do |format|
Chris@1296 59 format.html {
Chris@1296 60 # Paginate results
Chris@1296 61 @entry_count = scope.count
Chris@1296 62 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
Chris@1296 63 @entries = scope.all(
Chris@1296 64 :include => [:project, :activity, :user, {:issue => :tracker}],
Chris@1296 65 :order => sort_clause,
Chris@1296 66 :limit => @entry_pages.items_per_page,
Chris@1296 67 :offset => @entry_pages.current.offset
Chris@1296 68 )
Chris@1296 69 @total_hours = scope.sum(:hours).to_f
Chris@1296 70
Chris@1296 71 render :layout => !request.xhr?
Chris@1296 72 }
Chris@1296 73 format.api {
Chris@1296 74 @entry_count = scope.count
Chris@1296 75 @offset, @limit = api_offset_and_limit
Chris@1296 76 @entries = scope.all(
Chris@1296 77 :include => [:project, :activity, :user, {:issue => :tracker}],
Chris@1296 78 :order => sort_clause,
Chris@1296 79 :limit => @limit,
Chris@1296 80 :offset => @offset
Chris@1296 81 )
Chris@1296 82 }
Chris@1296 83 format.atom {
Chris@1296 84 entries = scope.all(
Chris@1296 85 :include => [:project, :activity, :user, {:issue => :tracker}],
Chris@1296 86 :order => "#{TimeEntry.table_name}.created_on DESC",
Chris@1296 87 :limit => Setting.feeds_limit.to_i
Chris@1296 88 )
Chris@1296 89 render_feed(entries, :title => l(:label_spent_time))
Chris@1296 90 }
Chris@1296 91 format.csv {
Chris@1296 92 # Export all entries
Chris@1296 93 @entries = scope.all(
Chris@1296 94 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
Chris@1296 95 :order => sort_clause
Chris@1296 96 )
Chris@1296 97 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
Chris@1296 98 }
Chris@1296 99 end
Chris@1296 100 end
Chris@1296 101
Chris@1296 102 def report
Chris@1296 103 retrieve_date_range
Chris@1296 104 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
Chris@1296 105
Chris@1296 106 respond_to do |format|
Chris@1296 107 format.html { render :layout => !request.xhr? }
Chris@1296 108 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
Chris@1296 109 end
Chris@1296 110 end
Chris@1296 111
Chris@1296 112 def show
Chris@1296 113 respond_to do |format|
Chris@1296 114 # TODO: Implement html response
Chris@1296 115 format.html { render :nothing => true, :status => 406 }
Chris@1296 116 format.api
Chris@1296 117 end
Chris@1296 118 end
Chris@1296 119
Chris@1296 120 def new
Chris@1296 121 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
Chris@1296 122 @time_entry.safe_attributes = params[:time_entry]
Chris@1296 123 end
Chris@1296 124
Chris@1296 125 def create
Chris@1296 126 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
Chris@1296 127 @time_entry.safe_attributes = params[:time_entry]
Chris@1296 128
Chris@1296 129 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
Chris@1296 130
Chris@1296 131 if @time_entry.save
Chris@1296 132 respond_to do |format|
Chris@1296 133 format.html {
Chris@1296 134 flash[:notice] = l(:notice_successful_create)
Chris@1296 135 if params[:continue]
Chris@1296 136 if params[:project_id]
Chris@1296 137 redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue,
Chris@1296 138 :time_entry => {:issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
Chris@1296 139 :back_url => params[:back_url]
Chris@1296 140 else
Chris@1296 141 redirect_to :action => 'new',
Chris@1296 142 :time_entry => {:project_id => @time_entry.project_id, :issue_id => @time_entry.issue_id, :activity_id => @time_entry.activity_id},
Chris@1296 143 :back_url => params[:back_url]
Chris@1296 144 end
Chris@1296 145 else
Chris@1296 146 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
Chris@1296 147 end
Chris@1296 148 }
Chris@1296 149 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
Chris@1296 150 end
Chris@1296 151 else
Chris@1296 152 respond_to do |format|
Chris@1296 153 format.html { render :action => 'new' }
Chris@1296 154 format.api { render_validation_errors(@time_entry) }
Chris@1296 155 end
Chris@1296 156 end
Chris@1296 157 end
Chris@1296 158
Chris@1296 159 def edit
Chris@1296 160 @time_entry.safe_attributes = params[:time_entry]
Chris@1296 161 end
Chris@1296 162
Chris@1296 163 def update
Chris@1296 164 @time_entry.safe_attributes = params[:time_entry]
Chris@1296 165
Chris@1296 166 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
Chris@1296 167
Chris@1296 168 if @time_entry.save
Chris@1296 169 respond_to do |format|
Chris@1296 170 format.html {
Chris@1296 171 flash[:notice] = l(:notice_successful_update)
Chris@1296 172 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
Chris@1296 173 }
Chris@1296 174 format.api { render_api_ok }
Chris@1296 175 end
Chris@1296 176 else
Chris@1296 177 respond_to do |format|
Chris@1296 178 format.html { render :action => 'edit' }
Chris@1296 179 format.api { render_validation_errors(@time_entry) }
Chris@1296 180 end
Chris@1296 181 end
Chris@1296 182 end
Chris@1296 183
Chris@1296 184 def bulk_edit
Chris@1296 185 @available_activities = TimeEntryActivity.shared.active
Chris@1296 186 @custom_fields = TimeEntry.first.available_custom_fields
Chris@1296 187 end
Chris@1296 188
Chris@1296 189 def bulk_update
Chris@1296 190 attributes = parse_params_for_bulk_time_entry_attributes(params)
Chris@1296 191
Chris@1296 192 unsaved_time_entry_ids = []
Chris@1296 193 @time_entries.each do |time_entry|
Chris@1296 194 time_entry.reload
Chris@1296 195 time_entry.safe_attributes = attributes
Chris@1296 196 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
Chris@1296 197 unless time_entry.save
Chris@1296 198 # Keep unsaved time_entry ids to display them in flash error
Chris@1296 199 unsaved_time_entry_ids << time_entry.id
Chris@1296 200 end
Chris@1296 201 end
Chris@1296 202 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
Chris@1296 203 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
Chris@1296 204 end
Chris@1296 205
Chris@1296 206 def destroy
Chris@1296 207 destroyed = TimeEntry.transaction do
Chris@1296 208 @time_entries.each do |t|
Chris@1296 209 unless t.destroy && t.destroyed?
Chris@1296 210 raise ActiveRecord::Rollback
Chris@1296 211 end
Chris@1296 212 end
Chris@1296 213 end
Chris@1296 214
Chris@1296 215 respond_to do |format|
Chris@1296 216 format.html {
Chris@1296 217 if destroyed
Chris@1296 218 flash[:notice] = l(:notice_successful_delete)
Chris@1296 219 else
Chris@1296 220 flash[:error] = l(:notice_unable_delete_time_entry)
Chris@1296 221 end
Chris@1296 222 redirect_back_or_default(:action => 'index', :project_id => @projects.first)
Chris@1296 223 }
Chris@1296 224 format.api {
Chris@1296 225 if destroyed
Chris@1296 226 render_api_ok
Chris@1296 227 else
Chris@1296 228 render_validation_errors(@time_entries)
Chris@1296 229 end
Chris@1296 230 }
Chris@1296 231 end
Chris@1296 232 end
Chris@1296 233
Chris@1296 234 private
Chris@1296 235 def find_time_entry
Chris@1296 236 @time_entry = TimeEntry.find(params[:id])
Chris@1296 237 unless @time_entry.editable_by?(User.current)
Chris@1296 238 render_403
Chris@1296 239 return false
Chris@1296 240 end
Chris@1296 241 @project = @time_entry.project
Chris@1296 242 rescue ActiveRecord::RecordNotFound
Chris@1296 243 render_404
Chris@1296 244 end
Chris@1296 245
Chris@1296 246 def find_time_entries
Chris@1296 247 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
Chris@1296 248 raise ActiveRecord::RecordNotFound if @time_entries.empty?
Chris@1296 249 @projects = @time_entries.collect(&:project).compact.uniq
Chris@1296 250 @project = @projects.first if @projects.size == 1
Chris@1296 251 rescue ActiveRecord::RecordNotFound
Chris@1296 252 render_404
Chris@1296 253 end
Chris@1296 254
Chris@1296 255 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
Chris@1296 256 if unsaved_time_entry_ids.empty?
Chris@1296 257 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
Chris@1296 258 else
Chris@1296 259 flash[:error] = l(:notice_failed_to_save_time_entries,
Chris@1296 260 :count => unsaved_time_entry_ids.size,
Chris@1296 261 :total => time_entries.size,
Chris@1296 262 :ids => '#' + unsaved_time_entry_ids.join(', #'))
Chris@1296 263 end
Chris@1296 264 end
Chris@1296 265
Chris@1296 266 def find_optional_project_for_new_time_entry
Chris@1296 267 if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
Chris@1296 268 @project = Project.find(project_id)
Chris@1296 269 end
Chris@1296 270 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
Chris@1296 271 @issue = Issue.find(issue_id)
Chris@1296 272 @project ||= @issue.project
Chris@1296 273 end
Chris@1296 274 rescue ActiveRecord::RecordNotFound
Chris@1296 275 render_404
Chris@1296 276 end
Chris@1296 277
Chris@1296 278 def find_project_for_new_time_entry
Chris@1296 279 find_optional_project_for_new_time_entry
Chris@1296 280 if @project.nil?
Chris@1296 281 render_404
Chris@1296 282 end
Chris@1296 283 end
Chris@1296 284
Chris@1296 285 def find_optional_project
Chris@1296 286 if !params[:issue_id].blank?
Chris@1296 287 @issue = Issue.find(params[:issue_id])
Chris@1296 288 @project = @issue.project
Chris@1296 289 elsif !params[:project_id].blank?
Chris@1296 290 @project = Project.find(params[:project_id])
Chris@1296 291 end
Chris@1296 292 end
Chris@1296 293
Chris@1296 294 # Retrieves the date range based on predefined ranges or specific from/to param dates
Chris@1296 295 def retrieve_date_range
Chris@1296 296 @free_period = false
Chris@1296 297 @from, @to = nil, nil
Chris@1296 298
Chris@1296 299 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
Chris@1296 300 case params[:period].to_s
Chris@1296 301 when 'today'
Chris@1296 302 @from = @to = Date.today
Chris@1296 303 when 'yesterday'
Chris@1296 304 @from = @to = Date.today - 1
Chris@1296 305 when 'current_week'
Chris@1296 306 @from = Date.today - (Date.today.cwday - 1)%7
Chris@1296 307 @to = @from + 6
Chris@1296 308 when 'last_week'
Chris@1296 309 @from = Date.today - 7 - (Date.today.cwday - 1)%7
Chris@1296 310 @to = @from + 6
Chris@1296 311 when 'last_2_weeks'
Chris@1296 312 @from = Date.today - 14 - (Date.today.cwday - 1)%7
Chris@1296 313 @to = @from + 13
Chris@1296 314 when '7_days'
Chris@1296 315 @from = Date.today - 7
Chris@1296 316 @to = Date.today
Chris@1296 317 when 'current_month'
Chris@1296 318 @from = Date.civil(Date.today.year, Date.today.month, 1)
Chris@1296 319 @to = (@from >> 1) - 1
Chris@1296 320 when 'last_month'
Chris@1296 321 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
Chris@1296 322 @to = (@from >> 1) - 1
Chris@1296 323 when '30_days'
Chris@1296 324 @from = Date.today - 30
Chris@1296 325 @to = Date.today
Chris@1296 326 when 'current_year'
Chris@1296 327 @from = Date.civil(Date.today.year, 1, 1)
Chris@1296 328 @to = Date.civil(Date.today.year, 12, 31)
Chris@1296 329 end
Chris@1296 330 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
Chris@1296 331 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
Chris@1296 332 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
Chris@1296 333 @free_period = true
Chris@1296 334 else
Chris@1296 335 # default
Chris@1296 336 end
Chris@1296 337
Chris@1296 338 @from, @to = @to, @from if @from && @to && @from > @to
Chris@1296 339 end
Chris@1296 340
Chris@1296 341 def parse_params_for_bulk_time_entry_attributes(params)
Chris@1296 342 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
Chris@1296 343 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
Chris@1296 344 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
Chris@1296 345 attributes
Chris@1296 346 end
Chris@1296 347 end