##// END OF EJS Templates
Don't add the inclusion error when tracker is not set, the blank error is enough....
Don't add the inclusion error when tracker is not set, the blank error is enough. git-svn-id: http://svn.redmine.org/redmine/trunk@15492 e93f8b46-1217-0410-a6f0-8f06a7374b81

File last commit:

r14989:699a75910db7
r15110:90d14b71b365
Show More
issue_query.rb
565 lines | 23.1 KiB | text/x-ruby | RubyLexer
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 # Redmine - project management software
Jean-Philippe Lang
Updates copyright for 2016....
r14856 # Copyright (C) 2006-2016 Jean-Philippe Lang
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 #
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class IssueQuery < Query
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 self.queried_class = Issue
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 self.available_columns = [
Jean-Philippe Lang
Refactor: makes issue id a regular QueryColumn....
r11217 QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => '#', :frozen => true),
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
Jean-Philippe Lang
Adds options to display totals on the issue list (#1561)....
r14260 QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours", :totalable => true),
Jean-Philippe Lang
Adds a Total estimated hours column on issue list (#20688)....
r14172 QueryColumn.new(:total_estimated_hours,
:sortable => "COALESCE((SELECT SUM(estimated_hours) FROM #{Issue.table_name} subtasks" +
" WHERE subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)",
:default_order => 'desc'),
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 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'),
Jean-Philippe Lang
Makes closed_on available as column and filter on the issue list (#824)....
r11173 QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 QueryColumn.new(:relations, :caption => :label_related_issues),
QueryColumn.new(:description, :inline => false)
]
scope :visible, lambda {|*args|
user = args.shift || User.current
base = Project.allowed_to_condition(user, :view_issues, *args)
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 scope = joins("LEFT OUTER JOIN #{Project.table_name} ON #{table_name}.project_id = #{Project.table_name}.id").
where("#{table_name}.project_id IS NULL OR (#{base})")
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
Jean-Philippe Lang
Role based custom queries (#1019)....
r11764 if user.admin?
scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
elsif user.memberships.any?
scope.where("#{table_name}.visibility = ?" +
" OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" +
"SELECT DISTINCT q.id FROM #{table_name} q" +
" INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" +
" INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" +
" INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" +
" WHERE q.project_id IS NULL OR q.project_id = m.project_id))" +
" OR #{table_name}.user_id = ?",
VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id)
elsif user.logged?
scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id)
else
scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC)
end
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 }
def initialize(attributes=nil, *args)
super attributes
self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
end
# Returns true if the query is visible to +user+ or the current user.
def visible?(user=User.current)
Jean-Philippe Lang
Role based custom queries (#1019)....
r11764 return true if user.admin?
return false unless project.nil? || user.allowed_to?(:view_issues, project)
case visibility
when VISIBILITY_PUBLIC
true
when VISIBILITY_ROLES
if project
(user.roles_for_project(project) & roles).any?
else
Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any?
end
else
user == self.user
end
end
def is_private?
visibility == VISIBILITY_PRIVATE
end
def is_public?
!is_private?
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end
Jean-Philippe Lang
Ability to save Gantt query filters (#7836)....
r11790 def draw_relations
r = options[:draw_relations]
r.nil? || r == '1'
end
def draw_relations=(arg)
options[:draw_relations] = (arg == '0' ? '0' : nil)
end
def draw_progress_line
r = options[:draw_progress_line]
r == '1'
end
def draw_progress_line=(arg)
options[:draw_progress_line] = (arg == '1' ? '1' : nil)
end
def build_from_params(params)
super
self.draw_relations = params[:draw_relations] || (params[:query] && params[:query][:draw_relations])
self.draw_progress_line = params[:draw_progress_line] || (params[:query] && params[:query][:draw_progress_line])
self
end
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 def initialize_available_filters
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 principals = []
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 subprojects = []
versions = []
categories = []
issue_custom_fields = []
Toshi MARUYAMA
remove trailing white-spaces and redundant empty line from app/models/issue_query.rb...
r11496
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 if project
Jean-Philippe Lang
Adds a role setting for controlling visibility of users: all or members of visible projects (#11724)....
r13202 principals += project.principals.visible
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 unless project.leaf?
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 subprojects = project.descendants.visible.to_a
Jean-Philippe Lang
Adds a role setting for controlling visibility of users: all or members of visible projects (#11724)....
r13202 principals += Principal.member_of(subprojects).visible
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 versions = project.shared_versions.to_a
categories = project.issue_categories.to_a
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 issue_custom_fields = project.all_issue_custom_fields
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 else
if all_projects.any?
Jean-Philippe Lang
Adds a role setting for controlling visibility of users: all or members of visible projects (#11724)....
r13202 principals += Principal.member_of(all_projects).visible
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 versions = Version.visible.where(:sharing => 'system').to_a
Jean-Philippe Lang
Query#add_custom_fields_filters now takes a custom fields scope....
r11687 issue_custom_fields = IssueCustomField.where(:is_for_all => true)
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end
principals.uniq!
principals.sort!
Jean-Philippe Lang
Adds buit-in groups to give specific permissions to anonymous and non members users per project (#17976)....
r13053 principals.reject! {|p| p.is_a?(GroupBuiltin)}
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 users = principals.select {|p| p.is_a?(User)}
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter "status_id",
Toshi MARUYAMA
remove unneeded Relation#all from IssueQuery model...
r12449 :type => :list_status, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] }
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142
if project.nil?
project_values = []
if User.current.logged? && User.current.memberships.any?
project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
end
project_values += all_projects_values
add_available_filter("project_id",
:type => :list, :values => project_values
) unless project_values.empty?
end
add_available_filter "tracker_id",
:type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
add_available_filter "priority_id",
:type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
author_values = []
author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
author_values += users.collect{|s| [s.name, s.id.to_s] }
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter("author_id",
:type => :list, :values => author_values
) unless author_values.empty?
assigned_to_values = []
assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
assigned_to_values += (Setting.issue_group_assignment? ?
principals : users).collect{|s| [s.name, s.id.to_s] }
add_available_filter("assigned_to_id",
:type => :list_optional, :values => assigned_to_values
) unless assigned_to_values.empty?
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
Jean-Philippe Lang
Adds a role setting for controlling visibility of users: all or members of visible projects (#11724)....
r13202 group_values = Group.givable.visible.collect {|g| [g.name, g.id.to_s] }
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter("member_of_group",
:type => :list_optional, :values => group_values
) unless group_values.empty?
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter("assigned_to_role",
:type => :list_optional, :values => role_values
) unless role_values.empty?
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
Jean-Philippe Lang
Don't omit version/category filters if project has no versions/categories....
r14429 add_available_filter "fixed_version_id",
:type => :list_optional,
:values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
Jean-Philippe Lang
Don't omit version/category filters if project has no versions/categories....
r14429 add_available_filter "category_id",
:type => :list_optional,
:values => categories.collect{|s| [s.name, s.id.to_s] }
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142
add_available_filter "subject", :type => :text
Jean-Philippe Lang
Allow issue description to be searchable as a filter (#1159)....
r14293 add_available_filter "description", :type => :text
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter "created_on", :type => :date_past
add_available_filter "updated_on", :type => :date_past
Jean-Philippe Lang
Makes closed_on available as column and filter on the issue list (#824)....
r11173 add_available_filter "closed_on", :type => :date_past
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter "start_date", :type => :date
add_available_filter "due_date", :type => :date
add_available_filter "estimated_hours", :type => :float
add_available_filter "done_ratio", :type => :integer
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 add_available_filter "is_private",
:type => :list,
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
end
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142
if User.current.logged?
add_available_filter "watcher_id",
:type => :list, :values => [["<< #{l(:label_me)} >>", "me"]]
end
if subprojects.any?
add_available_filter "subproject_id",
:type => :list_subprojects,
:values => subprojects.collect{|s| [s.name, s.id.to_s] }
end
add_custom_fields_filters(issue_custom_fields)
add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
IssueRelation::TYPES.each do |relation_type, options|
add_available_filter relation_type, :type => :relation, :label => options[:name]
end
Jean-Philippe Lang
Adds issue filters on parent/subtasks (#6118)....
r13922 add_available_filter "parent_id", :type => :tree, :label => :field_parent_issue
add_available_filter "child_id", :type => :tree, :label => :label_subtask_plural
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142
Jean-Philippe Lang
Filter by issue id (#4806)....
r14989 add_available_filter "issue_id", :type => :integer, :label => :label_issue
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 Tracker.disabled_core_fields(trackers).each {|field|
Jean-Philippe Lang
Refactor: use an ordered hash to store available filters and remove :order option (#13154)....
r11142 delete_available_filter field
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 }
end
def available_columns
return @available_columns if @available_columns
@available_columns = self.class.available_columns.dup
@available_columns += (project ?
project.all_issue_custom_fields :
Jean-Philippe Lang
Role-based issue custom field visibility (#5037)....
r11782 IssueCustomField
).visible.collect {|cf| QueryCustomFieldColumn.new(cf) }
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
if User.current.allowed_to?(:view_time_entries, project, :global => true)
Jean-Philippe Lang
"Total estimated time" should be inserted after "Estimated time" (#20733)....
r14220 index = @available_columns.find_index {|column| column.name == :total_estimated_hours}
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 index = (index ? index + 1 : -1)
Jean-Philippe Lang
"Total estimated time" should be inserted after "Estimated time" (#20733)....
r14220 # insert the column after total_estimated_hours or at the end
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 @available_columns.insert index, QueryColumn.new(:spent_hours,
Jean-Philippe Lang
Prevent sqlserver adapter from breaking the sub-query (#12713)....
r10886 :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)",
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 :default_order => 'desc',
Jean-Philippe Lang
Adds options to display totals on the issue list (#1561)....
r14260 :caption => :label_spent_time,
:totalable => true
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 )
Jean-Philippe Lang
Adds "Total spent hours" column available on the issue list (#11253)....
r14024 @available_columns.insert index+1, QueryColumn.new(:total_spent_hours,
:sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} JOIN #{Issue.table_name} subtasks ON subtasks.id = #{TimeEntry.table_name}.issue_id" +
" WHERE subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)",
:default_order => 'desc',
:caption => :label_total_spent_time
)
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end
if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
@available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
end
disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
@available_columns.reject! {|column|
disabled_fields.include?(column.name.to_s)
}
@available_columns
end
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 def default_columns_names
@default_columns_names ||= begin
default_columns = Setting.issue_list_default_columns.map(&:to_sym)
project.present? ? default_columns : [:project] | default_columns
end
end
Jean-Philippe Lang
Adds options to display totals on the issue list (#1561)....
r14260 def base_scope
Issue.visible.joins(:status, :project).where(statement)
end
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 # Returns the issue count
def issue_count
Jean-Philippe Lang
Adds options to display totals on the issue list (#1561)....
r14260 base_scope.count
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
Jean-Philippe Lang
Display totals for each group on grouped queries (#1561)....
r14283 # Returns the issue count by group or nil if query is not grouped
def issue_count_by_group
grouped_query do |scope|
scope.count
end
Jean-Philippe Lang
Adds options to display totals on the issue list (#1561)....
r14260 end
Jean-Philippe Lang
Display totals for each group on grouped queries (#1561)....
r14283 # Returns sum of all the issue's estimated_hours
def total_for_estimated_hours(scope)
Jean-Philippe Lang
Fixes float conversion failures (#1561)....
r14286 map_total(scope.sum(:estimated_hours)) {|t| t.to_f.round(2)}
Jean-Philippe Lang
Adds options to display totals on the issue list (#1561)....
r14260 end
Jean-Philippe Lang
Display totals for each group on grouped queries (#1561)....
r14283 # Returns sum of all the issue's time entries hours
def total_for_spent_hours(scope)
Jean-Philippe Lang
Fixes float conversion failures (#1561)....
r14286 total = if group_by_column.try(:name) == :project
Jean-Philippe Lang
Workaround for spent time grouped by project (#1561)....
r14285 # TODO: remove this when https://github.com/rails/rails/issues/21922 is fixed
# We have to do a custom join without the time_entries.project_id column
# that would trigger a ambiguous column name error
scope.joins("JOIN (SELECT issue_id, hours FROM #{TimeEntry.table_name}) AS joined_time_entries ON joined_time_entries.issue_id = #{Issue.table_name}.id").
sum("joined_time_entries.hours")
else
scope.joins(:time_entries).sum("#{TimeEntry.table_name}.hours")
end
Jean-Philippe Lang
Fixes float conversion failures (#1561)....
r14286 map_total(total) {|t| t.to_f.round(2)}
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end
# Returns the issues
# Valid options are :order, :offset, :limit, :include, :conditions
def issues(options={})
Jean-Philippe Lang
Pass the order option as an array to satisfy sqlserver adapter (#12713)....
r10885 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
Jean-Philippe Lang
Avoid N queries when displaying the issue list with custom fields....
r11993 scope = Issue.visible.
Jean-Philippe Lang
Cleanup of finders with :conditions option....
r11733 joins(:status, :project).
where(statement).
includes(([:status, :project] + (options[:include] || [])).uniq).
where(options[:conditions]).
order(order_option).
joins(joins_for_order_statement(order_option.join(','))).
limit(options[:limit]).
Jean-Philippe Lang
Avoid N queries when displaying the issue list with custom fields....
r11993 offset(options[:offset])
Jean-Philippe Lang
Always preload issues custom values (#16091)....
r12619 scope = scope.preload(:custom_values)
Jean-Philippe Lang
Preload issue authors when displayed on the issue list (#16091)....
r12636 if has_column?(:author)
scope = scope.preload(:author)
end
Jean-Philippe Lang
Avoid N queries when displaying the issue list with custom fields....
r11993
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 issues = scope.to_a
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
if has_column?(:spent_hours)
Issue.load_visible_spent_hours(issues)
end
Jean-Philippe Lang
Preload total spent time on the issue list with 1 query (#11253)....
r14025 if has_column?(:total_spent_hours)
Issue.load_visible_total_spent_hours(issues)
end
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 if has_column?(:relations)
Issue.load_visible_relations(issues)
end
issues
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
# Returns the issues ids
def issue_ids(options={})
Jean-Philippe Lang
Pass the order option as an array to satisfy sqlserver adapter (#12713)....
r10885 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737
Jean-Philippe Lang
Cleanup of finders with :conditions option....
r11733 Issue.visible.
joins(:status, :project).
where(statement).
includes(([:status, :project] + (options[:include] || [])).uniq).
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 references(([:status, :project] + (options[:include] || [])).uniq).
Jean-Philippe Lang
Cleanup of finders with :conditions option....
r11733 where(options[:conditions]).
order(order_option).
joins(joins_for_order_statement(order_option.join(','))).
limit(options[:limit]).
offset(options[:offset]).
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 pluck(:id)
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
# Returns the journals
# Valid options are :order, :offset, :limit
def journals(options={})
Jean-Philippe Lang
Cleanup of finders with :conditions option....
r11733 Journal.visible.
joins(:issue => [:project, :status]).
where(statement).
order(options[:order]).
limit(options[:limit]).
offset(options[:offset]).
preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 to_a
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
# Returns the versions
# Valid options are :conditions
def versions(options={})
Jean-Philippe Lang
Cleanup of finders with :conditions option....
r11733 Version.visible.
where(project_statement).
where(options[:conditions]).
includes(:project).
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 references(:project).
to_a
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738
def sql_for_watcher_id_field(field, operator, value)
db_table = Watcher.table_name
"#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " +
sql_for_field(field, '=', value, db_table, 'user_id') + ')'
end
def sql_for_member_of_group_field(field, operator, value)
if operator == '*' # Any group
Jean-Philippe Lang
Adds buit-in groups to give specific permissions to anonymous and non members users per project (#17976)....
r13053 groups = Group.givable
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 operator = '=' # Override the operator since we want to find by assigned_to
elsif operator == "!*"
Jean-Philippe Lang
Adds buit-in groups to give specific permissions to anonymous and non members users per project (#17976)....
r13053 groups = Group.givable
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 operator = '!' # Override the operator since we want to find by assigned_to
else
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 groups = Group.where(:id => value).to_a
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 end
groups ||= []
members_of_groups = groups.inject([]) {|user_ids, group|
Jean-Philippe Lang
Code cleanup....
r11061 user_ids + group.user_ids + [group.id]
}.uniq.compact.sort.collect(&:to_s)
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738
'(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
end
def sql_for_assigned_to_role_field(field, operator, value)
case operator
when "*", "!*" # Member / Not member
sw = operator == "!*" ? 'NOT' : ''
nl = operator == "!*" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
"(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}" +
" WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
when "=", "!"
role_cond = value.any? ?
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")" :
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 "1=0"
sw = operator == "!" ? 'NOT' : ''
nl = operator == "!" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
"(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}, #{MemberRole.table_name}" +
" WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id AND #{Member.table_name}.id = #{MemberRole.table_name}.member_id AND #{role_cond}))"
end
end
def sql_for_is_private_field(field, operator, value)
op = (operator == "=" ? 'IN' : 'NOT IN')
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 va = value.map {|v| v == '0' ? self.class.connection.quoted_false : self.class.connection.quoted_true}.uniq.join(',')
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738
"#{Issue.table_name}.is_private #{op} (#{va})"
end
Jean-Philippe Lang
Adds issue filters on parent/subtasks (#6118)....
r13922 def sql_for_parent_id_field(field, operator, value)
case operator
when "="
"#{Issue.table_name}.parent_id = #{value.first.to_i}"
when "~"
root_id, lft, rgt = Issue.where(:id => value.first.to_i).pluck(:root_id, :lft, :rgt).first
if root_id && lft && rgt
"#{Issue.table_name}.root_id = #{root_id} AND #{Issue.table_name}.lft > #{lft} AND #{Issue.table_name}.rgt < #{rgt}"
else
"1=0"
end
when "!*"
"#{Issue.table_name}.parent_id IS NULL"
when "*"
"#{Issue.table_name}.parent_id IS NOT NULL"
end
end
def sql_for_child_id_field(field, operator, value)
case operator
when "="
parent_id = Issue.where(:id => value.first.to_i).pluck(:parent_id).first
if parent_id
"#{Issue.table_name}.id = #{parent_id}"
else
"1=0"
end
when "~"
root_id, lft, rgt = Issue.where(:id => value.first.to_i).pluck(:root_id, :lft, :rgt).first
if root_id && lft && rgt
"#{Issue.table_name}.root_id = #{root_id} AND #{Issue.table_name}.lft < #{lft} AND #{Issue.table_name}.rgt > #{rgt}"
else
"1=0"
end
when "!*"
"#{Issue.table_name}.rgt - #{Issue.table_name}.lft = 1"
when "*"
"#{Issue.table_name}.rgt - #{Issue.table_name}.lft > 1"
end
end
Jean-Philippe Lang
Filter by issue id (#4806)....
r14989 def sql_for_issue_id_field(field, operator, value)
ids = value.first.to_s.scan(/\d+/).map(&:to_i).join(",")
if ids.present?
"#{Issue.table_name}.id IN (#{ids})"
else
"1=0"
end
end
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 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')
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}')"
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 when "=", "!"
op = (operator == "=" ? 'IN' : 'NOT IN')
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 when "=p", "=!p", "!p"
op = (operator == "!p" ? 'NOT IN' : 'IN')
comp = (operator == "=!p" ? '<>' : '=')
Jean-Philippe Lang
Merged rails-4.1 branch (#14534)....
r13100 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
Jean-Philippe Lang
Ability to filter issues blocked by any/no open issues (#16621)....
r14427 when "*o", "!o"
op = (operator == "!o" ? 'NOT IN' : 'IN')
"#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false}))"
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 end
if relation_options[:sym] == field && !options[:reverse]
sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
Jean-Philippe Lang
Filtering issues on "related to" may ignore other filters (#14401)....
r11828 sql = sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 end
Jean-Philippe Lang
Filtering issues on "related to" may ignore other filters (#14401)....
r11828 "(#{sql})"
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 end
Jean-Philippe Lang
Add users to assignee/author filters if they are missing (#3398)....
r14340 def find_assigned_to_id_filter_values(values)
Principal.visible.where(:id => values).map {|p| [p.name, p.id.to_s]}
end
alias :find_author_id_filter_values :find_assigned_to_id_filter_values
Jean-Philippe Lang
Moves issue specific code to IssueQuery....
r10738 IssueRelation::TYPES.keys.each do |relation_type|
alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
end
Jean-Philippe Lang
Adds STI to Query model. Issue queries are now IssueQuery instances....
r10737 end