From a8369db4f222676040a072ceb0bde647ef237f14 Mon Sep 17 00:00:00 2001 From: eal Date: Sun, 29 Apr 2018 16:02:46 +0300 Subject: MastoAPI: add lists. --- lib/pleroma/list.ex | 86 +++++++++++++++++++ .../web/mastodon_api/mastodon_api_controller.ex | 98 +++++++++++++++++++++- lib/pleroma/web/mastodon_api/views/list_view.ex | 15 ++++ lib/pleroma/web/router.ex | 11 ++- 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/list.ex create mode 100644 lib/pleroma/web/mastodon_api/views/list_view.ex (limited to 'lib') diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex new file mode 100644 index 000000000..657d42626 --- /dev/null +++ b/lib/pleroma/list.ex @@ -0,0 +1,86 @@ +defmodule Pleroma.List do + use Ecto.Schema + import Ecto.{Changeset, Query} + alias Pleroma.{User, Repo} + + schema "lists" do + belongs_to(:user, Pleroma.User) + field(:title, :string) + field(:following, {:array, :string}, default: []) + + timestamps() + end + + def title_changeset(list, attrs \\ %{}) do + list + |> cast(attrs, [:title]) + |> validate_required([:title]) + end + + def follow_changeset(list, attrs \\ %{}) do + list + |> cast(attrs, [:following]) + |> validate_required([:following]) + end + + def for_user(user, opts) do + query = + from( + l in Pleroma.List, + where: l.user_id == ^user.id, + order_by: [desc: l.id], + limit: 50 + ) + + Repo.all(query) + end + + def get(%{id: user_id} = _user, id) do + query = + from( + l in Pleroma.List, + where: l.id == ^id, + where: l.user_id == ^user_id + ) + + Repo.one(query) + end + + def get_following(%Pleroma.List{following: following} = list) do + q = from( + u in User, + where: u.follower_address in ^following + ) + {:ok, Repo.all(q)} + end + + def rename(%Pleroma.List{} = list, title) do + list + |> title_changeset(%{title: title}) + |> Repo.update() + end + + def create(title, %User{} = creator) do + list = %Pleroma.List{user_id: creator.id, title: title} + Repo.insert(list) + end + + # TODO check that user is following followed + def follow(%Pleroma.List{following: following} = list, %User{} = followed) do + update_follows(list, %{following: Enum.uniq([followed.follower_address | following])}) + end + + def unfollow(%Pleroma.List{following: following} = list, %User{} = unfollowed) do + update_follows(list, %{following: List.delete(following, unfollowed.follower_address)}) + end + + def delete(%Pleroma.List{} = list) do + Repo.delete(list) + end + + def update_follows(%Pleroma.List{} = list, attrs) do + list + |> follow_changeset(attrs) + |> Repo.update() + end +end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index c84c226e8..ff52b2aab 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -2,7 +2,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller alias Pleroma.{Repo, Activity, User, Notification, Stats} alias Pleroma.Web - alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView} + alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.{CommonAPI, OStatus} alias Pleroma.Web.OAuth.{Authorization, Token, App} @@ -565,6 +565,102 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end + def get_lists(%{assigns: %{user: user}} = conn, opts) do + lists = Pleroma.List.for_user(user, opts) + res = ListView.render("lists.json", lists: lists) + json(conn, res) + end + + def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with %Pleroma.List{} = list <- Pleroma.List.get(user, id) do + res = ListView.render("list.json", list: list) + json(conn, res) + else + _e -> json(conn, "error") + end + end + + def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + {:ok, _list} <- Pleroma.List.delete(list) do + json(conn, %{}) + else + _e -> + json(conn, "error") + end + end + + def create_list(%{assigns: %{user: user}} = conn, %{"title" => title}) do + with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do + res = ListView.render("list.json", list: list) + json(conn, res) + end + end + + def add_to_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do + accounts + |> Enum.each(fn account_id -> + with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + %User{} = followed <- Repo.get(User, account_id) do + ret = Pleroma.List.follow(list, followed) + end + end) + + json(conn, %{}) + end + + def remove_from_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do + accounts + |> Enum.each(fn account_id -> + with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + %User{} = followed <- Repo.get(Pleroma.User, account_id) do + Pleroma.List.unfollow(list, followed) + end + end) + + json(conn, %{}) + end + + def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + {:ok, users} = Pleroma.List.get_following(list) do + render(conn, AccountView, "accounts.json", %{users: users, as: :user}) + end + end + + def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title}) do + with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + {:ok, list} <- Pleroma.List.rename(list, title) do + res = ListView.render("list.json", list: list) + json(conn, res) + else + _e -> + json(conn, "error") + end + end + + def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do + with %Pleroma.List{title: title, following: following} <- Pleroma.List.get(user, id) do + params = + params + |> Map.put("type", "Create") + |> Map.put("blocking_user", user) + + # adding title is a hack to not make empty lists function like a public timeline + activities = + ActivityPub.fetch_activities([title | following], params) + |> Enum.reverse() + + conn + |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) + else + _e -> + conn + |> put_status(403) + |> json(%{error: "Error."}) + end + end + def index(%{assigns: %{user: user}} = conn, _params) do token = conn diff --git a/lib/pleroma/web/mastodon_api/views/list_view.ex b/lib/pleroma/web/mastodon_api/views/list_view.ex new file mode 100644 index 000000000..1a1b7430b --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/list_view.ex @@ -0,0 +1,15 @@ +defmodule Pleroma.Web.MastodonAPI.ListView do + use Pleroma.Web, :view + alias Pleroma.Web.MastodonAPI.ListView + + def render("lists.json", %{lists: lists} = opts) do + render_many(lists, ListView, "list.json", opts) + end + + def render("list.json", %{list: list}) do + %{ + id: to_string(list.id), + title: list.title + } + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index cecf5527c..c58f77817 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -102,7 +102,6 @@ defmodule Pleroma.Web.Router do get("/domain_blocks", MastodonAPIController, :empty_array) get("/follow_requests", MastodonAPIController, :empty_array) get("/mutes", MastodonAPIController, :empty_array) - get("/lists", MastodonAPIController, :empty_array) get("/timelines/home", MastodonAPIController, :home_timeline) @@ -121,6 +120,15 @@ defmodule Pleroma.Web.Router do get("/notifications/:id", MastodonAPIController, :get_notification) post("/media", MastodonAPIController, :upload) + + get("/lists", MastodonAPIController, :get_lists) + get("/lists/:id", MastodonAPIController, :get_list) + delete("/lists/:id", MastodonAPIController, :delete_list) + post("/lists", MastodonAPIController, :create_list) + put("/lists/:id", MastodonAPIController, :rename_list) + get("/lists/:id/accounts", MastodonAPIController, :list_accounts) + post("/lists/:id/accounts", MastodonAPIController, :add_to_list) + delete("/lists/:id/accounts", MastodonAPIController, :remove_from_list) end scope "/api/web", Pleroma.Web.MastodonAPI do @@ -138,6 +146,7 @@ defmodule Pleroma.Web.Router do get("/timelines/public", MastodonAPIController, :public_timeline) get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) + get("/timelines/list/:list_id", MastodonAPIController, :list_timeline) get("/statuses/:id", MastodonAPIController, :get_status) get("/statuses/:id/context", MastodonAPIController, :get_context) -- cgit v1.2.3 From 3dbd9809d44297c2edc8e08bde33f9ef7b998412 Mon Sep 17 00:00:00 2001 From: eal Date: Sun, 29 Apr 2018 16:02:46 +0300 Subject: MastoAPI: add lists. --- lib/pleroma/list.ex | 13 +++++++------ lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 16 ++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index 657d42626..9d0b9285b 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -35,7 +35,7 @@ defmodule Pleroma.List do Repo.all(query) end - def get(%{id: user_id} = _user, id) do + def get(id, %{id: user_id} = _user) do query = from( l in Pleroma.List, @@ -47,10 +47,12 @@ defmodule Pleroma.List do end def get_following(%Pleroma.List{following: following} = list) do - q = from( - u in User, - where: u.follower_address in ^following - ) + q = + from( + u in User, + where: u.follower_address in ^following + ) + {:ok, Repo.all(q)} end @@ -65,7 +67,6 @@ defmodule Pleroma.List do Repo.insert(list) end - # TODO check that user is following followed def follow(%Pleroma.List{following: following} = list, %User{} = followed) do update_follows(list, %{following: Enum.uniq([followed.follower_address | following])}) end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index ff52b2aab..82ddb9a5d 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -572,7 +572,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def get_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Pleroma.List{} = list <- Pleroma.List.get(user, id) do + with %Pleroma.List{} = list <- Pleroma.List.get(id, user) do res = ListView.render("list.json", list: list) json(conn, res) else @@ -581,7 +581,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def delete_list(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), {:ok, _list} <- Pleroma.List.delete(list) do json(conn, %{}) else @@ -600,9 +600,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def add_to_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do accounts |> Enum.each(fn account_id -> - with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), %User{} = followed <- Repo.get(User, account_id) do - ret = Pleroma.List.follow(list, followed) + Pleroma.List.follow(list, followed) end end) @@ -612,7 +612,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def remove_from_list(%{assigns: %{user: user}} = conn, %{"id" => id, "account_ids" => accounts}) do accounts |> Enum.each(fn account_id -> - with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), %User{} = followed <- Repo.get(Pleroma.User, account_id) do Pleroma.List.unfollow(list, followed) end @@ -622,14 +622,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def list_accounts(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), {:ok, users} = Pleroma.List.get_following(list) do render(conn, AccountView, "accounts.json", %{users: users, as: :user}) end end def rename_list(%{assigns: %{user: user}} = conn, %{"id" => id, "title" => title}) do - with %Pleroma.List{} = list <- Pleroma.List.get(user, id), + with %Pleroma.List{} = list <- Pleroma.List.get(id, user), {:ok, list} <- Pleroma.List.rename(list, title) do res = ListView.render("list.json", list: list) json(conn, res) @@ -640,7 +640,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do - with %Pleroma.List{title: title, following: following} <- Pleroma.List.get(user, id) do + with %Pleroma.List{title: title, following: following} <- Pleroma.List.get(id, user) do params = params |> Map.put("type", "Create") -- cgit v1.2.3