Patch #39181 » 0001-API-compatibility-to-legacy-status-and-name-query-pa.patch
app/controllers/users_controller.rb | ||
---|---|---|
45 | 45 |
use_session = !request.format.csv? |
46 | 46 |
retrieve_query(UserQuery, use_session) |
47 | 47 | |
48 |
# API backwards compatibility: handle legacy status and name filter parameters |
|
49 |
unless request.format.html? |
|
50 |
if status_id = params[:status].presence |
|
51 |
@query.add_filter 'status', '=', [status_id] |
|
52 |
end |
|
53 |
if name = params[:name].presence |
|
54 |
@query.add_filter 'name', '~', [name] |
|
55 |
end |
|
56 |
end |
|
57 | ||
48 | 58 |
if @query.valid? |
49 | 59 |
scope = @query.results_scope |
50 | 60 |
app/models/user_query.rb | ||
---|---|---|
51 | 51 |
type: :list_optional, |
52 | 52 |
values: ->{ Redmine::Twofa.available_schemes.map {|s| [I18n.t("twofa__#{s}__name"), s] } } |
53 | 53 |
end |
54 |
add_available_filter "name", type: :text, label: :field_name_or_email_or_login |
|
54 | 55 |
add_available_filter "login", type: :string |
55 | 56 |
add_available_filter "firstname", type: :string |
56 | 57 |
add_available_filter "lastname", type: :string |
... | ... | |
165 | 166 | |
166 | 167 |
joins.any? ? joins.join(' ') : nil |
167 | 168 |
end |
169 | ||
170 |
def sql_for_name_field(field, operator, value) |
|
171 |
case operator |
|
172 |
when '*' |
|
173 |
'1=1' |
|
174 |
when '!*' |
|
175 |
'1=0' |
|
176 |
else |
|
177 |
# match = (operator == '~') |
|
178 |
match = !operator.start_with?('!') |
|
179 |
matching_operator = operator.sub /^\!/, '' |
|
180 |
name_sql = %w(login firstname lastname).map{|field| sql_for_field(:name, operator, value, User.table_name, field)} |
|
181 | ||
182 |
emails = EmailAddress.table_name |
|
183 |
email_sql = <<-SQL |
|
184 |
#{match ? "EXISTS" : "NOT EXISTS"} |
|
185 |
(SELECT 1 FROM #{emails} WHERE |
|
186 |
#{emails}.user_id = #{User.table_name}.id AND |
|
187 |
#{sql_for_field(:name, matching_operator, value, emails, 'address')}) |
|
188 |
SQL |
|
189 | ||
190 |
conditions = name_sql + [email_sql] |
|
191 |
op = match ? " OR " : " AND " |
|
192 |
"(#{conditions.map{|s| "(#{s})"}.join(op)})" |
|
193 |
end |
|
194 | ||
195 |
end |
|
168 | 196 |
end |
config/locales/en.yml | ||
---|---|---|
1410 | 1410 |
twofa_text_group_disabled: "This setting is only effective when the global two factor authentication setting is set to 'optional'. Currently, two factor authentication is disabled." |
1411 | 1411 |
text_user_destroy_confirmation: "Are you sure you want to delete this user and remove all references to them? This cannot be undone. Often, locking a user instead of deleting them is the better solution. To confirm, please enter their login (%{login}) below." |
1412 | 1412 |
text_project_destroy_enter_identifier: "To confirm, please enter the project's identifier (%{identifier}) below." |
1413 |
field_name_or_email_or_login: Name, email or login |
test/unit/user_query_test.rb | ||
---|---|---|
108 | 108 |
end |
109 | 109 |
end |
110 | 110 | |
111 |
def test_name_or_email_or_login_filter |
|
112 |
[ |
|
113 |
['~', 'jsmith', [2]], |
|
114 |
['^', 'jsm', [2]], |
|
115 |
['$', 'ith', [2]], |
|
116 |
['~', 'john', [2]], |
|
117 |
['~', 'smith', [2]], |
|
118 |
['~', 'somenet', [1, 2, 3, 4]], |
|
119 |
['!~', 'somenet', [7, 8, 9]], |
|
120 |
['^', 'dlop', [3]], |
|
121 |
['$', 'bar', [7, 8, 9]], |
|
122 |
['=', 'bar', []], |
|
123 |
['=', 'someone@foo.bar', [7]], |
|
124 |
['*', '', [1, 2, 3, 4, 7, 8, 9]], |
|
125 |
['!*', '', []], |
|
126 |
].each do |op, string, result| |
|
127 |
q = UserQuery.new name: '_' |
|
128 |
q.add_filter('name', op, [string]) |
|
129 |
users = find_users_with_query q |
|
130 |
assert_equal result, users.map(&:id).sort, "#{op} #{string} should have found #{result}" |
|
131 |
end |
|
132 |
end |
|
133 | ||
111 | 134 |
def test_group_filter |
112 | 135 |
q = UserQuery.new name: '_' |
113 | 136 |
q.add_filter('is_member_of_group', '=', ['10', '99']) |