Merge branch 'develop' into 'feature/staff-discovery-api'

# Conflicts:
#   lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
This commit is contained in:
kaniini 2018-09-03 14:55:42 +00:00
commit 76c67a41c1
16 changed files with 484 additions and 8 deletions

62
lib/pleroma/filter.ex Normal file
View file

@ -0,0 +1,62 @@
defmodule Pleroma.Filter do
use Ecto.Schema
import Ecto.{Changeset, Query}
alias Pleroma.{User, Repo, Activity}
schema "filters" do
belongs_to(:user, Pleroma.User)
field(:filter_id, :integer)
field(:hide, :boolean, default: false)
field(:whole_word, :boolean, default: true)
field(:phrase, :string)
field(:context, {:array, :string})
field(:expires_at, :utc_datetime)
timestamps()
end
def get(id, %{id: user_id} = _user) do
query =
from(
f in Pleroma.Filter,
where: f.filter_id == ^id,
where: f.user_id == ^user_id
)
Repo.one(query)
end
def get_filters(%Pleroma.User{id: user_id} = user) do
query =
from(
f in Pleroma.Filter,
where: f.user_id == ^user_id
)
Repo.all(query)
end
def create(%Pleroma.Filter{} = filter) do
Repo.insert(filter)
end
def delete(%Pleroma.Filter{id: filter_key} = filter) when is_number(filter_key) do
Repo.delete(filter)
end
def delete(%Pleroma.Filter{id: filter_key} = filter) when is_nil(filter_key) do
%Pleroma.Filter{id: id} = get(filter.filter_id, %{id: filter.user_id})
filter
|> Map.put(:id, id)
|> Repo.delete()
end
def update(%Pleroma.Filter{} = filter) do
destination = Map.from_struct(filter)
Pleroma.Filter.get(filter.filter_id, %{id: filter.user_id})
|> cast(destination, [:phrase, :context, :hide, :expires_at, :whole_word])
|> Repo.update()
end
end

View file

@ -158,10 +158,12 @@ def emojify(text, emoji) do
end) end)
end end
def get_emoji(text) do def get_emoji(text) when is_binary(text) do
Enum.filter(@emoji, fn {emoji, _} -> String.contains?(text, ":#{emoji}:") end) Enum.filter(@emoji, fn {emoji, _} -> String.contains?(text, ":#{emoji}:") end)
end end
def get_emoji(_), do: []
def get_custom_emoji() do def get_custom_emoji() do
@emoji @emoji
end end

View file

@ -93,6 +93,11 @@ def stream_out(activity) do
Pleroma.Web.Streamer.stream("public:local", activity) Pleroma.Web.Streamer.stream("public:local", activity)
end end
activity.data["object"]
|> Map.get("tag", [])
|> Enum.filter(fn tag -> is_bitstring(tag) end)
|> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
if activity.data["object"]["attachment"] != [] do if activity.data["object"]["attachment"] != [] do
Pleroma.Web.Streamer.stream("public:media", activity) Pleroma.Web.Streamer.stream("public:media", activity)

View file

@ -2,7 +2,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats} alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
alias Pleroma.Web alias Pleroma.Web
alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView} alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView, FilterView}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
@ -125,7 +125,7 @@ def user(conn, %{"id" => id}) do
end end
@instance Application.get_env(:pleroma, :instance) @instance Application.get_env(:pleroma, :instance)
@mastodon_api_level "2.3.3" @mastodon_api_level "2.4.3"
def masto_instance(conn, _params) do def masto_instance(conn, _params) do
response = %{ response = %{
@ -1030,6 +1030,8 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
NaiveDateTime.to_iso8601(created_at) NaiveDateTime.to_iso8601(created_at)
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
id = id |> to_string
case activity.data["type"] do case activity.data["type"] do
"Create" -> "Create" ->
%{ %{
@ -1075,6 +1077,65 @@ def render_notification(user, %{id: id, activity: activity, inserted_at: created
end end
end end
def get_filters(%{assigns: %{user: user}} = conn, params) do
filters = Pleroma.Filter.get_filters(user)
res = FilterView.render("filters.json", filters: filters)
json(conn, res)
end
def create_filter(
%{assigns: %{user: user}} = conn,
%{"phrase" => phrase, "context" => context} = params
) do
query = %Pleroma.Filter{
user_id: user.id,
phrase: phrase,
context: context,
hide: Map.get(params, "irreversible", nil),
whole_word: Map.get(params, "boolean", true)
# expires_at
}
{:ok, response} = Pleroma.Filter.create(query)
res = FilterView.render("filter.json", filter: response)
json(conn, res)
end
def get_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id} = params) do
filter = Pleroma.Filter.get(filter_id, user)
res = FilterView.render("filter.json", filter: filter)
json(conn, res)
end
def update_filter(
%{assigns: %{user: user}} = conn,
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params
) do
query = %Pleroma.Filter{
user_id: user.id,
filter_id: filter_id,
phrase: phrase,
context: context,
hide: Map.get(params, "irreversible", nil),
whole_word: Map.get(params, "boolean", true)
# expires_at
}
{:ok, response} = Pleroma.Filter.update(query)
res = FilterView.render("filter.json", filter: response)
json(conn, res)
end
def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id} = params) do
query = %Pleroma.Filter{
user_id: user.id,
filter_id: filter_id
}
{:ok, response} = Pleroma.Filter.delete(query)
json(conn, %{})
end
def errors(conn, _) do def errors(conn, _) do
conn conn
|> put_status(500) |> put_status(500)

View file

@ -23,16 +23,18 @@ def connect(params, socket) do
"public:local:media", "public:local:media",
"user", "user",
"direct", "direct",
"list" "list",
"hashtag"
] <- params["stream"] do ] <- params["stream"] do
topic = if stream == "list", do: "list:#{params["list"]}", else: stream topic = if stream == "list", do: "list:#{params["list"]}", else: stream
socket_stream = if stream == "hashtag", do: "hashtag:#{params["tag"]}", else: stream
socket = socket =
socket socket
|> assign(:topic, topic) |> assign(:topic, topic)
|> assign(:user, user) |> assign(:user, user)
Pleroma.Web.Streamer.add_socket(params["stream"], socket) Pleroma.Web.Streamer.add_socket(socket_stream, socket)
{:ok, socket} {:ok, socket}
else else
_e -> :error _e -> :error

View file

@ -13,6 +13,7 @@ def render("account.json", %{user: user}) 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)
bot = (user.info["source_data"]["type"] || "Person") in ["Application", "Service"]
emojis = emojis =
(user.info["source_data"]["tag"] || []) (user.info["source_data"]["tag"] || [])
@ -26,6 +27,11 @@ def render("account.json", %{user: user}) do
} }
end) end)
fields =
(user.info["source_data"]["attachment"] || [])
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
%{ %{
id: to_string(user.id), id: to_string(user.id),
username: username_from_nickname(user.nickname), username: username_from_nickname(user.nickname),
@ -43,7 +49,8 @@ def render("account.json", %{user: user}) do
header: header, header: header,
header_static: header, header_static: header,
emojis: emojis, emojis: emojis,
fields: [], fields: fields,
bot: bot,
source: %{ source: %{
note: "", note: "",
privacy: user_info.default_scope, privacy: user_info.default_scope,

View file

@ -0,0 +1,27 @@
defmodule Pleroma.Web.MastodonAPI.FilterView do
use Pleroma.Web, :view
alias Pleroma.Web.MastodonAPI.FilterView
alias Pleroma.Web.CommonAPI.Utils
def render("filters.json", %{filters: filters} = opts) do
render_many(filters, FilterView, "filter.json", opts)
end
def render("filter.json", %{filter: filter}) do
expires_at =
if filter.expires_at do
Utils.to_masto_date(filter.expires_at)
else
nil
end
%{
id: to_string(filter.filter_id),
phrase: filter.phrase,
context: filter.context,
expires_at: expires_at,
irreversible: filter.hide,
whole_word: false
}
end
end

View file

@ -23,6 +23,8 @@ def nodeinfo(conn, %{"version" => "2.0"}) do
instance = Application.get_env(:pleroma, :instance) instance = Application.get_env(:pleroma, :instance)
media_proxy = Application.get_env(:pleroma, :media_proxy) media_proxy = Application.get_env(:pleroma, :media_proxy)
suggestions = Application.get_env(:pleroma, :suggestions) suggestions = Application.get_env(:pleroma, :suggestions)
chat = Application.get_env(:pleroma, :chat)
gopher = Application.get_env(:pleroma, :gopher)
stats = Stats.get_stats() stats = Stats.get_stats()
staff_accounts = staff_accounts =
@ -59,7 +61,9 @@ def nodeinfo(conn, %{"version" => "2.0"}) do
timeout: Keyword.get(suggestions, :timeout, 5000), timeout: Keyword.get(suggestions, :timeout, 5000),
web: Keyword.get(suggestions, :web, "") web: Keyword.get(suggestions, :web, "")
}, },
staffAccounts: staff_accounts staffAccounts: staff_accounts,
chat: Keyword.get(chat, :enabled),
gopher: Keyword.get(gopher, :enabled)
} }
} }

View file

@ -154,6 +154,12 @@ def user_fetcher(username_or_email) do
post("/domain_blocks", MastodonAPIController, :block_domain) post("/domain_blocks", MastodonAPIController, :block_domain)
delete("/domain_blocks", MastodonAPIController, :unblock_domain) delete("/domain_blocks", MastodonAPIController, :unblock_domain)
get("/filters", MastodonAPIController, :get_filters)
post("/filters", MastodonAPIController, :create_filter)
get("/filters/:id", MastodonAPIController, :get_filter)
put("/filters/:id", MastodonAPIController, :update_filter)
delete("/filters/:id", MastodonAPIController, :delete_filter)
get("/suggestions", MastodonAPIController, :suggestions) get("/suggestions", MastodonAPIController, :suggestions)
end end

View file

@ -181,6 +181,7 @@ def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activ
def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do
user = get_user(activity.data["actor"], opts) user = get_user(activity.data["actor"], opts)
liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
liked_activity_id = if liked_activity, do: liked_activity.id, else: nil
created_at = created_at =
activity.data["published"] activity.data["published"]
@ -197,7 +198,7 @@ def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity}
"is_post_verb" => false, "is_post_verb" => false,
"uri" => "tag:#{activity.data["id"]}:objectType=Favourite", "uri" => "tag:#{activity.data["id"]}:objectType=Favourite",
"created_at" => created_at, "created_at" => created_at,
"in_reply_to_status_id" => liked_activity.id, "in_reply_to_status_id" => liked_activity_id,
"external_url" => activity.data["id"], "external_url" => activity.data["id"],
"activity_type" => "like" "activity_type" => "like"
} }

View file

@ -0,0 +1,20 @@
defmodule Pleroma.Repo.Migrations.CreateFilters do
use Ecto.Migration
def change do
create table(:filters) do
add :user_id, references(:users, on_delete: :delete_all)
add :filter_id, :integer
add :hide, :boolean
add :phrase, :string
add :context, {:array, :string}
add :expires_at, :datetime
add :whole_word, :boolean
timestamps()
end
create index(:filters, [:user_id])
create index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index)
end
end

85
test/filter_test.exs Normal file
View file

@ -0,0 +1,85 @@
defmodule Pleroma.FilterTest do
alias Pleroma.{User, Repo}
use Pleroma.DataCase
import Pleroma.Factory
import Ecto.Query
test "creating a filter" do
user = insert(:user)
query = %Pleroma.Filter{
user_id: user.id,
filter_id: 42,
phrase: "knights",
context: ["home"]
}
{:ok, %Pleroma.Filter{} = filter} = Pleroma.Filter.create(query)
result = Pleroma.Filter.get(filter.filter_id, user)
assert query.phrase == result.phrase
end
test "deleting a filter" do
user = insert(:user)
query = %Pleroma.Filter{
user_id: user.id,
filter_id: 0,
phrase: "knights",
context: ["home"]
}
{:ok, filter} = Pleroma.Filter.create(query)
{:ok, filter} = Pleroma.Filter.delete(query)
assert is_nil(Repo.get(Pleroma.Filter, filter.filter_id))
end
test "getting all filters by an user" do
user = insert(:user)
query_one = %Pleroma.Filter{
user_id: user.id,
filter_id: 1,
phrase: "knights",
context: ["home"]
}
query_two = %Pleroma.Filter{
user_id: user.id,
filter_id: 2,
phrase: "who",
context: ["home"]
}
{:ok, filter_one} = Pleroma.Filter.create(query_one)
{:ok, filter_two} = Pleroma.Filter.create(query_two)
filters = Pleroma.Filter.get_filters(user)
assert filter_one in filters
assert filter_two in filters
end
test "updating a filter" do
user = insert(:user)
query_one = %Pleroma.Filter{
user_id: user.id,
filter_id: 1,
phrase: "knights",
context: ["home"]
}
query_two = %Pleroma.Filter{
user_id: user.id,
filter_id: 1,
phrase: "who",
context: ["home", "timeline"]
}
{:ok, filter_one} = Pleroma.Filter.create(query_one)
{:ok, filter_two} = Pleroma.Filter.update(query_two)
assert filter_one != filter_two
assert filter_two.phrase == query_two.phrase
assert filter_two.context == query_two.context
end
end

View file

@ -214,4 +214,14 @@ test "it returns the emoji used in the text" do
assert Formatter.get_emoji(text) == [{"moominmamma", "/finmoji/128px/moominmamma-128.png"}] assert Formatter.get_emoji(text) == [{"moominmamma", "/finmoji/128px/moominmamma-128.png"}]
end end
test "it returns a nice empty result when no emojis are present" do
text = "I love moominamma"
assert Formatter.get_emoji(text) == []
end
test "it doesn't die when text is absent" do
text = nil
assert Formatter.get_emoji(text) == []
end
end end

View file

@ -49,6 +49,44 @@ test "Represent a user account" do
} }
], ],
fields: [], fields: [],
bot: false,
source: %{
note: "",
privacy: "public",
sensitive: "false"
}
}
assert expected == AccountView.render("account.json", %{user: user})
end
test "Represent a Service(bot) account" do
user =
insert(:user, %{
info: %{"note_count" => 5, "follower_count" => 3, "source_data" => %{"type" => "Service"}},
nickname: "shp@shitposter.club",
inserted_at: ~N[2017-08-15 15:47:06.597036]
})
expected = %{
id: to_string(user.id),
username: "shp",
acct: user.nickname,
display_name: user.name,
locked: false,
created_at: "2017-08-15T15:47:06.000Z",
followers_count: 3,
following_count: 0,
statuses_count: 5,
note: user.bio,
url: user.ap_id,
avatar: "http://localhost:4001/images/avi.png",
avatar_static: "http://localhost:4001/images/avi.png",
header: "http://localhost:4001/images/banner.png",
header_static: "http://localhost:4001/images/banner.png",
emojis: [],
fields: [],
bot: true,
source: %{ source: %{
note: "", note: "",
privacy: "public", privacy: "public",

View file

@ -263,6 +263,125 @@ test "when you didn't create it", %{conn: conn} do
end end
end end
describe "filters" do
test "creating a filter", %{conn: conn} do
user = insert(:user)
filter = %Pleroma.Filter{
phrase: "knights",
context: ["home"]
}
conn =
conn
|> assign(:user, user)
|> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
assert response = json_response(conn, 200)
assert response["phrase"] == filter.phrase
assert response["context"] == filter.context
end
test "fetching a list of filters", %{conn: conn} do
user = insert(:user)
query_one = %Pleroma.Filter{
user_id: user.id,
filter_id: 1,
phrase: "knights",
context: ["home"]
}
query_two = %Pleroma.Filter{
user_id: user.id,
filter_id: 2,
phrase: "who",
context: ["home"]
}
{:ok, filter_one} = Pleroma.Filter.create(query_one)
{:ok, filter_two} = Pleroma.Filter.create(query_two)
conn =
conn
|> assign(:user, user)
|> get("/api/v1/filters")
assert response = json_response(conn, 200)
end
test "get a filter", %{conn: conn} do
user = insert(:user)
query = %Pleroma.Filter{
user_id: user.id,
filter_id: 2,
phrase: "knight",
context: ["home"]
}
{:ok, filter} = Pleroma.Filter.create(query)
conn =
conn
|> assign(:user, user)
|> get("/api/v1/filters/#{filter.filter_id}")
assert response = json_response(conn, 200)
end
test "update a filter", %{conn: conn} do
user = insert(:user)
query = %Pleroma.Filter{
user_id: user.id,
filter_id: 2,
phrase: "knight",
context: ["home"]
}
{:ok, filter} = Pleroma.Filter.create(query)
new = %Pleroma.Filter{
phrase: "nii",
context: ["home"]
}
conn =
conn
|> assign(:user, user)
|> put("/api/v1/filters/#{query.filter_id}", %{
phrase: new.phrase,
context: new.context
})
assert response = json_response(conn, 200)
assert response["phrase"] == new.phrase
assert response["context"] == new.context
end
test "delete a filter", %{conn: conn} do
user = insert(:user)
query = %Pleroma.Filter{
user_id: user.id,
filter_id: 2,
phrase: "knight",
context: ["home"]
}
{:ok, filter} = Pleroma.Filter.create(query)
conn =
conn
|> assign(:user, user)
|> delete("/api/v1/filters/#{filter.filter_id}")
assert response = json_response(conn, 200)
assert response == %{}
end
end
describe "lists" do describe "lists" do
test "creating a list", %{conn: conn} do test "creating a list", %{conn: conn} do
user = insert(:user) user = insert(:user)

View file

@ -126,6 +126,33 @@ test "a like activity" do
assert result == expected assert result == expected
end end
test "a like activity for deleted post" do
user = insert(:user)
other_user = insert(:user, %{nickname: "shp"})
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
{:ok, like, _object} = CommonAPI.favorite(activity.id, other_user)
CommonAPI.delete(activity.id, user)
result = ActivityView.render("activity.json", activity: like)
expected = %{
"activity_type" => "like",
"created_at" => like.data["published"] |> Utils.date_to_asctime(),
"external_url" => like.data["id"],
"id" => like.id,
"in_reply_to_status_id" => nil,
"is_local" => true,
"is_post_verb" => false,
"statusnet_html" => "shp favorited a status.",
"text" => "shp favorited a status.",
"uri" => "tag:#{like.data["id"]}:objectType=Favourite",
"user" => UserView.render("show.json", user: other_user)
}
assert result == expected
end
test "an announce activity" do test "an announce activity" do
user = insert(:user) user = insert(:user)
other_user = insert(:user, %{nickname: "shp"}) other_user = insert(:user, %{nickname: "shp"})