Merge branch 'bugfix/emoji-size' into 'develop'
Bugfix: emoji size in profile Closes #269 See merge request pleroma/pleroma!792
This commit is contained in:
commit
4948a73e37
@ -113,9 +113,7 @@ def emojify(text, emoji, strip \\ false) do
|
|||||||
|
|
||||||
html =
|
html =
|
||||||
if not strip do
|
if not strip do
|
||||||
"<img height='32px' width='32px' alt='#{emoji}' title='#{emoji}' src='#{
|
"<img class='emoji' alt='#{emoji}' title='#{emoji}' src='#{MediaProxy.url(file)}' />"
|
||||||
MediaProxy.url(file)
|
|
||||||
}' />"
|
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
@ -130,12 +128,23 @@ def demojify(text) do
|
|||||||
|
|
||||||
def demojify(text, nil), do: text
|
def demojify(text, nil), do: text
|
||||||
|
|
||||||
|
@doc "Outputs a list of the emoji-shortcodes in a text"
|
||||||
def get_emoji(text) when is_binary(text) do
|
def get_emoji(text) when is_binary(text) do
|
||||||
Enum.filter(Emoji.get_all(), fn {emoji, _, _} -> String.contains?(text, ":#{emoji}:") end)
|
Enum.filter(Emoji.get_all(), fn {emoji, _, _} -> String.contains?(text, ":#{emoji}:") end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_emoji(_), do: []
|
def get_emoji(_), do: []
|
||||||
|
|
||||||
|
@doc "Outputs a list of the emoji-Maps in a text"
|
||||||
|
def get_emoji_map(text) when is_binary(text) do
|
||||||
|
get_emoji(text)
|
||||||
|
|> Enum.reduce(%{}, fn {name, file, _group}, acc ->
|
||||||
|
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_emoji_map(_), do: []
|
||||||
|
|
||||||
def html_escape({text, mentions, hashtags}, type) do
|
def html_escape({text, mentions, hashtags}, type) do
|
||||||
{html_escape(text, type), mentions, hashtags}
|
{html_escape(text, type), mentions, hashtags}
|
||||||
end
|
end
|
||||||
|
@ -151,6 +151,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
|||||||
Meta.allow_tag_with_these_attributes("img", [
|
Meta.allow_tag_with_these_attributes("img", [
|
||||||
"width",
|
"width",
|
||||||
"height",
|
"height",
|
||||||
|
"class",
|
||||||
"title",
|
"title",
|
||||||
"alt"
|
"alt"
|
||||||
])
|
])
|
||||||
@ -221,6 +222,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
|||||||
Meta.allow_tag_with_these_attributes("img", [
|
Meta.allow_tag_with_these_attributes("img", [
|
||||||
"width",
|
"width",
|
||||||
"height",
|
"height",
|
||||||
|
"class",
|
||||||
"title",
|
"title",
|
||||||
"alt"
|
"alt"
|
||||||
])
|
])
|
||||||
|
@ -11,7 +11,6 @@ defmodule Pleroma.User do
|
|||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Bookmark
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Formatter
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Registration
|
alias Pleroma.Registration
|
||||||
@ -1331,18 +1330,15 @@ def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_bio(bio, user \\ %User{info: %{source_data: %{}}})
|
def parse_bio(bio) when is_binary(bio) and bio != "" do
|
||||||
def parse_bio(nil, _user), do: ""
|
bio
|
||||||
def parse_bio(bio, _user) when bio == "", do: bio
|
|> CommonUtils.format_input("text/plain", mentions_format: :full)
|
||||||
|
|> elem(0)
|
||||||
|
end
|
||||||
|
|
||||||
def parse_bio(bio, user) do
|
def parse_bio(_), do: ""
|
||||||
emoji =
|
|
||||||
(user.info.source_data["tag"] || [])
|
|
||||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
|
||||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
|
||||||
{String.trim(name, ":"), url}
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
def parse_bio(bio, user) when is_binary(bio) and bio != "" do
|
||||||
# TODO: get profile URLs other than user.ap_id
|
# TODO: get profile URLs other than user.ap_id
|
||||||
profile_urls = [user.ap_id]
|
profile_urls = [user.ap_id]
|
||||||
|
|
||||||
@ -1352,9 +1348,10 @@ def parse_bio(bio, user) do
|
|||||||
rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
|
rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
|
||||||
)
|
)
|
||||||
|> elem(0)
|
|> elem(0)
|
||||||
|> Formatter.emojify(emoji)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_bio(_, _), do: ""
|
||||||
|
|
||||||
def tag(user_identifiers, tags) when is_list(user_identifiers) do
|
def tag(user_identifiers, tags) when is_list(user_identifiers) do
|
||||||
Repo.transaction(fn ->
|
Repo.transaction(fn ->
|
||||||
for user_identifier <- user_identifiers, do: tag(user_identifier, tags)
|
for user_identifier <- user_identifiers, do: tag(user_identifier, tags)
|
||||||
|
@ -41,6 +41,7 @@ defmodule Pleroma.User.Info do
|
|||||||
field(:hide_favorites, :boolean, default: true)
|
field(:hide_favorites, :boolean, default: true)
|
||||||
field(:pinned_activities, {:array, :string}, default: [])
|
field(:pinned_activities, {:array, :string}, default: [])
|
||||||
field(:flavour, :string, default: nil)
|
field(:flavour, :string, default: nil)
|
||||||
|
field(:emoji, {:array, :map}, default: [])
|
||||||
|
|
||||||
field(:notification_settings, :map,
|
field(:notification_settings, :map,
|
||||||
default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
|
default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
|
||||||
|
@ -856,10 +856,16 @@ def add_mention_tags(object) do
|
|||||||
|> Map.put("tag", tags ++ mentions)
|
|> Map.put("tag", tags ++ mentions)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_emoji_tags(%User{info: %{"emoji" => _emoji} = user_info} = object) do
|
||||||
|
user_info = add_emoji_tags(user_info)
|
||||||
|
|
||||||
|
object
|
||||||
|
|> Map.put(:info, user_info)
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: we should probably send mtime instead of unix epoch time for updated
|
# TODO: we should probably send mtime instead of unix epoch time for updated
|
||||||
def add_emoji_tags(object) do
|
def add_emoji_tags(%{"emoji" => emoji} = object) do
|
||||||
tags = object["tag"] || []
|
tags = object["tag"] || []
|
||||||
emoji = object["emoji"] || []
|
|
||||||
|
|
||||||
out =
|
out =
|
||||||
emoji
|
emoji
|
||||||
@ -877,6 +883,10 @@ def add_emoji_tags(object) do
|
|||||||
|> Map.put("tag", tags ++ out)
|
|> Map.put("tag", tags ++ out)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_emoji_tags(object) do
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
def set_conversation(object) do
|
def set_conversation(object) do
|
||||||
Map.put(object, "conversation", object["context"])
|
Map.put(object, "conversation", object["context"])
|
||||||
end
|
end
|
||||||
|
@ -69,6 +69,11 @@ def render("user.json", %{user: user}) do
|
|||||||
|
|
||||||
endpoints = render("endpoints.json", %{user: user})
|
endpoints = render("endpoints.json", %{user: user})
|
||||||
|
|
||||||
|
user_tags =
|
||||||
|
user
|
||||||
|
|> Transmogrifier.add_emoji_tags()
|
||||||
|
|> Map.get("tag", [])
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => user.ap_id,
|
"id" => user.ap_id,
|
||||||
"type" => "Person",
|
"type" => "Person",
|
||||||
@ -87,7 +92,7 @@ def render("user.json", %{user: user}) do
|
|||||||
"publicKeyPem" => public_key
|
"publicKeyPem" => public_key
|
||||||
},
|
},
|
||||||
"endpoints" => endpoints,
|
"endpoints" => endpoints,
|
||||||
"tag" => user.info.source_data["tag"] || []
|
"tag" => (user.info.source_data["tag"] || []) ++ user_tags
|
||||||
}
|
}
|
||||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||||
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|
||||||
|
@ -151,8 +151,8 @@ def post(user, %{"status" => status} = data) do
|
|||||||
),
|
),
|
||||||
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
||||||
context <- make_context(in_reply_to),
|
context <- make_context(in_reply_to),
|
||||||
cw <- data["spoiler_text"],
|
cw <- data["spoiler_text"] || "",
|
||||||
full_payload <- String.trim(status <> (data["spoiler_text"] || "")),
|
full_payload <- String.trim(status <> cw),
|
||||||
length when length in 1..limit <- String.length(full_payload),
|
length when length in 1..limit <- String.length(full_payload),
|
||||||
object <-
|
object <-
|
||||||
make_note_data(
|
make_note_data(
|
||||||
@ -170,10 +170,7 @@ def post(user, %{"status" => status} = data) do
|
|||||||
Map.put(
|
Map.put(
|
||||||
object,
|
object,
|
||||||
"emoji",
|
"emoji",
|
||||||
(Formatter.get_emoji(status) ++ Formatter.get_emoji(data["spoiler_text"]))
|
Formatter.get_emoji_map(full_payload)
|
||||||
|> Enum.reduce(%{}, fn {name, file, _}, acc ->
|
|
||||||
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
|
|
||||||
end)
|
|
||||||
) do
|
) do
|
||||||
res =
|
res =
|
||||||
ActivityPub.create(
|
ActivityPub.create(
|
||||||
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||||||
alias Pleroma.Bookmark
|
alias Pleroma.Bookmark
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Filter
|
alias Pleroma.Filter
|
||||||
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Fetcher
|
alias Pleroma.Object.Fetcher
|
||||||
@ -86,7 +87,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
|||||||
user_params =
|
user_params =
|
||||||
%{}
|
%{}
|
||||||
|> add_if_present(params, "display_name", :name)
|
|> add_if_present(params, "display_name", :name)
|
||||||
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value)} end)
|
|> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end)
|
||||||
|> add_if_present(params, "avatar", :avatar, fn value ->
|
|> add_if_present(params, "avatar", :avatar, fn value ->
|
||||||
with %Plug.Upload{} <- value,
|
with %Plug.Upload{} <- value,
|
||||||
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
|
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
|
||||||
@ -96,6 +97,12 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
||||||
|
|
||||||
|
user_info_emojis =
|
||||||
|
((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text))
|
||||||
|
|> Enum.dedup()
|
||||||
|
|
||||||
info_params =
|
info_params =
|
||||||
[:no_rich_text, :locked, :hide_followers, :hide_follows, :hide_favorites, :show_role]
|
[:no_rich_text, :locked, :hide_followers, :hide_follows, :hide_favorites, :show_role]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
@ -112,6 +119,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
|||||||
_ -> :error
|
_ -> :error
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Map.put(:emoji, user_info_emojis)
|
||||||
|
|
||||||
info_cng = User.Info.profile_update(user.info, info_params)
|
info_cng = User.Info.profile_update(user.info, info_params)
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||||||
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
@ -653,7 +654,22 @@ defp build_info_cng(user, params) do
|
|||||||
|
|
||||||
defp parse_profile_bio(user, params) do
|
defp parse_profile_bio(user, params) do
|
||||||
if bio = params["description"] do
|
if bio = params["description"] do
|
||||||
Map.put(params, "bio", User.parse_bio(bio, user))
|
emojis_text = (params["description"] || "") <> " " <> (params["name"] || "")
|
||||||
|
|
||||||
|
emojis =
|
||||||
|
((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text))
|
||||||
|
|> Enum.dedup()
|
||||||
|
|
||||||
|
user_info =
|
||||||
|
user.info
|
||||||
|
|> Map.put(
|
||||||
|
"emoji",
|
||||||
|
emojis
|
||||||
|
)
|
||||||
|
|
||||||
|
params
|
||||||
|
|> Map.put("bio", User.parse_bio(bio, user))
|
||||||
|
|> Map.put("info", user_info)
|
||||||
else
|
else
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
@ -67,6 +67,13 @@ defp do_render("user.json", %{user: user = %User{}} = assigns) do
|
|||||||
{String.trim(name, ":"), url}
|
{String.trim(name, ":"), url}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
emoji = Enum.dedup(emoji ++ user.info.emoji)
|
||||||
|
|
||||||
|
description_html =
|
||||||
|
(user.bio || "")
|
||||||
|
|> HTML.filter_tags(User.html_filter_policy(for_user))
|
||||||
|
|> Formatter.emojify(emoji)
|
||||||
|
|
||||||
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
|
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
|
||||||
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
|
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
|
||||||
fields =
|
fields =
|
||||||
@ -78,7 +85,7 @@ defp do_render("user.json", %{user: user = %User{}} = assigns) do
|
|||||||
%{
|
%{
|
||||||
"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, User.html_filter_policy(for_user)),
|
"description_html" => description_html,
|
||||||
"favourites_count" => 0,
|
"favourites_count" => 0,
|
||||||
"followers_count" => user_info[:follower_count],
|
"followers_count" => user_info[:follower_count],
|
||||||
"following" => following,
|
"following" => following,
|
||||||
|
@ -248,7 +248,7 @@ test "it adds cool emoji" do
|
|||||||
text = "I love :firefox:"
|
text = "I love :firefox:"
|
||||||
|
|
||||||
expected_result =
|
expected_result =
|
||||||
"I love <img height=\"32px\" width=\"32px\" alt=\"firefox\" title=\"firefox\" src=\"/emoji/Firefox.gif\" />"
|
"I love <img class=\"emoji\" alt=\"firefox\" title=\"firefox\" src=\"/emoji/Firefox.gif\" />"
|
||||||
|
|
||||||
assert Formatter.emojify(text) == expected_result
|
assert Formatter.emojify(text) == expected_result
|
||||||
end
|
end
|
||||||
@ -263,7 +263,7 @@ test "it does not add XSS emoji" do
|
|||||||
}
|
}
|
||||||
|
|
||||||
expected_result =
|
expected_result =
|
||||||
"I love <img height=\"32px\" width=\"32px\" alt=\"\" title=\"\" src=\"https://placehold.it/1x1\" />"
|
"I love <img class=\"emoji\" alt=\"\" title=\"\" src=\"https://placehold.it/1x1\" />"
|
||||||
|
|
||||||
assert Formatter.emojify(text, custom_emoji) == expected_result
|
assert Formatter.emojify(text, custom_emoji) == expected_result
|
||||||
end
|
end
|
||||||
|
@ -1103,7 +1103,7 @@ test "preserves hosts in user links text" do
|
|||||||
expected_text =
|
expected_text =
|
||||||
"A.k.a. <span class='h-card'><a data-user='#{remote_user.id}' class='u-url mention' href='#{
|
"A.k.a. <span class='h-card'><a data-user='#{remote_user.id}' class='u-url mention' href='#{
|
||||||
remote_user.ap_id
|
remote_user.ap_id
|
||||||
}'>" <> "@<span>nick@domain.com</span></a></span>"
|
}'>@<span>nick@domain.com</span></a></span>"
|
||||||
|
|
||||||
assert expected_text == User.parse_bio(bio, user)
|
assert expected_text == User.parse_bio(bio, user)
|
||||||
end
|
end
|
||||||
|
@ -2351,6 +2351,33 @@ test "requires 'write' permission", %{conn: conn} do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updates profile emojos", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
note = "*sips :blank:*"
|
||||||
|
name = "I am :firefox:"
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"note" => note,
|
||||||
|
"display_name" => name
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}")
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert user["note"] == note
|
||||||
|
assert user["display_name"] == name
|
||||||
|
assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "get instance information", %{conn: conn} do
|
test "get instance information", %{conn: conn} do
|
||||||
|
@ -1611,6 +1611,34 @@ test "it unlocks an account", %{conn: conn} do
|
|||||||
|
|
||||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Broken before the change to class="emoji" and non-<img/> in the DB
|
||||||
|
@tag :skip
|
||||||
|
test "it formats emojos", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/account/update_profile.json", %{
|
||||||
|
"bio" => "I love our :moominmamma:"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"description" => "I love our :moominmamma:",
|
||||||
|
"description_html" =>
|
||||||
|
~s{I love our <img class="emoji" alt="moominmamma" title="moominmamma" src="} <>
|
||||||
|
_
|
||||||
|
} = response
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/users/show.json?user_id=#{user.nickname}")
|
||||||
|
|
||||||
|
assert response == json_response(conn, 200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp valid_user(_context) do
|
defp valid_user(_context) do
|
||||||
|
@ -100,7 +100,7 @@ test "a create activity with a summary containing emoji" do
|
|||||||
expected = ":firefox: meow"
|
expected = ":firefox: meow"
|
||||||
|
|
||||||
expected_html =
|
expected_html =
|
||||||
"<img height=\"32px\" width=\"32px\" alt=\"firefox\" title=\"firefox\" src=\"http://localhost:4001/emoji/Firefox.gif\" /> meow"
|
"<img class=\"emoji\" alt=\"firefox\" title=\"firefox\" src=\"http://localhost:4001/emoji/Firefox.gif\" /> meow"
|
||||||
|
|
||||||
assert result["summary"] == expected
|
assert result["summary"] == expected
|
||||||
assert result["summary_html"] == expected_html
|
assert result["summary_html"] == expected_html
|
||||||
|
@ -32,7 +32,7 @@ test "A user with an avatar object", %{user: user} do
|
|||||||
|
|
||||||
test "A user with emoji in username" do
|
test "A user with emoji in username" do
|
||||||
expected =
|
expected =
|
||||||
"<img height=\"32px\" width=\"32px\" alt=\"karjalanpiirakka\" title=\"karjalanpiirakka\" src=\"/file.png\" /> man"
|
"<img class=\"emoji\" alt=\"karjalanpiirakka\" title=\"karjalanpiirakka\" src=\"/file.png\" /> man"
|
||||||
|
|
||||||
user =
|
user =
|
||||||
insert(:user, %{
|
insert(:user, %{
|
||||||
|
Loading…
Reference in New Issue
Block a user