forked from AkkomaGang/akkoma
Merge branch 'feature/rich-text-optout-backend' into 'develop'
backend support for opting out of rich text on a per-account basis See merge request pleroma/pleroma!354
This commit is contained in:
commit
ec6a7799d8
10 changed files with 68 additions and 26 deletions
|
@ -12,17 +12,19 @@ def get_scrubbers() do
|
||||||
|> get_scrubbers
|
|> get_scrubbers
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_tags(html, scrubber) do
|
def filter_tags(html, nil) do
|
||||||
html |> Scrubber.scrub(scrubber)
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_tags(html) do
|
|
||||||
get_scrubbers()
|
get_scrubbers()
|
||||||
|> Enum.reduce(html, fn scrubber, html ->
|
|> Enum.reduce(html, fn scrubber, html ->
|
||||||
filter_tags(html, scrubber)
|
filter_tags(html, scrubber)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_tags(html, scrubber) do
|
||||||
|
html |> Scrubber.scrub(scrubber)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_tags(html), do: filter_tags(html, nil)
|
||||||
|
|
||||||
def strip_tags(html) do
|
def strip_tags(html) do
|
||||||
html |> Scrubber.scrub(Scrubber.StripTags)
|
html |> Scrubber.scrub(Scrubber.StripTags)
|
||||||
end
|
end
|
||||||
|
|
|
@ -669,6 +669,12 @@ def delete(%User{} = user) do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def html_filter_policy(%User{info: %{"no_rich_text" => true}}) do
|
||||||
|
Pleroma.HTML.Scrubber.TwitterText
|
||||||
|
end
|
||||||
|
|
||||||
|
def html_filter_policy(_), do: nil
|
||||||
|
|
||||||
def get_or_fetch_by_ap_id(ap_id) do
|
def get_or_fetch_by_ap_id(ap_id) do
|
||||||
user = get_by_ap_id(ap_id)
|
user = get_by_ap_id(ap_id)
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
CommonAPI.update(user)
|
CommonAPI.update(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
json(conn, AccountView.render("account.json", %{user: user}))
|
json(conn, AccountView.render("account.json", %{user: user, for: user}))
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
conn
|
conn
|
||||||
|
@ -108,13 +108,13 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
account = AccountView.render("account.json", %{user: user})
|
account = AccountView.render("account.json", %{user: user, for: user})
|
||||||
json(conn, account)
|
json(conn, account)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(conn, %{"id" => id}) do
|
def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||||
with %User{} = user <- Repo.get(User, id) do
|
with %User{} = user <- Repo.get(User, id) do
|
||||||
account = AccountView.render("account.json", %{user: user})
|
account = AccountView.render("account.json", %{user: user, for: for_user})
|
||||||
json(conn, account)
|
json(conn, account)
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
@ -588,7 +588,7 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||||
with %User{} = followed <- Repo.get_by(User, nickname: uri),
|
with %User{} = followed <- Repo.get_by(User, nickname: uri),
|
||||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
|
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
|
||||||
render(conn, AccountView, "account.json", %{user: followed})
|
render(conn, AccountView, "account.json", %{user: followed, for: follower})
|
||||||
else
|
else
|
||||||
{:error, message} ->
|
{:error, message} ->
|
||||||
conn
|
conn
|
||||||
|
@ -858,7 +858,9 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
|
||||||
if user && token do
|
if user && token do
|
||||||
mastodon_emoji = mastodonized_emoji()
|
mastodon_emoji = mastodonized_emoji()
|
||||||
accounts = Map.put(%{}, user.id, AccountView.render("account.json", %{user: user}))
|
|
||||||
|
accounts =
|
||||||
|
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user}))
|
||||||
|
|
||||||
initial_state =
|
initial_state =
|
||||||
%{
|
%{
|
||||||
|
@ -1038,7 +1040,7 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
|
||||||
id: id,
|
id: id,
|
||||||
type: "mention",
|
type: "mention",
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
account: AccountView.render("account.json", %{user: actor}),
|
account: AccountView.render("account.json", %{user: actor, for: user}),
|
||||||
status: StatusView.render("status.json", %{activity: activity, for: user})
|
status: StatusView.render("status.json", %{activity: activity, for: user})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1049,7 +1051,7 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
|
||||||
id: id,
|
id: id,
|
||||||
type: "favourite",
|
type: "favourite",
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
account: AccountView.render("account.json", %{user: actor}),
|
account: AccountView.render("account.json", %{user: actor, for: user}),
|
||||||
status: StatusView.render("status.json", %{activity: liked_activity, for: user})
|
status: StatusView.render("status.json", %{activity: liked_activity, for: user})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1060,7 +1062,7 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
|
||||||
id: id,
|
id: id,
|
||||||
type: "reblog",
|
type: "reblog",
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
account: AccountView.render("account.json", %{user: actor}),
|
account: AccountView.render("account.json", %{user: actor, for: user}),
|
||||||
status: StatusView.render("status.json", %{activity: announced_activity, for: user})
|
status: StatusView.render("status.json", %{activity: announced_activity, for: user})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1069,7 +1071,7 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
|
||||||
id: id,
|
id: id,
|
||||||
type: "follow",
|
type: "follow",
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
account: AccountView.render("account.json", %{user: actor})
|
account: AccountView.render("account.json", %{user: actor, for: user})
|
||||||
}
|
}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -10,7 +10,7 @@ def render("accounts.json", %{users: users} = opts) do
|
||||||
render_many(users, AccountView, "account.json", opts)
|
render_many(users, AccountView, "account.json", opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("account.json", %{user: user}) do
|
def render("account.json", %{user: user} = opts) do
|
||||||
image = User.avatar_url(user) |> MediaProxy.url()
|
image = User.avatar_url(user) |> MediaProxy.url()
|
||||||
header = User.banner_url(user) |> MediaProxy.url()
|
header = User.banner_url(user) |> MediaProxy.url()
|
||||||
user_info = User.user_info(user)
|
user_info = User.user_info(user)
|
||||||
|
@ -33,6 +33,8 @@ def render("account.json", %{user: user}) do
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||||
|
|
||||||
|
bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(user.id),
|
id: to_string(user.id),
|
||||||
username: username_from_nickname(user.nickname),
|
username: username_from_nickname(user.nickname),
|
||||||
|
@ -43,7 +45,7 @@ def render("account.json", %{user: user}) do
|
||||||
followers_count: user_info.follower_count,
|
followers_count: user_info.follower_count,
|
||||||
following_count: user_info.following_count,
|
following_count: user_info.following_count,
|
||||||
statuses_count: user_info.note_count,
|
statuses_count: user_info.note_count,
|
||||||
note: HTML.filter_tags(user.bio) || "",
|
note: bio || "",
|
||||||
url: user.ap_id,
|
url: user.ap_id,
|
||||||
avatar: image,
|
avatar: image,
|
||||||
avatar_static: image,
|
avatar_static: image,
|
||||||
|
|
|
@ -122,6 +122,10 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
%{shortcode: name, url: url, static_url: url, visible_in_picker: false}
|
%{shortcode: name, url: url, static_url: url, visible_in_picker: false}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
content =
|
||||||
|
render_content(object)
|
||||||
|
|> HTML.filter_tags(User.html_filter_policy(opts[:for]))
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(activity.id),
|
id: to_string(activity.id),
|
||||||
uri: object["id"],
|
uri: object["id"],
|
||||||
|
@ -130,7 +134,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
|
||||||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
in_reply_to_id: reply_to && to_string(reply_to.id),
|
||||||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||||
reblog: nil,
|
reblog: nil,
|
||||||
content: render_content(object),
|
content: content,
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
reblogs_count: announcement_count,
|
reblogs_count: announcement_count,
|
||||||
replies_count: 0,
|
replies_count: 0,
|
||||||
|
@ -224,7 +228,7 @@ def render_content(%{"type" => "Video"} = object) do
|
||||||
object["content"]
|
object["content"]
|
||||||
end
|
end
|
||||||
|
|
||||||
HTML.filter_tags(content)
|
content
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_content(%{"type" => "Article"} = object) do
|
def render_content(%{"type" => "Article"} = object) do
|
||||||
|
@ -237,10 +241,8 @@ def render_content(%{"type" => "Article"} = object) do
|
||||||
object["content"]
|
object["content"]
|
||||||
end
|
end
|
||||||
|
|
||||||
HTML.filter_tags(content)
|
content
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_content(object) do
|
def render_content(object), do: object["content"]
|
||||||
HTML.filter_tags(object["content"])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -168,7 +168,7 @@ def to_map(
|
||||||
{summary, content} = ActivityView.render_content(object)
|
{summary, content} = ActivityView.render_content(object)
|
||||||
|
|
||||||
html =
|
html =
|
||||||
HTML.filter_tags(content)
|
HTML.filter_tags(content, User.html_filter_policy(opts[:for]))
|
||||||
|> Formatter.emojify(object["emoji"])
|
|> Formatter.emojify(object["emoji"])
|
||||||
|
|
||||||
video =
|
video =
|
||||||
|
|
|
@ -443,6 +443,20 @@ def update_profile(%{assigns: %{user: user}} = conn, params) do
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
user =
|
||||||
|
if no_rich_text = params["no_rich_text"] do
|
||||||
|
with no_rich_text <- no_rich_text == "true",
|
||||||
|
new_info <- Map.put(user.info, "no_rich_text", no_rich_text),
|
||||||
|
change <- User.info_changeset(user, %{info: new_info}),
|
||||||
|
{:ok, user} <- User.update_and_set_cache(change) do
|
||||||
|
user
|
||||||
|
else
|
||||||
|
_e -> user
|
||||||
|
end
|
||||||
|
else
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
user =
|
user =
|
||||||
if default_scope = params["default_scope"] do
|
if default_scope = params["default_scope"] do
|
||||||
with new_info <- Map.put(user.info, "default_scope", default_scope),
|
with new_info <- Map.put(user.info, "default_scope", default_scope),
|
||||||
|
|
|
@ -233,7 +233,7 @@ def render(
|
||||||
{summary, content} = render_content(object)
|
{summary, content} = render_content(object)
|
||||||
|
|
||||||
html =
|
html =
|
||||||
HTML.filter_tags(content)
|
HTML.filter_tags(content, User.html_filter_policy(opts[:for]))
|
||||||
|> Formatter.emojify(object["emoji"])
|
|> Formatter.emojify(object["emoji"])
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -40,7 +40,7 @@ def render("user.json", %{user: user = %User{}} = assigns) do
|
||||||
data = %{
|
data = %{
|
||||||
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
"created_at" => user.inserted_at |> Utils.format_naive_asctime(),
|
||||||
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
"description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||||
"description_html" => HTML.filter_tags(user.bio),
|
"description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(assigns[:for])),
|
||||||
"favourites_count" => 0,
|
"favourites_count" => 0,
|
||||||
"followers_count" => user_info[:follower_count],
|
"followers_count" => user_info[:follower_count],
|
||||||
"following" => following,
|
"following" => following,
|
||||||
|
|
|
@ -526,4 +526,18 @@ test "insert or update a user from given data" do
|
||||||
|
|
||||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "per-user rich-text filtering" do
|
||||||
|
test "html_filter_policy returns nil when rich-text is enabled" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert nil == User.html_filter_policy(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
|
||||||
|
user = insert(:user, %{info: %{"no_rich_text" => true}})
|
||||||
|
|
||||||
|
assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue