You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

user_query.rb 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2023 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. class UserQuery < Query
  18. self.queried_class = Principal # must be Principal (not User) for custom field filters to work
  19. self.available_columns = [
  20. QueryColumn.new(:login, sortable: "#{User.table_name}.login"),
  21. QueryColumn.new(:firstname, sortable: "#{User.table_name}.firstname"),
  22. QueryColumn.new(:lastname, sortable: "#{User.table_name}.lastname"),
  23. QueryColumn.new(:mail, sortable: "#{EmailAddress.table_name}.address"),
  24. QueryColumn.new(:admin, sortable: "#{User.table_name}.admin"),
  25. QueryColumn.new(:created_on, :sortable => "#{User.table_name}.created_on"),
  26. QueryColumn.new(:updated_on, :sortable => "#{User.table_name}.updated_on"),
  27. QueryColumn.new(:last_login_on, :sortable => "#{User.table_name}.last_login_on"),
  28. QueryColumn.new(:passwd_changed_on, :sortable => "#{User.table_name}.passwd_changed_on"),
  29. QueryColumn.new(:status, sortable: "#{User.table_name}.status"),
  30. QueryAssociationColumn.new(:auth_source, :name, caption: :field_auth_source, sortable: "#{AuthSource.table_name}.name")
  31. ]
  32. def initialize(attributes=nil, *args)
  33. super(attributes)
  34. self.filters ||= { 'status' => {operator: "=", values: [User::STATUS_ACTIVE]} }
  35. end
  36. def initialize_available_filters
  37. add_available_filter "status",
  38. type: :list, values: ->{ user_statuses_values }
  39. add_available_filter "auth_source_id",
  40. type: :list_optional, values: ->{ auth_sources_values }
  41. add_available_filter "is_member_of_group",
  42. type: :list_optional,
  43. values: ->{ Group.givable.visible.pluck(:name, :id).map {|name, id| [name, id.to_s]} }
  44. if Setting.twofa?
  45. add_available_filter "twofa_scheme",
  46. type: :list_optional,
  47. values: ->{ Redmine::Twofa.available_schemes.map {|s| [I18n.t("twofa__#{s}__name"), s] } }
  48. end
  49. add_available_filter "name", type: :text, label: :field_name_or_email_or_login
  50. add_available_filter "login", type: :string
  51. add_available_filter "firstname", type: :string
  52. add_available_filter "lastname", type: :string
  53. add_available_filter "mail", type: :string
  54. add_available_filter "created_on", type: :date_past
  55. add_available_filter "last_login_on", type: :date_past
  56. add_available_filter "admin",
  57. type: :list,
  58. values: [[l(:general_text_yes), '1'], [l(:general_text_no), '0']]
  59. add_custom_fields_filters(user_custom_fields)
  60. end
  61. def auth_sources_values
  62. AuthSource.order(name: :asc).pluck(:name, :id)
  63. end
  64. def user_statuses_values
  65. [
  66. [l(:status_active), User::STATUS_ACTIVE.to_s],
  67. [l(:status_registered), User::STATUS_REGISTERED.to_s],
  68. [l(:status_locked), User::STATUS_LOCKED.to_s]
  69. ]
  70. end
  71. def available_columns
  72. return @available_columns if @available_columns
  73. @available_columns = self.class.available_columns.dup
  74. if Setting.twofa?
  75. @available_columns << QueryColumn.new(:twofa_scheme, sortable: "#{User.table_name}.twofa_scheme")
  76. end
  77. @available_columns += user_custom_fields.visible.
  78. map {|cf| QueryCustomFieldColumn.new(cf)}
  79. @available_columns
  80. end
  81. # Returns a scope of user custom fields that are available as columns or filters
  82. def user_custom_fields
  83. UserCustomField.sorted
  84. end
  85. def default_columns_names
  86. @default_columns_names ||= [:login, :firstname, :lastname, :mail, :admin, :created_on, :last_login_on]
  87. end
  88. def default_sort_criteria
  89. [['login', 'asc']]
  90. end
  91. def base_scope
  92. User.logged.where(statement).includes(:email_address)
  93. end
  94. def results_scope(options={})
  95. order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?)
  96. base_scope.
  97. order(order_option).
  98. joins(joins_for_order_statement(order_option.join(',')))
  99. end
  100. def sql_for_admin_field(field, operator, value)
  101. return unless value = value.first
  102. true_value = operator == '=' ? '1' : '0'
  103. val =
  104. if value.to_s == true_value
  105. self.class.connection.quoted_true
  106. else
  107. self.class.connection.quoted_false
  108. end
  109. "(#{User.table_name}.admin = #{val})"
  110. end
  111. def sql_for_is_member_of_group_field(field, operator, value)
  112. if ["*", "!*"].include? operator
  113. value = Group.givable.ids
  114. end
  115. e = operator.start_with?("!") ? "NOT EXISTS" : "EXISTS"
  116. "(#{e} (SELECT 1 FROM groups_users WHERE #{User.table_name}.id = groups_users.user_id AND #{sql_for_field(field, '=', value, 'groups_users', 'group_id')}))"
  117. end
  118. def sql_for_mail_field(field, operator, value)
  119. if operator == '!*'
  120. match = false
  121. operator = '*'
  122. else
  123. match = true
  124. end
  125. emails = EmailAddress.table_name
  126. <<-SQL
  127. #{match ? 'EXISTS' : 'NOT EXISTS'}
  128. (SELECT 1 FROM #{emails} WHERE
  129. #{emails}.user_id = #{User.table_name}.id AND
  130. #{sql_for_field(:mail, operator, value, emails, 'address')})
  131. SQL
  132. end
  133. def joins_for_order_statement(order_options)
  134. joins = [super]
  135. if order_options
  136. if order_options.include?('auth_source')
  137. joins << "LEFT OUTER JOIN #{AuthSource.table_name} auth_sources ON auth_sources.id = #{queried_table_name}.auth_source_id"
  138. end
  139. end
  140. joins.any? ? joins.join(' ') : nil
  141. end
  142. def sql_for_name_field(field, operator, value)
  143. case operator
  144. when '*'
  145. '1=1'
  146. when '!*'
  147. '1=0'
  148. else
  149. # match = (operator == '~')
  150. match = !operator.start_with?('!')
  151. matching_operator = operator.sub /^!/, ''
  152. name_sql = %w(login firstname lastname).map{|field| sql_for_field(:name, operator, value, User.table_name, field)}
  153. emails = EmailAddress.table_name
  154. email_sql = <<-SQL
  155. #{match ? "EXISTS" : "NOT EXISTS"}
  156. (SELECT 1 FROM #{emails} WHERE
  157. #{emails}.user_id = #{User.table_name}.id AND
  158. #{sql_for_field(:name, matching_operator, value, emails, 'address')})
  159. SQL
  160. conditions = name_sql + [email_sql]
  161. op = match ? " OR " : " AND "
  162. "(#{conditions.map{|s| "(#{s})"}.join(op)})"
  163. end
  164. end
  165. end