public
Fork of edavis10/redmine
Description: A fork of Redmine with Texuna Technologies patches applied.
Homepage: http://www.redmine.org
Clone URL: git://github.com/artemv/redmine_tt.git
(continued with modified files) All changes currently applied at TT (except Trac 
migration changes), rebased up to r1778
unknown (author)
Tue Sep 02 10:05:00 -0700 2008
commit  e4722216ff3893f032dc9a757715a161afb51339
tree    39ee50af2494b4caf203a392a9a25fb89df2b2d9
parent  ad632ca3eb21b976ca606dc111f858678806d47e
...
21
22
23
 
 
 
 
 
 
24
25
26
...
102
103
104
105
 
 
 
106
107
108
...
222
223
224
 
 
 
 
 
225
...
21
22
23
24
25
26
27
28
29
30
31
32
...
108
109
110
 
111
112
113
114
115
116
...
230
231
232
233
234
235
236
237
238
0
@@ -21,6 +21,12 @@ class ApplicationController < ActionController::Base
0
   layout 'base'
0
   
0
   before_filter :user_setup, :check_if_login_required, :set_localization
0
+  before_filter :set_time_zone
0
+
0
+  def set_time_zone
0
+    Time.zone = User.current.time_zone if User.current
0
+  end
0
+
0
   filter_parameter_logging :password
0
   
0
   include Redmine::MenuManager::MenuController
0
@@ -102,7 +108,9 @@ class ApplicationController < ActionController::Base
0
 
0
   # Authorize the user for the requested action
0
   def authorize(ctrl = params[:controller], action = params[:action])
0
-    allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project)
0
+    options = {}
0
+    options.merge!(:global => true) if !@project
0
+    allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, options)
0
     allowed ? true : deny_access
0
   end
0
   
0
@@ -222,4 +230,9 @@ class ApplicationController < ActionController::Base
0
   def filename_for_content_disposition(name)
0
     request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
0
   end
0
+
0
+  def url_path(url_parameters)
0
+    rs = ::ActionController::Routing::Routes
0
+    rs.generate url_parameters
0
+  end
0
 end
...
31
32
33
34
35
36
37
38
39
 
 
 
 
 
 
40
41
42
...
31
32
33
 
 
34
 
 
 
35
36
37
38
39
40
41
42
43
0
@@ -31,12 +31,13 @@ class AttachmentsController < ApplicationController
0
   end
0
   
0
   def download
0
-    @attachment.increment_download if @attachment.container.is_a?(Version)
0
-    
0
     # images are sent inline
0
-    send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
0
-                                    :type => @attachment.content_type, 
0
-                                    :disposition => (@attachment.image? ? 'inline' : 'attachment')
0
+    options = {:filename => 
0
+        filename_for_content_disposition(@attachment.filename), 
0
+      :disposition => (@attachment.image? ? 'inline' : 'attachment')}
0
+    
0
+    options.merge!(:type => @attachment.content_type) if @attachment.content_type
0
+    send_file @attachment.diskfile, options
0
   end
0
  
0
 private
...
21
22
23
24
 
 
 
 
25
26
27
...
100
101
102
103
 
104
105
106
...
133
134
135
136
137
138
 
139
140
141
...
156
157
158
 
159
160
161
...
172
173
174
175
 
 
 
 
176
177
178
...
277
278
279
 
280
 
 
281
282
283
...
361
362
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
365
366
...
428
429
430
 
 
 
 
 
 
431
...
21
22
23
 
24
25
26
27
28
29
30
...
103
104
105
 
106
107
108
109
...
136
137
138
 
 
 
139
140
141
142
...
157
158
159
160
161
162
163
...
174
175
176
 
177
178
179
180
181
182
183
...
282
283
284
285
286
287
288
289
290
291
...
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
...
450
451
452
453
454
455
456
457
458
459
0
@@ -21,7 +21,10 @@ class IssuesController < ApplicationController
0
   before_filter :find_issue, :only => [:show, :edit, :reply, :destroy_attachment]
0
   before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
0
   before_filter :find_project, :only => [:new, :update_form, :preview]
0
-  before_filter :authorize, :except => [:index, :changes, :preview, :update_form, :context_menu]
0
+
0
+  before_filter :authorize, :except => [:index, :changes, :preview, 
0
+    :update_form, :context_menu, :diff]
0
+
0
   before_filter :find_optional_project, :only => [:index, :changes]
0
   accept_key_auth :index, :changes
0
 
0
@@ -100,7 +103,7 @@ class IssuesController < ApplicationController
0
     @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
0
     @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
0
     @priorities = Enumeration::get_values('IPRI')
0
-    @time_entry = TimeEntry.new
0
+    @time_entry = @issue.time_entry_in_progress(User.current) || TimeEntry.new
0
     respond_to do |format|
0
       format.html { render :template => 'issues/show.rhtml' }
0
       format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
0
@@ -133,9 +136,7 @@ class IssuesController < ApplicationController
0
     @issue.status = default_status
0
     @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)).uniq
0
     
0
-    if request.get? || request.xhr?
0
-      @issue.start_date ||= Date.today
0
-    else
0
+    if !request.get? && !request.xhr?
0
       requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
0
       # Check that the user is allowed to apply the requested status
0
       @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
0
@@ -156,6 +157,7 @@ class IssuesController < ApplicationController
0
   UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
0
   
0
   def edit
0
+    @issue = flash[:error_issue] if flash[:error_issue]
0
     @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
0
     @priorities = Enumeration::get_values('IPRI')
0
     @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
0
@@ -172,7 +174,10 @@ class IssuesController < ApplicationController
0
     end
0
 
0
     if request.post?
0
-      @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
0
+      @time_entry = TimeEntry.find_by_id(params[:time_entry][:id]) if params[:time_entry]
0
+      @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, 
0
+              :user => User.current, :spent_on => Date.today)
0
+            
0
       @time_entry.attributes = params[:time_entry]
0
       attachments = attach_files(@issue, params[:attachments])
0
       attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
0
@@ -277,7 +282,10 @@ class IssuesController < ApplicationController
0
       if unsaved_issue_ids.empty?
0
         flash[:notice] = l(:notice_successful_update) unless @issues.empty?
0
       else
0
+        flash[:error_issue] = @issues[0]
0
         flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
0
+        redirect_to :controller => 'issues', :action => :edit, :id => @issues[0]
0
+        return
0
       end
0
       redirect_to :controller => 'issues', :action => 'index', :project_id => @project
0
       return
0
@@ -361,6 +369,20 @@ class IssuesController < ApplicationController
0
     render :partial => 'common/preview'
0
   end
0
   
0
+  def diff
0
+    @detail = JournalDetail.find(params[:id])
0
+    @issue = @detail.journal.issue
0
+    Tempfile.open 'issue_description_oldval', 'tmp' do |old_val_file|
0
+      old_val_file.write normalize(@detail.old_value)
0
+      Tempfile.open 'issue_description_oldval', 'tmp' do |new_val_file|
0
+        new_val_file.write normalize(@detail.value)
0
+        old_val_file.close
0
+        new_val_file.close
0
+        @diff = `diff #{old_val_file.path} #{new_val_file.path} -U 3`
0
+      end
0
+    end
0
+  end
0
+
0
 private
0
   def find_issue
0
     @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
0
@@ -428,4 +450,10 @@ private
0
       end
0
     end
0
   end
0
+
0
+  def normalize(long_string)
0
+    return long_string if long_string["\r\n"]
0
+    long_string.split("\n").join("\r\n")
0
+  end
0
+
0
 end
...
74
75
76
 
 
 
 
 
77
78
79
...
74
75
76
77
78
79
80
81
82
83
84
0
@@ -74,6 +74,11 @@ class MyController < ApplicationController
0
     @notification_option = @user.mail_notification? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')    
0
   end
0
 
0
+  def show_account
0
+    @user = User.current
0
+    redirect_to :controller => 'account', :action => 'show', :id => @user.id
0
+  end
0
+  
0
   # Manage user's password
0
   def password
0
     @user = User.current
...
148
149
150
 
151
152
153
 
154
155
156
...
189
190
191
 
 
192
193
194
 
 
 
 
 
 
 
 
 
 
 
 
 
195
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
198
199
...
217
218
219
220
 
221
 
 
222
223
224
...
241
242
243
 
244
 
 
 
 
245
246
247
...
269
270
271
 
 
272
273
274
...
281
282
283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
285
...
148
149
150
151
152
153
 
154
155
156
157
...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
 
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
...
246
247
248
 
249
250
251
252
253
254
255
...
272
273
274
275
276
277
278
279
280
281
282
283
...
305
306
307
308
309
310
311
312
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
0
@@ -148,9 +148,10 @@ class TimelogController < ApplicationController
0
     else
0
       cond << ["#{TimeEntry.table_name}.issue_id = ?", @issue.id]
0
     end
0
+    cond << ["#{TimeEntry.table_name}.user_id = ?", @user.id] if @user
0
     
0
     retrieve_date_range
0
-    cond << ['spent_on BETWEEN ? AND ?', @from, @to]
0
+    cond << ['spent_on BETWEEN ? AND ?', @from, @to] if @apply_date_filter
0
 
0
     TimeEntry.visible_by(User.current) do
0
       respond_to do |format|
0
@@ -189,11 +190,39 @@ class TimelogController < ApplicationController
0
   end
0
   
0
   def edit
0
+    return if redirect_if_in_progress
0
+    
0
     render_403 and return if @time_entry && !@time_entry.editable_by?(User.current)
0
     @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
0
     @time_entry.attributes = params[:time_entry]
0
+    if !@time_entry.hours
0
+      if !@time_entry.start_time
0
+        @activate_field = 'time_entry_start_time'         
0
+      else
0
+        @activate_field = 'time_entry_end_time'
0
+      end
0
+    end
0
+    
0
+    url_writer = lambda do |entry| 
0
+      "<a href = \"#{url_path(:controller => :timelog, :action => :edit, 
0
+        :id => entry.id)}\">##{entry.issue_id}-#{entry.id}</a>"
0
+    end
0
+    
0
     if request.post? and @time_entry.save
0
-      flash[:notice] = l(:notice_successful_update)
0
+      intersecting = @time_entry.find_intersecting_entries
0
+      logger.debug "intersecting = #{intersecting.inspect}"
0
+      msg = l(:notice_successful_update)
0
+      if !intersecting.empty? 
0
+        
0
+        list = lwr(:text_time_entry_intersecting_notice_entry, 
0
+          intersecting.size) + ' ' + intersecting.
0
+          map { |entry| url_writer.call(entry) }.
0
+          to_sentence(:skip_last_comma => true, :connector => l(:text_and))
0
+        
0
+        msg += ' ' + l(:text_time_entry_intersecting_notice, 
0
+          url_writer.call(@time_entry), list)
0
+      end
0
+      flash[:notice] = msg
0
       redirect_to(params[:back_url].blank? ? {:action => 'details', :project_id => @time_entry.project} : params[:back_url])
0
       return
0
     end    
0
@@ -217,8 +246,10 @@ private
0
     elsif params[:issue_id]
0
       @issue = Issue.find(params[:issue_id])
0
       @project = @issue.project
0
-    elsif params[:project_id]
0
+    elsif params[:project_id] && !params[:project_id].empty?
0
       @project = Project.find(params[:project_id])
0
+    elsif params[:user_id]
0
+      @user = User.find(params[:user_id])
0
     else
0
       render_404
0
       return false
0
@@ -241,7 +272,12 @@ private
0
   def retrieve_date_range
0
     @free_period = false
0
     @from, @to = nil, nil
0
+    @apply_date_filter = true
0
 
0
+    if !params[:period_type] && !params[:period] && !params[:from] && !params[:to]
0
+      @apply_date_filter = false
0
+    end
0
+    
0
     if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
0
       case params[:period].to_s
0
       when 'today'
0
@@ -269,6 +305,8 @@ private
0
       when 'current_year'
0
         @from = Date.civil(Date.today.year, 1, 1)
0
         @to = Date.civil(Date.today.year, 12, 31)
0
+      when 'all'
0
+        @apply_date_filter = false
0
       end
0
     elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
0
       begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
0
@@ -281,5 +319,21 @@ private
0
     @from, @to = @to, @from if @from && @to && @from > @to
0
     @from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1
0
     @to   ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today)
0
+
0
+    @from ||= Date.today - 1
0
+    @to   ||= Date.today
0
+  end
0
+  
0
+  def redirect_if_in_progress
0
+    if !@time_entry && @issue
0
+      in_progress_entry = @issue.time_entry_in_progress(User.current)
0
+      if in_progress_entry
0
+        #in order to avoid :id form parameter and not complicate :find_project filter
0
+        redirect_to(:controller => 'timelog', :action => 'edit', 
0
+            :id => in_progress_entry)
0
+        return true
0
+      end
0
+    end
0
+    false
0
   end
0
 end
...
90
91
92
93
 
94
95
96
97
98
 
 
 
99
100
101
 
 
102
103
104
...
90
91
92
 
93
94
95
96
 
 
97
98
99
100
101
 
102
103
104
105
106
0
@@ -90,15 +90,17 @@ module ApplicationHelper
0
     @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
0
     date.strftime(@date_format)
0
   end
0
-  
0
+
0
   def format_time(time, include_date = true)
0
     return nil unless time
0
     time = time.to_time if time.is_a?(String)
0
-    zone = User.current.time_zone
0
-    local = zone ? time.in_time_zone(zone) : (time.utc? ? time.utc_to_local : time)
0
+    local = time
0
+    #zone = User.current.time_zone
0
+    #local = zone ? time.in_time_zone(zone) : (time.utc? ? time.utc_to_local : time)
0
     @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
0
     @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
0
-    include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
0
+    format = include_date ? "#{@date_format} #{@time_format}" : @time_format    
0
+    local.strftime(format)
0
   end
0
   
0
   # Truncates and returns the string as a single line
...
75
76
77
 
 
 
78
79
80
...
184
185
186
 
 
 
 
 
 
 
187
...
75
76
77
78
79
80
81
82
83
...
187
188
189
190
191
192
193
194
195
196
197
0
@@ -75,6 +75,9 @@ module IssuesHelper
0
       when 'fixed_version_id'
0
         v = Version.find_by_id(detail.value) and value = v.name if detail.value
0
         v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
0
+      when 'description'
0
+        return content_tag('strong', 'Description') + ' ' + link_to('changed', :controller => 'issues', :action => 'diff', :id => detail.id)
0
+
0
       when 'estimated_hours'
0
         value = "%0.02f" % detail.value.to_f unless detail.value.blank?
0
         old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
0
@@ -184,4 +187,11 @@ module IssuesHelper
0
     export.rewind
0
     export
0
   end
0
+  
0
+  def spent_hours(issue)
0
+    return '-' if issue.time_entries.empty?
0
+    spent_hours = lwr(:label_f_hour, issue.spent_hours)
0
+    spent_hours += " (#{l(:text_in_progress)})" if issue.time_entry_in_progress
0
+    link_to spent_hours, {:controller => 'timelog', :action => 'details', :project_id => issue.project, :issue_id => issue}, :class => 'icon icon-time'
0
+  end
0
 end
...
44
45
46
 
 
 
 
 
47
48
49
...
44
45
46
47
48
49
50
51
52
53
54
0
@@ -44,6 +44,11 @@ module TimelogHelper
0
     sum
0
   end
0
   
0
+  def hours(entry)
0
+    return '<span class="hours hours-dec">[%s]</span>' % l(:text_in_progress) if !entry.hours
0
+    html_hours("%.2f" % entry.hours)
0
+  end
0
+  
0
   def options_for_period_select(value)
0
     options_for_select([[l(:label_all_time), 'all'],
0
                         [l(:label_today), 'today'],
...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
 
 
 
 
 
 
 
37
38
39
40
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
43
44
...
23
24
25
 
 
 
 
 
 
 
 
 
 
 
26
27
28
29
30
31
32
33
34
 
 
35
 
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
0
@@ -23,22 +23,31 @@ module VersionsHelper
0
     criteria ||= 'category'
0
     raise 'Unknown criteria' unless STATUS_BY_CRITERIAS.include?(criteria)
0
     
0
-    h = Hash.new {|k,v| k[v] = [0, 0]}
0
-    begin
0
-      # Total issue count
0
-      Issue.count(:group => criteria,
0
-                  :conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s}
0
-      # Open issues count
0
-      Issue.count(:group => criteria,
0
-                  :include => :status,
0
-                  :conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s}
0
-    rescue ActiveRecord::RecordNotFound
0
-    # When grouping by an association, Rails throws this exception if there's no result (bug)
0
+    #sort them alphabetically by category name
0
+    metrics = version.get_grouped_metrics(criteria).to_a.sort {|x, y| x[0].to_s <=> y[0].to_s} 
0
+    max = {}
0
+    
0
+    [{:count => :total}, {:time => :total}].each do |metric_info| 
0
+      metrics_group, total_metric = metric_info.to_a.flatten
0
+      max[metrics_group] = metrics.map{|item| item[1]}.map {|item| item[metrics_group]}.map {|item| item[total_metric]}.max
0
+      max[metrics_group] = 1 if max[metrics_group] == 0
0
     end
0
-    counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
0
-    max = counts.collect {|c| c[:total]}.max
0
     
0
-    render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
0
+    render :partial => 'issue_counts', :locals => {:version => version, 
0
+      :criteria => criteria, :grouped_metrics => metrics, :max => max, 
0
+      :spent_time_allowed => User.current.allowed_to?(:view_time_entries, @project), 
0
+      }
0
+  end
0
+
0
+  def time_progress(time_info)  
0
+    logger.debug "time_info[:spent] = #{time_info[:spent].inspect}"
0
+    logger.debug "time_info[:total] = #{time_info[:total].inspect}"
0
+    if (time_info[:total] != 0)
0
+      time_progress = time_info[:spent].to_f / time_info[:total]
0
+    else
0
+      time_progress = 0 #no total also means there's no spent time
0
+    end    
0
+    time_progress
0
   end
0
   
0
   def status_by_options_for_select(value)
...
108
109
110
 
 
 
 
 
 
 
 
 
 
 
111
112
113
...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
0
@@ -108,6 +108,17 @@ class Attachment < ActiveRecord::Base
0
     self.filename =~ /\.(patch|diff)$/i
0
   end
0
   
0
+  def guess_and_fill_mime_type(save = true)
0
+    require 'mime/types'
0
+    types = MIME::Types.type_for(self.filename)
0
+    if !types.empty?
0
+      content_type = types[0].content_type
0
+      self.content_type = content_type
0
+      self.save! if save
0
+      true
0
+    end
0
+  end
0
+  
0
 private
0
   def sanitize_filename(value)
0
     # get only the filename, not the whole path
...
48
49
50
51
 
 
 
 
 
52
53
54
...
48
49
50
 
51
52
53
54
55
56
57
58
0
@@ -48,7 +48,11 @@ protected
0
       when 'date'
0
         errors.add(:value, :activerecord_error_not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/
0
       when 'list'
0
-        errors.add(:value, :activerecord_error_inclusion) unless custom_field.possible_values.include?(value)
0
+        if !custom_field.possible_values.include?(value)
0
+          errors.add(:value, "Value '#{value}' is not one of this custom " + 
0
+              "field's possible values " + 
0
+              "(#{custom_field.possible_values.inspect})")
0
+        end
0
       end
0
     end
0
   end
...
28
29
30
 
31
32
33
...
45
46
47
 
48
49
50
51
 
 
52
53
54
...
83
84
85
 
86
87
88
...
92
93
94
95
 
96
97
98
...
123
124
125
126
 
127
128
129
...
136
137
138
139
 
140
141
142
...
260
261
262
 
 
 
 
 
 
 
 
 
263
...
28
29
30
31
32
33
34
...
46
47
48
49
50
51
52
53
54
55
56
57
58
...
87
88
89
90
91
92
93
...
97
98
99
 
100
101
102
103
...
128
129
130
 
131
132
133
134
...
141
142
143
 
144
145
146
147
...
265
266
267
268
269
270
271
272
273
274
275
276
277
0
@@ -28,6 +28,7 @@ class Issue < ActiveRecord::Base
0
   has_many :journals, :as => :journalized, :dependent => :destroy
0
   has_many :attachments, :as => :container, :dependent => :destroy
0
   has_many :time_entries, :dependent => :delete_all
0
+  
0
   has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
0
   
0
   has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
0
@@ -45,10 +46,13 @@ class Issue < ActiveRecord::Base
0
   acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]}
0
   
0
   validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
0
+  validates_presence_of :fixed_version, :if => lambda {|i| !i.project.versions.empty? && !i.allow_empty_fixed_version}
0
   validates_length_of :subject, :maximum => 255
0
   validates_inclusion_of :done_ratio, :in => 0..100
0
   validates_numericality_of :estimated_hours, :allow_nil => true
0
 
0
+  attr_accessor :allow_empty_fixed_version
0
+
0
   def after_initialize
0
     if new_record?
0
       # set default values for new records only
0
@@ -83,6 +87,7 @@ class Issue < ActiveRecord::Base
0
         new_category = category.nil? ? nil : new_project.issue_categories.find_by_name(category.name)
0
         self.category = new_category
0
         self.fixed_version = nil
0
+        self.allow_empty_fixed_version = true
0
         self.project = new_project
0
       end
0
       if new_tracker
0
@@ -92,7 +97,7 @@ class Issue < ActiveRecord::Base
0
         # Manually update project_id on related time entries
0
         TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
0
       else
0
-        rollback_db_transaction
0
+        self.connection.rollback_db_transaction
0
         return false
0
       end
0
     end
0
@@ -123,7 +128,7 @@ class Issue < ActiveRecord::Base
0
   end
0
   
0
   def validate_on_create
0
-    errors.add :tracker_id, :activerecord_error_invalid unless project.trackers.include?(tracker)
0
+    errors.add :tracker_id, "project doesn't contain this tracker" unless project.trackers.include?(tracker)
0
   end
0
   
0
   def before_create
0
@@ -136,7 +141,7 @@ class Issue < ActiveRecord::Base
0
   def before_save  
0
     if @current_journal
0
       # attributes changes
0
-      (Issue.column_names - %w(id description)).each {|c|
0
+      (Issue.column_names - %w(id)).each {|c|
0
         @current_journal.details << JournalDetail.new(:property => 'attr',
0
                                                       :prop_key => c,
0
                                                       :old_value => @issue_before_change.send(c),
0
@@ -260,4 +265,13 @@ class Issue < ActiveRecord::Base
0
   def to_s
0
     "#{tracker} ##{id}: #{subject}"
0
   end
0
+  
0
+  def time_entry_in_progress(user = nil)
0
+    TimeEntry.find_by_issue_id(self.id,  
0
+        :conditions => 'start_time IS NOT NULL and hours IS NULL' + (user ? " and user_id = #{user.id}" : ''))
0
+  end
0
+
0
+  def active_versions
0
+    project.active_versions(:for => self)
0
+  end
0
 end
...
22
23
24
25
 
26
27
28
...
22
23
24
 
25
26
27
28
0
@@ -22,7 +22,7 @@ class IssueCategory < ActiveRecord::Base
0
   
0
   validates_presence_of :name
0
   validates_uniqueness_of :name, :scope => [:project_id]
0
-  validates_length_of :name, :maximum => 30
0
+  validates_length_of :name, :maximum => 50
0
   
0
   alias :destroy_without_reassign :destroy
0
   
...
17
18
19
20
21
22
23
24
25
...
17
18
19
 
 
 
 
 
20
0
@@ -17,9 +17,4 @@
0
 
0
 class JournalDetail < ActiveRecord::Base
0
   belongs_to :journal
0
-  
0
-  def before_save
0
-    self.value = value[0..254] if value && value.is_a?(String)
0
-    self.old_value = old_value[0..254] if old_value && old_value.is_a?(String)
0
-  end
0
 end
...
58
59
60
61
 
62
63
64
...
245
246
247
 
 
 
 
 
 
 
 
248
249
250
...
58
59
60
 
61
62
63
64
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
0
@@ -58,7 +58,7 @@ class Project < ActiveRecord::Base
0
   validates_associated :repository, :wiki
0
   validates_length_of :name, :maximum => 30
0
   validates_length_of :homepage, :maximum => 255
0
-  validates_length_of :identifier, :in => 3..20
0
+  validates_length_of :identifier, :in => 2..20
0
   validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
0
   
0
   before_destroy :delete_all_members
0
@@ -245,6 +245,14 @@ class Project < ActiveRecord::Base
0
     p.nil? ? nil : p.identifier.to_s.succ
0
   end
0
 
0
+  def active_versions(options = {})
0
+    issue = options[:for] if options[:for].is_a?(Issue)
0
+    versions.select do |v| 
0
+      !v.completed? || issue && issue.fixed_version && 
0
+        v.id == issue.fixed_version.id
0
+    end
0
+  end
0
+  
0
 protected
0
   def validate
0
     errors.add(parent_id, " must be a root project") if parent and parent.parent
...
26
27
28
29
30
 
 
 
 
 
 
 
31
32
33
34
 
35
36
37
...
48
49
50
51
 
 
 
 
 
 
 
 
 
 
 
 
 
52
53
54
...
76
77
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
...
26
27
28
 
 
29
30
31
32
33
34
35
36
37
38
 
39
40
41
42
...
53
54
55
 
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
0
@@ -26,12 +26,17 @@ class TimeEntry < ActiveRecord::Base
0
   attr_protected :project_id, :user_id, :tyear, :tmonth, :tweek
0
 
0
   acts_as_customizable
0
-  acts_as_event :title => Proc.new {|o| "#{o.user}: #{lwr(:label_f_hour, o.hours)} (#{(o.issue || o.project).event_title})"},
0
-                :url => Proc.new {|o| {:controller => 'timelog', :action => 'details', :project_id => o.project}},
0
+  title_proc = Proc.new do |entry|    
0
+    hours = entry.hours ? lwr(:label_f_hour, entry.hours) : "[#{l(:text_in_progress)}]"
0
+    "#{entry.user}: #{hours} (#{(entry.issue || entry.project).event_title})"
0
+  end
0
+  
0
+  acts_as_event :title => title_proc,
0
+                :url => Proc.new {|entry| {:controller => 'timelog', :action => 'details', :project_id => entry.project}},
0
                 :author => :user,
0
                 :description => :comments
0
   
0
-  validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
0
+  validates_presence_of :user_id, :activity_id, :project_id, :spent_on
0
   validates_numericality_of :hours, :allow_nil => true
0
   validates_length_of :comments, :maximum => 255, :allow_nil => true
0
 
0
@@ -48,7 +53,19 @@ class TimeEntry < ActiveRecord::Base
0
   end
0
   
0
   def validate
0
-    errors.add :hours, :activerecord_error_invalid if hours && (hours < 0 || hours >= 1000)
0
+    errors.add :hours, :activerecord_error_invalid if hours && (hours >= 1000 || hours < 0)
0
+    
0
+    if !start_time && !hours
0
+      #rather verbose, but l() always translate to English here for some reason
0
+      errors.add :hours, ll(User.current.language, 
0
+          :activerecord_error_field_must_be_set_if_other_is_not, 
0
+          ll(User.current.language, :field_start_time))
0
+
0
+      errors.add :start_time, ll(User.current.language, 
0
+          :activerecord_error_field_must_be_set_if_other_is_not, 
0
+          ll(User.current.language, :field_hours))
0
+    end
0
+      
0
     errors.add :project_id, :activerecord_error_invalid if project.nil?
0
     errors.add :issue_id, :activerecord_error_invalid if (issue_id && !issue) || (issue && project!=issue.project)
0
   end
0
@@ -76,4 +93,25 @@ class TimeEntry < ActiveRecord::Base
0
       yield
0
     end
0
   end
0
+  
0
+  def before_save
0
+    if !hours && start_time && end_time
0
+      self.hours = (end_time - start_time) / 3600
0
+    end
0
+  end
0
+  
0
+  def find_intersecting_entries
0
+    params = {:start_time => start_time, :end_time => end_time, :id => id}
0
+    
0
+    self.class.find_all_by_user_id(user_id, :conditions => 
0
+        ["(" + 
0
+          #this entry's start time or end time is between other's start_time and end_time
0
+          "start_time < :start_time and :start_time < end_time OR " + 
0
+          "start_time < :end_time and :end_time < end_time OR " + 
0
+          #other's entry's start time or end time is between this entry's start_time and end_time
0
+          "start_time > :start_time and start_time < :end_time OR " + 
0
+          "end_time > :start_time and end_time < :end_time" +
0
+          ")" + 
0
+          "and id <> :id", params])
0
+  end
0
 end
...
23
24
25
26
 
27
28
29
...
43
44
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
47
48
...
99
100
101
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
104
105
...
23
24
25
 
26
27
28
29
...
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
...
121
122
123
 
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
0
@@ -23,7 +23,7 @@ class Version < ActiveRecord::Base
0
 
0
   validates_presence_of :name
0
   validates_uniqueness_of :name, :scope => [:project_id]
0
-  validates_length_of :name, :maximum => 60
0
+  validates_length_of :name, :maximum => 100
0
   validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :activerecord_error_not_a_date, :allow_nil => true
0
   
0
   def start_date
0
@@ -43,6 +43,28 @@ class Version < ActiveRecord::Base
0
   def spent_hours
0
     @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f
0
   end
0
+
0
+  
0
+  def calc_remaining_and_total_time
0
+    @remaining_hours = 0
0
+    @total_hours = 0
0
+    get_grouped_metrics(:category).to_a.map{|item| item[1]}.map {|item| item[:time]}.each do |times| 
0
+      @remaining_hours += times[:remaining]
0
+      @total_hours += times[:total]
0
+    end
0
+  end
0
+  
0
+  def remaining_hours
0
+    return @remaining_hours if @remaining_hours
0
+    calc_remaining_and_total_time
0
+    @remaining_hours    
0
+  end
0
+  
0
+  def total_hours
0
+    return @total_hours if @total_hours
0
+    calc_remaining_and_total_time
0
+    @total_hours
0
+  end
0
   
0
   # Returns true if the version is completed: due date reached and no open issues
0
   def completed?
0
@@ -99,7 +121,53 @@ class Version < ActiveRecord::Base
0
     end
0
   end
0
   
0
-private
0
+  def get_grouped_metrics(criteria)
0
+    condition = issues_version_condition
0
+    
0
+    issues = Issue.find(:all, :include => [:status, criteria], 
0
+      :conditions => condition)
0
+    
0
+    spent_times = {}
0
+    TimeEntry.sum(:hours, :group => :issue_id, :include => :issue,
0
+        :conditions => condition).each do |issue_id, hours| 
0
+        
0
+      spent_times[issue_id] = hours
0
+    end
0
+
0
+    categories_metrics = {}
0
+    issues.each do |issue|
0
+      category = issue.send(criteria)
0
+      categories_metrics[category] ||= {}
0
+      categories_metrics[category][:time] ||= {:estimated => 0, 
0
+        :spent => 0, :remaining => 0, :total => 0}
0
+      metrics = categories_metrics[category][:time]
0
+      
0
+      estimated = issue.estimated_hours || 0
0
+      metrics[:estimated] += estimated
0
+      spent = spent_times[issue.id] || 0
0
+      metrics[:spent] += spent
0
+      remaining = issue.closed? ? 0 : estimated - spent
0
+      remaining = 0 if remaining < 0
0
+      metrics[:remaining] += remaining
0
+      metrics[:total] += (remaining + spent)
0
+      
0
+      categories_metrics[category][:count] ||= {:open => 0, :closed => 0, :total => 0}
0
+      metrics = categories_metrics[category][:count]
0
+      metrics[:total] += 1
0
+      if issue.closed?
0
+        metrics[:closed] += 1 
0
+      else
0
+        metrics[:open] += 1 
0
+      end
0
+    end
0
+    categories_metrics
0
+  end
0
+  
0
+  def issues_version_condition
0
+    ["#{Issue.table_name}.fixed_version_id = ?", id]
0
+  end
0
+
0
+  private
0
   def check_integrity
0
     raise "Can't delete version" if self.fixed_issues.find(:first)
0
   end
...
29
30
31
32
33
 
 
...
29
30
31
 
32
33
34
0
@@ -29,4 +29,5 @@
0
 <h3><%=l(:label_activity)%></h3>
0
 <p>
0
 <%=l(:label_reported_issues)%>: <%= Issue.count(:conditions => ["author_id=?", @user.id]) %>
0
-</p>
0
\ No newline at end of file
0
+</p>
0
+<%= link_to(l(:label_spent_time_log), users_timelog_path(@user.id, :details)) %>
...
 
 
 
1
2
3
...
18
19
20
 
21
22
 
23
24
25
 
26
27
28
...
1
2
3
4
5
6
...
21
22
23
24
25
26
27
28
29
30
31
32
33
34
0
@@ -1,3 +1,6 @@
0
+<% content_for :header_tags do %>
0
+    <%= javascript_include_tag 'time' %>
0
+<% end %>
0
 <% labelled_tabular_form_for :issue, @issue,
0
                              :url => {:action => 'edit', :id => @issue},
0
                              :html => {:id => 'issue-form',
0
@@ -18,11 +21,14 @@
0
     <% if authorize_for('timelog', 'edit') %>
0
         <fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
0
         <% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
0
+        <%= time_entry.hidden_field :id %>
0
         <div class="splitcontentleft">
0
         <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p>
0
+        <p><%= time_entry.time_field :start_time %></p>
0
         </div>
0
         <div class="splitcontentright">
0
         <p><%= time_entry.select :activity_id, activity_collection_for_select_options %></p>
0
+        <p><%= time_entry.time_field :end_time %></p>
0
         </div>
0
         <p><%= time_entry.text_field :comments, :size => 60 %></p>
0
         <% @time_entry.custom_field_values.each do |value| %>
...
30
31
32
33
34
 
 
35
36
37
...
30
31
32
 
 
33
34
35
36
37
0
@@ -30,8 +30,8 @@
0
                      {:controller => 'projects', :action => 'add_issue_category', :id => @project},
0
                      :class => 'small', :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
0
 <%= content_tag('p', f.select(:fixed_version_id, 
0
-                              (@project.versions.sort.collect {|v| [v.name, v.id]}),
0
-                              { :include_blank => true })) unless @project.versions.empty? %>
0
+                              (@issue.active_versions.sort.collect {|v| [v.name, v.id]}),
0
+                              { :include_blank => true, :required => true })) unless @project.versions.empty? %>
0
 </div>
0
 
0
 <div class="splitcontentright">
...
5
6
7
8
9
 
 
10
...
5
6
7
 
 
8
9
10
0
@@ -5,6 +5,6 @@
0
 <div class="splitcontentright">
0
 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
0
 <%= content_tag('p', f.select(:fixed_version_id, 
0
-                          (@project.versions.sort.collect {|v| [v.name, v.id]}),
0
-                          { :include_blank => true })) unless @project.versions.empty? %>
0
+                          (@issue.active_versions.sort.collect {|v| [v.name, v.id]}),
0
+                          { :include_blank => true, :required => true })) unless @project.versions.empty? %>
0
 </div>
...
27
28
29
30
 
31
32
33
...
27
28
29
 
30
31
32
33
0
@@ -27,7 +27,7 @@
0
 <label><%= l(:field_fixed_version) %>: 
0
 <%= select_tag('fixed_version_id', content_tag('option', l(:label_no_change_option), :value => '') +
0
                                    content_tag('option', l(:label_none), :value => 'none') +
0
-                                   options_from_collection_for_select(@project.versions, :id, :name)) %></label>
0
+                                   options_from_collection_for_select(@project.active_versions, :id, :name)) %></label>
0
 </p>
0
 
0
 <p>
...
25
26
27
28
 
29
30
31
32
 
33
34
35
...
25
26
27
 
28
29
30
31
 
32
33
34
35
0
@@ -25,11 +25,11 @@
0
     <% end -%>
0
     </ul>
0
   </li>
0
-  <% unless @project.nil? || @project.versions.empty? -%>
0
+  <% unless @project.nil? || @project.active_versions.empty? -%>
0
   <li class="folder">      
0
     <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
0
     <ul>
0
-    <% @project.versions.sort.each do |v| -%>
0
+    <% @project.active_versions.sort.each do |v| -%>
0
         <li><%= context_menu_link v.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => v, :back_to => @back}, :method => :post,
0
                                   :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
0
     <% end -%>
...
33
34
35
36
 
37
38
39
...
33
34
35
 
36
37
38
39
0
@@ -33,7 +33,7 @@
0
     <td><b><%=l(:field_category)%>:</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
0
     <% if User.current.allowed_to?(:view_time_entries, @project) %>
0
     <td><b><%=l(:label_spent_time)%>:</b></td>
0
-    <td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td>
0
+    <td><%= spent_hours(@issue) %></td>
0
     <% end %>
0
 </tr>
0
 <tr>
...
23
24
25
26
 
27
28
29
...
23
24
25
 
26
27
28
29
0
@@ -23,7 +23,7 @@
0
     <div id="account">
0
         <%= render_menu :account_menu -%>
0
     </div>
0
-    <%= content_tag('div', "#{l(:label_logged_as)} #{User.current.login}", :id => 'loggedas') if User.current.logged? %>
0
+    <%= content_tag('div', "#{l(:label_logged_as)} #{link_to User.current.login, show_my_account_path}", :id => 'loggedas') if User.current.logged? %>
0
     <%= render_menu :top_menu -%>
0
 </div>
0
       
...
8
9
10
11
 
12
13
14
...
25
26
27
28
 
29
30
31
...
33
34
35
36
 
37
38
39
...
8
9
10
 
11
12
13
14
...
25
26
27
 
28
29
30
31
...
33
34
35
 
36
37
38
39
0
@@ -8,7 +8,7 @@ entries_by_day = entries.group_by(&:spent_on)
0
 %>
0
 
0
 <div class="total-hours">
0
-<p><%= l(:label_total) %>: <%= html_hours("%.2f" % entries.sum(&:hours).to_f) %></p>
0
+<p><%= l(:label_total) %>: <%= html_hours("%.2f" % (entries.sum {|e| e.hours.to_f})) %></p>
0
 </div>
0
 
0
 <% if entries.any? %>
0
@@ -25,7 +25,7 @@ entries_by_day = entries.group_by(&:spent_on)
0
     <tr class="odd">
0
     <td><strong><%= day == Date.today ? l(:label_today).titleize : format_date(day) %></strong></td>
0
     <td colspan="2"></td>
0
-    <td class="hours"><em><%= html_hours("%.2f" % entries_by_day[day].sum(&:hours).to_f) %></em></td>
0
+    <td class="hours"><em><%= html_hours("%.2f" % (entries_by_day[day].sum {|e| e.hours.to_f})) %></em></td>
0
     <td></td>
0
     </tr>
0
     <% entries_by_day[day].each do |entry| -%>
0
@@ -33,7 +33,7 @@ entries_by_day = entries.group_by(&:spent_on)
0
     <td class="activity"><%=h entry.activity %></td>
0
     <td class="subject"><%=h entry.project %> <%= ' - ' + link_to_issue(entry.issue, :title => h("#{entry.issue.subject} (#{entry.issue.status})")) if entry.issue %></td>
0
     <td class="comments"><%=h entry.comments %></td>
0
-    <td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
0
+    <td class="hours"><%= html_hours("%.2f" % entry.hours.to_f) %></td>
0
     <td align="center">
0
     <% if entry.editable_by?(@user) -%>
0
         <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry},
...
9
10
11
12
 
13
14
15
...
9
10
11
 
12
13
14
15
0
@@ -9,7 +9,7 @@
0
     <% if version.effective_date %>
0
       <p><%= format_date(version.effective_date) %></p>
0
     <% end %>
0
-    <p><%=h version.description %></p>
0
+    <p><%=textilizable version.description %></p>
0
     <% issues = version.fixed_issues.find(:all,
0
                                  :include => [:status, :tracker],
0
                                  :conditions => ["#{IssueStatus.table_name}.is_closed=? AND #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", true],
...
23
24
25
26
 
27
28
...
23
24
25
 
26
27
28
0
@@ -23,6 +23,6 @@
0
     <li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'details', :project_id => @project }),
0
                                        :class => (@controller.action_name == 'details' ? 'selected' : nil)) %></li>
0
     <li><%= link_to(l(:label_report), url_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project}),
0
-                                       :class => (@controller.action_name == 'report' ? 'selected' : nil)) %></li>
0
+                                       :class => (@controller.action_name == 'report' ? 'selected' : nil)) if @report %></li>
0
 </ul>
0
 </div>
...
2
3
4
 
 
 
5
6
7
8
9
10
11
12
13
...
15
16
17
 
 
 
18
19
20
...
24
25
26
27
28
29
30
...
2
3
4
5
6
7
8
9
10
11
12
 
13
14
15
...
17
18
19
20
21
22
23
24
25
...
29
30
31
 
32
33
34
0
@@ -2,12 +2,14 @@
0
 <thead>
0
 <tr>
0
 <%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>
0
+<%= sort_header_tag('start_time', :caption => l(:field_start_time), :default_order => 'desc') %>
0
+<%= sort_header_tag('end_time', :caption => l(:field_end_time), :default_order => 'desc') %>
0
+<%= sort_header_tag('hours', :caption => l(:field_hours)) %>
0
 <%= sort_header_tag('user_id', :caption => l(:label_member)) %>
0
 <%= sort_header_tag('activity_id', :caption => l(:label_activity)) %>
0
 <%= sort_header_tag("#{Project.table_name}.name", :caption => l(:label_project)) %>
0
 <%= sort_header_tag('issue_id', :caption => l(:label_issue), :default_order => 'desc') %>
0
 <th><%= l(:field_comments) %></th>
0
-<%= sort_header_tag('hours', :caption => l(:field_hours)) %>
0
 <th></th>
0
 </tr>
0
 </thead>
0
@@ -15,6 +17,9 @@
0
 <% entries.each do |entry| -%>
0
 <tr class="time-entry <%= cycle("odd", "even") %>">
0
 <td class="spent_on"><%= format_date(entry.spent_on) %></td>
0
+<td class="start_time"><%= format_time(entry.start_time) %></td>
0
+<td class="end_time"><%= format_time(entry.end_time) %></td>
0
+<td class="hours"><%= hours(entry) %></td>
0
 <td class="user"><%=h entry.user %></td>
0
 <td class="activity"><%=h entry.activity %></td>
0
 <td class="project"><%=h entry.project %></td>
0
@@ -24,7 +29,6 @@
0
 <% end -%>
0
 </td>
0
 <td class="comments"><%=h entry.comments %></td>
0
-<td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
0
 <td align="center">
0
 <% if entry.editable_by?(User.current) -%>
0
     <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry, :project_id => nil},
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
0
@@ -1,20 +1,33 @@
0
-<h2><%= l(:label_spent_time) %></h2>
0
-
0
-<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :project_id => @time_entry.project} do |f| %>
0
-<%= error_messages_for 'time_entry' %>
0
-<%= back_url_hidden_field_tag %>
0
-
0
-<div class="box">
0
-<p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
0
-<p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
0
-<p><%= f.text_field :hours, :size => 6, :required => true %></p>
0
-<p><%= f.text_field :comments, :size => 100 %></p>
0
-<p><%= f.select :activity_id, activity_collection_for_select_options, :required => true %></p>
0
-<% @time_entry.custom_field_values.each do |value| %>
0
-  <p><%= custom_field_tag_with_label :time_entry, value %></p>
0
-<% end %>
0
-</div>
0
-
0
-<%= submit_tag l(:button_save) %>
0
-
0
-<% end %>
0
+<h2><%= l(:label_spent_time) %></h2>
0
+
0
+<% labelled_tabular_form_for :time_entry, @time_entry, :url => {:action => 'edit', :project_id => @time_entry.project} do |f| %>
0
+<%= error_messages_for 'time_entry' %>
0
+<%= back_url_hidden_field_tag %>
0
+<% content_for :header_tags do %>
0
+    <%= javascript_include_tag 'time' %>
0
+<% end %>
0
+
0
+<% content_for :header_tags do %>
0
+  <script language="javascript">
0
+    window.onload = function() {
0
+      $('<%= @activate_field %>').activate();
0
+    };
0
+  </script>
0
+<% end %>
0
+
0
+<div class="box">
0
+<p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
0
+<p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
0
+<p><%= f.time_field :start_time %></p>
0
+<p><%= f.time_field :end_time %></p>
0
+<p><%= f.text_field :hours, :size => 6 %> <em> <%= l(:text_clear_to_recalculate_time_by_range) %></em> </p> 
0
+<p><%= f.text_field :comments, :size => 100 %></p>
0
+<p><%= f.select :activity_id, activity_collection_for_select_options, :required => true %></p>
0
+<% @time_entry.custom_field_values.each do |value| %>
0
+  <p><%= custom_field_tag_with_label :time_entry, value %></p>
0
+<% end %>
0
+</div>
0
+
0
+<%= submit_tag l(:button_save) %>
0
+
0
+<% end %>
...
2
3
4
5
 
 
 
 
 
 
6
7
8
...
2
3
4
 
5
6
7
8
9
10
11
12
13
0
@@ -2,7 +2,12 @@
0
 
0
 <div class="box">
0
 <p><%= f.text_field :name, :size => 60, :required => true %></p>
0
-<p><%= f.text_field :description, :size => 60 %></p>
0
+<p><%= f.text_area :description,
0
+                   :cols => 60,
0
+                   :rows => (@version.description.blank? ? 10 : [[10, @version.description.length / 50].max, 100].min),
0
+                   :accesskey => accesskey(:edit),
0
+                   :class => 'wiki-edit' %></p>
0
+
0
 <p><%= f.text_field :wiki_page_title, :label => :label_wiki_page, :size => 60, :disabled => @project.wiki.nil? %></p>
0
 <p><%= f.text_field :effective_date, :size => 10 %><%= calendar_for('version_effective_date') %></p>
0
 </div>
...
8
9
10
11
 
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
32
 
33
34
35
...
8
9
10
 
11
12
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
 
103
104
105
106
0
@@ -8,28 +8,99 @@
0
                    :onchange => remote_function(:url => { :action => :status_by, :id => version },
0
                                                 :with => "Form.serialize('status_by_form')"))) %>
0
 </legend>
0
-<% if counts.empty? %>
0
+<% if grouped_metrics.empty? %>
0
     <p><em><%= l(:label_no_data) %></em></p>
0
 <% else %>
0
-    <table>
0
-    <% counts.each do |count| %>
0
-    <tr>
0
-        <td width="130px" align="right" >
0
-            <%= link_to count[:group], {:controller => 'issues', 
0
-                                        :action => 'index',
0
-                                        :project_id => version.project,
0
-                                        :set_filter => 1,
0
-                                        :fixed_version_id => version,
0
-                                        "#{criteria}_id" => count[:group]} %>
0
-        </td>
0
-        <td width="240px">
0
-            <%= progress_bar((count[:closed].to_f / count[:total])*100, 
0
-                  :legend => "#{count[:closed]}/#{count[:total]}",
0
-                  :width => "#{(count[:total].to_f / max * 200).floor}px;") %>
0
-        </td>
0
-    </tr>
0
+    <table class="category_metrics">
0
+    <% grouped_metrics.each do |metrics_group| 
0
+      category, metrics = *metrics_group
0
+    %>
0
+    <% color_class = cycle('odd', 'even')%>
0
+        <tr class="header <%= color_class %>">
0
+            <td colspan="5">
0
+                <%= criteria_operator = category ? "=" : "!*" 
0
+                link_to category || "[#{l(:text_not_assigned)}]", 
0
+                                            {:controller => 'issues', 
0
+                                            :action => 'index',
0
+                                            :project_id => version.project,
0
+                                            :set_filter => 1,
0
+                                            :fields => ["#{criteria}_id", "fixed_version_id", "status_id"],
0
+                                            :values => {"#{criteria}_id" => [category], "fixed_version_id" => [version], "status_id" => [1]},
0
+                                            :operators => {"#{criteria}_id" => criteria_operator, "fixed_version_id" => "=", "status_id" => "*"}
0
+                                            }
0
+                                          %>
0
+            </td>
0
+        </tr>
0
+        <tr class="<%= color_class %>">
0
+            <td><%= l(:label_issues_count) %> </td>
0
+            <% if spent_time_allowed %>
0
+            <td><%= l(:label_time) %></td>
0
+            <% end %>
0
+            <td class="metric_comment">
0
+                <span title="<%= l(:field_estimated_hours) %>">
0
+                    <%= l(:label_estimated_time_short) %> 
0
+                </span>
0
+            </td>
0
+            <% if spent_time_allowed %>
0
+            <td class="metric_comment">
0
+                <span title="<%= l(:label_spent_time) %>">
0
+                    <%= l(:label_spent_time_short) %> 
0
+                </span>
0
+            </td>
0
+            <td class="metric_comment">
0
+                <span title="<%= l(:label_remaining_time) %>">
0
+                    <%= l(:label_remaining_time_short) %> 
0
+                </span>
0
+            </td>
0
+            <% max_progress_width = 70 %>
0
+            <% else
0
+                max_progress_width = 150
0
+            end %>
0
+        </tr>
0
+        <tr class="<%= color_class %>">
0
+            <td class="progress count">
0
+                <%= count = metrics[:count]; progress_bar((count[:closed].to_f / count[:total])*100, 
0
+                      :legend => 
0
+                        "<span title=\"#{l(:label_closed_issues_plural)}\">" + 
0
+                        "#{count[:closed]}</span>/" + 
0
+                        "<span title=\"#{l(:label_total).chars.downcase}\">" + 
0
+                        "#{count[:total]}</span>",                          
0
+                      :width => "#{(count[:total].to_f / max[:count] * max_progress_width).floor}px;") %>
0
+            </td>
0
+            <% 
0
+              time = metrics[:time]
0
+              if spent_time_allowed %>
0
+            <td class="progress time">
0
+                <%= progress_bar(time_progress(time)*100, 
0
+                      :legend => 
0
+                        "<span title=\"#{l(:label_spent_time)}\">" + 
0
+                        "#{time[:spent].ceil}</span>/" + 
0
+                        "<span title=\"#{l(:label_current_total_time)}\">" + 
0
+                        "#{time[:total].ceil}</span>",                          
0
+                      :width => "#{(time[:total] / max[:time] * max_progress_width).floor}px;") %>
0
+            </td>
0
+            <% end %>
0
+            <% hours = l(:text_hours_short) %>
0
+            <td class="metric_comment">
0
+                <span title="<%= l(:field_estimated_hours) %>">
0
+                    <%= "#{time[:estimated].ceil}#{hours}" %>
0
+                </span>                
0
+            </td>
0
+            <% if spent_time_allowed %>
0
+            <td class="metric_comment">
0
+                <span title="<%= l(:label_spent_time) %>">
0
+                    <%= "#{time[:spent].ceil}#{hours}" %>
0
+                </span>                                
0
+            </td>
0
+            <td class="metric_comment">
0
+                <span title="<%= l(:label_remaining_time) %>">
0
+                    <%= "#{time[:remaining].ceil}#{hours}" %>
0
+                </span>                                                
0
+            </td>
0
+            <% end %>
0
+        </tr>
0
     <% end %>
0
-    </table>
0
+        </table>
0
 <% end %>
0
 </fieldset>
0
 </form>
...
6
7
8
9
 
10
11
12
...
6
7
8
 
9
10
11
12
0
@@ -6,7 +6,7 @@
0
   <p><strong><%=l(:label_roadmap_due_in)%> <%= distance_of_time_in_words Time.now, version.effective_date %> (<%= format_date(version.effective_date) %>)</strong></p>
0
 <% end %>
0
 
0
-<p><%=h version.description %></p>
0
+<p><%= textilizable version.description %></p>
0
 
0
 <% if version.fixed_issues.count > 0 %>
0
     <%= progress_bar([version.closed_pourcent, version.completed_pourcent], :width => '40em', :legend => ('%0.0f%' % version.completed_pourcent)) %>
...
9
10
11
12
13
 
 
14
15
16
17
18
 
 
 
 
 
 
 
 
 
 
 
19
20
21
...
9
10
11
 
 
12
13
14
15
16
 
 
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0
@@ -9,13 +9,22 @@
0
 <fieldset><legend><%= l(:label_time_tracking) %></legend>
0
 <table>
0
 <tr>
0
-    <td width="130px" align="right"><%= l(:field_estimated_hours) %></td>
0
-    <td width="240px" class="total-hours"width="130px" align="right"><%= html_hours(lwr(:label_f_hour, @version.estimated_hours)) %></td>
0
+    <td class="label"><%= l(:field_estimated_hours) %></td>
0
+    <td class="total-hours"><%= html_hours(lwr(:label_f_hour, @version.estimated_hours)) %></td>
0
 </tr>
0
 <% if User.current.allowed_to?(:view_time_entries, @project) %>
0
 <tr>
0
-    <td width="130px" align="right"><%= l(:label_spent_time) %></td>
0
-    <td width="240px" class="total-hours"><%= html_hours(lwr(:label_f_hour, @version.spent_hours)) %></td>
0
+    <td class="label"><%= l(:label_spent_time) %></td>
0
+    <td class="total-hours"><%= html_hours(lwr(:label_f_hour, @version.spent_hours)) %></td>
0
+</tr>
0
+<tr>
0
+    <td class="label"><%= l(:label_remaining_time) %></td>
0
+    <td class="total-hours"><%= html_hours(lwr(:label_f_hour, @version.remaining_hours)) %></td>
0
+</tr>
0
+<tr>
0
+    <% title = "#{l(:label_spent_time)} + #{l(:label_remaining_time)}" %>
0
+    <td class="label" title="<%= title %>"><%= l(:label_current_total_time) %></td>
0
+    <td class="total-hours" title="<%= title %>"><%= html_hours(lwr(:label_f_hour, @version.total_hours)) %></td>
0
 </tr>
0
 <% end %>
0
 </table>
...
54
55
56
 
57
...
54
55
56
57
58
0
@@ -54,4 +54,5 @@ Rails::Initializer.run do |config|
0
   # Define your email configuration in email.yml instead.
0
   # It will automatically turn deliveries on
0
   config.action_mailer.perform_deliveries = false
0
+  config.time_zone = 'Moscow'
0
 end
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
0
@@ -1,46 +1,49 @@
0
-ActionController::Routing::Routes.draw do |map|
0
-  # Add your own custom routes here.
0
-  # The priority is based upon order of creation: first created -> highest priority.
0
-  
0
-  # Here's a sample route:
0
-  # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
0
-  # Keep in mind you can assign values other than :controller and :action
0
-
0
-  map.home '', :controller => 'welcome'
0
-  map.signin 'login', :controller => 'account', :action => 'login'
0
-  map.signout 'logout', :controller => 'account', :action => 'logout'
0
-  
0
-  map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
0
-  map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
0
-  map.connect 'help/:ctrl/:page', :controller => 'help'
0
-  #map.connect ':controller/:action/:id/:sort_key/:sort_order'
0
-  
0
-  map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
0
-  map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
0
-  map.connect 'projects/:project_id/news/:action', :controller => 'news'
0
-  map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
0
-  map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
0
-  map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
0
-  map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
0
-
0
-  map.with_options :controller => 'repositories' do |omap|
0
-    omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
0
-    omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
0
-    omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
0
-    omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
0
-    omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
0
-    omap.repositories_revision 'repositories/revision/:id/:rev', :action => 'revision'
0
-  end
0
-  
0
-  map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
0
-  map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
0
-  map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
0
-   
0
-  # Allow downloading Web Service WSDL as a file with an extension
0
-  # instead of a file named 'wsdl'
0
-  map.connect ':controller/service.wsdl', :action => 'wsdl'
0
-
0
0
-  # Install the default route as the lowest priority.
0
-  map.connect ':controller/:action/:id'
0
-end
0
+ActionController::Routing::Routes.draw do |map|
0
+  # Add your own custom routes here.
0
+  # The priority is based upon order of creation: first created -> highest priority.
0
+  
0
+  # Here's a sample route:
0
+  # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
0
+  # Keep in mind you can assign values other than :controller and :action
0
+
0
+  map.home '', :controller => 'welcome'
0
+  map.signin 'login', :controller => 'account', :action => 'login'
0
+  map.signout 'logout', :controller => 'account', :action => 'logout'
0
+  map.show_my_account 'my/account/show', :controller => 'my', :action => 'show_account'
0
+  
0
+  map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
0
+  map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
0
+  map.connect 'help/:ctrl/:page', :controller => 'help'
0
+  #map.connect ':controller/:action/:id/:sort_key/:sort_order'
0
+  
0
+  map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
0
+  map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
0
+  map.connect 'projects/:project_id/news/:action', :controller => 'news'
0
+  map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
0
+  map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
0
+  map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
0
+  map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
0
+
0
+  map.users_timelog 'users/:user_id/timelog/:action', :controller => 'timelog'
0
+
0
+  map.with_options :controller => 'repositories' do |omap|
0
+    omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
0
+    omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
0
+    omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
0
+    omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
0
+    omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
0
+    omap.repositories_revision 'repositories/revision/:id/:rev', :action => 'revision'
0
+  end
0
+  
0
+  map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
0
+  map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
0
+  map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
0
+   
0
+  # Allow downloading Web Service WSDL as a file with an extension
0
+  # instead of a file named 'wsdl'
0
+  map.connect ':controller/service.wsdl', :action => 'wsdl'
0
+
0
0
+  # Install the default route as the lowest priority.
0
+  map.connect ':controller/:action/:id'
0
+end
...
632
633
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
636
637
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
0
@@ -632,6 +632,26 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
637
638
639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
641
642
...
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
0
@@ -637,6 +637,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
634
635
636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
638
639
...
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
0
@@ -634,6 +634,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
633
634
635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
637
638
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
0
@@ -633,6 +633,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
170
171
172
 
 
173
174
175
...
523
524
525
 
 
 
 
 
 
 
 
 
526
527
528
...
562
563
564
 
 
565
566
567
...
612
613
614
 
 
615
616
617
...
638
639
640
 
 
 
 
 
 
 
...
170
171
172
173
174
175
176
177
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
573
574
575
576
577
578
579
580
...
625
626
627
628
629
630
631
632
...
653
654
655
656
657
658
659
660
661
662
0
@@ -170,6 +170,8 @@ field_subproject: Subproject
0
 field_hours: Hours
0
 field_activity: Activity
0
 field_spent_on: Date
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
 field_identifier: Identifier
0
 field_is_filter: Used as a filter
0
 field_issue_to_id: Related issue
0
@@ -523,6 +525,15 @@ label_reverse_chronological_order: In reverse chronological order
0
 label_planning: Planning
0
 label_incoming_emails: Incoming emails
0
 label_generate_key: Generate a key
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+label_spent_time_log: Spent time log
0
+
0
 label_issue_watchers: Watchers
0
 
0
 button_login: Login
0
@@ -562,6 +573,8 @@ button_copy: Copy
0
 button_annotate: Annotate
0
 button_update: Update
0
 button_configure: Configure
0
+
0
+button_now: Now
0
 button_quote: Quote
0
 
0
 status_active: active
0
@@ -612,6 +625,8 @@ text_user_wrote: '%s wrote:'
0
 text_enumeration_destroy_question: '%d objects are assigned to this value.'
0
 text_enumeration_category_reassign_to: 'Reassign them to this value:'
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+text_not_assigned: Not assigned
0
+text_hours_short: h
0
 
0
 default_role_manager: Manager
0
 default_role_developper: Developer
0
@@ -638,3 +653,10 @@ default_activity_development: Development
0
 enumeration_issue_priorities: Issue priorities
0
 enumeration_doc_categories: Document categories
0
 enumeration_activities: Activities (time tracking)
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
...
635
636
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
639
640
...
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
0
@@ -635,6 +635,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
632
633
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
636
637
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
0
@@ -632,6 +632,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
638
639
640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
0
@@ -638,3 +638,22 @@ default_activity_development: Développement
0
 enumeration_issue_priorities: Priorités des demandes
0
 enumeration_doc_categories: Catégories des documents
0
 enumeration_activities: Activités (suivi du temps)
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
...
632
633
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
636
637
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
0
@@ -632,6 +632,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
633
634
635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
637
638
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
0
@@ -633,6 +633,27 @@ label_generate_key: Kulcs generálása
0
 setting_mail_handler_api_enabled: Web Service engedélyezése a beérkezett levelekhez
0
 setting_mail_handler_api_key: API kulcs
0
 text_email_delivery_not_configured: "Az E-mail küldés nincs konfigurálva, és az értesítések ki vannak kapcsolva.\nÁllítsd be az SMTP szervert a config/email.yml fájlban és indítsd újra az alkalmazást, hogy érvénybe lépjen."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
632
633
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
636
637
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
0
@@ -632,6 +632,27 @@ label_generate_key: Genera una chiave
0
 setting_mail_handler_api_enabled: Abilita WS per le e-mail in arrivo
0
 setting_mail_handler_api_key: chiave API
0
 text_email_delivery_not_configured: "La consegna via e-mail non è configurata e le notifiche sono disabilitate.\nConfigura il tuo server SMTP in config/email.yml e riavvia l'applicazione per abilitarle."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
633
634
635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
637
638
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
0
@@ -633,6 +633,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
632
633
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
636
637
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
0
@@ -632,6 +632,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
635
636
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
639
640
...
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
0
@@ -635,6 +635,27 @@ setting_mail_handler_api_enabled: Įgalinti WS įeinantiems laiškams
0
 setting_mail_handler_api_key: API raktas
0
 
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
633
634
635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
637
638
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
0
@@ -633,6 +633,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
633
634
635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
636
637
638
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
0
@@ -633,6 +633,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
632
633
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
636
637
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
0
@@ -632,6 +632,27 @@ label_generate_key: Generate a key
0
 setting_mail_handler_api_enabled: Enable WS for incoming emails
0
 setting_mail_handler_api_key: API key
0
 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
0
+
0
+field_start_time: Start Time
0
+field_end_time: End Time
0
+text_in_progress: in progress
0
+activerecord_error_field_must_be_set_if_other_is_not: must be set if %s is not
0
+text_clear_to_recalculate_time_by_range: Clear to recalculate by start/end time
0
+text_and: and
0
+text_time_entry_intersecting_notice: Notice that this timelog entry (%s) intersects with %s.
0
+text_time_entry_intersecting_notice_entry: entry
0
+text_time_entry_intersecting_notice_entry_plural: entries
0
+button_now: Now
0
+label_remaining_time: Remaining time
0
+label_current_total_time: Total
0
+label_estimated_time_short: Est.
0
+label_spent_time_short: Spent
0
+label_remaining_time_short: Rem.
0
+label_issues_count: Count
0
+label_time: Time
0
+text_hours_short: h
0
+text_not_assigned: Not assigned
0
+
0
 field_parent_title: Parent page
0
 label_issue_watchers: Watchers
0
 setting_commit_logs_encoding: Commit messages encoding
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
0
@@ -1,639 +1,660 @@
0
-_gloc_rule_default: '|n| n==1 ? "" : "_plural" '
0
-
0
-actionview_datehelper_select_day_prefix:
0
-actionview_datehelper_select_month_names: Janeiro,Fevereiro,Março,Abrill,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro
0
-actionview_datehelper_select_month_names_abbr: Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez
0
-actionview_datehelper_select_month_prefix:
0
-actionview_datehelper_select_year_prefix:
0
-actionview_datehelper_time_in_words_day: 1 dia
0
-actionview_datehelper_time_in_words_day_plural: %d dias
0
-actionview_datehelper_time_in_words_hour_about: aproximadamente uma hora
0
-actionview_datehelper_time_in_words_hour_about_plural: aproximadamente %d horas
0
-actionview_datehelper_time_in_words_hour_about_single: aproximadamente uma hora
0
-actionview_datehelper_time_in_words_minute: 1 minuto
0
-actionview_datehelper_time_in_words_minute_half: meio minuto
0
-actionview_datehelper_time_in_words_minute_less_than: menos de um minuto
0
-actionview_datehelper_time_in_words_minute_plural: %d minutos
0
-actionview_datehelper_time_in_words_minute_single: 1 minuto
0
-actionview_datehelper_time_in_words_second_less_than: menos de um segundo
0
-actionview_datehelper_time_in_words_second_less_than_plural: menos de %d segundos
0
-actionview_instancetag_blank_option: Selecione
0
-
0
-activerecord_error_inclusion: não está incluso na lista
0
-activerecord_error_exclusion: está reservado
0
-activerecord_error_invalid: é inválido
0
-activerecord_error_confirmation: confirmação não confere
0
-activerecord_error_accepted: deve ser aceito
0
-activerecord_error_empty: não pode ser vazio
0
-activerecord_error_blank: não pode estar em branco
0
-activerecord_error_too_long: é muito longo
0
-activerecord_error_too_short: é muito curto
0
-activerecord_error_wrong_length: esta com o tamanho errado
0
-activerecord_error_taken: já foi obtido
0
-activerecord_error_not_a_number: não é um numero
0
-activerecord_error_not_a_date: não é uma data valida
0
-activerecord_error_greater_than_start_date: deve ser maior que a data inicial
0
-activerecord_error_not_same_project: não pode pertencer ao mesmo projeto
0
-activerecord_error_circular_dependency: Esta relação geraria uma dependência circular
0
-
0
-general_fmt_age: %d ano
0
-general_fmt_age_plural: %d anos
0
-general_fmt_date: %%d/%%m/%%Y
0
-general_fmt_datetime: %%d/%%m/%%Y %%I:%%M %%p