diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index e7e455081a..a4349a16a5 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -374,28 +374,28 @@ function observeProjectIdentifier() {
}
function observeParentIssueField(url) {
- new Ajax.Autocompleter('issue_parent_issue_id',
+ new Ajax.Autocompleter('issue_parent_id',
'parent_issue_candidates',
url,
{ minChars: 1,
frequency: 0.5,
paramName: 'q',
updateElement: function(value) {
- document.getElementById('issue_parent_issue_id').value = value.id;
+ document.getElementById('issue_parent_id').value = value.id;
},
parameters: 'scope=all'
});
}
function observeWorkPackageParentField(url) {
- new Ajax.Autocompleter('work_package_parent_issue_id',
+ new Ajax.Autocompleter('work_package_parent_id',
'parent_issue_candidates',
url,
{ minChars: 1,
frequency: 0.5,
paramName: 'q',
updateElement: function(value) {
- document.getElementById('work_package_parent_issue_id').value = value.id;
+ document.getElementById('work_package_parent_id').value = value.id;
},
parameters: 'scope=all'
});
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index accf4d0427..ce26b5ee70 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -145,7 +145,7 @@ class IssuesController < ApplicationController
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
respond_to do |format|
format.html {
- redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:type_id => @issue.type, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
+ redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:type_id => @issue.type, :parent_id => @issue.parent_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
end
diff --git a/app/helpers/work_packages_helper.rb b/app/helpers/work_packages_helper.rb
index 57155a8a79..ed38e4c2f0 100644
--- a/app/helpers/work_packages_helper.rb
+++ b/app/helpers/work_packages_helper.rb
@@ -269,7 +269,7 @@ module WorkPackagesHelper
def work_package_form_parent_attribute(form, work_package, locals = {})
if User.current.allowed_to?(:manage_subtasks, locals[:project])
field = if work_package.is_a?(Issue)
- form.text_field :parent_issue_id, :size => 10, :title => l(:description_autocomplete)
+ form.text_field :parent_id, :size => 10, :title => l(:description_autocomplete)
else
form.text_field :parent_id, :size => 10, :title => l(:description_autocomplete)
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 08d9cec480..0c2783e574 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -33,7 +33,6 @@ class Issue < WorkPackage
validate :validate_fixed_version_is_assignable
validate :validate_fixed_version_is_still_open
validate :validate_enabled_type
- validate :validate_correct_parent
scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
@@ -66,30 +65,8 @@ class Issue < WorkPackage
title << ')'
end
- # find all issues
- # * having set a parent_id where the root_id
- # 1) points to self
- # 2) points to an issue with a parent
- # 3) points to an issue having a different root_id
- # * having not set a parent_id but a root_id
- # This unfortunately does not find the issue with the id 3 in the following example
- # | id | parent_id | root_id |
- # | 1 | | 1 |
- # | 2 | 1 | 2 |
- # | 3 | 2 | 2 |
- # This would only be possible using recursive statements
- #scope :invalid_root_ids, { :conditions => "(issues.parent_id IS NOT NULL AND " +
- # "(issues.root_id = issues.id OR " +
- # "(issues.root_id = parent_issues.id AND parent_issues.parent_id IS NOT NULL) OR " +
- # "(issues.root_id != parent_issues.root_id))" +
- # ") OR " +
- # "(issues.parent_id IS NULL AND issues.root_id != issues.id)",
- # :joins => "LEFT OUTER JOIN issues parent_issues ON parent_issues.id = issues.parent_id" }
-
before_create :default_assign
before_save :close_duplicates, :update_done_ratio_from_issue_status
- after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes
- after_destroy :update_parent_attributes
before_destroy :remove_attachments
after_initialize :set_default_values
@@ -109,7 +86,7 @@ class Issue < WorkPackage
# Moves/copies an issue to a new project and type
# Returns the moved/copied issue on success, false on failure
def move_to_project(*args)
- ret = Issue.transaction do
+ Issue.transaction do
move_to_project_without_transaction(*args) || raise(ActiveRecord::Rollback)
end || false
end
@@ -132,7 +109,7 @@ class Issue < WorkPackage
if !Setting.cross_project_issue_relations? &&
parent && parent.project_id != project_id
- self.parent_issue_id = nil
+ self.parent_id = nil
end
end
if new_type
@@ -206,7 +183,7 @@ class Issue < WorkPackage
safe_attributes 'type_id',
'status_id',
- 'parent_issue_id',
+ 'parent_id',
'category_id',
'assigned_to_id',
'priority_id',
@@ -251,15 +228,15 @@ class Issue < WorkPackage
end
end
- if @parent_issue.present?
+ if parent.present?
attrs.reject! {|k,v| %w(priority_id done_ratio start_date due_date estimated_hours).include?(k)}
end
- if attrs.has_key?('parent_issue_id')
+ if attrs.has_key?('parent_id')
if !user.allowed_to?(:manage_subtasks, project)
- attrs.delete('parent_issue_id')
- elsif !attrs['parent_issue_id'].blank?
- attrs.delete('parent_issue_id') unless Issue.visible(user).exists?(attrs['parent_issue_id'].to_i)
+ attrs.delete('parent_id')
+ elsif !attrs['parent_id'].blank?
+ attrs.delete('parent_id') unless WorkPackage.visible(user).exists?(attrs['parent_id'].to_i)
end
end
@@ -322,24 +299,6 @@ class Issue < WorkPackage
end
end
- def validate_correct_parent
- # Checks parent issue assignment
- if @parent_issue
- if !Setting.cross_project_issue_relations? && @parent_issue.project_id != self.project_id
- errors.add :parent_issue_id, :not_a_valid_parent
- elsif !new_record?
- # moving an existing issue
- if @parent_issue.root_id != root_id
- # we can always move to another tree
- elsif move_possible?(@parent_issue)
- # move accepted inside tree
- else
- errors.add :parent_issue_id, :not_a_valid_parent
- end
- end
- end
- end
-
# Set the done_ratio using the status if that setting is set. This will keep the done_ratios
# even if the user turns off the setting later
def update_done_ratio_from_issue_status
@@ -379,12 +338,6 @@ class Issue < WorkPackage
return done_date <= Date.today
end
- # Does this issue have children?
- def children?
- !leaf?
- end
-
-
# Returns an array of status that user is able to apply
def new_statuses_allowed_to(user, include_default=false)
return [] if status.nil?
@@ -447,23 +400,6 @@ class Issue < WorkPackage
end
end
- # The number of "items" this issue spans in it's nested set
- #
- # A parent issue would span all of it's children + 1 left + 1 right (3)
- #
- # | parent |
- # || child ||
- #
- # A child would span only itself (1)
- #
- # |child|
- def nested_set_span
- rgt - lft
- end
-
- # TODO: remove. This is left here to avoid regression
- # but the code was duplicated to work_packages_helper
- # and thus should be removed as soon as possible.
# Returns a string of css classes that apply to the issue
def css_classes
s = "issue status-#{status.position} priority-#{priority.position}"
@@ -536,26 +472,6 @@ class Issue < WorkPackage
Issue.update_versions(["#{Version.table_name}.project_id IN (?) OR #{Issue.table_name}.project_id IN (?)", moved_project_ids, moved_project_ids])
end
- def parent_issue_id=(arg)
- parent_issue_id = arg.blank? ? nil : arg.to_i
- if parent_issue_id && @parent_issue = Issue.find_by_id(parent_issue_id)
- journal_changes["parent_id"] = [self.parent_id, @parent_issue.id]
- @parent_issue.id
- else
- @parent_issue = nil
- journal_changes["parent_id"] = [self.parent_id, nil]
- nil
- end
- end
-
- def parent_issue_id
- if instance_variable_defined? :@parent_issue
- @parent_issue.nil? ? nil : @parent_issue.id
- else
- parent_id
- end
- end
-
# Extracted from the ReportsController.
def self.by_type(project)
count_and_group_by(:project => project,
@@ -623,110 +539,9 @@ class Issue < WorkPackage
projects
end
- # method from acts_as_nested_set
- def self.valid?
- super && invalid_root_ids.empty?
- end
-
- def self.all_invalid
- (super + invalid_root_ids).uniq
- end
-
- def self.rebuild_silently!(roots = nil)
-
- invalid_root_ids_to_fix = if roots.is_a? Array
- roots
- elsif roots.present?
- [roots]
- else
- []
- end
-
- known_issue_parents = Hash.new do |hash, ancestor_id|
- hash[ancestor_id] = Issue.find_by_id(ancestor_id)
- end
-
- fix_known_invalid_root_ids = lambda do
- issues = invalid_root_ids
-
- issues_roots = []
-
- issues.each do |issue|
- # At this point we can not trust nested set methods as the root_id is invalid.
- # Therefore we trust the parent_issue_id to fetch all ancestors until we find the root
- ancestor = issue
-
- while ancestor.parent_issue_id do
- ancestor = known_issue_parents[ancestor.parent_issue_id]
- end
-
- issues_roots << ancestor
-
- if invalid_root_ids_to_fix.empty? || invalid_root_ids_to_fix.map(&:id).include?(ancestor.id)
- Issue.update_all({ :root_id => ancestor.id },
- { :id => issue.id })
- end
- end
-
- fix_known_invalid_root_ids.call unless (issues_roots.map(&:id) & invalid_root_ids_to_fix.map(&:id)).empty?
- end
-
- fix_known_invalid_root_ids.call
-
- super
- end
private
- def update_nested_set_attributes
- if root_id.nil?
- # issue was just created
- self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id)
- set_default_left_and_right
- Issue.update_all("root_id = #{root_id}, lft = #{lft}, rgt = #{rgt}", ["id = ?", id])
- if @parent_issue
- move_to_child_of(@parent_issue)
- end
- reload
- elsif parent_issue_id != parent_id
- former_parent_id = parent_id
- # moving an existing issue
- if @parent_issue && @parent_issue.root_id == root_id
- # inside the same tree
- move_to_child_of(@parent_issue)
- else
- # to another tree
- unless root?
- move_to_right_of(root)
- reload
- end
- old_root_id = root_id
- self.root_id = (@parent_issue.nil? ? id : @parent_issue.root_id )
- target_maxright = nested_set_scope.maximum(right_column_name) || 0
- offset = target_maxright + 1 - lft
- Issue.update_all("root_id = #{root_id}, lft = lft + #{offset}, rgt = rgt + #{offset}",
- ["root_id = ? AND lft >= ? AND rgt <= ? ", old_root_id, lft, rgt])
- self[left_column_name] = lft + offset
- self[right_column_name] = rgt + offset
- if @parent_issue
- move_to_child_of(@parent_issue)
- end
- end
- reload
-
- # delete invalid relations of all descendants
- self_and_descendants.each do |issue|
- issue.relations.each do |relation|
- relation.destroy unless relation.valid?
- end
- end
-
- # update former parent
- recalculate_attributes_for(former_parent_id) if former_parent_id
- end
- remove_instance_variable(:@parent_issue) if instance_variable_defined?(:@parent_issue)
- end
-
# this removes all attachments separately before destroying the issue
# avoids getting a ActiveRecord::StaleObjectError when deleting an issue
def remove_attachments
@@ -735,9 +550,6 @@ class Issue < WorkPackage
reload # important
end
- def update_parent_attributes
- recalculate_attributes_for(parent_id) if parent_id
- end
# Update issues so their versions are not pointing to a
# fixed_version that is not shared with the issue's project
diff --git a/app/models/permitted_params.rb b/app/models/permitted_params.rb
index 45a8247d64..a7dc23f3bb 100644
--- a/app/models/permitted_params.rb
+++ b/app/models/permitted_params.rb
@@ -147,7 +147,7 @@ class PermittedParams < Struct.new(:params, :user)
:due_date,
:planning_element_type_id,
:parent_id,
- :parent_issue_id,
+ :parent_id,
:assigned_to_id,
:responsible_id,
:type_id,
diff --git a/app/models/planning_element.rb b/app/models/planning_element.rb
index 27e959f61d..b904151035 100644
--- a/app/models/planning_element.rb
+++ b/app/models/planning_element.rb
@@ -61,9 +61,6 @@ class PlanningElement < WorkPackage
}
- scope :visible, lambda {|*args| { :include => :project,
- :conditions => PlanningElement.visible_condition(args.first || User.current) } }
-
scope :at_time, lambda { |time|
{:select => SQL_FOR_AT[:select],
:conditions => ["(#{PlanningElement.quoted_table_name}.deleted_at IS NULL
@@ -119,9 +116,6 @@ class PlanningElement < WorkPackage
before_save :append_scenario_dates_to_journal
- after_save :update_parent_attributes
- after_save :create_alternate_date
-
validates_presence_of :subject, :project
validates_length_of :subject, :maximum => 255, :unless => lambda { |e| e.subject.blank? }
@@ -134,10 +128,6 @@ class PlanningElement < WorkPackage
end
end
- def is_milestone?
- planning_element_type && planning_element_type.is_milestone?
- end
-
validate do
if self.due_date and self.start_date and self.due_date < self.start_date
errors.add :due_date, :greater_than_start_date
@@ -159,10 +149,6 @@ class PlanningElement < WorkPackage
end
- def leaf?
- self.children.count == 0
- end
-
def all_scenarios
project.scenarios.sort_by(&:id).map do |scenario|
alternate_date = alternate_dates.to_a.find { |a| a.scenario_id.to_s == scenario.id.to_s }
@@ -216,75 +202,4 @@ class PlanningElement < WorkPackage
end
end
end
-
- def note
- @journal_notes
- end
-
- def note=(text)
- @journal_notes = text
- end
-
- def trash
- unless new_record? or self.deleted_at
- self.children.each{|child| child.trash}
-
- self.reload
- self.deleted_at = Time.now
- self.save!
- end
- freeze
- end
-
- def restore!
- unless parent && parent.deleted?
- self.deleted_at = nil
- self.save
- else
- raise "You cannot restore an element whose parent is deleted. Restore the parent first!"
- end
- end
-
- def deleted?
- !!read_attribute(:deleted_at)
- end
-
- # Aliasing the parent_issue_id methods here in order
- # to improve compatibility between
- # planning elments and issues
- alias_method :parent_issue_id, :parent_id
-
- # I am not sure why it is not possible to
- # alias_method :parent_issue_id=, :parent_id=
- def parent_issue_id=(arg)
- parent_id = arg
- end
-
- protected
-
- def update_parent_attributes
- if parent.present?
- parent.reload
-
- unless parent.children.without_deleted.empty?
- children = parent.children.without_deleted
-
- parent.start_date = [children.minimum(:start_date), children.minimum(:due_date)].reject(&:nil?).min
- parent.due_date = [children.maximum(:start_date), children.maximum(:due_date)].reject(&:nil?).max
-
- if parent.changes.present?
- parent.note = I18n.t('timelines.planning_element_updated_automatically_by_child_changes', :child => "*#{id}")
-
- # Ancestors will be updated by parent's after_save hook.
- parent.save(:validate => false)
- end
- end
- end
- end
-
- def create_alternate_date
- if start_date_changed? or due_date_changed?
- alternate_dates.create(:start_date => start_date, :due_date => due_date)
- end
- end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 11ec70fdcf..0c57d6d34f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -976,7 +976,7 @@ class Project < ActiveRecord::Base
# Parent issue
if issue.parent_id
if copied_parent = work_packages_map[issue.parent_id]
- new_issue.parent_issue_id = copied_parent.id
+ new_issue.parent_id = copied_parent.id
end
end
diff --git a/app/models/setting.rb b/app/models/setting.rb
index eb6c41b48d..4c4d3f3a53 100644
--- a/app/models/setting.rb
+++ b/app/models/setting.rb
@@ -129,7 +129,7 @@ class Setting < ActiveRecord::Base
def self.[]=(name, v)
setting = find_or_default(name)
setting.value = (v ? v : "")
- Rails.cache.delete_matched(Regexp.compile(base_cache_key(name, '.+')))
+ Rails.cache.delete(cache_key(name))
setting.save
setting.value
end
diff --git a/app/models/work_package.rb b/app/models/work_package.rb
index 6fe457145e..f6e1730a88 100644
--- a/app/models/work_package.rb
+++ b/app/models/work_package.rb
@@ -53,7 +53,17 @@ class WorkPackage < ActiveRecord::Base
acts_as_watchable
- acts_as_nested_set :scope => 'root_id', :dependent => :destroy
+ before_save :store_former_parent_id
+ include OpenProject::NestedSet::WithRootIdScope
+ after_save :reschedule_following_issues,
+ :update_parent_attributes,
+ :create_alternate_date
+
+ after_move :remove_invalid_relations,
+ :recalculate_attributes_for_former_parent
+
+ after_destroy :update_parent_attributes
+
acts_as_customizable
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
@@ -111,6 +121,17 @@ class WorkPackage < ActiveRecord::Base
:responsible_id
register_on_journal_formatter :scenario_date, /^scenario_(\d+)_(start|due)_date$/
+ # acts_as_journalized will create an initial journal on wp creation
+ # and touch the journaled object:
+ # journal.rb:47
+ #
+ # This will result in optimistic locking increasing the lock_version attribute to 1.
+ # In order to avoid stale object errors we reload the attributes in question
+ # after the wp is created.
+ # As after_create is run before after_save, and journal creation is triggered by an
+ # after_save hook, we rely on after_save and a specific version, here.
+ after_save :reload_lock_and_timestamps, :if => Proc.new { |wp| wp.lock_version == 0 }
+
# Returns a SQL conditions string used to find all work units visible by the specified user
def self.visible_condition(user, options={})
Project.allowed_to_condition(user, :view_work_packages, options)
@@ -153,7 +174,7 @@ class WorkPackage < ActiveRecord::Base
# attributes don't come from form, so it's save to force assign
self.force_attributes = work_package.attributes.dup.except(*merged_options[:exclude])
- self.parent_issue_id = work_package.parent_id if work_package.parent_id
+ self.parent_id = work_package.parent_id if work_package.parent_id
self.custom_field_values = work_package.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
self.status = work_package.status
self
@@ -243,6 +264,30 @@ class WorkPackage < ActiveRecord::Base
end
end
+ def trash
+ unless new_record? or self.deleted_at
+ self.children.each{|child| child.trash}
+
+ self.reload
+ self.deleted_at = Time.now
+ self.save!
+ end
+ freeze
+ end
+
+ def restore!
+ unless parent && parent.deleted?
+ self.deleted_at = nil
+ self.save
+ else
+ raise "You cannot restore an element whose parent is deleted. Restore the parent first!"
+ end
+ end
+
+ def deleted?
+ !!read_attribute(:deleted_at)
+ end
+
# Users the work_package can be assigned to
delegate :assignable_users, :to => :project
@@ -290,47 +335,8 @@ class WorkPackage < ActiveRecord::Base
end
end
- def recalculate_attributes_for(work_package_id)
- if work_package_id.is_a? WorkPackage
- p = work_package_id
- else
- p = WorkPackage.find_by_id(work_package_id)
- end
-
- if p
- # priority = highest priority of children
- if priority_position = p.children.joins(:priority).maximum("#{IssuePriority.table_name}.position")
- p.priority = IssuePriority.find_by_position(priority_position)
- end
-
- # start/due dates = lowest/highest dates of children
- p.start_date = p.children.minimum(:start_date)
- p.due_date = p.children.maximum(:due_date)
- if p.start_date && p.due_date && p.due_date < p.start_date
- p.start_date, p.due_date = p.due_date, p.start_date
- end
-
- # done ratio = weighted average ratio of leaves
- unless WorkPackage.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
- leaves_count = p.leaves.count
- if leaves_count > 0
- average = p.leaves.average(:estimated_hours).to_f
- if average == 0
- average = 1
- end
- done = p.leaves.joins(:status).sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
- progress = done / (average * leaves_count)
- p.done_ratio = progress.round
- end
- end
-
- # estimate = sum of leaves estimates
- p.estimated_hours = p.leaves.sum(:estimated_hours).to_f
- p.estimated_hours = nil if p.estimated_hours == 0.0
-
- # ancestors will be recursively updated
- p.save(:validate => false) if p.changed?
- end
+ def is_milestone?
+ planning_element_type && planning_element_type.is_milestone?
end
# This is a dummy implementation that is currently overwritten
@@ -354,6 +360,99 @@ class WorkPackage < ActiveRecord::Base
.sum("#{TimeEntry.table_name}.hours").to_f || 0.0
end
+ protected
+
+ def recalculate_attributes_for(work_package_id)
+ p = if work_package_id.is_a? WorkPackage
+ work_package_id
+ else
+ WorkPackage.find_by_id(work_package_id)
+ end
+
+ return unless p
+
+ p.inherit_priority_from_children
+
+ p.inherit_dates_from_children
+
+ p.inherit_done_ratio_from_leaves
+
+ p.inherit_estimated_hours_from_leaves
+
+ # ancestors will be recursively updated
+ if p.changed?
+ p.journal_notes = I18n.t('timelines.planning_element_updated_automatically_by_child_changes', :child => "*#{id}")
+
+ # Ancestors will be updated by parent's after_save hook.
+ p.save(:validate => false)
+ end
+ end
+
+ def update_parent_attributes
+ recalculate_attributes_for(parent_id) if parent_id.present?
+ end
+
+ def inherit_priority_from_children
+ # priority = highest priority of children
+ if priority_position = children.joins(:priority).maximum("#{IssuePriority.table_name}.position")
+ self.priority = IssuePriority.find_by_position(priority_position)
+ end
+ end
+
+ def inherit_dates_from_children
+ active_children = children.without_deleted
+
+ unless active_children.empty?
+ self.start_date = [active_children.minimum(:start_date), active_children.minimum(:due_date)].compact.min
+ self.due_date = [active_children.maximum(:start_date), active_children.maximum(:due_date)].compact.max
+ end
+ end
+
+ def inherit_done_ratio_from_leaves
+ # done ratio = weighted average ratio of leaves
+ unless WorkPackage.use_status_for_done_ratio? && status && status.default_done_ratio
+ leaves_count = leaves.count
+ if leaves_count > 0
+ average = leaves.average(:estimated_hours).to_f
+ if average == 0
+ average = 1
+ end
+ done = leaves.joins(:status).sum("COALESCE(estimated_hours, #{average}) * (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
+ progress = done / (average * leaves_count)
+
+ self.done_ratio = progress.round
+ end
+ end
+ end
+
+ def inherit_estimated_hours_from_leaves
+ # estimate = sum of leaves estimates
+ self.estimated_hours = leaves.sum(:estimated_hours).to_f
+ self.estimated_hours = nil if estimated_hours == 0.0
+ end
+
+ def store_former_parent_id
+ @former_parent_id = parent_id_changed? ? parent_id_was : false
+ true # force callback to return true
+ end
+
+ def remove_invalid_relations
+ # delete invalid relations of all descendants
+ self_and_descendants.each do |issue|
+ issue.relations.each do |relation|
+ relation.destroy unless relation.valid?
+ end
+ end
+ end
+
+ def recalculate_attributes_for_former_parent
+ recalculate_attributes_for(@former_parent_id) if @former_parent_id
+ end
+
+ def reload_lock_and_timestamps
+ reload(:select => [:lock_version, :created_at, :updated_at])
+ end
+
private
def add_time_entry_for(user, attributes)
@@ -364,4 +463,16 @@ class WorkPackage < ActiveRecord::Base
time_entries.build(attributes)
end
+
+ def create_alternate_date
+ # This is a hack.
+ # It is required as long as alternate dates exist/are not moved up to work_packages.
+ # Its purpose is to allow for setting the after_save filter in the correct order
+ # before acts as journalized and the cleanup method reload_lock_and_timestamps.
+ return true unless respond_to?(:alternate_dates)
+
+ if start_date_changed? or due_date_changed?
+ alternate_dates.create(:start_date => start_date, :due_date => due_date)
+ end
+ end
end
diff --git a/app/views/issues/_form.html.erb b/app/views/issues/_form.html.erb
index ea3503031c..445ef11e64 100644
--- a/app/views/issues/_form.html.erb
+++ b/app/views/issues/_form.html.erb
@@ -22,7 +22,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= f.text_field :subject, :size => 80, :required => true %>
<% if User.current.allowed_to?(:manage_subtasks, @project) %>
- <%= f.text_field :parent_issue_id, :size => 10, :title => l(:description_autocomplete) %>
+ <%= f.text_field :parent_id, :size => 10, :title => l(:description_autocomplete) %>
<%= javascript_tag "observeParentIssueField('#{issues_auto_complete_path(:id => @issue, :project_id => @project, :escape => false) }')" %>
<% end %>
diff --git a/app/views/issues/_list.html.erb b/app/views/issues/_list.html.erb
index 826500a6c2..7b363609cc 100644
--- a/app/views/issues/_list.html.erb
+++ b/app/views/issues/_list.html.erb
@@ -45,7 +45,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= check_box_tag("ids[]", issue.id, false, :id => "issue#{issue.id}") %>
- <% if parent_issue = issue.parent_issue_id %>
+ <% if parent_issue = issue.parent_id %>
<%=l(:description_subissue) + ' ' + l(:label_issue) + ' #' + parent_issue.to_s %>
<% end -%>
<%= link_to issue.id, :controller => '/issues', :action => 'show', :id => issue %>
diff --git a/app/views/issues/_subissues_paragraph.html.erb b/app/views/issues/_subissues_paragraph.html.erb
index de5e59d145..b714a11317 100644
--- a/app/views/issues/_subissues_paragraph.html.erb
+++ b/app/views/issues/_subissues_paragraph.html.erb
@@ -11,7 +11,7 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<%= l(:label_issue_hierarchy) %>
-(<%= link_to(l(:label_add_subtask), {:controller => '/issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>)
+(<%= link_to(l(:label_add_subtask), {:controller => '/issues', :action => 'new', :project_id => @project, :issue => {:parent_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>)
<% if !@issue.leaf? || @issue.parent %>
<% indent = 0 %>
diff --git a/app/views/issues/bulk_edit.html.erb b/app/views/issues/bulk_edit.html.erb
index cdf1ce767e..dc223d8e65 100644
--- a/app/views/issues/bulk_edit.html.erb
+++ b/app/views/issues/bulk_edit.html.erb
@@ -72,8 +72,8 @@ See doc/COPYRIGHT.rdoc for more details.
<% if @project && User.current.allowed_to?(:manage_subtasks, @project) %>
-
- <%= text_field_tag 'issue[parent_issue_id]', '', :size => 10 %>
+
+ <%= text_field_tag 'issue[parent_id]', '', :size => 10 %>
<%= javascript_tag "observeParentIssueField('#{issues_auto_complete_path }')" %>
diff --git a/app/views/planning_elements/_form.html.erb b/app/views/planning_elements/_form.html.erb
index abdb187277..2dfcf1fb50 100644
--- a/app/views/planning_elements/_form.html.erb
+++ b/app/views/planning_elements/_form.html.erb
@@ -146,11 +146,11 @@ See doc/COPYRIGHT.rdoc for more details.
<% unless planning_element.new_record? %>
<%= l(:field_notes) %>
- |