Feature #5458 » 5458-use_time_in_issue_start_and_due_date_2.3_stable.diff
| app/controllers/issues_controller.rb (working copy) | ||
|---|---|---|
| 406 | 406 | 
    render_error l(:error_no_tracker_in_project)  | 
| 407 | 407 | 
    return false  | 
| 408 | 408 | 
    end  | 
| 409 | 
        @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
   | 
|
| 409 | 
        @issue.start_date ||= DateTime.now if Setting.default_issue_start_date_to_creation_date?
   | 
|
| 410 | 410 | 
    @issue.safe_attributes = params[:issue]  | 
| 411 | 411 | |
| 412 | 412 | 
    @priorities = IssuePriority.active  | 
| app/helpers/application_helper.rb (working copy) | ||
|---|---|---|
| 291 | 291 | 
    end  | 
| 292 | 292 | 
    end  | 
| 293 | 293 | |
| 294 | 
      def time_select_tag( name, stime, options = {} )
   | 
|
| 295 | 
    time = stime.to_time(:utc)  | 
|
| 296 | 
    if time.nil?  | 
|
| 297 | 
          selected = {:hour => '', :min => ''}
   | 
|
| 298 | 
    else  | 
|
| 299 | 
    time = time.in_time_zone(User.current.time_zone)  | 
|
| 300 | 
          selected = {:hour => time.hour, :min => time.min}
   | 
|
| 301 | 
    end  | 
|
| 302 | ||
| 303 | 
    out = ''  | 
|
| 304 | ||
| 305 | 
    if options[:required]  | 
|
| 306 | 
    hours = []  | 
|
| 307 | 
    mins = []  | 
|
| 308 | 
    else  | 
|
| 309 | 
    hours = [['', '']]  | 
|
| 310 | 
    mins = [['', '']]  | 
|
| 311 | 
    end  | 
|
| 312 | ||
| 313 | 
        hours += (0..23).map{|i| ['%02d' % i, i] } # Zero pad
   | 
|
| 314 | 
    out << select_tag(  | 
|
| 315 | 
          "#{name}[hour]",
   | 
|
| 316 | 
    options_for_select( hours, selected[:hour] ),  | 
|
| 317 | 
    :style => 'min-width: 10px;max-width: 50px;'  | 
|
| 318 | 
    )  | 
|
| 319 | ||
| 320 | 
    out << ':'  | 
|
| 321 | 
        mins += (0..59).map{|i| ['%02d' % i, i] } # Zero pad
   | 
|
| 322 | 
    out << select_tag(  | 
|
| 323 | 
          "#{name}[minute]",
   | 
|
| 324 | 
    options_for_select( mins, selected[:min] ),  | 
|
| 325 | 
    :style => 'min-width: 10px;max-width: 50px;'  | 
|
| 326 | 
    )  | 
|
| 327 | 
    end  | 
|
| 328 | ||
| 294 | 329 | 
      def project_tree_options_for_select(projects, options = {})
   | 
| 295 | 330 | 
    s = ''  | 
| 296 | 331 | 
    project_tree(projects) do |project, level|  | 
| app/helpers/queries_helper.rb (working copy) | ||
|---|---|---|
| 100 | 100 | 
    h(value)  | 
| 101 | 101 | 
    end  | 
| 102 | 102 | 
    when 'Time'  | 
| 103 | 
    format_time(value)  | 
|
| 103 | 
    if ( column.name == :start_date or column.name == :due_date ) and  | 
|
| 104 | 
            ( !issue.project.use_datetime_for_issues or value.strftime('%H%M')=='0000' )
   | 
|
| 105 | 
    format_date(value)  | 
|
| 106 | 
    else  | 
|
| 107 | 
    format_time(value)  | 
|
| 108 | 
    end  | 
|
| 104 | 109 | 
    when 'Date'  | 
| 105 | 110 | 
    format_date(value)  | 
| 106 | 111 | 
    when 'Fixnum'  | 
| app/models/issue.rb (working copy) | ||
|---|---|---|
| 61 | 61 | 
    DONE_RATIO_OPTIONS = %w(issue_field issue_status)  | 
| 62 | 62 | |
| 63 | 63 | 
    attr_reader :current_journal  | 
| 64 | 
    attr_accessor :start_time  | 
|
| 65 | 
    attr_accessor :due_time  | 
|
| 66 | ||
| 64 | 67 | 
    delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true  | 
| 65 | 68 | |
| 66 | 69 | 
    validates_presence_of :subject, :priority, :project, :tracker, :author, :status  | 
| ... | ... | |
| 68 | 71 | 
    validates_length_of :subject, :maximum => 255  | 
| 69 | 72 | 
    validates_inclusion_of :done_ratio, :in => 0..100  | 
| 70 | 73 | 
      validates :estimated_hours, :numericality => {:greater_than_or_equal_to => 0, :allow_nil => true, :message => :invalid}
   | 
| 71 | 
    validates :start_date, :date => true  | 
|
| 72 | 
    validates :due_date, :date => true  | 
|
| 74 | 
      #validates :start_date, :date => true
   | 
|
| 75 | 
      #validates :due_date, :date => true
   | 
|
| 73 | 76 | 
    validate :validate_issue, :validate_required_fields  | 
| 74 | 77 | |
| 75 | 78 | 
      scope :visible, lambda {|*args|
   | 
| ... | ... | |
| 90 | 93 | 
        ids.any? ? where(:fixed_version_id => ids) : where('1=0')
   | 
| 91 | 94 | 
    }  | 
| 92 | 95 | |
| 93 | 
    before_create :default_assign  | 
|
| 94 | 
    before_save :close_duplicates, :update_done_ratio_from_issue_status, :force_updated_on_change, :update_closed_on  | 
|
| 96 | 
      before_create :default_assign, :add_start_and_due_time
   | 
|
| 97 | 
      before_save :close_duplicates, :update_done_ratio_from_issue_status, :force_updated_on_change, :update_closed_on, :add_start_and_due_time
   | 
|
| 95 | 98 | 
      after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?} 
   | 
| 96 | 99 | 
    after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal  | 
| 97 | 100 | 
    # Should be after_create but would be called before previous after_save callbacks  | 
| ... | ... | |
| 361 | 364 | 
    'subject',  | 
| 362 | 365 | 
    'description',  | 
| 363 | 366 | 
    'start_date',  | 
| 367 | 
    'start_time',  | 
|
| 364 | 368 | 
    'due_date',  | 
| 369 | 
    'due_time',  | 
|
| 365 | 370 | 
    'done_ratio',  | 
| 366 | 371 | 
    'estimated_hours',  | 
| 367 | 372 | 
    'custom_field_values',  | 
| ... | ... | |
| 1387 | 1392 | 
    end  | 
| 1388 | 1393 | 
    end  | 
| 1389 | 1394 | |
| 1395 | 
    # Callback on start and due time  | 
|
| 1396 | 
    def add_start_and_due_time  | 
|
| 1397 | 
    return if not project.use_datetime_for_issues  | 
|
| 1398 | ||
| 1399 | 
    # Not sure if this is a hack or not, but it works :)  | 
|
| 1400 | 
    time_zone = User.current.time_zone  | 
|
| 1401 | 
    system_time_zone = Time.zone  | 
|
| 1402 | 
    if time_zone  | 
|
| 1403 | 
    Time.zone = time_zone  | 
|
| 1404 | 
    end  | 
|
| 1405 | ||
| 1406 | 
    if st=start_time and sd=start_date  | 
|
| 1407 | 
    if st['hour'].to_i >= 0 or st['minute'].to_i >= 0  | 
|
| 1408 | 
            self.start_date = Time.zone.parse( "#{sd.year}.#{sd.month}.#{sd.day} #{st['hour']}:#{st['minute']}:00" ).utc # Parse in as local but save as UTC
   | 
|
| 1409 | 
    end  | 
|
| 1410 | 
    end  | 
|
| 1411 | ||
| 1412 | 
    if dt=due_time and dd=due_date  | 
|
| 1413 | 
    if dt['hour'].to_i >= 0 or dt['minute'].to_i >= 0  | 
|
| 1414 | 
            self.due_date = Time.zone.parse( "#{dd.year}.#{dd.month}.#{dd.day} #{dt['hour']}:#{dt['minute']}:00").utc # Parse in as local but save as UTC
   | 
|
| 1415 | 
    end  | 
|
| 1416 | 
    end  | 
|
| 1417 | ||
| 1418 | 
    # Since we fudged the timezone to get the values parsing in okay, let's reset it to the system timezone.  | 
|
| 1419 | 
    Time.zone = system_time_zone  | 
|
| 1420 | 
    end  | 
|
| 1421 | ||
| 1390 | 1422 | 
    # Default assignment based on category  | 
| 1391 | 1423 | 
    def default_assign  | 
| 1392 | 1424 | 
    if assigned_to.nil? && category && category.assigned_to  | 
| app/models/project.rb (working copy) | ||
|---|---|---|
| 650 | 650 | 
    'description',  | 
| 651 | 651 | 
    'homepage',  | 
| 652 | 652 | 
    'is_public',  | 
| 653 | 
    'use_datetime_for_issues',  | 
|
| 653 | 654 | 
    'identifier',  | 
| 654 | 655 | 
    'custom_field_values',  | 
| 655 | 656 | 
    'custom_fields',  | 
| app/views/issues/_attributes.html.erb (working copy) | ||
|---|---|---|
| 47 | 47 | 
    <% end %>  | 
| 48 | 48 | |
| 49 | 49 | 
    <% if @issue.safe_attribute? 'start_date' %>  | 
| 50 | 
    <p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('start_date') %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
   | 
|
| 50 | 
    <p>  | 
|
| 51 | 
      <%= f.text_field :start_date, :value => (@issue.start_date ? @issue.start_date.strftime('%Y-%m-%d') : ''), :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('start_date') %>
   | 
|
| 52 | 
      <%= calendar_for('issue_start_date') if @issue.leaf? %>
   | 
|
| 53 | 
      <%== time_select_tag( "issue[start_time]", @issue.start_date.to_s, :required => @issue.required_attribute?('start_date') ) if @project.use_datetime_for_issues %>
   | 
|
| 54 | 
    </p>  | 
|
| 51 | 55 | 
    <% end %>  | 
| 52 | 56 | |
| 53 | 57 | 
    <% if @issue.safe_attribute? 'due_date' %>  | 
| 54 | 
    <p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('due_date') %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
   | 
|
| 58 | 
    <p><%= f.text_field :due_date, :value => (@issue.due_date ? @issue.due_date.strftime('%Y-%m-%d') : ''), :size => 10, :disabled => !@issue.leaf?, :required => @issue.required_attribute?('due_date') %>
   | 
|
| 59 | 
       <%= calendar_for('issue_due_date') if @issue.leaf? %>
   | 
|
| 60 | 
       <%== time_select_tag( "issue[due_time]", @issue.due_date.to_s, :required => @issue.required_attribute?('due_date') ) if @project.use_datetime_for_issues %>
   | 
|
| 61 | 
    </p>  | 
|
| 55 | 62 | 
    <% end %>  | 
| 56 | 63 | |
| 57 | 64 | 
    <% if @issue.safe_attribute? 'estimated_hours' %>  | 
| app/views/issues/show.html.erb (working copy) | ||
|---|---|---|
| 47 | 47 | 
    end  | 
| 48 | 48 | |
| 49 | 49 | 
      unless @issue.disabled_core_fields.include?('start_date')
   | 
| 50 | 
        rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date'
   | 
|
| 50 | 
        rows.right l(:field_start_date), (@project.use_datetime_for_issues ? format_time(@issue.start_date) : format_date(@issue.start_date)), :class => 'start-date'
   | 
|
| 51 | 51 | 
    end  | 
| 52 | 52 | 
      unless @issue.disabled_core_fields.include?('due_date')
   | 
| 53 | 
        rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date'
   | 
|
| 53 | 
        rows.right l(:field_due_date), (@project.use_datetime_for_issues ? format_time(@issue.due_date) : format_date(@issue.due_date)), :class => 'due-date'
   | 
|
| 54 | 54 | 
    end  | 
| 55 | 55 | 
      unless @issue.disabled_core_fields.include?('done_ratio')
   | 
| 56 | 56 | 
        rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress'
   | 
| app/views/projects/_form.html.erb (working copy) | ||
|---|---|---|
| 11 | 11 | 
    <% end %></p>  | 
| 12 | 12 | 
    <p><%= f.text_field :homepage, :size => 60 %></p>  | 
| 13 | 13 | 
    <p><%= f.check_box :is_public %></p>  | 
| 14 | 
    <p><%= f.check_box :use_datetime_for_issues %></p>  | 
|
| 14 | 15 | |
| 15 | 16 | 
    <% unless @project.allowed_parents.compact.empty? %>  | 
| 16 | 17 | 
    <p><%= label(:project, :parent_id, l(:field_parent)) %><%= parent_project_select_tag(@project) %></p>  | 
| config/locales/cs.yml (working copy) | ||
|---|---|---|
| 311 | 311 | 
    field_assigned_to_role: Role přiřaditele  | 
| 312 | 312 | 
    field_text: Textové pole  | 
| 313 | 313 | 
    field_visible: Viditelný  | 
| 314 | ||
| 314 | 
    field_use_datetime_for_issues: Použít u tiketů také čas  | 
|
| 315 | 315 | 
    setting_app_title: Název aplikace  | 
| 316 | 316 | 
    setting_app_subtitle: Podtitulek aplikace  | 
| 317 | 317 | 
    setting_welcome_text: Uvítací text  | 
| config/locales/en-GB.yml (working copy) | ||
|---|---|---|
| 311 | 311 | 
    field_assigned_to_role: "Assignee's role"  | 
| 312 | 312 | 
    field_text: Text field  | 
| 313 | 313 | 
    field_visible: Visible  | 
| 314 | 
    field_use_datetime_for_issues: Use time in tickets too  | 
|
| 314 | 315 | 
    field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"  | 
| 315 | 316 | |
| 316 | 317 | 
    setting_app_title: Application title  | 
| config/locales/en.yml (working copy) | ||
|---|---|---|
| 314 | 314 | 
    field_assigned_to_role: "Assignee's role"  | 
| 315 | 315 | 
    field_text: Text field  | 
| 316 | 316 | 
    field_visible: Visible  | 
| 317 | 
    field_use_datetime_for_issues: Use time in tickets too  | 
|
| 317 | 318 | 
    field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"  | 
| 318 | 319 | 
    field_issues_visibility: Issues visibility  | 
| 319 | 320 | 
    field_is_private: Private  | 
| config/locales/es.yml (working copy) | ||
|---|---|---|
| 1110 | 1110 | 
    button_hide: Ocultar  | 
| 1111 | 1111 | 
    setting_non_working_week_days: Días no laborables  | 
| 1112 | 1112 | 
    label_in_the_next_days: en los próximos  | 
| 1113 | 
    field_use_datetime_for_issues: Usar hora en prog peticiones  | 
|
| 1113 | 1114 | 
    label_in_the_past_days: en los anteriores  | 
| 1114 | 1115 | 
      label_attribute_of_user: "%{name} del usuario"
   | 
| 1115 | 1116 | 
    text_turning_multiple_off: Si desactiva los valores múltiples, éstos serán eliminados para dejar un único valor por elemento.  | 
| db/migrate/20130531174459_add_time_to_issue_start_date_and_issue_due_date.rb (working copy) | ||
|---|---|---|
| 1 | 
    class AddTimeToIssueStartDateAndIssueDueDate < ActiveRecord::Migration  | 
|
| 2 | 
    def self.up  | 
|
| 3 | 
    change_column :issues, :start_date, :datetime  | 
|
| 4 | 
    change_column :issues, :due_date, :datetime  | 
|
| 5 | 
    end  | 
|
| 6 | 
     | 
|
| 7 | 
    def self.down  | 
|
| 8 | 
    change_column :issues, :start_date, :date  | 
|
| 9 | 
    change_column :issues, :due_date, :date  | 
|
| 10 | 
    end  | 
|
| 11 | 
    end  | 
|
| db/migrate/20130531174549_add_use_datetime_for_issues_to_projects.rb (working copy) | ||
|---|---|---|
| 1 | 
    class AddUseDatetimeForIssuesToProjects < ActiveRecord::Migration  | 
|
| 2 | ||
| 3 | 
    def self.up  | 
|
| 4 | 
    add_column :projects, :use_datetime_for_issues, :boolean, :default => false  | 
|
| 5 | 
    end  | 
|
| 6 | ||
| 7 | 
    def self.down  | 
|
| 8 | 
    remove_column :projects, :use_datetime_for_issues  | 
|
| 9 | 
    end  | 
|
| 10 | ||
| 11 | 
    end  | 
|
| lib/redmine/utils.rb (working copy) | ||
|---|---|---|
| 60 | 60 | 
    weeks = days / 7  | 
| 61 | 61 | 
    result = weeks * (7 - non_working_week_days.size)  | 
| 62 | 62 | 
    days_left = days - weeks * 7  | 
| 63 | 
    start_cwday = from.cwday  | 
|
| 63 | 
              start_cwday = from.to_date.cwday
   | 
|
| 64 | 64 | 
    days_left.times do |i|  | 
| 65 | 65 | 
    unless non_working_week_days.include?(((start_cwday + i - 1) % 7) + 1)  | 
| 66 | 66 | 
    result += 1  | 
| ... | ... | |
| 78 | 78 | 
    weeks = working_days / (7 - non_working_week_days.size)  | 
| 79 | 79 | 
    result = weeks * 7  | 
| 80 | 80 | 
    days_left = working_days - weeks * (7 - non_working_week_days.size)  | 
| 81 | 
    cwday = date.cwday  | 
|
| 81 | 
              cwday = date.to_date.cwday
   | 
|
| 82 | 82 | 
    while days_left > 0  | 
| 83 | 83 | 
    cwday += 1  | 
| 84 | 84 | 
    unless non_working_week_days.include?(((cwday - 1) % 7) + 1)  | 
| ... | ... | |
| 94 | 94 | |
| 95 | 95 | 
    # Returns the date of the first day on or after the given date that is a working day  | 
| 96 | 96 | 
    def next_working_date(date)  | 
| 97 | 
    cwday = date.cwday  | 
|
| 97 | 
            cwday = date.to_date.cwday
   | 
|
| 98 | 98 | 
    days = 0  | 
| 99 | 99 | 
    while non_working_week_days.include?(((cwday + days - 1) % 7) + 1)  | 
| 100 | 100 | 
    days += 1  |