@@ -8,7 +8,7 @@ use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
8
8
use sqlx:: { query, query_as, Acquire , ConnectOptions , SqlitePool } ;
9
9
use url:: Url ;
10
10
11
- use super :: database:: TABLES_TO_TRUNCATE ;
11
+ use super :: database:: { UsersFilters , UsersSorting , TABLES_TO_TRUNCATE } ;
12
12
use crate :: databases:: database;
13
13
use crate :: databases:: database:: { Category , Database , Driver , Sorting , TorrentCompact } ;
14
14
use crate :: models:: category:: CategoryId ;
@@ -159,6 +159,8 @@ impl Database for Sqlite {
159
159
async fn get_user_profiles_search_paginated (
160
160
& self ,
161
161
search : & Option < String > ,
162
+ filters : & Option < Vec < String > > ,
163
+ sort : & UsersSorting ,
162
164
offset : u64 ,
163
165
limit : u8 ,
164
166
) -> Result < UserProfilesResponse , database:: Error > {
@@ -167,7 +169,78 @@ impl Database for Sqlite {
167
169
Some ( v) => format ! ( "%{v}%" ) ,
168
170
} ;
169
171
170
- let mut query_string = "SELECT * FROM torrust_user_profiles WHERE username LIKE ?" . to_string ( ) ;
172
+ let sort_query: String = match sort {
173
+ UsersSorting :: DateRegisteredNewest => "date_registered ASC" . to_string ( ) ,
174
+ UsersSorting :: DateRegisteredOldest => "date_registered DESC" . to_string ( ) ,
175
+ UsersSorting :: UsernameAZ => "username ASC" . to_string ( ) ,
176
+ UsersSorting :: UsernameZA => "username DESC" . to_string ( ) ,
177
+ } ;
178
+
179
+ let join_filters_query = if let Some ( filters) = filters {
180
+ let mut join_filters = String :: new ( ) ;
181
+ for filter in filters {
182
+ // don't take user input in the db query
183
+ if let Some ( sanitized_filter) = self . get_filters_from_name ( filter) . await {
184
+ match sanitized_filter {
185
+ UsersFilters :: TorrentUploader => join_filters. push_str (
186
+ "INNER JOIN torrust_torrents tt
187
+ ON tu.user_id = tt_uploader_id" ,
188
+ ) ,
189
+ _ => break ,
190
+ }
191
+ }
192
+ }
193
+ join_filters
194
+ } else {
195
+ String :: new ( )
196
+ } ;
197
+
198
+ let where_filters_query = if let Some ( filters) = filters {
199
+ let mut i = 0 ;
200
+ let mut where_filters = String :: new ( ) ;
201
+ for filter in filters {
202
+ // don't take user input in the db query
203
+ if let Some ( sanitized_filter) = self . get_filters_from_name ( filter) . await {
204
+ let mut filter_query = String :: new ( ) ;
205
+ match sanitized_filter {
206
+ UsersFilters :: EmailNotVerified => filter_query. push_str ( "email_verified = false" ) ,
207
+ UsersFilters :: EmailVerified => filter_query. push_str ( "email_verified = true" ) ,
208
+ _ => break ,
209
+ } ;
210
+
211
+ let mut str = format ! ( "'{}'" , filter_query) ;
212
+ if i > 0 {
213
+ str = format ! ( " AND {str}" ) ;
214
+ }
215
+ where_filters. push_str ( & str) ;
216
+ i += 1 ;
217
+ }
218
+ }
219
+ if where_filters. is_empty ( ) {
220
+ String :: new ( )
221
+ } else {
222
+ String :: new ( )
223
+ }
224
+ } else {
225
+ String :: new ( )
226
+ } ;
227
+
228
+ let mut query_string = format ! (
229
+ "SELECT
230
+ tp.user_id,
231
+ tp.username,
232
+ tp.email,
233
+ tp.email_verified,
234
+ tu.date_registered,
235
+ tu.administrator
236
+ FROM torrust_user_profiles tp
237
+ INNER JOIN torrust_users tu
238
+ ON tp.user_id = tu.user_id
239
+ {join_filters_query}
240
+ WHERE username LIKE ?
241
+ {where_filters_query}
242
+ "
243
+ ) ;
171
244
172
245
let count_query = format ! ( "SELECT COUNT(*) as count FROM ({query_string}) AS count_table" ) ;
173
246
@@ -180,7 +253,7 @@ impl Database for Sqlite {
180
253
181
254
let count = count_result?;
182
255
183
- query_string = format ! ( "{query_string} LIMIT ?, ?" ) ;
256
+ query_string = format ! ( "{query_string} ORDER BY {sort_query} LIMIT ?, ?" ) ;
184
257
185
258
let res: Vec < UserProfile > = sqlx:: query_as :: < _ , UserProfile > ( & query_string)
186
259
. bind ( user_name. clone ( ) )
@@ -204,6 +277,15 @@ impl Database for Sqlite {
204
277
. map_err ( |_| database:: Error :: UserNotFound )
205
278
}
206
279
280
+ async fn get_filters_from_name ( & self , filter_name : & str ) -> Option < UsersFilters > {
281
+ match filter_name {
282
+ "torrentUploader" => Some ( UsersFilters :: TorrentUploader ) ,
283
+ "emailNotVerified" => Some ( UsersFilters :: EmailNotVerified ) ,
284
+ "emailVerified" => Some ( UsersFilters :: EmailVerified ) ,
285
+ _ => None ,
286
+ }
287
+ }
288
+
207
289
async fn get_user_tracker_key ( & self , user_id : i64 ) -> Option < TrackerKey > {
208
290
const HOUR_IN_SECONDS : i64 = 3600 ;
209
291
0 commit comments