git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10513 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
69b8931e92
commit
1b6da80e16
|
@ -64,10 +64,12 @@ module ApplicationHelper
|
|||
# link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
|
||||
# link_to_issue(issue, :subject => false) # => Defect #6
|
||||
# link_to_issue(issue, :project => true) # => Foo - Defect #6
|
||||
# link_to_issue(issue, :subject => false, :tracker => false) # => #6
|
||||
#
|
||||
def link_to_issue(issue, options={})
|
||||
title = nil
|
||||
subject = nil
|
||||
text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
|
||||
if options[:subject] == false
|
||||
title = truncate(issue.subject, :length => 60)
|
||||
else
|
||||
|
@ -76,7 +78,7 @@ module ApplicationHelper
|
|||
subject = truncate(subject, :length => options[:truncate])
|
||||
end
|
||||
end
|
||||
s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
|
||||
s = link_to text, {:controller => "issues", :action => "show", :id => issue},
|
||||
:class => issue.css_classes,
|
||||
:title => title
|
||||
s << h(": #{subject}") if subject
|
||||
|
|
|
@ -35,7 +35,7 @@ module QueriesHelper
|
|||
def column_content(column, issue)
|
||||
value = column.value(issue)
|
||||
if value.is_a?(Array)
|
||||
value.collect {|v| column_value(column, issue, v)}.compact.sort.join(', ').html_safe
|
||||
value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe
|
||||
else
|
||||
column_value(column, issue, value)
|
||||
end
|
||||
|
@ -73,6 +73,11 @@ module QueriesHelper
|
|||
l(:general_text_No)
|
||||
when 'Issue'
|
||||
link_to_issue(value, :subject => false)
|
||||
when 'IssueRelation'
|
||||
other = value.other_issue(issue)
|
||||
content_tag('span',
|
||||
(l(value.label_for(issue)) + " " + link_to_issue(other, :subject => false, :tracker => false)).html_safe,
|
||||
:class => value.css_classes_for(issue))
|
||||
else
|
||||
h(value)
|
||||
end
|
||||
|
|
|
@ -752,7 +752,7 @@ class Issue < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def relations
|
||||
@relations ||= (relations_from + relations_to).sort
|
||||
@relations ||= IssueRelations.new(self, (relations_from + relations_to).sort)
|
||||
end
|
||||
|
||||
# Preloads relations for a collection of issues
|
||||
|
@ -775,6 +775,25 @@ class Issue < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# Preloads visible relations for a collection of issues
|
||||
def self.load_visible_relations(issues, user=User.current)
|
||||
if issues.any?
|
||||
issue_ids = issues.map(&:id)
|
||||
# Relations with issue_from in given issues and visible issue_to
|
||||
relations_from = IssueRelation.includes(:issue_to => [:status, :project]).where(visible_condition(user)).where(:issue_from_id => issue_ids).all
|
||||
# Relations with issue_to in given issues and visible issue_from
|
||||
relations_to = IssueRelation.includes(:issue_from => [:status, :project]).where(visible_condition(user)).where(:issue_to_id => issue_ids).all
|
||||
|
||||
issues.each do |issue|
|
||||
relations =
|
||||
relations_from.select {|relation| relation.issue_from_id == issue.id} +
|
||||
relations_to.select {|relation| relation.issue_to_id == issue.id}
|
||||
|
||||
issue.instance_variable_set "@relations", IssueRelations.new(issue, relations.sort)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Finds an issue relation given its id.
|
||||
def find_relation(relation_id)
|
||||
IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id])
|
||||
|
|
|
@ -15,6 +15,20 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# Class used to represent the relations of an issue
|
||||
class IssueRelations < Array
|
||||
include Redmine::I18n
|
||||
|
||||
def initialize(issue, *args)
|
||||
@issue = issue
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def to_s(*args)
|
||||
map {|relation| "#{l(relation.label_for(@issue))} ##{relation.other_issue(@issue).id}"}.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
class IssueRelation < ActiveRecord::Base
|
||||
belongs_to :issue_from, :class_name => 'Issue', :foreign_key => 'issue_from_id'
|
||||
belongs_to :issue_to, :class_name => 'Issue', :foreign_key => 'issue_to_id'
|
||||
|
@ -103,6 +117,10 @@ class IssueRelation < ActiveRecord::Base
|
|||
TYPES[relation_type] ? TYPES[relation_type][(self.issue_from_id == issue.id) ? :name : :sym_name] : :unknow
|
||||
end
|
||||
|
||||
def css_classes_for(issue)
|
||||
"rel-#{relation_type_for(issue)}"
|
||||
end
|
||||
|
||||
def handle_issue_order
|
||||
reverse_if_needed
|
||||
|
||||
|
@ -128,7 +146,8 @@ class IssueRelation < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def <=>(relation)
|
||||
TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order]
|
||||
r = TYPES[self.relation_type][:order] <=> TYPES[relation.relation_type][:order]
|
||||
r == 0 ? id <=> relation.id : r
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -113,7 +113,9 @@ class Query < ActiveRecord::Base
|
|||
"<t-" => :label_more_than_ago,
|
||||
"t-" => :label_ago,
|
||||
"~" => :label_contains,
|
||||
"!~" => :label_not_contains }
|
||||
"!~" => :label_not_contains,
|
||||
"=p" => :label_any_issues_in_project,
|
||||
"=!p" => :label_any_issues_not_in_project}
|
||||
|
||||
cattr_reader :operators
|
||||
|
||||
|
@ -126,7 +128,8 @@ class Query < ActiveRecord::Base
|
|||
:string => [ "=", "~", "!", "!~", "!*", "*" ],
|
||||
:text => [ "~", "!~", "!*", "*" ],
|
||||
:integer => [ "=", ">=", "<=", "><", "!*", "*" ],
|
||||
:float => [ "=", ">=", "<=", "><", "!*", "*" ] }
|
||||
:float => [ "=", ">=", "<=", "><", "!*", "*" ],
|
||||
:relation => ["=", "=p", "=!p", "!*", "*"]}
|
||||
|
||||
cattr_reader :operators_by_filter_type
|
||||
|
||||
|
@ -147,6 +150,7 @@ class Query < ActiveRecord::Base
|
|||
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
|
||||
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
|
||||
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
|
||||
QueryColumn.new(:relations, :caption => :label_related_issues)
|
||||
]
|
||||
cattr_reader :available_columns
|
||||
|
||||
|
@ -233,6 +237,10 @@ class Query < ActiveRecord::Base
|
|||
"estimated_hours" => { :type => :float, :order => 13 },
|
||||
"done_ratio" => { :type => :integer, :order => 14 }}
|
||||
|
||||
IssueRelation::TYPES.each do |relation_type, options|
|
||||
@available_filters[relation_type] = {:type => :relation, :order => @available_filters.size + 100, :label => options[:name]}
|
||||
end
|
||||
|
||||
principals = []
|
||||
if project
|
||||
principals += project.principals.sort
|
||||
|
@ -244,7 +252,6 @@ class Query < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
else
|
||||
all_projects = Project.visible.all
|
||||
if all_projects.any?
|
||||
# members of visible projects
|
||||
principals += Principal.member_of(all_projects)
|
||||
|
@ -254,10 +261,7 @@ class Query < ActiveRecord::Base
|
|||
if User.current.logged? && User.current.memberships.any?
|
||||
project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
|
||||
end
|
||||
Project.project_tree(all_projects) do |p, level|
|
||||
prefix = (level > 0 ? ('--' * level + ' ') : '')
|
||||
project_values << ["#{prefix}#{p.name}", p.id.to_s]
|
||||
end
|
||||
project_values += all_projects_values
|
||||
@available_filters["project_id"] = { :type => :list, :order => 1, :values => project_values} unless project_values.empty?
|
||||
end
|
||||
end
|
||||
|
@ -317,7 +321,7 @@ class Query < ActiveRecord::Base
|
|||
}
|
||||
|
||||
@available_filters.each do |field, options|
|
||||
options[:name] ||= l("field_#{field}".gsub(/_id$/, ''))
|
||||
options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, ''))
|
||||
end
|
||||
|
||||
@available_filters
|
||||
|
@ -332,6 +336,21 @@ class Query < ActiveRecord::Base
|
|||
json
|
||||
end
|
||||
|
||||
def all_projects
|
||||
@all_projects ||= Project.visible.all
|
||||
end
|
||||
|
||||
def all_projects_values
|
||||
return @all_projects_values if @all_projects_values
|
||||
|
||||
values = []
|
||||
Project.project_tree(all_projects) do |p, level|
|
||||
prefix = (level > 0 ? ('--' * level + ' ') : '')
|
||||
values << ["#{prefix}#{p.name}", p.id.to_s]
|
||||
end
|
||||
@all_projects_values = values
|
||||
end
|
||||
|
||||
def add_filter(field, operator, values)
|
||||
# values must be an array
|
||||
return unless values.nil? || values.is_a?(Array)
|
||||
|
@ -635,6 +654,9 @@ class Query < ActiveRecord::Base
|
|||
if has_column?(:spent_hours)
|
||||
Issue.load_visible_spent_hours(issues)
|
||||
end
|
||||
if has_column?(:relations)
|
||||
Issue.load_visible_relations(issues)
|
||||
end
|
||||
issues
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise StatementInvalid.new(e.message)
|
||||
|
@ -729,6 +751,41 @@ class Query < ActiveRecord::Base
|
|||
"#{Issue.table_name}.is_private #{op} (#{va})"
|
||||
end
|
||||
|
||||
def sql_for_relations(field, operator, value, options={})
|
||||
relation_options = IssueRelation::TYPES[field]
|
||||
return relation_options unless relation_options
|
||||
|
||||
relation_type = field
|
||||
join_column, target_join_column = "issue_from_id", "issue_to_id"
|
||||
if relation_options[:reverse] || options[:reverse]
|
||||
relation_type = relation_options[:reverse] || relation_type
|
||||
join_column, target_join_column = target_join_column, join_column
|
||||
end
|
||||
|
||||
sql = case operator
|
||||
when "*", "!*"
|
||||
op = (operator == "*" ? 'IN' : 'NOT IN')
|
||||
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
|
||||
when "=", "!"
|
||||
op = (operator == "=" ? 'IN' : 'NOT IN')
|
||||
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
|
||||
when "=p", "=!p"
|
||||
op = (operator == "=p" ? '=' : '<>')
|
||||
"#{Issue.table_name}.id IN (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{op} #{value.first.to_i})"
|
||||
end
|
||||
|
||||
if relation_options[:sym] == field && !options[:reverse]
|
||||
sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
|
||||
sqls.join(["!", "!*"].include?(operator) ? " AND " : " OR ")
|
||||
else
|
||||
sql
|
||||
end
|
||||
end
|
||||
|
||||
IssueRelation::TYPES.keys.each do |relation_type|
|
||||
alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sql_for_custom_field(field, operator, value, custom_field_id)
|
||||
|
|
|
@ -3,6 +3,7 @@ var operatorLabels = <%= raw_json Query.operators_labels %>;
|
|||
var operatorByType = <%= raw_json Query.operators_by_filter_type %>;
|
||||
var availableFilters = <%= raw_json query.available_filters_as_json %>;
|
||||
var labelDayPlural = <%= raw_json l(:label_day_plural) %>;
|
||||
var allProjects = <%= raw query.all_projects_values.to_json %>;
|
||||
$(document).ready(function(){
|
||||
initFilters();
|
||||
<% query.filters.each do |field, options| %>
|
||||
|
|
|
@ -669,6 +669,8 @@ en:
|
|||
label_ago: days ago
|
||||
label_contains: contains
|
||||
label_not_contains: doesn't contain
|
||||
label_any_issues_in_project: any issues in project
|
||||
label_any_issues_not_in_project: any issues not in project
|
||||
label_day_plural: days
|
||||
label_repository: Repository
|
||||
label_repository_new: New repository
|
||||
|
@ -737,15 +739,15 @@ en:
|
|||
label_loading: Loading...
|
||||
label_relation_new: New relation
|
||||
label_relation_delete: Delete relation
|
||||
label_relates_to: related to
|
||||
label_duplicates: duplicates
|
||||
label_duplicated_by: duplicated by
|
||||
label_blocks: blocks
|
||||
label_blocked_by: blocked by
|
||||
label_precedes: precedes
|
||||
label_follows: follows
|
||||
label_copied_to: copied to
|
||||
label_copied_from: copied from
|
||||
label_relates_to: Related to
|
||||
label_duplicates: Duplicates
|
||||
label_duplicated_by: Duplicated by
|
||||
label_blocks: Blocks
|
||||
label_blocked_by: Blocked by
|
||||
label_precedes: Precedes
|
||||
label_follows: Follows
|
||||
label_copied_to: Copied to
|
||||
label_copied_from: Copied from
|
||||
label_end_to_start: end to start
|
||||
label_end_to_end: end to end
|
||||
label_start_to_start: start to start
|
||||
|
|
|
@ -659,6 +659,8 @@ fr:
|
|||
label_ago: il y a
|
||||
label_contains: contient
|
||||
label_not_contains: ne contient pas
|
||||
label_any_issues_in_project: une demande du projet
|
||||
label_any_issues_not_in_project: une demande hors du projet
|
||||
label_day_plural: jours
|
||||
label_repository: Dépôt
|
||||
label_repository_new: Nouveau dépôt
|
||||
|
@ -721,15 +723,15 @@ fr:
|
|||
label_loading: Chargement...
|
||||
label_relation_new: Nouvelle relation
|
||||
label_relation_delete: Supprimer la relation
|
||||
label_relates_to: lié à
|
||||
label_duplicates: duplique
|
||||
label_duplicated_by: dupliqué par
|
||||
label_blocks: bloque
|
||||
label_blocked_by: bloqué par
|
||||
label_precedes: précède
|
||||
label_follows: suit
|
||||
label_copied_to: copié vers
|
||||
label_copied_from: copié depuis
|
||||
label_relates_to: Lié à
|
||||
label_duplicates: Duplique
|
||||
label_duplicated_by: Dupliqué par
|
||||
label_blocks: Bloque
|
||||
label_blocked_by: Bloqué par
|
||||
label_precedes: Précède
|
||||
label_follows: Suit
|
||||
label_copied_to: Copié vers
|
||||
label_copied_from: Copié depuis
|
||||
label_end_to_start: fin à début
|
||||
label_end_to_end: fin à fin
|
||||
label_start_to_start: début à début
|
||||
|
|
|
@ -178,6 +178,20 @@ function buildFilterRow(field, operator, values) {
|
|||
);
|
||||
$('#values_'+fieldId).val(values[0]);
|
||||
break;
|
||||
case "relation":
|
||||
tr.find('td.values').append(
|
||||
'<span style="display:none;"><input type="text" name="v['+field+'][]" id="values_'+fieldId+'" size="6" class="value" /></span>' +
|
||||
'<span style="display:none;"><select class="value" name="v['+field+'][]" id="values_'+fieldId+'_1"></select></span>'
|
||||
);
|
||||
$('#values_'+fieldId).val(values[0]);
|
||||
select = tr.find('td.values select');
|
||||
for (i=0;i<allProjects.length;i++){
|
||||
var filterValue = allProjects[i];
|
||||
var option = $('<option>');
|
||||
option.val(filterValue[1]).text(filterValue[0]);
|
||||
if (values[0] == filterValue[1]) {option.attr('selected', true)};
|
||||
select.append(option);
|
||||
}
|
||||
case "integer":
|
||||
case "float":
|
||||
tr.find('td.values').append(
|
||||
|
@ -244,6 +258,10 @@ function toggleOperator(field) {
|
|||
case "t-":
|
||||
enableValues(field, [2]);
|
||||
break;
|
||||
case "=p":
|
||||
case "=!p":
|
||||
enableValues(field, [1]);
|
||||
break;
|
||||
default:
|
||||
enableValues(field, [0]);
|
||||
break;
|
||||
|
|
|
@ -120,7 +120,7 @@ a#toggle-completed-versions {color:#999;}
|
|||
/***** Tables *****/
|
||||
table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
|
||||
table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
|
||||
table.list td { vertical-align: top; }
|
||||
table.list td { vertical-align: top; padding-right:10px; }
|
||||
table.list td.id { width: 2%; text-align: center;}
|
||||
table.list td.checkbox { width: 15px; padding: 2px 0 0 0; }
|
||||
table.list td.checkbox input {padding:0px;}
|
||||
|
@ -144,9 +144,10 @@ tr.project.idnt-8 td.name {padding-left: 11em;}
|
|||
tr.project.idnt-9 td.name {padding-left: 12.5em;}
|
||||
|
||||
tr.issue { text-align: center; white-space: nowrap; }
|
||||
tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text { white-space: normal; }
|
||||
tr.issue td.subject { text-align: left; }
|
||||
tr.issue td.subject, tr.issue td.category, td.assigned_to, tr.issue td.string, tr.issue td.text, tr.issue td.relations { white-space: normal; }
|
||||
tr.issue td.subject, tr.issue td.relations { text-align: left; }
|
||||
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
|
||||
tr.issue td.relations span {white-space: nowrap;}
|
||||
|
||||
tr.issue.idnt td.subject a {background: url(../images/bullet_arrow_right.png) no-repeat 0 50%; padding-left: 16px;}
|
||||
tr.issue.idnt-1 td.subject {padding-left: 0.5em;}
|
||||
|
@ -340,12 +341,14 @@ fieldset#date-range p { margin: 2px 0 2px 0; }
|
|||
fieldset#filters table { border-collapse: collapse; }
|
||||
fieldset#filters table td { padding: 0; vertical-align: middle; }
|
||||
fieldset#filters tr.filter { height: 2.1em; }
|
||||
fieldset#filters td.field { width:250px; }
|
||||
fieldset#filters td.operator { width:170px; }
|
||||
fieldset#filters td.field { width:230px; }
|
||||
fieldset#filters td.operator { width:180px; }
|
||||
fieldset#filters td.operator select {max-width:170px;}
|
||||
fieldset#filters td.values { white-space:nowrap; }
|
||||
fieldset#filters td.values select {min-width:130px;}
|
||||
fieldset#filters td.values input {height:1em;}
|
||||
fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
|
||||
|
||||
.toggle-multiselect {background: url(../images/bullet_toggle_plus.png) no-repeat 0% 40%; padding-left:8px; margin-left:0; cursor:pointer;}
|
||||
.buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
|
||||
|
||||
|
|
|
@ -766,7 +766,7 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_index_with_done_ratio
|
||||
def test_index_with_done_ratio_column
|
||||
Issue.find(1).update_attribute :done_ratio, 40
|
||||
|
||||
get :index, :set_filter => 1, :c => %w(done_ratio)
|
||||
|
@ -792,12 +792,48 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
assert_no_tag 'td', :attributes => {:class => /spent_hours/}
|
||||
end
|
||||
|
||||
def test_index_with_fixed_version
|
||||
def test_index_with_fixed_version_column
|
||||
get :index, :set_filter => 1, :c => %w(fixed_version)
|
||||
assert_tag 'td', :attributes => {:class => /fixed_version/},
|
||||
:child => {:tag => 'a', :content => '1.0', :attributes => {:href => '/versions/2'}}
|
||||
end
|
||||
|
||||
def test_index_with_relations_column
|
||||
IssueRelation.delete_all
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
|
||||
IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
|
||||
IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
|
||||
|
||||
get :index, :set_filter => 1, :c => %w(subject relations)
|
||||
assert_response :success
|
||||
assert_select "tr#issue-1 td.relations" do
|
||||
assert_select "span", 3
|
||||
assert_select "span", :text => "Related to #7"
|
||||
assert_select "span", :text => "Related to #8"
|
||||
assert_select "span", :text => "Blocks #11"
|
||||
end
|
||||
assert_select "tr#issue-2 td.relations" do
|
||||
assert_select "span", 1
|
||||
assert_select "span", :text => "Blocked by #12"
|
||||
end
|
||||
assert_select "tr#issue-3 td.relations" do
|
||||
assert_select "span", 0
|
||||
end
|
||||
|
||||
get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
|
||||
assert_response :success
|
||||
assert_equal 'text/csv; header=present', response.content_type
|
||||
lines = response.body.chomp.split("\n")
|
||||
assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
|
||||
assert_include '2,Blocked by #12', lines
|
||||
assert_include '3,""', lines
|
||||
|
||||
get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
|
||||
assert_response :success
|
||||
assert_equal 'application/pdf', response.content_type
|
||||
end
|
||||
|
||||
def test_index_send_html_if_query_is_invalid
|
||||
get :index, :f => ['start_date'], :op => {:start_date => '='}
|
||||
assert_equal 'text/html', @response.content_type
|
||||
|
|
|
@ -624,6 +624,76 @@ class QueryTest < ActiveSupport::TestCase
|
|||
assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
|
||||
end
|
||||
|
||||
def test_filter_on_relations_with_a_specific_issue
|
||||
IssueRelation.delete_all
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '=', :values => ['1']}}
|
||||
assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '=', :values => ['2']}}
|
||||
assert_equal [1], find_issues_with_query(query).map(&:id).sort
|
||||
end
|
||||
|
||||
def test_filter_on_relations_with_any_issues_in_a_project
|
||||
IssueRelation.delete_all
|
||||
with_settings :cross_project_issue_relations => '1' do
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
|
||||
end
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
|
||||
assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
|
||||
assert_equal [1], find_issues_with_query(query).map(&:id).sort
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
|
||||
assert_equal [], find_issues_with_query(query).map(&:id).sort
|
||||
end
|
||||
|
||||
def test_filter_on_relations_with_any_issues_not_in_a_project
|
||||
IssueRelation.delete_all
|
||||
with_settings :cross_project_issue_relations => '1' do
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
|
||||
#IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
|
||||
end
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
|
||||
assert_equal [1], find_issues_with_query(query).map(&:id).sort
|
||||
end
|
||||
|
||||
def test_filter_on_relations_with_no_issues
|
||||
IssueRelation.delete_all
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '!*', :values => ['']}}
|
||||
ids = find_issues_with_query(query).map(&:id)
|
||||
assert_equal [], ids & [1, 2, 3]
|
||||
assert_include 4, ids
|
||||
end
|
||||
|
||||
def test_filter_on_relations_with_any_issue
|
||||
IssueRelation.delete_all
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
|
||||
IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
|
||||
|
||||
query = Query.new(:name => '_')
|
||||
query.filters = {"relates" => {:operator => '*', :values => ['']}}
|
||||
assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id)
|
||||
end
|
||||
|
||||
def test_statement_should_be_nil_with_no_filters
|
||||
q = Query.new(:name => '_')
|
||||
q.filters = {}
|
||||
|
|
Loading…
Reference in New Issue