Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Pleroma
pleroma
Commits
6c6a2dbf
Commit
6c6a2dbf
authored
Mar 03, 2019
by
kaniini
Browse files
Merge branch 'feature/add-pagination-to-users-admin-api' into 'develop'
Add pagination and search to users See merge request
pleroma/pleroma!873
parents
b531e366
08c6aeee
Pipeline
#8490
failed with stages
in 3 minutes and 21 seconds
Changes
10
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
docs/Admin-API.md
View file @
6c6a2dbf
...
...
@@ -7,17 +7,51 @@ Authentication is required and the user must be an admin.
### List users
-
Method
`GET`
-
Params:
-
`page`
:
**integer**
page number
-
`page_size`
:
**integer**
number of users per page (default is
`50`
)
-
Response:
```
JSON
[
{
"page_size": integer,
"count": integer,
"users": [
{
"deactivated": bool,
"id": integer,
"nickname": string
"deactivated": bool,
"id": integer,
"nickname": string
},
...
]
]
}
```
## `/api/pleroma/admin/users/search?query={query}&local={local}&page={page}&page_size={page_size}`
### Search users by name or nickname
-
Method
`GET`
-
Params:
-
`query`
:
**string**
search term
-
`local`
:
**bool**
whether to return only local users
-
`page`
:
**integer**
page number
-
`page_size`
:
**integer**
number of users per page (default is
`50`
)
-
Response:
```
JSON
{
"page_size": integer,
"count": integer,
"users": [
{
"deactivated": bool,
"id": integer,
"nickname": string
},
...
]
}
```
## `/api/pleroma/admin/user`
...
...
@@ -49,9 +83,9 @@ Authentication is required and the user must be an admin.
```
JSON
{
"deactivated": bool,
"id": integer,
"nickname": string
"deactivated": bool,
"id": integer,
"nickname": string
}
```
...
...
@@ -81,8 +115,8 @@ Authentication is required and the user must be an admin.
```
JSON
{
"is_moderator": bool,
"is_admin": bool
"is_moderator": bool,
"is_admin": bool
}
```
...
...
@@ -98,8 +132,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
```
JSON
{
"is_moderator": bool,
"is_admin": bool
"is_moderator": bool,
"is_admin": bool
}
```
...
...
lib/pleroma/user.ex
View file @
6c6a2dbf
...
...
@@ -547,11 +547,8 @@ def get_followers_query(%User{id: id, follower_address: follower_address}, nil)
end
def
get_followers_query
(
user
,
page
)
do
from
(
u
in
get_followers_query
(
user
,
nil
),
limit:
20
,
offset:
^
((
page
-
1
)
*
20
)
)
from
(
u
in
get_followers_query
(
user
,
nil
))
|>
paginate
(
page
,
20
)
end
def
get_followers_query
(
user
),
do
:
get_followers_query
(
user
,
nil
)
...
...
@@ -577,11 +574,8 @@ def get_friends_query(%User{id: id, following: following}, nil) do
end
def
get_friends_query
(
user
,
page
)
do
from
(
u
in
get_friends_query
(
user
,
nil
),
limit:
20
,
offset:
^
((
page
-
1
)
*
20
)
)
from
(
u
in
get_friends_query
(
user
,
nil
))
|>
paginate
(
page
,
20
)
end
def
get_friends_query
(
user
),
do
:
get_friends_query
(
user
,
nil
)
...
...
@@ -754,6 +748,46 @@ def get_recipients_from_activity(%Activity{recipients: to}) do
Repo
.
all
(
query
)
end
@spec
search_for_admin
(
binary
(),
%{
admin:
Pleroma
.
User
.
t
(),
local:
boolean
(),
page:
number
(),
page_size:
number
()
})
::
{
:ok
,
[
Pleroma
.
User
.
t
()],
number
()}
def
search_for_admin
(
term
,
%{
admin:
admin
,
local:
local
,
page:
page
,
page_size:
page_size
})
do
term
=
String
.
trim_leading
(
term
,
"@"
)
local_paginated_query
=
User
|>
maybe_local_user_query
(
local
)
|>
paginate
(
page
,
page_size
)
search_query
=
fts_search_subquery
(
term
,
local_paginated_query
)
count
=
term
|>
fts_search_subquery
()
|>
maybe_local_user_query
(
local
)
|>
Repo
.
aggregate
(
:count
,
:id
)
{
:ok
,
do_search
(
search_query
,
admin
),
count
}
end
@spec
all_for_admin
(
number
(),
number
())
::
{
:ok
,
[
Pleroma
.
User
.
t
()],
number
()}
def
all_for_admin
(
page
,
page_size
)
do
query
=
from
(
u
in
User
,
order_by:
u
.
id
)
paginated_query
=
query
|>
paginate
(
page
,
page_size
)
count
=
query
|>
Repo
.
aggregate
(
:count
,
:id
)
{
:ok
,
Repo
.
all
(
paginated_query
),
count
}
end
def
search
(
query
,
resolve
\\
false
,
for_user
\\
nil
)
do
# Strip the beginning @ off if there is a query
query
=
String
.
trim_leading
(
query
,
"@"
)
...
...
@@ -771,12 +805,6 @@ def search(query, resolve \\ false, for_user \\ nil) do
Enum
.
uniq_by
(
fts_results
++
trigram_results
,
&
&1
.
id
)
end
def
all_except_one
(
user
)
do
query
=
from
(
u
in
User
,
where:
u
.
id
!=
^
user
.
id
)
Repo
.
all
(
query
)
end
defp
do_search
(
subquery
,
for_user
,
options
\\
[])
do
q
=
from
(
...
...
@@ -793,9 +821,9 @@ defp do_search(subquery, for_user, options \\ []) do
boost_search_results
(
results
,
for_user
)
end
defp
fts_search_subquery
(
query
)
do
defp
fts_search_subquery
(
term
,
query
\\
User
)
do
processed_query
=
qu
er
y
t
er
m
|>
String
.
replace
(
~r/\W+/
,
" "
)
|>
String
.
trim
()
|>
String
.
split
()
...
...
@@ -803,7 +831,7 @@ defp fts_search_subquery(query) do
|>
Enum
.
join
(
" | "
)
from
(
u
in
Us
er
,
u
in
qu
er
y
,
select_merge:
%{
search_rank:
fragment
(
...
...
@@ -833,19 +861,19 @@ defp fts_search_subquery(query) do
)
end
defp
trigram_search_subquery
(
qu
er
y
)
do
defp
trigram_search_subquery
(
t
er
m
)
do
from
(
u
in
User
,
select_merge:
%{
search_rank:
fragment
(
"similarity(?, trim(? || ' ' || coalesce(?, '')))"
,
^
qu
er
y
,
^
t
er
m
,
u
.
nickname
,
u
.
name
)
},
where:
fragment
(
"trim(? || ' ' || coalesce(?, '')) % ?"
,
u
.
nickname
,
u
.
name
,
^
qu
er
y
)
where:
fragment
(
"trim(? || ' ' || coalesce(?, '')) % ?"
,
u
.
nickname
,
u
.
name
,
^
t
er
m
)
)
end
...
...
@@ -1003,9 +1031,13 @@ def unblock_domain(user, domain) do
update_and_set_cache
(
cng
)
end
def
local_user_query
do
def
maybe_local_user_query
(
query
,
local
)
do
if
local
,
do
:
local_user_query
(
query
),
else
:
query
end
def
local_user_query
(
query
\\
User
)
do
from
(
u
in
Us
er
,
u
in
qu
er
y
,
where:
u
.
local
==
true
,
where:
not
is_nil
(
u
.
nickname
)
)
...
...
@@ -1297,4 +1329,11 @@ def all_superusers do
)
|>
Repo
.
all
()
end
defp
paginate
(
query
,
page
,
page_size
)
do
from
(
u
in
query
,
limit:
^
page_size
,
offset:
^
((
page
-
1
)
*
page_size
)
)
end
end
lib/pleroma/web/admin_api/admin_api_controller.ex
View file @
6c6a2dbf
...
...
@@ -3,10 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule
Pleroma
.
Web
.
AdminAPI
.
AdminAPIController
do
@users_page_size
50
use
Pleroma
.
Web
,
:controller
alias
Pleroma
.
User
alias
Pleroma
.
Web
.
ActivityPub
.
Relay
alias
Pleroma
.
Web
.
TwitterAPI
.
User
View
alias
Pleroma
.
Web
.
MastodonAPI
.
Admin
.
Account
View
import
Pleroma
.
Web
.
ControllerHelper
,
only:
[
json_response:
3
]
...
...
@@ -48,7 +50,7 @@ def user_toggle_activation(conn, %{"nickname" => nickname}) do
{
:ok
,
updated_user
}
=
User
.
deactivate
(
user
,
!user
.
info
.
deactivated
)
conn
|>
json
(
User
View
.
render
(
"show
_for_admin
.json"
,
%{
user:
updated_user
}))
|>
json
(
Account
View
.
render
(
"show.json"
,
%{
user:
updated_user
}))
end
def
tag_users
(
conn
,
%{
"nicknames"
=>
nicknames
,
"tags"
=>
tags
})
do
...
...
@@ -61,11 +63,40 @@ def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
do
:
json_response
(
conn
,
:no_content
,
""
)
end
def
list_users
(%{
assigns:
%{
user:
admin
}}
=
conn
,
_data
)
do
users
=
User
.
all_except_one
(
admin
)
def
list_users
(
conn
,
params
)
do
{
page
,
page_size
}
=
page_params
(
params
)
with
{
:ok
,
users
,
count
}
<-
User
.
all_for_admin
(
page
,
page_size
),
do
:
conn
|>
json
(
AccountView
.
render
(
"index.json"
,
users:
users
,
count:
count
,
page_size:
page_size
)
)
end
conn
|>
json
(
UserView
.
render
(
"index_for_admin.json"
,
%{
users:
users
}))
def
search_users
(%{
assigns:
%{
user:
admin
}}
=
conn
,
%{
"query"
=>
query
}
=
params
)
do
{
page
,
page_size
}
=
page_params
(
params
)
with
{
:ok
,
users
,
count
}
<-
User
.
search_for_admin
(
query
,
%{
admin:
admin
,
local:
params
[
"local"
]
==
"true"
,
page:
page
,
page_size:
page_size
}),
do
:
conn
|>
json
(
AccountView
.
render
(
"index.json"
,
users:
users
,
count:
count
,
page_size:
page_size
)
)
end
def
right_add
(
conn
,
%{
"permission_group"
=>
permission_group
,
"nickname"
=>
nickname
})
...
...
@@ -211,4 +242,26 @@ def errors(conn, _) do
|>
put_status
(
500
)
|>
json
(
"Something went wrong"
)
end
defp
page_params
(
params
)
do
{
get_page
(
params
[
"page"
]),
get_page_size
(
params
[
"page_size"
])}
end
defp
get_page
(
page_string
)
when
is_nil
(
page_string
),
do
:
1
defp
get_page
(
page_string
)
do
case
Integer
.
parse
(
page_string
)
do
{
page
,
_
}
->
page
:error
->
1
end
end
defp
get_page_size
(
page_size_string
)
when
is_nil
(
page_size_string
),
do
:
@users_page_size
defp
get_page_size
(
page_size_string
)
do
case
Integer
.
parse
(
page_size_string
)
do
{
page_size
,
_
}
->
page_size
:error
->
@users_page_size
end
end
end
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
View file @
6c6a2dbf
...
...
@@ -894,7 +894,7 @@ def status_search(user, query) do
end
def
search2
(%{
assigns:
%{
user:
user
}}
=
conn
,
%{
"q"
=>
query
}
=
params
)
do
accounts
=
User
.
search
(
query
,
params
[
"resolve"
]
==
"true"
,
user
)
accounts
=
User
.
search
(
query
,
resolve:
params
[
"resolve"
]
==
"true"
,
for_user:
user
)
statuses
=
status_search
(
user
,
query
)
...
...
@@ -919,7 +919,7 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
end
def
search
(%{
assigns:
%{
user:
user
}}
=
conn
,
%{
"q"
=>
query
}
=
params
)
do
accounts
=
User
.
search
(
query
,
params
[
"resolve"
]
==
"true"
,
user
)
accounts
=
User
.
search
(
query
,
resolve:
params
[
"resolve"
]
==
"true"
,
for_user:
user
)
statuses
=
status_search
(
user
,
query
)
...
...
@@ -941,7 +941,7 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
end
def
account_search
(%{
assigns:
%{
user:
user
}}
=
conn
,
%{
"q"
=>
query
}
=
params
)
do
accounts
=
User
.
search
(
query
,
params
[
"resolve"
]
==
"true"
,
user
)
accounts
=
User
.
search
(
query
,
resolve:
params
[
"resolve"
]
==
"true"
,
for_user:
user
)
res
=
AccountView
.
render
(
"accounts.json"
,
users:
accounts
,
for:
user
,
as:
:user
)
...
...
lib/pleroma/web/mastodon_api/views/admin/account_view.ex
0 → 100644
View file @
6c6a2dbf
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule
Pleroma
.
Web
.
MastodonAPI
.
Admin
.
AccountView
do
use
Pleroma
.
Web
,
:view
alias
Pleroma
.
Web
.
MastodonAPI
.
Admin
.
AccountView
def
render
(
"index.json"
,
%{
users:
users
,
count:
count
,
page_size:
page_size
})
do
%{
users:
render_many
(
users
,
AccountView
,
"show.json"
,
as:
:user
),
count:
count
,
page_size:
page_size
}
end
def
render
(
"show.json"
,
%{
user:
user
})
do
%{
"id"
=>
user
.
id
,
"nickname"
=>
user
.
nickname
,
"deactivated"
=>
user
.
info
.
deactivated
}
end
end
lib/pleroma/web/router.ex
View file @
6c6a2dbf
...
...
@@ -140,6 +140,7 @@ defmodule Pleroma.Web.Router do
pipe_through
([
:admin_api
,
:oauth_write
])
get
(
"/users"
,
AdminAPIController
,
:list_users
)
get
(
"/users/search"
,
AdminAPIController
,
:search_users
)
delete
(
"/user"
,
AdminAPIController
,
:user_delete
)
patch
(
"/users/:nickname/toggle_activation"
,
AdminAPIController
,
:user_toggle_activation
)
post
(
"/user"
,
AdminAPIController
,
:user_create
)
...
...
lib/pleroma/web/twitter_api/twitter_api_controller.ex
View file @
6c6a2dbf
...
...
@@ -703,7 +703,7 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do
end
def
search_user
(%{
assigns:
%{
user:
user
}}
=
conn
,
%{
"query"
=>
query
})
do
users
=
User
.
search
(
query
,
true
,
user
)
users
=
User
.
search
(
query
,
resolve:
true
,
for_user:
user
)
conn
|>
put_view
(
UserView
)
...
...
lib/pleroma/web/twitter_api/views/user_view.ex
View file @
6c6a2dbf
...
...
@@ -9,7 +9,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
alias
Pleroma
.
User
alias
Pleroma
.
Web
.
CommonAPI
.
Utils
alias
Pleroma
.
Web
.
MediaProxy
alias
Pleroma
.
Web
.
TwitterAPI
.
UserView
def
render
(
"show.json"
,
%{
user:
user
=
%
User
{}}
=
assigns
)
do
render_one
(
user
,
Pleroma
.
Web
.
TwitterAPI
.
UserView
,
"user.json"
,
assigns
)
...
...
@@ -27,19 +26,6 @@ def render("user.json", %{user: user = %User{}} = assigns) do
else
:
%{}
end
def
render
(
"index_for_admin.json"
,
%{
users:
users
}
=
opts
)
do
users
|>
render_many
(
UserView
,
"show_for_admin.json"
,
opts
)
end
def
render
(
"show_for_admin.json"
,
%{
user:
user
})
do
%{
"id"
=>
user
.
id
,
"nickname"
=>
user
.
nickname
,
"deactivated"
=>
user
.
info
.
deactivated
}
end
def
render
(
"short.json"
,
%{
user:
%
User
{
nickname:
nickname
,
...
...
test/user_test.exs
View file @
6c6a2dbf
...
...
@@ -929,7 +929,8 @@ test "finds users, boosting ranks of friends and followers" do
{
:ok
,
follower
}
=
User
.
follow
(
follower
,
u1
)
{
:ok
,
u1
}
=
User
.
follow
(
u1
,
friend
)
assert
[
friend
.
id
,
follower
.
id
,
u2
.
id
]
==
Enum
.
map
(
User
.
search
(
"doe"
,
false
,
u1
),
&
&1
.
id
)
assert
[
friend
.
id
,
follower
.
id
,
u2
.
id
]
--
Enum
.
map
(
User
.
search
(
"doe"
,
resolve:
false
,
for_user:
u1
),
&
&1
.
id
)
==
[]
end
test
"finds a user whose name is nil"
do
...
...
@@ -951,7 +952,7 @@ test "does not yield false-positive matches" do
end
test
"works with URIs"
do
results
=
User
.
search
(
"http://mastodon.example.org/users/admin"
,
true
)
results
=
User
.
search
(
"http://mastodon.example.org/users/admin"
,
resolve:
true
)
result
=
results
|>
List
.
first
()
user
=
User
.
get_by_ap_id
(
"http://mastodon.example.org/users/admin"
)
...
...
test/web/admin_api/admin_api_controller_test.exs
View file @
6c6a2dbf
...
...
@@ -331,22 +331,49 @@ test "/api/pleroma/admin/password_reset" do
assert
conn
.
status
==
200
end
test
"GET /api/pleroma/admin/users"
do
admin
=
insert
(
:user
,
info:
%{
is_admin:
true
})
user
=
insert
(
:user
)
describe
"GET /api/pleroma/admin/users"
do
test
"renders users array for the first page"
do
admin
=
insert
(
:user
,
info:
%{
is_admin:
true
})
user
=
insert
(
:user
)
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users"
)
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users
?page=1
"
)
assert
json_response
(
conn
,
200
)
==
[
%{
"deactivated"
=>
user
.
info
.
deactivated
,
"id"
=>
user
.
id
,
"nickname"
=>
user
.
nickname
assert
json_response
(
conn
,
200
)
==
%{
"count"
=>
2
,
"page_size"
=>
50
,
"users"
=>
[
%{
"deactivated"
=>
admin
.
info
.
deactivated
,
"id"
=>
admin
.
id
,
"nickname"
=>
admin
.
nickname
},
%{
"deactivated"
=>
user
.
info
.
deactivated
,
"id"
=>
user
.
id
,
"nickname"
=>
user
.
nickname
}
]
}
end
test
"renders empty array for the second page"
do
admin
=
insert
(
:user
,
info:
%{
is_admin:
true
})
insert
(
:user
)
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users?page=2"
)
assert
json_response
(
conn
,
200
)
==
%{
"count"
=>
2
,
"page_size"
=>
50
,
"users"
=>
[]
}
]
end
end
test
"PATCH /api/pleroma/admin/users/:nickname/toggle_activation"
do
...
...
@@ -365,4 +392,92 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
"nickname"
=>
user
.
nickname
}
end
describe
"GET /api/pleroma/admin/users/search"
do
test
"regular search"
do
admin
=
insert
(
:user
,
info:
%{
is_admin:
true
})
user
=
insert
(
:user
,
nickname:
"bob"
)
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users/search?query=bo"
)
assert
json_response
(
conn
,
200
)
==
%{
"count"
=>
1
,
"page_size"
=>
50
,
"users"
=>
[
%{
"deactivated"
=>
user
.
info
.
deactivated
,
"id"
=>
user
.
id
,
"nickname"
=>
user
.
nickname
}
]
}
end
test
"regular search with page size"
do
admin
=
insert
(
:user
,
info:
%{
is_admin:
true
})
user
=
insert
(
:user
,
nickname:
"bob"
)
user2
=
insert
(
:user
,
nickname:
"bo"
)
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users/search?query=bo&page_size=1&page=1"
)
assert
json_response
(
conn
,
200
)
==
%{
"count"
=>
2
,
"page_size"
=>
1
,
"users"
=>
[
%{
"deactivated"
=>
user
.
info
.
deactivated
,
"id"
=>
user
.
id
,
"nickname"
=>
user
.
nickname
}
]
}
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users/search?query=bo&page_size=1&page=2"
)
assert
json_response
(
conn
,
200
)
==
%{
"count"
=>
2
,
"page_size"
=>
1
,
"users"
=>
[
%{
"deactivated"
=>
user2
.
info
.
deactivated
,
"id"
=>
user2
.
id
,
"nickname"
=>
user2
.
nickname
}
]
}
end
test
"only local users"
do
admin
=
insert
(
:user
,
info:
%{
is_admin:
true
},
nickname:
"john"
)
user
=
insert
(
:user
,
nickname:
"bob"
)
insert
(
:user
,
nickname:
"bobb"
,
local:
false
)
conn
=
build_conn
()
|>
assign
(
:user
,
admin
)
|>
get
(
"/api/pleroma/admin/users/search?query=bo&local=true"
)
assert
json_response
(
conn
,
200
)
==
%{
"count"
=>
1
,
"page_size"
=>
50
,
"users"
=>
[
%{
"deactivated"
=>
user
.
info
.
deactivated
,