git-svn-id: http://redmine.rubyforge.org/svn/trunk@920 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/0.6.1
@@ -194,6 +194,7 @@ class IssuesController < ApplicationController | |||
:change_status => User.current.allowed_to?(:change_issue_status, @project), | |||
:add => User.current.allowed_to?(:add_issues, @project), | |||
:move => User.current.allowed_to?(:move_issues, @project), | |||
:copy => (@project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), | |||
:delete => User.current.allowed_to?(:delete_issues, @project)} | |||
render :layout => false | |||
end |
@@ -57,11 +57,13 @@ class ProjectsController < ApplicationController | |||
# Add a new project | |||
def add | |||
@custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position") | |||
@trackers = Tracker.all | |||
@root_projects = Project.find(:all, :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}") | |||
@project = Project.new(params[:project]) | |||
@project.enabled_module_names = Redmine::AccessControl.available_project_modules | |||
if request.get? | |||
@custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project) } | |||
@project.trackers = Tracker.all | |||
else | |||
@project.custom_fields = CustomField.find(params[:custom_field_ids]) if params[:custom_field_ids] | |||
@custom_values = ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| CustomValue.new(:custom_field => x, :customized => @project, :value => (params[:custom_fields] ? params["custom_fields"][x.id.to_s] : nil)) } | |||
@@ -80,7 +82,7 @@ class ProjectsController < ApplicationController | |||
@members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role} | |||
@subprojects = @project.active_children | |||
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") | |||
@trackers = Tracker.find(:all, :order => 'position') | |||
@trackers = @project.trackers | |||
@open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false]) | |||
@total_issues_by_tracker = Issue.count(:group => :tracker, :conditions => ["project_id=?", @project.id]) | |||
@total_hours = @project.time_entries.sum(:hours) | |||
@@ -92,6 +94,7 @@ class ProjectsController < ApplicationController | |||
@custom_fields = IssueCustomField.find(:all) | |||
@issue_category ||= IssueCategory.new | |||
@member ||= @project.members.new | |||
@trackers = Tracker.all | |||
@custom_values ||= ProjectCustomField.find(:all, :order => "#{CustomField.table_name}.position").collect { |x| @project.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x) } | |||
@repository ||= @project.repository | |||
@wiki ||= @project.wiki | |||
@@ -207,7 +210,7 @@ class ProjectsController < ApplicationController | |||
@issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue]) | |||
@issue.project = @project | |||
@issue.author = User.current | |||
@issue.tracker ||= Tracker.find(params[:tracker_id]) | |||
@issue.tracker ||= @project.trackers.find(params[:tracker_id]) | |||
default_status = IssueStatus.default | |||
unless default_status | |||
@@ -293,6 +296,7 @@ class ProjectsController < ApplicationController | |||
def move_issues | |||
@issues = @project.issues.find(params[:issue_ids]) if params[:issue_ids] | |||
redirect_to :controller => 'issues', :action => 'index', :project_id => @project and return unless @issues | |||
@projects = [] | |||
# find projects to which the user is allowed to move the issue | |||
if User.current.admin? | |||
@@ -301,14 +305,14 @@ class ProjectsController < ApplicationController | |||
else | |||
User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:move_issues)} | |||
end | |||
# issue can be moved to any tracker | |||
@trackers = Tracker.find(:all) | |||
if request.post? && params[:new_project_id] && @projects.collect(&:id).include?(params[:new_project_id].to_i) && params[:new_tracker_id] | |||
new_project = Project.find_by_id(params[:new_project_id]) | |||
new_tracker = params[:new_tracker_id].blank? ? nil : Tracker.find_by_id(params[:new_tracker_id]) | |||
@target_project = @projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id] | |||
@target_project ||= @project | |||
@trackers = @target_project.trackers | |||
if request.post? | |||
new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id]) | |||
unsaved_issue_ids = [] | |||
@issues.each do |issue| | |||
unsaved_issue_ids << issue.id unless issue.move_to(new_project, new_tracker) | |||
unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker) | |||
end | |||
if unsaved_issue_ids.empty? | |||
flash[:notice] = l(:notice_successful_update) unless @issues.empty? | |||
@@ -316,7 +320,9 @@ class ProjectsController < ApplicationController | |||
flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #')) | |||
end | |||
redirect_to :controller => 'issues', :action => 'index', :project_id => @project | |||
return | |||
end | |||
render :layout => false if request.xhr? | |||
end | |||
# Add a news to @project | |||
@@ -354,13 +360,13 @@ class ProjectsController < ApplicationController | |||
# Show changelog for @project | |||
def changelog | |||
@trackers = Tracker.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position') | |||
@trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position') | |||
retrieve_selected_tracker_ids(@trackers) | |||
@versions = @project.versions.sort | |||
end | |||
def roadmap | |||
@trackers = Tracker.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position') | |||
@trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true]) | |||
retrieve_selected_tracker_ids(@trackers) | |||
@versions = @project.versions.sort | |||
@versions = @versions.select {|v| !v.completed? } unless params[:completed] |
@@ -25,7 +25,7 @@ class ReportsController < ApplicationController | |||
case params[:detail] | |||
when "tracker" | |||
@field = "tracker_id" | |||
@rows = Tracker.find :all, :order => 'position' | |||
@rows = @project.trackers | |||
@data = issues_by_tracker | |||
@report_title = l(:field_tracker) | |||
render :template => "reports/issue_report_details" | |||
@@ -60,7 +60,7 @@ class ReportsController < ApplicationController | |||
@report_title = l(:field_subproject) | |||
render :template => "reports/issue_report_details" | |||
else | |||
@trackers = Tracker.find(:all, :order => 'position') | |||
@trackers = @project.trackers | |||
@versions = @project.versions.sort | |||
@priorities = Enumeration::get_values('IPRI') | |||
@categories = @project.issue_categories |
@@ -190,7 +190,7 @@ module ProjectsHelper | |||
end if Object.const_defined?(:Magick) | |||
def new_issue_selector | |||
trackers = Tracker.find(:all, :order => 'position') | |||
trackers = @project.trackers | |||
# can't use form tag inside helper | |||
content_tag('form', | |||
select_tag('tracker_id', '<option></option>' + options_from_collection_for_select(trackers, 'id', 'name'), :onchange => "if (this.value != '') {this.form.submit()}"), |
@@ -31,9 +31,9 @@ protected | |||
when 'float' | |||
begin; !value.blank? && Kernel.Float(value); rescue; errors.add(:value, :activerecord_error_invalid) end | |||
when 'date' | |||
errors.add(:value, :activerecord_error_not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ or value.empty? | |||
errors.add(:value, :activerecord_error_not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ or value.blank? | |||
when 'list' | |||
errors.add(:value, :activerecord_error_inclusion) unless custom_field.possible_values.include? value or value.empty? | |||
errors.add(:value, :activerecord_error_inclusion) unless custom_field.possible_values.include?(value) or value.blank? | |||
end | |||
end | |||
end |
@@ -40,7 +40,7 @@ class Issue < ActiveRecord::Base | |||
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id}: #{o.subject}"}, | |||
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}} | |||
validates_presence_of :subject, :description, :priority, :tracker, :author, :status | |||
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status | |||
validates_length_of :subject, :maximum => 255 | |||
validates_inclusion_of :done_ratio, :in => 0..100 | |||
validates_numericality_of :estimated_hours, :allow_nil => true | |||
@@ -106,6 +106,10 @@ class Issue < ActiveRecord::Base | |||
end | |||
end | |||
def validate_on_create | |||
errors.add :tracker_id, :activerecord_error_invalid unless project.trackers.include?(tracker) | |||
end | |||
def before_create | |||
# default assignment based on category | |||
if assigned_to.nil? && category && category.assigned_to |
@@ -24,6 +24,7 @@ class Project < ActiveRecord::Base | |||
has_many :users, :through => :members | |||
has_many :custom_values, :dependent => :delete_all, :as => :customized | |||
has_many :enabled_modules, :dependent => :delete_all | |||
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" | |||
has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker] | |||
has_many :issue_changes, :through => :issues, :source => :journals | |||
has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" |
@@ -29,6 +29,10 @@ class Tracker < ActiveRecord::Base | |||
def to_s; name end | |||
def self.all | |||
find(:all, :order => 'position') | |||
end | |||
private | |||
def check_integrity | |||
raise "Can't delete tracker" if Issue.find(:first, :conditions => ["tracker_id=?", self.id]) |
@@ -1,4 +1,4 @@ | |||
<% if authorize_for('projects', 'add_issue') %> | |||
<% if authorize_for('projects', 'add_issue') && @project.trackers.any? %> | |||
<h3><%= l(:label_issue_new) %></h3> | |||
<%= l(:label_tracker) %>: <%= new_issue_selector %> | |||
<% end %> |
@@ -32,7 +32,7 @@ | |||
</ul> | |||
</li> | |||
<li><%= context_menu_link l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue}, | |||
:class => 'icon-copy', :disabled => !@can[:add] %></li> | |||
:class => 'icon-copy', :disabled => !@can[:copy] %></li> | |||
<li><%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, | |||
:class => 'icon-move', :disabled => !@can[:move] %> | |||
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, |
@@ -17,16 +17,32 @@ | |||
<% for @custom_value in @custom_values %> | |||
<p><%= custom_field_tag_with_label @custom_value %></p> | |||
<% end %> | |||
</div> | |||
<% unless @trackers.empty? %> | |||
<fieldset class="box"><legend><%=l(:label_tracker_plural)%></legend> | |||
<% @trackers.each do |tracker| %> | |||
<label class="floating"> | |||
<%= check_box_tag 'project[tracker_ids][]', tracker.id, @project.trackers.include?(tracker) %> | |||
<%= tracker %> | |||
</label> | |||
<% end %> | |||
<%= hidden_field_tag 'project[tracker_ids][]', '' %> | |||
</fieldset> | |||
<% end %> | |||
<% unless @custom_fields.empty? %> | |||
<p><label><%=l(:label_custom_field_plural)%></label> | |||
<fieldset class="box"><legend><%=l(:label_custom_field_plural)%></legend> | |||
<% for custom_field in @custom_fields %> | |||
<label class="floating"> | |||
<%= check_box_tag "custom_field_ids[]", custom_field.id, ((@project.custom_fields.include? custom_field) or custom_field.is_for_all?), (custom_field.is_for_all? ? {:disabled => "disabled"} : {}) %> | |||
<%= custom_field.name %> | |||
<% end %></p> | |||
<%= custom_field.name %> | |||
</label> | |||
<% end %> | |||
</fieldset> | |||
<% end %> | |||
<!--[eoform:project]--> | |||
</div> | |||
<% content_for :header_tags do %> | |||
<%= javascript_include_tag 'calendar/calendar' %> |
@@ -3,13 +3,13 @@ | |||
<% labelled_tabular_form_for :project, @project, :url => { :action => "add" } do |f| %> | |||
<%= render :partial => 'form', :locals => { :f => f } %> | |||
<div class="box"> | |||
<p><label><%= l(:label_module_plural) %></label> | |||
<fieldset class="box"><legend><%= l(:label_module_plural) %></legend> | |||
<% Redmine::AccessControl.available_project_modules.each do |m| %> | |||
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %> <%= m.to_s.humanize %> | |||
<% end %></p> | |||
</div> | |||
<label class="floating"> | |||
<%= check_box_tag 'enabled_modules[]', m, @project.module_enabled?(m) %> <%= m.to_s.humanize %> | |||
</label> | |||
<% end %> | |||
</fieldset> | |||
<%= submit_tag l(:button_save) %> | |||
<% end %> |
@@ -1,7 +1,7 @@ | |||
<h2><%=l(:button_move)%></h2> | |||
<% form_tag({:action => 'move_issues', :id => @project}, :class => "tabular") do %> | |||
<% form_tag({:action => 'move_issues', :id => @project}, :class => 'tabular', :id => 'move_form') do %> | |||
<div class="box"> | |||
<p><label><%= l(:label_issue_plural) %> :</label> | |||
@@ -15,7 +15,12 @@ | |||
<!--[form:issue]--> | |||
<p><label for="new_project_id"><%=l(:field_project)%> :</label> | |||
<%= select_tag "new_project_id", options_from_collection_for_select(@projects, "id", "name", @project.id) %></p> | |||
<%= select_tag "new_project_id", | |||
options_from_collection_for_select(@projects, 'id', 'name', @target_project.id), | |||
:onchange => remote_function(:url => {:action => 'move_issues' , :id => @project}, | |||
:method => :get, | |||
:update => 'content', | |||
:with => "Form.serialize('move_form')") %></p> | |||
<p><label for="new_tracker_id"><%=l(:field_tracker)%> :</label> | |||
<%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p> |
@@ -56,7 +56,7 @@ | |||
</div> | |||
<% content_for :sidebar do %> | |||
<% if authorize_for('projects', 'add_issue') %> | |||
<% if authorize_for('projects', 'add_issue') && @project.trackers.any? %> | |||
<h3><%= l(:label_issue_new) %></h3> | |||
<%= l(:label_tracker) %>: <%= new_issue_selector %> | |||
<% end %> |
@@ -0,0 +1,19 @@ | |||
class CreateProjectsTrackers < ActiveRecord::Migration | |||
def self.up | |||
create_table :projects_trackers, :id => false do |t| | |||
t.column :project_id, :integer, :default => 0, :null => false | |||
t.column :tracker_id, :integer, :default => 0, :null => false | |||
end | |||
add_index :projects_trackers, :project_id, :name => :projects_trackers_project_id | |||
# Associates all trackers to all projects (as it was before) | |||
tracker_ids = Tracker.find(:all).collect(&:id) | |||
Project.find(:all).each do |project| | |||
project.tracker_ids = tracker_ids | |||
end | |||
end | |||
def self.down | |||
drop_table :projects_trackers | |||
end | |||
end |
@@ -0,0 +1,46 @@ | |||
--- | |||
projects_trackers_012: | |||
project_id: 4 | |||
tracker_id: 3 | |||
projects_trackers_001: | |||
project_id: 1 | |||
tracker_id: 1 | |||
projects_trackers_013: | |||
project_id: 5 | |||
tracker_id: 1 | |||
projects_trackers_002: | |||
project_id: 1 | |||
tracker_id: 2 | |||
projects_trackers_014: | |||
project_id: 5 | |||
tracker_id: 2 | |||
projects_trackers_003: | |||
project_id: 1 | |||
tracker_id: 3 | |||
projects_trackers_015: | |||
project_id: 5 | |||
tracker_id: 3 | |||
projects_trackers_004: | |||
project_id: 2 | |||
tracker_id: 1 | |||
projects_trackers_005: | |||
project_id: 2 | |||
tracker_id: 2 | |||
projects_trackers_006: | |||
project_id: 2 | |||
tracker_id: 3 | |||
projects_trackers_007: | |||
project_id: 3 | |||
tracker_id: 1 | |||
projects_trackers_008: | |||
project_id: 3 | |||
tracker_id: 2 | |||
projects_trackers_009: | |||
project_id: 3 | |||
tracker_id: 3 | |||
projects_trackers_010: | |||
project_id: 4 | |||
tracker_id: 1 | |||
projects_trackers_011: | |||
project_id: 4 | |||
tracker_id: 2 |
@@ -22,7 +22,7 @@ require 'projects_controller' | |||
class ProjectsController; def rescue_action(e) raise e end; end | |||
class ProjectsControllerTest < Test::Unit::TestCase | |||
fixtures :projects, :users, :roles, :members, :issues, :journals, :journal_details, :trackers, :issue_statuses, :enabled_modules, :enumerations | |||
fixtures :projects, :users, :roles, :members, :issues, :journals, :journal_details, :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations | |||
def setup | |||
@controller = ProjectsController.new |
@@ -18,7 +18,7 @@ | |||
require File.dirname(__FILE__) + '/../test_helper' | |||
class IssueTest < Test::Unit::TestCase | |||
fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :time_entries | |||
fixtures :projects, :users, :members, :trackers, :projects_trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :time_entries | |||
def test_category_based_assignment | |||
issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1) |