Class: Query
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Query
- Includes:
- Redmine::SubclassFactory
- Defined in:
- app/models/query.rb
Direct Known Subclasses
Defined Under Namespace
Classes: QueryError, StatementInvalid
Constant Summary collapse
- VISIBILITY_PRIVATE =
0
- VISIBILITY_ROLES =
1
- VISIBILITY_PUBLIC =
2
Class Method Summary collapse
- .add_available_column(column) ⇒ Object
-
.build_from_params(params, attributes = {}) ⇒ Object
Builds a new query from the given params and attributes.
-
.default(**_) ⇒ Object
to be implemented in subclasses that have a way to determine a default query for the given options.
-
.operators_labels ⇒ Object
Returns a hash of localized labels for all filter operators.
-
.visible(*args) ⇒ Object
Scope of visible queries, can be used from subclasses only.
Instance Method Summary collapse
-
#add_available_filter(field, options) ⇒ Object
Adds an available filter.
- #add_filter(field, operator, values = nil) ⇒ Object
- #add_filter_error(field, message) ⇒ Object
-
#add_filters(fields, operators, values) ⇒ Object
Add multiple filters using
add_filter
. - #add_short_filter(field, expression) ⇒ Object
- #all_projects ⇒ Object
- #all_projects_values ⇒ Object
- #as_params ⇒ Object
- #assigned_to_values ⇒ Object
- #author_values ⇒ Object
- #available_block_columns ⇒ Object
- #available_display_types ⇒ Object
-
#available_filters ⇒ Object
Return a hash of available filters.
-
#available_filters_as_json ⇒ Object
Returns a representation of the available filters for JSON serialization.
- #available_inline_columns ⇒ Object
- #available_totalable_columns ⇒ Object
- #block_columns ⇒ Object
-
#build_from_params(params, defaults = {}) ⇒ Object
Builds the query from the given params.
- #column_names=(names) ⇒ Object
- #columns ⇒ Object
- #css_classes ⇒ Object
- #default_columns_names ⇒ Object
- #default_display_type ⇒ Object
- #default_sort_criteria ⇒ Object
- #default_totalable_names ⇒ Object
-
#delete_available_filter(field) ⇒ Object
Removes an available filter.
- #display_type ⇒ Object
- #display_type=(type) ⇒ Object
- #editable_by?(user) ⇒ Boolean
- #fixed_version_values ⇒ Object
- #group_by_column ⇒ Object
-
#group_by_sort_order ⇒ Object
Returns the SQL sort order that should be prepended for grouping.
- #group_by_statement ⇒ Object
-
#groupable_columns ⇒ Object
Returns an array of columns that can be used to group the results.
-
#grouped? ⇒ Boolean
Returns true if the query is a grouped query.
- #has_column?(column) ⇒ Boolean
- #has_custom_field_column? ⇒ Boolean
- #has_default_columns? ⇒ Boolean
- #has_filter?(field) ⇒ Boolean
- #inline_columns ⇒ Object
-
#is_global? ⇒ Boolean
Returns true if the query is available for all projects.
- #is_private? ⇒ Boolean
- #is_public? ⇒ Boolean
-
#issue_custom_fields ⇒ Object
Returns a scope of issue custom fields that are available as columns or filters.
-
#issue_statuses_values ⇒ Object
Returns a scope of issue statuses that are available as columns for filters.
- #label_for(field) ⇒ Object
- #operator_for(field) ⇒ Object
- #principals ⇒ Object
-
#project_custom_fields ⇒ Object
Returns a scope of project custom fields that are available as columns or filters.
- #project_statement ⇒ Object
-
#project_statuses_values ⇒ Object
Returns a scope of project statuses that are available as columns or filters.
- #project_values ⇒ Object
- #queried_table_name ⇒ Object
-
#result_count_by_group ⇒ Object
Returns the result count by group or nil if query is not grouped.
- #sort_clause ⇒ Object
- #sort_criteria ⇒ Object
- #sort_criteria=(arg) ⇒ Object
- #sort_criteria_key(index) ⇒ Object
- #sort_criteria_order(index) ⇒ Object
-
#sortable_columns ⇒ Object
Returns a Hash of columns and the key for sorting.
- #statement ⇒ Object
- #subproject_values ⇒ Object
-
#time_entry_custom_fields ⇒ Object
Returns a scope of time entry custom fields that are available as columns or filters.
-
#total_by_group_for(column) ⇒ Object
Returns a hash of the sum of the given column for each group, or nil if the query is not grouped.
-
#total_for(column) ⇒ Object
Returns the sum of values for the given column.
- #totalable_columns ⇒ Object
- #totalable_names ⇒ Object
- #totalable_names=(names) ⇒ Object
- #totals {|totals| ... } ⇒ Object
- #totals_by_group {|totals| ... } ⇒ Object
- #trackers ⇒ Object
- #type_for(field) ⇒ Object
- #users ⇒ Object
- #validate_query_filters ⇒ Object
- #value_for(field, index = 0) ⇒ Object
- #values_for(field) ⇒ Object
-
#visible?(user = User.current) ⇒ Boolean
Returns true if the query is visible to
user
or the current user. - #watcher_values ⇒ Object
Methods included from Redmine::SubclassFactory
Methods inherited from ApplicationRecord
Class Method Details
.add_available_column(column) ⇒ Object
791 792 793 |
# File 'app/models/query.rb', line 791 def self.add_available_column(column) self.available_columns << (column) if column.is_a?(QueryColumn) end |
.build_from_params(params, attributes = {}) ⇒ Object
Builds a new query from the given params and attributes
469 470 471 |
# File 'app/models/query.rb', line 469 def self.build_from_params(params, attributes={}) new(attributes).build_from_params(params) end |
.default(**_) ⇒ Object
to be implemented in subclasses that have a way to determine a default query for the given options
370 371 372 |
# File 'app/models/query.rb', line 370 def self.default(**_) nil end |
.operators_labels ⇒ Object
Returns a hash of localized labels for all filter operators
553 554 555 |
# File 'app/models/query.rb', line 553 def self.operators_labels operators.inject({}) {|h, operator| h[operator.first] = l(*operator.last); h} end |
.visible(*args) ⇒ Object
Scope of visible queries, can be used from subclasses only. Unlike other visible scopes, a class methods is used as it let handle inheritance more nicely than scope DSL.
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
# File 'app/models/query.rb', line 377 def self.visible(*args) if self == ::Query # Visibility depends on permissions for each subclass, # raise an error if the scope is called from Query (eg. Query.visible) raise "Cannot call .visible scope from the base Query class, but from subclasses only." end user = args.shift || User.current base = Project.allowed_to_condition(user, , *args) 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})") 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 EXISTS (SELECT 1" \ " FROM #{table_name_prefix}queries_roles#{table_name_suffix} qr" \ " 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 = ?" \ " INNER JOIN #{Project.table_name} p ON p.id = m.project_id AND p.status <> ?" \ " WHERE qr.query_id = #{table_name}.id" \ " AND (#{table_name}.project_id IS NULL OR #{table_name}.project_id = m.project_id)))" \ " OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, Project::STATUS_ARCHIVED, 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 end |
Instance Method Details
#add_available_filter(field, options) ⇒ Object
Adds an available filter
713 714 715 716 717 |
# File 'app/models/query.rb', line 713 def add_available_filter(field, ) @available_filters ||= ActiveSupport::OrderedHash.new @available_filters[field] = QueryFilter.new(field, ) @available_filters end |
#add_filter(field, operator, values = nil) ⇒ Object
735 736 737 738 739 740 741 742 743 |
# File 'app/models/query.rb', line 735 def add_filter(field, operator, values=nil) # values must be an array return unless values.nil? || values.is_a?(Array) # check if field is defined as an available filter if available_filters.has_key? field filters[field] = {:operator => operator, :values => (values || [''])} end end |
#add_filter_error(field, message) ⇒ Object
533 534 535 536 |
# File 'app/models/query.rb', line 533 def add_filter_error(field, ) m = label_for(field) + " " + l(, :scope => 'activerecord.errors.messages') errors.add(:base, m) end |
#add_filters(fields, operators, values) ⇒ Object
Add multiple filters using add_filter
758 759 760 761 762 763 764 |
# File 'app/models/query.rb', line 758 def add_filters(fields, operators, values) if fields.present? && operators.present? fields.each do |field| add_filter(field, operators[field], values && values[field]) end end end |
#add_short_filter(field, expression) ⇒ Object
745 746 747 748 749 750 751 752 753 754 755 |
# File 'app/models/query.rb', line 745 def add_short_filter(field, expression) return unless expression && available_filters.has_key?(field) field_type = available_filters[field][:type] operators_by_filter_type[field_type].sort.reverse.detect do |operator| next unless expression =~ /^#{Regexp.escape(operator)}(.*)$/ values = $1 add_filter field, operator, values.present? ? values.split('|') : [''] end || add_filter(field, '=', expression.to_s.split('|')) end |
#all_projects ⇒ Object
578 579 580 |
# File 'app/models/query.rb', line 578 def all_projects @all_projects ||= Project.visible.to_a end |
#all_projects_values ⇒ Object
582 583 584 585 586 587 588 589 590 591 |
# File 'app/models/query.rb', line 582 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 |
#as_params ⇒ Object
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'app/models/query.rb', line 473 def as_params if new_record? params = {} filters.each do |field, | params[:f] ||= [] params[:f] << field params[:op] ||= {} params[:op][field] = [:operator] params[:v] ||= {} params[:v][field] = [:values] end params[:c] = column_names params[:group_by] = group_by.to_s if group_by.present? params[:t] = totalable_names.map(&:to_s) if totalable_names.any? params[:sort] = sort_criteria.to_param params[:set_filter] = 1 params else {:query_id => id} end end |
#assigned_to_values ⇒ Object
639 640 641 642 643 644 645 646 |
# File 'app/models/query.rb', line 639 def assigned_to_values assigned_to_values = [] assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? assigned_to_values += (Setting.issue_group_assignment? ? principals : users).sort_by{|p| [p.status, p]}. collect{|s| [s.name, s.id.to_s, l("status_#{User::LABEL_BY_STATUS[s.status]}")]} assigned_to_values end |
#author_values ⇒ Object
629 630 631 632 633 634 635 636 637 |
# File 'app/models/query.rb', line 629 def = [] << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? += users.sort_by{|p| [p.status, p]}. collect{|s| [s.name, s.id.to_s, l("status_#{User::LABEL_BY_STATUS[s.status]}")]} << [l(:label_user_anonymous), User.anonymous.id.to_s] end |
#available_block_columns ⇒ Object
830 831 832 |
# File 'app/models/query.rb', line 830 def available_block_columns available_columns.reject(&:inline?) end |
#available_display_types ⇒ Object
1096 1097 1098 |
# File 'app/models/query.rb', line 1096 def available_display_types ['list'] end |
#available_filters ⇒ Object
Return a hash of available filters
727 728 729 730 731 732 733 |
# File 'app/models/query.rb', line 727 def available_filters unless @available_filters initialize_available_filters @available_filters ||= {} end @available_filters end |
#available_filters_as_json ⇒ Object
Returns a representation of the available filters for JSON serialization
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'app/models/query.rb', line 558 def available_filters_as_json json = {} available_filters.each do |field, filter| = {:type => filter[:type], :name => filter[:name]} [:remote] = true if filter.remote if has_filter?(field) || !filter.remote [:values] = filter.values if [:values] && values_for(field) missing = Array(values_for(field)).select(&:present?) - [:values].pluck(1) if missing.any? && respond_to?(method = "find_#{field}_filter_values") [:values] += send(method, missing) end end end json[field] = .stringify_keys end json end |
#available_inline_columns ⇒ Object
826 827 828 |
# File 'app/models/query.rb', line 826 def available_inline_columns available_columns.select(&:inline?) end |
#available_totalable_columns ⇒ Object
834 835 836 |
# File 'app/models/query.rb', line 834 def available_totalable_columns available_columns.select(&:totalable) end |
#block_columns ⇒ Object
822 823 824 |
# File 'app/models/query.rb', line 822 def block_columns columns.reject(&:inline?) end |
#build_from_params(params, defaults = {}) ⇒ Object
Builds the query from the given params
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
# File 'app/models/query.rb', line 449 def build_from_params(params, defaults={}) if params[:fields] || params[:f] self.filters = {} add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) else available_filters.each_key do |field| add_short_filter(field, params[field]) if params[field] end end query_params = params[:query] || defaults || {} self.group_by = params[:group_by] || query_params[:group_by] || self.group_by self.column_names = params[:c] || query_params[:column_names] || self.column_names self.totalable_names = params[:t] || query_params[:totalable_names] || self.totalable_names self.sort_criteria = params[:sort] || query_params[:sort_criteria] || self.sort_criteria self.display_type = params[:display_type] || query_params[:display_type] || self.display_type self end |
#column_names=(names) ⇒ Object
850 851 852 853 854 855 856 857 858 859 860 861 862 863 |
# File 'app/models/query.rb', line 850 def column_names=(names) if names names = names.select {|n| n.is_a?(Symbol) || n.present?} names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym} if names.delete(:all_inline) names = available_inline_columns.map(&:name) | names end # Set column_names to nil if default columns if names == default_columns_names names = nil end end write_attribute(:column_names, names) end |
#columns ⇒ Object
808 809 810 811 812 813 814 815 816 |
# File 'app/models/query.rb', line 808 def columns return [] if available_columns.empty? # preserve the column_names order cols = (has_default_columns? ? default_columns_names : column_names).filter_map do |name| available_columns.find {|col| col.name == name} end available_columns.select(&:frozen?) | cols end |
#css_classes ⇒ Object
1077 1078 1079 1080 1081 1082 1083 |
# File 'app/models/query.rb', line 1077 def css_classes s = sort_criteria.first if s.present? key, asc = s "sort-by-#{key.to_s.dasherize} sort-#{asc}" end end |
#default_columns_names ⇒ Object
838 839 840 |
# File 'app/models/query.rb', line 838 def default_columns_names [] end |
#default_display_type ⇒ Object
846 847 848 |
# File 'app/models/query.rb', line 846 def default_display_type self.available_display_types.first end |
#default_sort_criteria ⇒ Object
894 895 896 |
# File 'app/models/query.rb', line 894 def default_sort_criteria [] end |
#default_totalable_names ⇒ Object
842 843 844 |
# File 'app/models/query.rb', line 842 def default_totalable_names [] end |
#delete_available_filter(field) ⇒ Object
Removes an available filter
720 721 722 723 724 |
# File 'app/models/query.rb', line 720 def delete_available_filter(field) if @available_filters @available_filters.delete(field) end end |
#display_type ⇒ Object
1085 1086 1087 |
# File 'app/models/query.rb', line 1085 def display_type [:display_type] || self.default_display_type end |
#display_type=(type) ⇒ Object
1089 1090 1091 1092 1093 1094 |
# File 'app/models/query.rb', line 1089 def display_type=(type) unless type && self.available_display_types.include?(type) type = self.available_display_types.first end [:display_type] = type end |
#editable_by?(user) ⇒ Boolean
538 539 540 541 542 543 544 545 546 |
# File 'app/models/query.rb', line 538 def editable_by?(user) return false unless user # Admin can edit them all and regular users can edit their private queries return true if user.admin? || (is_private? && self.user_id == user.id) # Members can not edit public queries that are for all project (only admin is allowed to) is_public? && !is_global? && user.allowed_to?(:manage_public_queries, project) end |
#fixed_version_values ⇒ Object
648 649 650 651 652 653 654 655 656 657 |
# File 'app/models/query.rb', line 648 def fixed_version_values versions = [] if project versions = project.shared_versions.to_a else versions = Version.visible.to_a end Version.sort_by_status(versions). collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")]} end |
#group_by_column ⇒ Object
944 945 946 |
# File 'app/models/query.rb', line 944 def group_by_column groupable_columns.detect {|c| c.groupable? && c.name.to_s == group_by} end |
#group_by_sort_order ⇒ Object
Returns the SQL sort order that should be prepended for grouping
927 928 929 930 931 932 933 934 935 936 937 |
# File 'app/models/query.rb', line 927 def group_by_sort_order if column = group_by_column order = (sort_criteria.order_for(column.name) || column.default_order || 'asc').try(:upcase) column_sortable = column.sortable if column.is_a?(TimestampQueryColumn) column_sortable = Redmine::Database.(column.sortable, User.current.time_zone) end Array(column_sortable).map {|s| Arel.sql("#{s} #{order}")} end end |
#group_by_statement ⇒ Object
948 949 950 |
# File 'app/models/query.rb', line 948 def group_by_statement group_by_column.try(:group_by_statement) end |
#groupable_columns ⇒ Object
Returns an array of columns that can be used to group the results
796 797 798 |
# File 'app/models/query.rb', line 796 def groupable_columns available_columns.select(&:groupable?) end |
#grouped? ⇒ Boolean
Returns true if the query is a grouped query
940 941 942 |
# File 'app/models/query.rb', line 940 def grouped? !group_by_column.nil? end |
#has_column?(column) ⇒ Boolean
865 866 867 868 |
# File 'app/models/query.rb', line 865 def has_column?(column) name = column.is_a?(QueryColumn) ? column.name : column columns.detect {|c| c.name == name} end |
#has_custom_field_column? ⇒ Boolean
870 871 872 |
# File 'app/models/query.rb', line 870 def has_custom_field_column? columns.any?(QueryCustomFieldColumn) end |
#has_default_columns? ⇒ Boolean
874 875 876 |
# File 'app/models/query.rb', line 874 def has_default_columns? column_names.nil? || column_names.empty? end |
#has_filter?(field) ⇒ Boolean
766 767 768 |
# File 'app/models/query.rb', line 766 def has_filter?(field) filters and filters[field] end |
#inline_columns ⇒ Object
818 819 820 |
# File 'app/models/query.rb', line 818 def inline_columns columns.select(&:inline?) end |
#is_global? ⇒ Boolean
Returns true if the query is available for all projects
440 441 442 |
# File 'app/models/query.rb', line 440 def is_global? new_record? ? project_id.nil? : project_id_in_database.nil? end |
#is_private? ⇒ Boolean
431 432 433 |
# File 'app/models/query.rb', line 431 def is_private? visibility == VISIBILITY_PRIVATE end |
#is_public? ⇒ Boolean
435 436 437 |
# File 'app/models/query.rb', line 435 def is_public? !is_private? end |
#issue_custom_fields ⇒ Object
Returns a scope of issue custom fields that are available as columns or filters
680 681 682 683 684 685 686 |
# File 'app/models/query.rb', line 680 def issue_custom_fields if project project.rolled_up_custom_fields else IssueCustomField.sorted end end |
#issue_statuses_values ⇒ Object
Returns a scope of issue statuses that are available as columns for filters
660 661 662 663 664 665 666 667 |
# File 'app/models/query.rb', line 660 def issue_statuses_values if project statuses = project.rolled_up_statuses else statuses = IssueStatus.all.sorted end statuses.pluck(:name, :id).map {|name, id| [name, id.to_s]} end |
#label_for(field) ⇒ Object
786 787 788 789 |
# File 'app/models/query.rb', line 786 def label_for(field) label = available_filters[field][:name] if available_filters.has_key?(field) label ||= queried_class.human_attribute_name(field, :default => field) end |
#operator_for(field) ⇒ Object
774 775 776 |
# File 'app/models/query.rb', line 774 def operator_for(field) has_filter?(field) ? filters[field][:operator] : nil end |
#principals ⇒ Object
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 |
# File 'app/models/query.rb', line 607 def principals @principal ||= begin principals = [] if project principals += Principal.member_of(project).visible unless project.leaf? principals += Principal.member_of(project.descendants.visible).visible end else principals += Principal.member_of(all_projects).visible end principals.uniq! principals.sort! principals.reject! {|p| p.is_a?(GroupBuiltin)} principals end end |
#project_custom_fields ⇒ Object
Returns a scope of project custom fields that are available as columns or filters
689 690 691 |
# File 'app/models/query.rb', line 689 def project_custom_fields ProjectCustomField.sorted end |
#project_statement ⇒ Object
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 |
# File 'app/models/query.rb', line 952 def project_statement project_clauses = [] subprojects_ids = [] subprojects_ids = project.descendants.where.not(status: Project::STATUS_ARCHIVED).ids if project if subprojects_ids.any? if has_filter?("subproject_id") case operator_for("subproject_id") when '=' # include the selected subprojects ids = [project.id] + values_for("subproject_id").map(&:to_i) project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',') when '!' # exclude the selected subprojects ids = [project.id] + subprojects_ids - values_for("subproject_id").map(&:to_i) project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',') when '!*' # main project only project_clauses << "#{Project.table_name}.id = %d" % project.id else # all subprojects project_clauses << "#{Project.table_name}.lft >= #{project.lft} AND #{Project.table_name}.rgt <= #{project.rgt}" end elsif Setting.display_subprojects_issues? project_clauses << "#{Project.table_name}.lft >= #{project.lft} AND #{Project.table_name}.rgt <= #{project.rgt}" else project_clauses << "#{Project.table_name}.id = %d" % project.id end elsif project project_clauses << "#{Project.table_name}.id = %d" % project.id end project_clauses.any? ? project_clauses.join(' AND ') : nil end |
#project_statuses_values ⇒ Object
Returns a scope of project statuses that are available as columns or filters
699 700 701 702 703 704 |
# File 'app/models/query.rb', line 699 def project_statuses_values [ [l(:project_status_active), "#{Project::STATUS_ACTIVE}"], [l(:project_status_closed), "#{Project::STATUS_CLOSED}"] ] end |
#project_values ⇒ Object
593 594 595 596 597 598 599 600 601 |
# File 'app/models/query.rb', line 593 def project_values project_values = [] if User.current.logged? project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] if User.current.memberships.any? project_values << ["<< #{l(:label_my_bookmarks).downcase} >>", "bookmarks"] if User.current.bookmarked_project_ids.any? end project_values += all_projects_values project_values end |
#queried_table_name ⇒ Object
444 445 446 |
# File 'app/models/query.rb', line 444 def queried_table_name @queried_table_name ||= self.class.queried_class.table_name end |
#result_count_by_group ⇒ Object
Returns the result count by group or nil if query is not grouped
1046 1047 1048 1049 1050 |
# File 'app/models/query.rb', line 1046 def result_count_by_group grouped_query do |scope| scope.count end end |
#sort_clause ⇒ Object
920 921 922 923 924 |
# File 'app/models/query.rb', line 920 def sort_clause if clause = sort_criteria.sort_clause(sortable_columns) clause.map {|c| Arel.sql c} end end |
#sort_criteria ⇒ Object
904 905 906 907 908 909 910 |
# File 'app/models/query.rb', line 904 def sort_criteria c = read_attribute(:sort_criteria) if c.blank? c = default_sort_criteria end Redmine::SortCriteria.new(c) end |
#sort_criteria=(arg) ⇒ Object
898 899 900 901 902 |
# File 'app/models/query.rb', line 898 def sort_criteria=(arg) c = Redmine::SortCriteria.new(arg) write_attribute(:sort_criteria, c.to_a) c end |
#sort_criteria_key(index) ⇒ Object
912 913 914 |
# File 'app/models/query.rb', line 912 def sort_criteria_key(index) sort_criteria[index].try(:first) end |
#sort_criteria_order(index) ⇒ Object
916 917 918 |
# File 'app/models/query.rb', line 916 def sort_criteria_order(index) sort_criteria[index].try(:last) end |
#sortable_columns ⇒ Object
Returns a Hash of columns and the key for sorting
801 802 803 804 805 806 |
# File 'app/models/query.rb', line 801 def sortable_columns available_columns.inject({}) do |h, column| h[column.name.to_s] = column.sortable h end end |
#statement ⇒ Object
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 |
# File 'app/models/query.rb', line 986 def statement # filters clauses filters_clauses = [] filters.each_key do |field| next if field == "subproject_id" v = values_for(field).clone next unless v and !v.empty? operator = operator_for(field) # "me" value substitution if %w(assigned_to_id author_id user_id watcher_id updated_by last_updated_by).include?(field) if v.delete("me") if User.current.logged? v.push(User.current.id.to_s) v += User.current.group_ids.map(&:to_s) if %w(assigned_to_id watcher_id).include?(field) else v.push("0") end end end if field == 'project_id' || (is_a?(ProjectQuery) && %w[id parent_id].include?(field)) if v.delete('mine') v += User.current.memberships.pluck(:project_id).map(&:to_s) end if v.delete('bookmarks') v += User.current.bookmarked_project_ids end end if field =~ /^cf_(\d+)\.cf_(\d+)$/ filters_clauses << sql_for_chained_custom_field(field, operator, v, $1, $2) elsif field =~ /cf_(\d+)$/ # custom field filters_clauses << sql_for_custom_field(field, operator, v, $1) elsif field =~ /^cf_(\d+)\.(.+)$/ filters_clauses << sql_for_custom_field_attribute(field, operator, v, $1, $2) elsif respond_to?(method = "sql_for_#{field.tr('.', '_')}_field") # specific statement filters_clauses << send(method, field, operator, v) else # regular field filters_clauses << '(' + sql_for_field(field, operator, v, queried_table_name, field) + ')' end end if filters and valid? if (c = group_by_column) && c.is_a?(QueryCustomFieldColumn) # Excludes results for which the grouped custom field is not visible filters_clauses << c.custom_field.visibility_by_project_condition end filters_clauses << project_statement filters_clauses.reject!(&:blank?) filters_clauses.any? ? filters_clauses.join(' AND ') : nil end |
#subproject_values ⇒ Object
603 604 605 |
# File 'app/models/query.rb', line 603 def subproject_values project.descendants.visible.pluck(:name, :id).map {|name, id| [name, id.to_s]} end |
#time_entry_custom_fields ⇒ Object
Returns a scope of time entry custom fields that are available as columns or filters
694 695 696 |
# File 'app/models/query.rb', line 694 def time_entry_custom_fields TimeEntryCustomField.sorted end |
#total_by_group_for(column) ⇒ Object
Returns a hash of the sum of the given column for each group, or nil if the query is not grouped
1059 1060 1061 1062 1063 |
# File 'app/models/query.rb', line 1059 def total_by_group_for(column) grouped_query do |scope| total_with_scope(column, scope) end end |
#total_for(column) ⇒ Object
Returns the sum of values for the given column
1053 1054 1055 |
# File 'app/models/query.rb', line 1053 def total_for(column) total_with_scope(column, base_scope) end |
#totalable_columns ⇒ Object
878 879 880 881 |
# File 'app/models/query.rb', line 878 def totalable_columns names = totalable_names available_totalable_columns.select {|column| names.include?(column.name)} end |
#totalable_names ⇒ Object
890 891 892 |
# File 'app/models/query.rb', line 890 def totalable_names [:totalable_names] || default_totalable_names || [] end |
#totalable_names=(names) ⇒ Object
883 884 885 886 887 888 |
# File 'app/models/query.rb', line 883 def totalable_names=(names) if names names = names.select(&:present?).map {|n| n.is_a?(Symbol) ? n : n.to_sym} end [:totalable_names] = names end |
#totals {|totals| ... } ⇒ Object
1065 1066 1067 1068 1069 |
# File 'app/models/query.rb', line 1065 def totals totals = totalable_columns.map {|column| [column, total_for(column)]} yield totals if block_given? totals end |
#totals_by_group {|totals| ... } ⇒ Object
1071 1072 1073 1074 1075 |
# File 'app/models/query.rb', line 1071 def totals_by_group totals = totalable_columns.map {|column| [column, total_by_group_for(column)]} yield totals if block_given? totals end |
#trackers ⇒ Object
548 549 550 |
# File 'app/models/query.rb', line 548 def trackers @trackers ||= (project.nil? ? Tracker.all : project.rolled_up_trackers).visible.sorted end |
#type_for(field) ⇒ Object
770 771 772 |
# File 'app/models/query.rb', line 770 def type_for(field) available_filters[field][:type] if available_filters.has_key?(field) end |
#users ⇒ Object
625 626 627 |
# File 'app/models/query.rb', line 625 def users principals.select {|p| p.is_a?(User)} end |
#validate_query_filters ⇒ Object
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
# File 'app/models/query.rb', line 495 def validate_query_filters filters.each_key do |field| if values_for(field) case type_for(field) when :integer if values_for(field).detect {|v| v.present? && !/\A[+-]?\d+(,[+-]?\d+)*\z/.match?(v)} add_filter_error(field, :invalid) end when :float if values_for(field).detect {|v| v.present? && !/\A[+-]?\d+(\.\d*)?\z/.match?(v)} add_filter_error(field, :invalid) end when :date, :date_past case operator_for(field) when "=", ">=", "<=", "><" if values_for(field).detect do |v| v.present? && (!/\A\d{4}-\d{2}-\d{2}(T\d{2}((:)?\d{2}){0,2}(Z|\d{2}:?\d{2})?)?\z/.match?(v) || parse_date(v).nil?) end add_filter_error(field, :invalid) end when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-" if values_for(field).detect {|v| v.present? && !/^\d+$/.match?(v)} add_filter_error(field, :invalid) end end end end add_filter_error(field, :blank) unless # filter requires one or more values (values_for(field) and values_for(field).first.present?) or # filter doesn't require any value ["o", "c", "!*", "*", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", "*o", "!o"].include? operator_for(field) end if filters end |
#value_for(field, index = 0) ⇒ Object
782 783 784 |
# File 'app/models/query.rb', line 782 def value_for(field, index=0) (values_for(field) || [])[index] end |
#values_for(field) ⇒ Object
778 779 780 |
# File 'app/models/query.rb', line 778 def values_for(field) has_filter?(field) ? filters[field][:values] : nil end |
#visible?(user = User.current) ⇒ Boolean
Returns true if the query is visible to user
or the current user.
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'app/models/query.rb', line 412 def visible?(user=User.current) return true if user.admin? return false unless project.nil? || user.allowed_to?(self.class., project) case visibility when VISIBILITY_PUBLIC true when VISIBILITY_ROLES if project user.roles_for_project(project).intersect?(roles) else user.memberships.joins(:member_roles).where(:member_roles => {:role_id => roles.map(&:id)}).any? end else user == self.user end end |
#watcher_values ⇒ Object
669 670 671 672 673 674 675 676 677 |
# File 'app/models/query.rb', line 669 def watcher_values watcher_values = [["<< #{l(:label_me)} >>", "me"]] if User.current.allowed_to?(:view_issue_watchers, self.project, global: true) watcher_values += principals.sort_by{|p| [p.status, p]}. collect{|s| [s.name, s.id.to_s, l("status_#{User::LABEL_BY_STATUS[s.status]}")]} end watcher_values end |