From eb1739c59699754297149c92ea3d03ec688ae16a Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 27 Aug 2019 12:29:19 +0300 Subject: [PATCH 01/14] Remove most of TwitterAPIController --- lib/pleroma/web/router.ex | 106 - .../web/twitter_api/twitter_api_controller.ex | 763 +----- .../twitter_api_controller_test.exs | 2150 ----------------- 3 files changed, 6 insertions(+), 3013 deletions(-) delete mode 100644 test/web/twitter_api/twitter_api_controller_test.exs diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 1ad33630c..53728e298 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -482,53 +482,12 @@ defmodule Pleroma.Web.Router do scope "/api", Pleroma.Web do pipe_through(:api) - post("/account/register", TwitterAPI.Controller, :register) - post("/account/password_reset", TwitterAPI.Controller, :password_reset) - - post("/account/resend_confirmation_email", TwitterAPI.Controller, :resend_confirmation_email) - get( "/account/confirm_email/:user_id/:token", TwitterAPI.Controller, :confirm_email, as: :confirm_email ) - - scope [] do - pipe_through(:oauth_read_or_public) - - get("/statuses/user_timeline", TwitterAPI.Controller, :user_timeline) - get("/qvitter/statuses/user_timeline", TwitterAPI.Controller, :user_timeline) - get("/users/show", TwitterAPI.Controller, :show_user) - - get("/statuses/followers", TwitterAPI.Controller, :followers) - get("/statuses/friends", TwitterAPI.Controller, :friends) - get("/statuses/blocks", TwitterAPI.Controller, :blocks) - get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status) - get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation) - - get("/search", TwitterAPI.Controller, :search) - get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) - end - end - - scope "/api", Pleroma.Web do - pipe_through([:api, :oauth_read_or_public]) - - get("/statuses/public_timeline", TwitterAPI.Controller, :public_timeline) - - get( - "/statuses/public_and_external_timeline", - TwitterAPI.Controller, - :public_and_external_timeline - ) - - get("/statuses/networkpublic_timeline", TwitterAPI.Controller, :public_and_external_timeline) - end - - scope "/api", Pleroma.Web, as: :twitter_api_search do - pipe_through([:api, :oauth_read_or_public]) - get("/pleroma/search_user", TwitterAPI.Controller, :search_user) end scope "/api", Pleroma.Web, as: :authenticated_twitter_api do @@ -536,71 +495,6 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) - - scope [] do - pipe_through(:oauth_read) - - get("/account/verify_credentials", TwitterAPI.Controller, :verify_credentials) - post("/account/verify_credentials", TwitterAPI.Controller, :verify_credentials) - - get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline) - get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) - get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) - get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline) - get("/statuses/dm_timeline", TwitterAPI.Controller, :dm_timeline) - get("/qvitter/statuses/notifications", TwitterAPI.Controller, :notifications) - - get("/pleroma/friend_requests", TwitterAPI.Controller, :friend_requests) - - get("/friends/ids", TwitterAPI.Controller, :friends_ids) - get("/friendships/no_retweets/ids", TwitterAPI.Controller, :empty_array) - - get("/mutes/users/ids", TwitterAPI.Controller, :empty_array) - get("/qvitter/mutes", TwitterAPI.Controller, :raw_empty_array) - - get("/externalprofile/show", TwitterAPI.Controller, :external_profile) - - post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) - end - - scope [] do - pipe_through(:oauth_write) - - post("/account/update_profile", TwitterAPI.Controller, :update_profile) - post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner) - post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background) - - post("/statuses/update", TwitterAPI.Controller, :status_update) - post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet) - post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet) - post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post) - - post("/statuses/pin/:id", TwitterAPI.Controller, :pin) - post("/statuses/unpin/:id", TwitterAPI.Controller, :unpin) - - post("/statusnet/media/upload", TwitterAPI.Controller, :upload) - post("/media/upload", TwitterAPI.Controller, :upload_json) - post("/media/metadata/create", TwitterAPI.Controller, :update_media) - - post("/favorites/create/:id", TwitterAPI.Controller, :favorite) - post("/favorites/create", TwitterAPI.Controller, :favorite) - post("/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite) - - post("/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar) - end - - scope [] do - pipe_through(:oauth_follow) - - post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request) - post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request) - - post("/friendships/create", TwitterAPI.Controller, :follow) - post("/friendships/destroy", TwitterAPI.Controller, :unfollow) - - post("/blocks/create", TwitterAPI.Controller, :block) - post("/blocks/destroy", TwitterAPI.Controller, :unblock) - end end pipeline :ap_service_actor do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 5dfab6a6c..1c3b11a57 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -5,448 +5,15 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [json_response: 3] - alias Ecto.Changeset - alias Pleroma.Activity - alias Pleroma.Formatter - alias Pleroma.Notification - alias Pleroma.Object - alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.OAuth.Token - alias Pleroma.Web.TwitterAPI.ActivityView - alias Pleroma.Web.TwitterAPI.NotificationView alias Pleroma.Web.TwitterAPI.TokenView - alias Pleroma.Web.TwitterAPI.TwitterAPI - alias Pleroma.Web.TwitterAPI.UserView require Logger - plug(Pleroma.Plugs.RateLimiter, :password_reset when action == :password_reset) - plug(:only_if_public_instance when action in [:public_timeline, :public_and_external_timeline]) action_fallback(:errors) - def verify_credentials(%{assigns: %{user: user}} = conn, _params) do - token = Phoenix.Token.sign(conn, "user socket", user.id) - - conn - |> put_view(UserView) - |> render("show.json", %{user: user, token: token, for: user}) - end - - def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do - with media_ids <- extract_media_ids(status_data), - {:ok, activity} <- - TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) do - conn - |> json(ActivityView.render("activity.json", activity: activity, for: user)) - else - _ -> empty_status_reply(conn) - end - end - - def status_update(conn, _status_data) do - empty_status_reply(conn) - end - - defp empty_status_reply(conn) do - bad_request_reply(conn, "Client must provide a 'status' parameter with a value.") - end - - defp extract_media_ids(status_data) do - with media_ids when not is_nil(media_ids) <- status_data["media_ids"], - split_ids <- String.split(media_ids, ","), - clean_ids <- Enum.reject(split_ids, fn id -> String.length(id) == 0 end) do - clean_ids - else - _e -> [] - end - end - - def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", ["Create", "Announce"]) - |> Map.put("blocking_user", user) - - activities = ActivityPub.fetch_public_activities(params) - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - - def public_timeline(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", ["Create", "Announce"]) - |> Map.put("local_only", true) - |> Map.put("blocking_user", user) - - activities = ActivityPub.fetch_public_activities(params) - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - - def friends_timeline(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", ["Create", "Announce", "Follow", "Like"]) - |> Map.put("blocking_user", user) - |> Map.put("user", user) - - activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - - def show_user(conn, params) do - for_user = conn.assigns.user - - with {:ok, shown} <- TwitterAPI.get_user(params), - true <- - User.auth_active?(shown) || - (for_user && (for_user.id == shown.id || User.superuser?(for_user))) do - params = - if for_user do - %{user: shown, for: for_user} - else - %{user: shown} - end - - conn - |> put_view(UserView) - |> render("show.json", params) - else - {:error, msg} -> - bad_request_reply(conn, msg) - - false -> - conn - |> put_status(404) - |> json(%{error: "Unconfirmed user"}) - end - end - - def user_timeline(%{assigns: %{user: user}} = conn, params) do - case TwitterAPI.get_user(user, params) do - {:ok, target_user} -> - # Twitter and ActivityPub use a different name and sense for this parameter. - {include_rts, params} = Map.pop(params, "include_rts") - - params = - case include_rts do - x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true") - _ -> params - end - - activities = ActivityPub.fetch_user_activities(target_user, user, params) - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - - {:error, msg} -> - bad_request_reply(conn, msg) - end - end - - def mentions_timeline(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", ["Create", "Announce", "Follow", "Like"]) - |> Map.put("blocking_user", user) - |> Map.put(:visibility, ~w[unlisted public private]) - - activities = ActivityPub.fetch_activities([user.ap_id], params) - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - - def dm_timeline(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", "Create") - |> Map.put("blocking_user", user) - |> Map.put("user", user) - |> Map.put(:visibility, "direct") - |> Map.put(:order, :desc) - - activities = - ActivityPub.fetch_activities_query([user.ap_id], params) - |> Repo.all() - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - - def notifications(%{assigns: %{user: user}} = conn, params) do - params = - if Map.has_key?(params, "with_muted") do - Map.put(params, :with_muted, params["with_muted"] in [true, "True", "true", "1"]) - else - params - end - - notifications = Notification.for_user(user, params) - - conn - |> put_view(NotificationView) - |> render("notification.json", %{notifications: notifications, for: user}) - end - - def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do - Notification.set_read_up_to(user, latest_id) - - notifications = Notification.for_user(user, params) - - conn - |> put_view(NotificationView) - |> render("notification.json", %{notifications: notifications, for: user}) - end - - def notifications_read(%{assigns: %{user: _user}} = conn, _) do - bad_request_reply(conn, "You need to specify latest_id") - end - - def follow(%{assigns: %{user: user}} = conn, params) do - case TwitterAPI.follow(user, params) do - {:ok, user, followed, _activity} -> - conn - |> put_view(UserView) - |> render("show.json", %{user: followed, for: user}) - - {:error, msg} -> - forbidden_json_reply(conn, msg) - end - end - - def block(%{assigns: %{user: user}} = conn, params) do - case TwitterAPI.block(user, params) do - {:ok, user, blocked} -> - conn - |> put_view(UserView) - |> render("show.json", %{user: blocked, for: user}) - - {:error, msg} -> - forbidden_json_reply(conn, msg) - end - end - - def unblock(%{assigns: %{user: user}} = conn, params) do - case TwitterAPI.unblock(user, params) do - {:ok, user, blocked} -> - conn - |> put_view(UserView) - |> render("show.json", %{user: blocked, for: user}) - - {:error, msg} -> - forbidden_json_reply(conn, msg) - end - end - - def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.delete(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - end - end - - def unfollow(%{assigns: %{user: user}} = conn, params) do - case TwitterAPI.unfollow(user, params) do - {:ok, user, unfollowed} -> - conn - |> put_view(UserView) - |> render("show.json", %{user: unfollowed, for: user}) - - {:error, msg} -> - forbidden_json_reply(conn, msg) - end - end - - def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with %Activity{} = activity <- Activity.get_by_id(id), - true <- Visibility.visible_for_user?(activity, user) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - end - end - - def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with context when is_binary(context) <- Utils.conversation_id_to_context(id), - activities <- - ActivityPub.fetch_activities_for_context(context, %{ - "blocking_user" => user, - "user" => user - }) do - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - end - - @doc """ - Updates metadata of uploaded media object. - Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create). - """ - def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do - object = Repo.get(Object, id) - description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"] - - {conn, status, response_body} = - cond do - !object -> - {halt(conn), :not_found, ""} - - !Object.authorize_mutation(object, user) -> - {halt(conn), :forbidden, "You can only update your own uploads."} - - !is_binary(description) -> - {conn, :not_modified, ""} - - true -> - new_data = Map.put(object.data, "name", description) - - {:ok, _} = - object - |> Object.change(%{data: new_data}) - |> Repo.update() - - {conn, :no_content, ""} - end - - conn - |> put_status(status) - |> json(response_body) - end - - def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do - response = TwitterAPI.upload(media, user) - - conn - |> put_resp_content_type("application/atom+xml") - |> send_resp(200, response) - end - - def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do - response = TwitterAPI.upload(media, user, "json") - - conn - |> json_reply(200, response) - end - - def get_by_id_or_ap_id(id) do - activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id) - - if activity.data["type"] == "Create" do - activity - else - Activity.get_create_by_object_ap_id(activity.data["object"]) - end - end - - def favorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.fav(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - else - _ -> json_reply(conn, 400, Jason.encode!(%{})) - end - end - - def unfavorite(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.unfav(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - else - _ -> json_reply(conn, 400, Jason.encode!(%{})) - end - end - - def retweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.repeat(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - else - _ -> json_reply(conn, 400, Jason.encode!(%{})) - end - end - - def unretweet(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.unrepeat(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - else - _ -> json_reply(conn, 400, Jason.encode!(%{})) - end - end - - def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.pin(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - else - {:error, message} -> bad_request_reply(conn, message) - err -> err - end - end - - def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- TwitterAPI.unpin(user, id) do - conn - |> put_view(ActivityView) - |> render("activity.json", %{activity: activity, for: user}) - else - {:error, message} -> bad_request_reply(conn, message) - err -> err - end - end - - def register(conn, params) do - with {:ok, user} <- TwitterAPI.register_user(params) do - conn - |> put_view(UserView) - |> render("show.json", %{user: user}) - else - {:error, errors} -> - conn - |> json_reply(400, Jason.encode!(errors)) - end - end - - def password_reset(conn, params) do - nickname_or_email = params["email"] || params["nickname"] - - with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do - json_response(conn, :no_content, "") - else - {:error, "unknown user"} -> - send_resp(conn, :not_found, "") - - {:error, _} -> - send_resp(conn, :bad_request, "") - end - end - def confirm_email(conn, %{"user_id" => uid, "token" => token}) do with %User{} = user <- User.get_cached_by_id(uid), true <- user.local, @@ -460,147 +27,6 @@ def confirm_email(conn, %{"user_id" => uid, "token" => token}) do end end - def resend_confirmation_email(conn, params) do - nickname_or_email = params["email"] || params["nickname"] - - with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), - {:ok, _} <- User.try_send_confirmation_email(user) do - conn - |> json_response(:no_content, "") - end - end - - def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do - change = Changeset.change(user, %{avatar: nil}) - {:ok, user} = User.update_and_set_cache(change) - CommonAPI.update(user) - - conn - |> put_view(UserView) - |> render("show.json", %{user: user, for: user}) - end - - def update_avatar(%{assigns: %{user: user}} = conn, params) do - {:ok, object} = ActivityPub.upload(params, type: :avatar) - change = Changeset.change(user, %{avatar: object.data}) - {:ok, user} = User.update_and_set_cache(change) - CommonAPI.update(user) - - conn - |> put_view(UserView) - |> render("show.json", %{user: user, for: user}) - end - - def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do - with new_info <- %{"banner" => %{}}, - info_cng <- User.Info.profile_update(user.info, new_info), - changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), - {:ok, user} <- User.update_and_set_cache(changeset) do - CommonAPI.update(user) - response = %{url: nil} |> Jason.encode!() - - conn - |> json_reply(200, response) - end - end - - def update_banner(%{assigns: %{user: user}} = conn, params) do - with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), - new_info <- %{"banner" => object.data}, - info_cng <- User.Info.profile_update(user.info, new_info), - changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), - {:ok, user} <- User.update_and_set_cache(changeset) do - CommonAPI.update(user) - %{"url" => [%{"href" => href} | _]} = object.data - response = %{url: href} |> Jason.encode!() - - conn - |> json_reply(200, response) - end - end - - def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do - with new_info <- %{"background" => %{}}, - info_cng <- User.Info.profile_update(user.info, new_info), - changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), - {:ok, _user} <- User.update_and_set_cache(changeset) do - response = %{url: nil} |> Jason.encode!() - - conn - |> json_reply(200, response) - end - end - - def update_background(%{assigns: %{user: user}} = conn, params) do - with {:ok, object} <- ActivityPub.upload(params, type: :background), - new_info <- %{"background" => object.data}, - info_cng <- User.Info.profile_update(user.info, new_info), - changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), - {:ok, _user} <- User.update_and_set_cache(changeset) do - %{"url" => [%{"href" => href} | _]} = object.data - response = %{url: href} |> Jason.encode!() - - conn - |> json_reply(200, response) - end - end - - def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => uri}) do - with {:ok, user_map} <- TwitterAPI.get_external_profile(current_user, uri), - response <- Jason.encode!(user_map) do - conn - |> json_reply(200, response) - else - _e -> - conn - |> put_status(404) - |> json(%{error: "Can't find user"}) - end - end - - def followers(%{assigns: %{user: for_user}} = conn, params) do - {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) - - with {:ok, user} <- TwitterAPI.get_user(for_user, params), - {:ok, followers} <- User.get_followers(user, page) do - followers = - cond do - for_user && user.id == for_user.id -> followers - user.info.hide_followers -> [] - true -> followers - end - - conn - |> put_view(UserView) - |> render("index.json", %{users: followers, for: conn.assigns[:user]}) - else - _e -> bad_request_reply(conn, "Can't get followers") - end - end - - def friends(%{assigns: %{user: for_user}} = conn, params) do - {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) - {:ok, export} = Ecto.Type.cast(:boolean, params["all"] || false) - - page = if export, do: nil, else: page - - with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), - {:ok, friends} <- User.get_friends(user, page) do - friends = - cond do - for_user && user.id == for_user.id -> friends - user.info.hide_follows -> [] - true -> friends - end - - conn - |> put_view(UserView) - |> render("index.json", %{users: friends, for: conn.assigns[:user]}) - else - _e -> bad_request_reply(conn, "Can't get friends") - end - end - def oauth_tokens(%{assigns: %{user: user}} = conn, _params) do with oauth_tokens <- Token.get_user_tokens(user) do conn @@ -615,189 +41,6 @@ def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do json_reply(conn, 201, "") end - def blocks(%{assigns: %{user: user}} = conn, _params) do - with blocked_users <- User.blocked_users(user) do - conn - |> put_view(UserView) - |> render("index.json", %{users: blocked_users, for: user}) - end - end - - def friend_requests(conn, params) do - with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), - {:ok, friend_requests} <- User.get_follow_requests(user) do - conn - |> put_view(UserView) - |> render("index.json", %{users: friend_requests, for: conn.assigns[:user]}) - else - _e -> bad_request_reply(conn, "Can't get friend requests") - end - end - - def approve_friend_request(conn, %{"user_id" => uid} = _params) do - with followed <- conn.assigns[:user], - %User{} = follower <- User.get_cached_by_id(uid), - {:ok, follower} <- CommonAPI.accept_follow_request(follower, followed) do - conn - |> put_view(UserView) - |> render("show.json", %{user: follower, for: followed}) - else - e -> bad_request_reply(conn, "Can't approve user: #{inspect(e)}") - end - end - - def deny_friend_request(conn, %{"user_id" => uid} = _params) do - with followed <- conn.assigns[:user], - %User{} = follower <- User.get_cached_by_id(uid), - {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do - conn - |> put_view(UserView) - |> render("show.json", %{user: follower, for: followed}) - else - e -> bad_request_reply(conn, "Can't deny user: #{inspect(e)}") - end - end - - def friends_ids(%{assigns: %{user: user}} = conn, _params) do - with {:ok, friends} <- User.get_friends(user) do - ids = - friends - |> Enum.map(fn x -> x.id end) - |> Jason.encode!() - - json(conn, ids) - else - _e -> bad_request_reply(conn, "Can't get friends") - end - end - - def empty_array(conn, _params) do - json(conn, Jason.encode!([])) - end - - def raw_empty_array(conn, _params) do - json(conn, []) - end - - defp build_info_cng(user, params) do - info_params = - [ - "no_rich_text", - "locked", - "hide_followers", - "hide_follows", - "hide_favorites", - "show_role", - "skip_thread_containment" - ] - |> Enum.reduce(%{}, fn key, res -> - if value = params[key] do - Map.put(res, key, value == "true") - else - res - end - end) - - info_params = - if value = params["default_scope"] do - Map.put(info_params, "default_scope", value) - else - info_params - end - - User.Info.profile_update(user.info, info_params) - end - - defp parse_profile_bio(user, params) do - if bio = params["description"] do - 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 - params - end - end - - def update_profile(%{assigns: %{user: user}} = conn, params) do - params = parse_profile_bio(user, params) - info_cng = build_info_cng(user, params) - - with changeset <- User.update_changeset(user, params), - changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), - {:ok, user} <- User.update_and_set_cache(changeset) do - CommonAPI.update(user) - - conn - |> put_view(UserView) - |> render("user.json", %{user: user, for: user}) - else - error -> - Logger.debug("Can't update user: #{inspect(error)}") - bad_request_reply(conn, "Can't update user") - end - end - - def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do - activities = TwitterAPI.search(user, params) - - conn - |> put_view(ActivityView) - |> render("index.json", %{activities: activities, for: user}) - end - - def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do - users = User.search(query, resolve: true, for_user: user) - - conn - |> put_view(UserView) - |> render("index.json", %{users: users, for: user}) - end - - defp bad_request_reply(conn, error_message) do - json = error_json(conn, error_message) - json_reply(conn, 400, json) - end - - defp json_reply(conn, status, json) do - conn - |> put_resp_content_type("application/json") - |> send_resp(status, json) - end - - defp forbidden_json_reply(conn, error_message) do - json = error_json(conn, error_message) - json_reply(conn, 403, json) - end - - def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn - - def only_if_public_instance(conn, _) do - if Pleroma.Config.get([:instance, :public]) do - conn - else - conn - |> forbidden_json_reply("Invalid credentials.") - |> halt() - end - end - - defp error_json(conn, error_message) do - %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!() - end - def errors(conn, {:param_cast, _}) do conn |> put_status(400) @@ -809,4 +52,10 @@ def errors(conn, _) do |> put_status(500) |> json("Something went wrong") end + + defp json_reply(conn, status, json) do + conn + |> put_resp_content_type("application/json") + |> send_resp(status, json) + end end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs deleted file mode 100644 index 8ef14b4c5..000000000 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ /dev/null @@ -1,2150 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.ControllerTest do - use Pleroma.Web.ConnCase - alias Comeonin.Pbkdf2 - alias Ecto.Changeset - alias Pleroma.Activity - alias Pleroma.Builders.ActivityBuilder - alias Pleroma.Builders.UserBuilder - alias Pleroma.Notification - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.OAuth.Token - alias Pleroma.Web.TwitterAPI.ActivityView - alias Pleroma.Web.TwitterAPI.Controller - alias Pleroma.Web.TwitterAPI.NotificationView - alias Pleroma.Web.TwitterAPI.TwitterAPI - alias Pleroma.Web.TwitterAPI.UserView - - import Mock - import Pleroma.Factory - import Swoosh.TestAssertions - - @banner "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - - describe "POST /api/account/update_profile_banner" do - test "it updates the banner", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post(authenticated_twitter_api__path(conn, :update_banner), %{"banner" => @banner}) - |> json_response(200) - - user = refresh_record(user) - assert user.info.banner["type"] == "Image" - end - - test "profile banner can be reset", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post(authenticated_twitter_api__path(conn, :update_banner), %{"banner" => ""}) - |> json_response(200) - - user = refresh_record(user) - assert user.info.banner == %{} - end - end - - describe "POST /api/qvitter/update_background_image" do - test "it updates the background", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post(authenticated_twitter_api__path(conn, :update_background), %{"img" => @banner}) - |> json_response(200) - - user = refresh_record(user) - assert user.info.background["type"] == "Image" - end - - test "background can be reset", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post(authenticated_twitter_api__path(conn, :update_background), %{"img" => ""}) - |> json_response(200) - - user = refresh_record(user) - assert user.info.background == %{} - end - end - - describe "POST /api/account/verify_credentials" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/account/verify_credentials.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: user} do - response = - conn - |> with_credentials(user.nickname, "test") - |> post("/api/account/verify_credentials.json") - |> json_response(200) - - assert response == - UserView.render("show.json", %{user: user, token: response["token"], for: user}) - end - end - - describe "POST /statuses/update.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/statuses/update.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: user} do - conn_with_creds = conn |> with_credentials(user.nickname, "test") - request_path = "/api/statuses/update.json" - - error_response = %{ - "request" => request_path, - "error" => "Client must provide a 'status' parameter with a value." - } - - conn = - conn_with_creds - |> post(request_path) - - assert json_response(conn, 400) == error_response - - conn = - conn_with_creds - |> post(request_path, %{status: ""}) - - assert json_response(conn, 400) == error_response - - conn = - conn_with_creds - |> post(request_path, %{status: " "}) - - assert json_response(conn, 400) == error_response - - # we post with visibility private in order to avoid triggering relay - conn = - conn_with_creds - |> post(request_path, %{status: "Nice meme.", visibility: "private"}) - - assert json_response(conn, 200) == - ActivityView.render("activity.json", %{ - activity: Repo.one(Activity), - user: user, - for: user - }) - end - end - - describe "GET /statuses/public_timeline.json" do - setup [:valid_user] - clear_config([:instance, :public]) - - test "returns statuses", %{conn: conn} do - user = insert(:user) - activities = ActivityBuilder.insert_list(30, %{}, %{user: user}) - ActivityBuilder.insert_list(10, %{}, %{user: user}) - since_id = List.last(activities).id - - conn = - conn - |> get("/api/statuses/public_timeline.json", %{since_id: since_id}) - - response = json_response(conn, 200) - - assert length(response) == 10 - end - - test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do - Pleroma.Config.put([:instance, :public], false) - - conn - |> get("/api/statuses/public_timeline.json") - |> json_response(403) - end - - test "returns 200 to authenticated request when the instance is not public", - %{conn: conn, user: user} do - Pleroma.Config.put([:instance, :public], false) - - conn - |> with_credentials(user.nickname, "test") - |> get("/api/statuses/public_timeline.json") - |> json_response(200) - end - - test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do - conn - |> get("/api/statuses/public_timeline.json") - |> json_response(200) - end - - test "returns 200 to authenticated request when the instance is public", - %{conn: conn, user: user} do - conn - |> with_credentials(user.nickname, "test") - |> get("/api/statuses/public_timeline.json") - |> json_response(200) - end - - test_with_mock "treats user as unauthenticated if `assigns[:token]` is present but lacks `read` permission", - Controller, - [:passthrough], - [] do - token = insert(:oauth_token, scopes: ["write"]) - - build_conn() - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/statuses/public_timeline.json") - |> json_response(200) - - assert called(Controller.public_timeline(%{assigns: %{user: nil}}, :_)) - end - end - - describe "GET /statuses/public_and_external_timeline.json" do - setup [:valid_user] - clear_config([:instance, :public]) - - test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do - Pleroma.Config.put([:instance, :public], false) - - conn - |> get("/api/statuses/public_and_external_timeline.json") - |> json_response(403) - end - - test "returns 200 to authenticated request when the instance is not public", - %{conn: conn, user: user} do - Pleroma.Config.put([:instance, :public], false) - - conn - |> with_credentials(user.nickname, "test") - |> get("/api/statuses/public_and_external_timeline.json") - |> json_response(200) - end - - test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do - conn - |> get("/api/statuses/public_and_external_timeline.json") - |> json_response(200) - end - - test "returns 200 to authenticated request when the instance is public", - %{conn: conn, user: user} do - conn - |> with_credentials(user.nickname, "test") - |> get("/api/statuses/public_and_external_timeline.json") - |> json_response(200) - end - end - - describe "GET /statuses/show/:id.json" do - test "returns one status", %{conn: conn} do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey!"}) - actor = User.get_cached_by_ap_id(activity.data["actor"]) - - conn = - conn - |> get("/api/statuses/show/#{activity.id}.json") - - response = json_response(conn, 200) - - assert response == ActivityView.render("activity.json", %{activity: activity, user: actor}) - end - end - - describe "GET /users/show.json" do - test "gets user with screen_name", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> get("/api/users/show.json", %{"screen_name" => user.nickname}) - - response = json_response(conn, 200) - - assert response["id"] == user.id - end - - test "gets user with user_id", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> get("/api/users/show.json", %{"user_id" => user.id}) - - response = json_response(conn, 200) - - assert response["id"] == user.id - end - - test "gets a user for a logged in user", %{conn: conn} do - user = insert(:user) - logged_in = insert(:user) - - {:ok, logged_in, user, _activity} = TwitterAPI.follow(logged_in, %{"user_id" => user.id}) - - conn = - conn - |> with_credentials(logged_in.nickname, "test") - |> get("/api/users/show.json", %{"user_id" => user.id}) - - response = json_response(conn, 200) - - assert response["following"] == true - end - end - - describe "GET /statusnet/conversation/:id.json" do - test "returns the statuses in the conversation", %{conn: conn} do - {:ok, _user} = UserBuilder.insert() - {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) - {:ok, _activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) - {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) - - conn = - conn - |> get("/api/statusnet/conversation/#{activity.data["context_id"]}.json") - - response = json_response(conn, 200) - - assert length(response) == 2 - end - end - - describe "GET /statuses/friends_timeline.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = get(conn, "/api/statuses/friends_timeline.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - user = insert(:user) - - activities = - ActivityBuilder.insert_list(30, %{"to" => [User.ap_followers(user)]}, %{user: user}) - - returned_activities = - ActivityBuilder.insert_list(10, %{"to" => [User.ap_followers(user)]}, %{user: user}) - - other_user = insert(:user) - ActivityBuilder.insert_list(10, %{}, %{user: other_user}) - since_id = List.last(activities).id - - current_user = - Changeset.change(current_user, following: [User.ap_followers(user)]) - |> Repo.update!() - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/friends_timeline.json", %{since_id: since_id}) - - response = json_response(conn, 200) - - assert length(response) == 10 - - assert response == - Enum.map(returned_activities, fn activity -> - ActivityView.render("activity.json", %{ - activity: activity, - user: User.get_cached_by_ap_id(activity.data["actor"]), - for: current_user - }) - end) - end - end - - describe "GET /statuses/dm_timeline.json" do - test "it show direct messages", %{conn: conn} do - user_one = insert(:user) - user_two = insert(:user) - - {:ok, user_two} = User.follow(user_two, user_one) - - {:ok, direct} = - CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" - }) - - {:ok, direct_two} = - CommonAPI.post(user_two, %{ - "status" => "Hi @#{user_one.nickname}!", - "visibility" => "direct" - }) - - {:ok, _follower_only} = - CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "private" - }) - - # Only direct should be visible here - res_conn = - conn - |> assign(:user, user_two) - |> get("/api/statuses/dm_timeline.json") - - [status, status_two] = json_response(res_conn, 200) - assert status["id"] == direct_two.id - assert status_two["id"] == direct.id - end - - test "doesn't include DMs from blocked users", %{conn: conn} do - blocker = insert(:user) - blocked = insert(:user) - user = insert(:user) - {:ok, blocker} = User.block(blocker, blocked) - - {:ok, _blocked_direct} = - CommonAPI.post(blocked, %{ - "status" => "Hi @#{blocker.nickname}!", - "visibility" => "direct" - }) - - {:ok, direct} = - CommonAPI.post(user, %{ - "status" => "Hi @#{blocker.nickname}!", - "visibility" => "direct" - }) - - res_conn = - conn - |> assign(:user, blocker) - |> get("/api/statuses/dm_timeline.json") - - [status] = json_response(res_conn, 200) - assert status["id"] == direct.id - end - end - - describe "GET /statuses/mentions.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = get(conn, "/api/statuses/mentions.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - {:ok, activity} = - CommonAPI.post(current_user, %{ - "status" => "why is tenshi eating a corndog so cute?", - "visibility" => "public" - }) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/mentions.json") - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{ - user: current_user, - for: current_user, - activity: activity - }) - end - - test "does not show DMs in mentions timeline", %{conn: conn, user: current_user} do - {:ok, _activity} = - CommonAPI.post(current_user, %{ - "status" => "Have you guys ever seen how cute tenshi eating a corndog is?", - "visibility" => "direct" - }) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/mentions.json") - - response = json_response(conn, 200) - - assert Enum.empty?(response) - end - end - - describe "GET /api/qvitter/statuses/notifications.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = get(conn, "/api/qvitter/statuses/notifications.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - other_user = insert(:user) - - {:ok, _activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/qvitter/statuses/notifications.json") - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert response == - NotificationView.render("notification.json", %{ - notifications: Notification.for_user(current_user), - for: current_user - }) - end - - test "muted user", %{conn: conn, user: current_user} do - other_user = insert(:user) - - {:ok, current_user} = User.mute(current_user, other_user) - - {:ok, _activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/qvitter/statuses/notifications.json") - - assert json_response(conn, 200) == [] - end - - test "muted user with with_muted parameter", %{conn: conn, user: current_user} do - other_user = insert(:user) - - {:ok, current_user} = User.mute(current_user, other_user) - - {:ok, _activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/qvitter/statuses/notifications.json", %{"with_muted" => "true"}) - - assert length(json_response(conn, 200)) == 1 - end - end - - describe "POST /api/qvitter/statuses/notifications/read" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567}) - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials, without any params", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/qvitter/statuses/notifications/read") - - assert json_response(conn, 400) == %{ - "error" => "You need to specify latest_id", - "request" => "/api/qvitter/statuses/notifications/read" - } - end - - test "with credentials, with params", %{conn: conn, user: current_user} do - other_user = insert(:user) - - {:ok, _activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) - - response_conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/qvitter/statuses/notifications.json") - - [notification] = response = json_response(response_conn, 200) - - assert length(response) == 1 - - assert notification["is_seen"] == 0 - - response_conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]}) - - [notification] = response = json_response(response_conn, 200) - - assert length(response) == 1 - - assert notification["is_seen"] == 1 - end - end - - describe "GET /statuses/user_timeline.json" do - setup [:valid_user] - - test "without any params", %{conn: conn} do - conn = get(conn, "/api/statuses/user_timeline.json") - - assert json_response(conn, 400) == %{ - "error" => "You need to specify screen_name or user_id", - "request" => "/api/statuses/user_timeline.json" - } - end - - test "with user_id", %{conn: conn} do - user = insert(:user) - {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: user}) - - conn = get(conn, "/api/statuses/user_timeline.json", %{"user_id" => user.id}) - response = json_response(conn, 200) - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{user: user, activity: activity}) - end - - test "with screen_name", %{conn: conn} do - user = insert(:user) - {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: user}) - - conn = get(conn, "/api/statuses/user_timeline.json", %{"screen_name" => user.nickname}) - response = json_response(conn, 200) - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{user: user, activity: activity}) - end - - test "with credentials", %{conn: conn, user: current_user} do - {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: current_user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json") - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{ - user: current_user, - for: current_user, - activity: activity - }) - end - - test "with credentials with user_id", %{conn: conn, user: current_user} do - user = insert(:user) - {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json", %{"user_id" => user.id}) - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{user: user, activity: activity}) - end - - test "with credentials screen_name", %{conn: conn, user: current_user} do - user = insert(:user) - {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json", %{"screen_name" => user.nickname}) - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{user: user, activity: activity}) - end - - test "with credentials with user_id, excluding RTs", %{conn: conn, user: current_user} do - user = insert(:user) - {:ok, activity} = ActivityBuilder.insert(%{"id" => 1, "type" => "Create"}, %{user: user}) - {:ok, _} = ActivityBuilder.insert(%{"id" => 2, "type" => "Announce"}, %{user: user}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json", %{ - "user_id" => user.id, - "include_rts" => "false" - }) - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{user: user, activity: activity}) - - conn = - conn - |> get("/api/statuses/user_timeline.json", %{"user_id" => user.id, "include_rts" => "0"}) - - response = json_response(conn, 200) - - assert length(response) == 1 - - assert Enum.at(response, 0) == - ActivityView.render("activity.json", %{user: user, activity: activity}) - end - end - - describe "POST /friendships/create.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/friendships/create.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - followed = insert(:user) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/friendships/create.json", %{user_id: followed.id}) - - current_user = User.get_cached_by_id(current_user.id) - assert User.ap_followers(followed) in current_user.following - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: followed, for: current_user}) - end - - test "for restricted account", %{conn: conn, user: current_user} do - followed = insert(:user, info: %User.Info{locked: true}) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/friendships/create.json", %{user_id: followed.id}) - - current_user = User.get_cached_by_id(current_user.id) - followed = User.get_cached_by_id(followed.id) - - refute User.ap_followers(followed) in current_user.following - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: followed, for: current_user}) - end - end - - describe "POST /friendships/destroy.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/friendships/destroy.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - followed = insert(:user) - - {:ok, current_user} = User.follow(current_user, followed) - assert User.ap_followers(followed) in current_user.following - ActivityPub.follow(current_user, followed) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/friendships/destroy.json", %{user_id: followed.id}) - - current_user = User.get_cached_by_id(current_user.id) - assert current_user.following == [current_user.ap_id] - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: followed, for: current_user}) - end - end - - describe "POST /blocks/create.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/blocks/create.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - blocked = insert(:user) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/blocks/create.json", %{user_id: blocked.id}) - - current_user = User.get_cached_by_id(current_user.id) - assert User.blocks?(current_user, blocked) - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: blocked, for: current_user}) - end - end - - describe "POST /blocks/destroy.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/blocks/destroy.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - blocked = insert(:user) - - {:ok, current_user, blocked} = TwitterAPI.block(current_user, %{"user_id" => blocked.id}) - assert User.blocks?(current_user, blocked) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/blocks/destroy.json", %{user_id: blocked.id}) - - current_user = User.get_cached_by_id(current_user.id) - assert current_user.info.blocks == [] - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: blocked, for: current_user}) - end - end - - describe "GET /help/test.json" do - test "returns \"ok\"", %{conn: conn} do - conn = get(conn, "/api/help/test.json") - assert json_response(conn, 200) == "ok" - end - end - - describe "POST /api/qvitter/update_avatar.json" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/qvitter/update_avatar.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - avatar_image = File.read!("test/fixtures/avatar_data_uri") - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/qvitter/update_avatar.json", %{img: avatar_image}) - - current_user = User.get_cached_by_id(current_user.id) - assert is_map(current_user.avatar) - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: current_user, for: current_user}) - end - - test "user avatar can be reset", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/qvitter/update_avatar.json", %{img: ""}) - - current_user = User.get_cached_by_id(current_user.id) - assert current_user.avatar == nil - - assert json_response(conn, 200) == - UserView.render("show.json", %{user: current_user, for: current_user}) - end - end - - describe "GET /api/qvitter/mutes.json" do - setup [:valid_user] - - test "unimplemented mutes without valid credentials", %{conn: conn} do - conn = get(conn, "/api/qvitter/mutes.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "unimplemented mutes with credentials", %{conn: conn, user: current_user} do - response = - conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/qvitter/mutes.json") - |> json_response(200) - - assert [] = response - end - end - - describe "POST /api/favorites/create/:id" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - note_activity = insert(:note_activity) - conn = post(conn, "/api/favorites/create/#{note_activity.id}.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - note_activity = insert(:note_activity) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/favorites/create/#{note_activity.id}.json") - - assert json_response(conn, 200) - end - - test "with credentials, invalid param", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/favorites/create/wrong.json") - - assert json_response(conn, 400) - end - - test "with credentials, invalid activity", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/favorites/create/1.json") - - assert json_response(conn, 400) - end - end - - describe "POST /api/favorites/destroy/:id" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - note_activity = insert(:note_activity) - conn = post(conn, "/api/favorites/destroy/#{note_activity.id}.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - ActivityPub.like(current_user, object) - - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/favorites/destroy/#{note_activity.id}.json") - - assert json_response(conn, 200) - end - end - - describe "POST /api/statuses/retweet/:id" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - note_activity = insert(:note_activity) - conn = post(conn, "/api/statuses/retweet/#{note_activity.id}.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - note_activity = insert(:note_activity) - - request_path = "/api/statuses/retweet/#{note_activity.id}.json" - - response = - conn - |> with_credentials(current_user.nickname, "test") - |> post(request_path) - - activity = Activity.get_by_id(note_activity.id) - activity_user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - assert json_response(response, 200) == - ActivityView.render("activity.json", %{ - user: activity_user, - for: current_user, - activity: activity - }) - end - end - - describe "POST /api/statuses/unretweet/:id" do - setup [:valid_user] - - test "without valid credentials", %{conn: conn} do - note_activity = insert(:note_activity) - conn = post(conn, "/api/statuses/unretweet/#{note_activity.id}.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: current_user} do - note_activity = insert(:note_activity) - - request_path = "/api/statuses/retweet/#{note_activity.id}.json" - - _response = - conn - |> with_credentials(current_user.nickname, "test") - |> post(request_path) - - request_path = String.replace(request_path, "retweet", "unretweet") - - response = - conn - |> with_credentials(current_user.nickname, "test") - |> post(request_path) - - activity = Activity.get_by_id(note_activity.id) - activity_user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - assert json_response(response, 200) == - ActivityView.render("activity.json", %{ - user: activity_user, - for: current_user, - activity: activity - }) - end - end - - describe "POST /api/account/register" do - test "it creates a new user", %{conn: conn} do - data = %{ - "nickname" => "lain", - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "bio" => "close the world.", - "password" => "bear", - "confirm" => "bear" - } - - conn = - conn - |> post("/api/account/register", data) - - user = json_response(conn, 200) - - fetched_user = User.get_cached_by_nickname("lain") - assert user == UserView.render("show.json", %{user: fetched_user}) - end - - test "it returns errors on a problem", %{conn: conn} do - data = %{ - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "bio" => "close the world.", - "password" => "bear", - "confirm" => "bear" - } - - conn = - conn - |> post("/api/account/register", data) - - errors = json_response(conn, 400) - - assert is_binary(errors["error"]) - end - end - - describe "POST /api/account/password_reset, with valid parameters" do - setup %{conn: conn} do - user = insert(:user) - conn = post(conn, "/api/account/password_reset?email=#{user.email}") - %{conn: conn, user: user} - end - - test "it returns 204", %{conn: conn} do - assert json_response(conn, :no_content) - end - - test "it creates a PasswordResetToken record for user", %{user: user} do - token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) - assert token_record - end - - test "it sends an email to user", %{user: user} do - token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) - - email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token) - notify_email = Pleroma.Config.get([:instance, :notify_email]) - instance_name = Pleroma.Config.get([:instance, :name]) - - assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - end - - describe "POST /api/account/password_reset, with invalid parameters" do - setup [:valid_user] - - test "it returns 404 when user is not found", %{conn: conn, user: user} do - conn = post(conn, "/api/account/password_reset?email=nonexisting_#{user.email}") - assert conn.status == 404 - assert conn.resp_body == "" - end - - test "it returns 400 when user is not local", %{conn: conn, user: user} do - {:ok, user} = Repo.update(Changeset.change(user, local: false)) - conn = post(conn, "/api/account/password_reset?email=#{user.email}") - assert conn.status == 400 - assert conn.resp_body == "" - end - end - - describe "GET /api/account/confirm_email/:id/:token" do - setup do - user = insert(:user) - info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true) - - {:ok, user} = - user - |> Changeset.change() - |> Changeset.put_embed(:info, info_change) - |> Repo.update() - - assert user.info.confirmation_pending - - [user: user] - end - - test "it redirects to root url", %{conn: conn, user: user} do - conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}") - - assert 302 == conn.status - end - - test "it confirms the user account", %{conn: conn, user: user} do - get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}") - - user = User.get_cached_by_id(user.id) - - refute user.info.confirmation_pending - refute user.info.confirmation_token - end - - test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do - conn = get(conn, "/api/account/confirm_email/0/#{user.info.confirmation_token}") - - assert 500 == conn.status - end - - test "it returns 500 if token is invalid", %{conn: conn, user: user} do - conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token") - - assert 500 == conn.status - end - end - - describe "POST /api/account/resend_confirmation_email" do - setup do - user = insert(:user) - info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true) - - {:ok, user} = - user - |> Changeset.change() - |> Changeset.put_embed(:info, info_change) - |> Repo.update() - - assert user.info.confirmation_pending - - [user: user] - end - - clear_config([:instance, :account_activation_required]) do - Pleroma.Config.put([:instance, :account_activation_required], true) - end - - test "it returns 204 No Content", %{conn: conn, user: user} do - conn - |> assign(:user, user) - |> post("/api/account/resend_confirmation_email?email=#{user.email}") - |> json_response(:no_content) - end - - test "it sends confirmation email", %{conn: conn, user: user} do - conn - |> assign(:user, user) - |> post("/api/account/resend_confirmation_email?email=#{user.email}") - - email = Pleroma.Emails.UserEmail.account_confirmation_email(user) - notify_email = Pleroma.Config.get([:instance, :notify_email]) - instance_name = Pleroma.Config.get([:instance, :name]) - - assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - end - - describe "GET /api/externalprofile/show" do - test "it returns the user", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> get("/api/externalprofile/show", %{profileurl: other_user.ap_id}) - - assert json_response(conn, 200) == UserView.render("show.json", %{user: other_user}) - end - end - - describe "GET /api/statuses/followers" do - test "it returns a user's followers", %{conn: conn} do - user = insert(:user) - follower_one = insert(:user) - follower_two = insert(:user) - _not_follower = insert(:user) - - {:ok, follower_one} = User.follow(follower_one, user) - {:ok, follower_two} = User.follow(follower_two, user) - - conn = - conn - |> assign(:user, user) - |> get("/api/statuses/followers") - - expected = UserView.render("index.json", %{users: [follower_one, follower_two], for: user}) - result = json_response(conn, 200) - assert Enum.sort(expected) == Enum.sort(result) - end - - test "it returns 20 followers per page", %{conn: conn} do - user = insert(:user) - followers = insert_list(21, :user) - - Enum.each(followers, fn follower -> - User.follow(follower, user) - end) - - res_conn = - conn - |> assign(:user, user) - |> get("/api/statuses/followers") - - result = json_response(res_conn, 200) - assert length(result) == 20 - - res_conn = - conn - |> assign(:user, user) - |> get("/api/statuses/followers?page=2") - - result = json_response(res_conn, 200) - assert length(result) == 1 - end - - test "it returns a given user's followers with user_id", %{conn: conn} do - user = insert(:user) - follower_one = insert(:user) - follower_two = insert(:user) - not_follower = insert(:user) - - {:ok, follower_one} = User.follow(follower_one, user) - {:ok, follower_two} = User.follow(follower_two, user) - - conn = - conn - |> assign(:user, not_follower) - |> get("/api/statuses/followers", %{"user_id" => user.id}) - - assert MapSet.equal?( - MapSet.new(json_response(conn, 200)), - MapSet.new( - UserView.render("index.json", %{ - users: [follower_one, follower_two], - for: not_follower - }) - ) - ) - end - - test "it returns empty when hide_followers is set to true", %{conn: conn} do - user = insert(:user, %{info: %{hide_followers: true}}) - follower_one = insert(:user) - follower_two = insert(:user) - not_follower = insert(:user) - - {:ok, _follower_one} = User.follow(follower_one, user) - {:ok, _follower_two} = User.follow(follower_two, user) - - response = - conn - |> assign(:user, not_follower) - |> get("/api/statuses/followers", %{"user_id" => user.id}) - |> json_response(200) - - assert [] == response - end - - test "it returns the followers when hide_followers is set to true if requested by the user themselves", - %{ - conn: conn - } do - user = insert(:user, %{info: %{hide_followers: true}}) - follower_one = insert(:user) - follower_two = insert(:user) - _not_follower = insert(:user) - - {:ok, _follower_one} = User.follow(follower_one, user) - {:ok, _follower_two} = User.follow(follower_two, user) - - conn = - conn - |> assign(:user, user) - |> get("/api/statuses/followers", %{"user_id" => user.id}) - - refute [] == json_response(conn, 200) - end - end - - describe "GET /api/statuses/blocks" do - test "it returns the list of users blocked by requester", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.block(user, other_user) - - conn = - conn - |> assign(:user, user) - |> get("/api/statuses/blocks") - - expected = UserView.render("index.json", %{users: [other_user], for: user}) - result = json_response(conn, 200) - assert Enum.sort(expected) == Enum.sort(result) - end - end - - describe "GET /api/statuses/friends" do - test "it returns the logged in user's friends", %{conn: conn} do - user = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - _not_followed = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - - conn = - conn - |> assign(:user, user) - |> get("/api/statuses/friends") - - expected = UserView.render("index.json", %{users: [followed_one, followed_two], for: user}) - result = json_response(conn, 200) - assert Enum.sort(expected) == Enum.sort(result) - end - - test "it returns 20 friends per page, except if 'export' is set to true", %{conn: conn} do - user = insert(:user) - followeds = insert_list(21, :user) - - {:ok, user} = - Enum.reduce(followeds, {:ok, user}, fn followed, {:ok, user} -> - User.follow(user, followed) - end) - - res_conn = - conn - |> assign(:user, user) - |> get("/api/statuses/friends") - - result = json_response(res_conn, 200) - assert length(result) == 20 - - res_conn = - conn - |> assign(:user, user) - |> get("/api/statuses/friends", %{page: 2}) - - result = json_response(res_conn, 200) - assert length(result) == 1 - - res_conn = - conn - |> assign(:user, user) - |> get("/api/statuses/friends", %{all: true}) - - result = json_response(res_conn, 200) - assert length(result) == 21 - end - - test "it returns a given user's friends with user_id", %{conn: conn} do - user = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - _not_followed = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - - conn = - conn - |> assign(:user, user) - |> get("/api/statuses/friends", %{"user_id" => user.id}) - - assert MapSet.equal?( - MapSet.new(json_response(conn, 200)), - MapSet.new( - UserView.render("index.json", %{users: [followed_one, followed_two], for: user}) - ) - ) - end - - test "it returns empty when hide_follows is set to true", %{conn: conn} do - user = insert(:user, %{info: %{hide_follows: true}}) - followed_one = insert(:user) - followed_two = insert(:user) - not_followed = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - - conn = - conn - |> assign(:user, not_followed) - |> get("/api/statuses/friends", %{"user_id" => user.id}) - - assert [] == json_response(conn, 200) - end - - test "it returns friends when hide_follows is set to true if the user themselves request it", - %{ - conn: conn - } do - user = insert(:user, %{info: %{hide_follows: true}}) - followed_one = insert(:user) - followed_two = insert(:user) - _not_followed = insert(:user) - - {:ok, _user} = User.follow(user, followed_one) - {:ok, _user} = User.follow(user, followed_two) - - response = - conn - |> assign(:user, user) - |> get("/api/statuses/friends", %{"user_id" => user.id}) - |> json_response(200) - - refute [] == response - end - - test "it returns a given user's friends with screen_name", %{conn: conn} do - user = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - _not_followed = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - - conn = - conn - |> assign(:user, user) - |> get("/api/statuses/friends", %{"screen_name" => user.nickname}) - - assert MapSet.equal?( - MapSet.new(json_response(conn, 200)), - MapSet.new( - UserView.render("index.json", %{users: [followed_one, followed_two], for: user}) - ) - ) - end - end - - describe "GET /friends/ids" do - test "it returns a user's friends", %{conn: conn} do - user = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - _not_followed = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - - conn = - conn - |> assign(:user, user) - |> get("/api/friends/ids") - - expected = [followed_one.id, followed_two.id] - - assert MapSet.equal?( - MapSet.new(Poison.decode!(json_response(conn, 200))), - MapSet.new(expected) - ) - end - end - - describe "POST /api/account/update_profile.json" do - test "it updates a user's profile", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "name" => "new name", - "description" => "hi @#{user2.nickname}" - }) - - user = Repo.get!(User, user.id) - assert user.name == "new name" - - assert user.bio == - "hi @#{user2.nickname}" - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) - end - - test "it sets and un-sets hide_follows", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "hide_follows" => "true" - }) - - user = Repo.get!(User, user.id) - assert user.info.hide_follows == true - - conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "hide_follows" => "false" - }) - - user = refresh_record(user) - assert user.info.hide_follows == false - assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) - end - - test "it sets and un-sets hide_followers", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "hide_followers" => "true" - }) - - user = Repo.get!(User, user.id) - assert user.info.hide_followers == true - - conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "hide_followers" => "false" - }) - - user = Repo.get!(User, user.id) - assert user.info.hide_followers == false - assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) - end - - test "it sets and un-sets show_role", %{conn: conn} do - user = insert(:user) - - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "show_role" => "true" - }) - - user = Repo.get!(User, user.id) - assert user.info.show_role == true - - conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "show_role" => "false" - }) - - user = Repo.get!(User, user.id) - assert user.info.show_role == false - assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) - end - - test "it sets and un-sets skip_thread_containment", %{conn: conn} do - user = insert(:user) - - response = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{"skip_thread_containment" => "true"}) - |> json_response(200) - - assert response["pleroma"]["skip_thread_containment"] == true - user = refresh_record(user) - assert user.info.skip_thread_containment - - response = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{"skip_thread_containment" => "false"}) - |> json_response(200) - - assert response["pleroma"]["skip_thread_containment"] == false - refute refresh_record(user).info.skip_thread_containment - end - - test "it locks an account", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "locked" => "true" - }) - - user = Repo.get!(User, user.id) - assert user.info.locked == true - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) - end - - test "it unlocks an account", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "locked" => "false" - }) - - user = Repo.get!(User, user.id) - assert user.info.locked == false - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) - end - - # Broken before the change to class="emoji" and non- 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 moominmamma Base.encode64("#{username}:#{password}") - put_req_header(conn, "authorization", header_content) - end - - describe "GET /api/search.json" do - test "it returns search results", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) - {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - - conn = - conn - |> get("/api/search.json", %{"q" => "2hu", "page" => "1", "rpp" => "1"}) - - assert [status] = json_response(conn, 200) - assert status["id"] == activity.id - end - end - - describe "GET /api/statusnet/tags/timeline/:tag.json" do - test "it returns the tags timeline", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about #2hu"}) - {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - - conn = - conn - |> get("/api/statusnet/tags/timeline/2hu.json") - - assert [status] = json_response(conn, 200) - assert status["id"] == activity.id - end - end - - test "Convert newlines to
in bio", %{conn: conn} do - user = insert(:user) - - _conn = - conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{ - "description" => "Hello,\r\nWorld! I\n am a test." - }) - - user = Repo.get!(User, user.id) - assert user.bio == "Hello,
World! I
am a test." - end - - describe "POST /api/pleroma/change_password" do - setup [:valid_user] - - test "without credentials", %{conn: conn} do - conn = post(conn, "/api/pleroma/change_password") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials and invalid password", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ - "password" => "hi", - "new_password" => "newpass", - "new_password_confirmation" => "newpass" - }) - - assert json_response(conn, 200) == %{"error" => "Invalid password."} - end - - test "with credentials, valid password and new password and confirmation not matching", %{ - conn: conn, - user: current_user - } do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "newpass", - "new_password_confirmation" => "notnewpass" - }) - - assert json_response(conn, 200) == %{ - "error" => "New password does not match confirmation." - } - end - - test "with credentials, valid password and invalid new password", %{ - conn: conn, - user: current_user - } do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "", - "new_password_confirmation" => "" - }) - - assert json_response(conn, 200) == %{ - "error" => "New password can't be blank." - } - end - - test "with credentials, valid password and matching new password and confirmation", %{ - conn: conn, - user: current_user - } do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "newpass", - "new_password_confirmation" => "newpass" - }) - - assert json_response(conn, 200) == %{"status" => "success"} - fetched_user = User.get_cached_by_id(current_user.id) - assert Pbkdf2.checkpw("newpass", fetched_user.password_hash) == true - end - end - - describe "POST /api/pleroma/delete_account" do - setup [:valid_user] - - test "without credentials", %{conn: conn} do - conn = post(conn, "/api/pleroma/delete_account") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials and invalid password", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/delete_account", %{"password" => "hi"}) - - assert json_response(conn, 200) == %{"error" => "Invalid password."} - end - - test "with credentials and valid password", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/delete_account", %{"password" => "test"}) - - assert json_response(conn, 200) == %{"status" => "success"} - # Wait a second for the started task to end - :timer.sleep(1000) - end - end - - describe "GET /api/pleroma/friend_requests" do - test "it lists friend requests" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = ActivityPub.follow(other_user, user) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - assert User.following?(other_user, user) == false - - conn = - build_conn() - |> assign(:user, user) - |> get("/api/pleroma/friend_requests") - - assert [relationship] = json_response(conn, 200) - assert other_user.id == relationship["id"] - end - - test "requires 'read' permission", %{conn: conn} do - token1 = insert(:oauth_token, scopes: ["write"]) - token2 = insert(:oauth_token, scopes: ["read"]) - - for token <- [token1, token2] do - conn = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/pleroma/friend_requests") - - if token == token1 do - assert %{"error" => "Insufficient permissions: read."} == json_response(conn, 403) - else - assert json_response(conn, 200) - end - end - end - end - - describe "POST /api/pleroma/friendships/approve" do - test "it approves a friend request" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = ActivityPub.follow(other_user, user) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - assert User.following?(other_user, user) == false - - conn = - build_conn() - |> assign(:user, user) - |> post("/api/pleroma/friendships/approve", %{"user_id" => other_user.id}) - - assert relationship = json_response(conn, 200) - assert other_user.id == relationship["id"] - assert relationship["follows_you"] == true - end - end - - describe "POST /api/pleroma/friendships/deny" do - test "it denies a friend request" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = ActivityPub.follow(other_user, user) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - assert User.following?(other_user, user) == false - - conn = - build_conn() - |> assign(:user, user) - |> post("/api/pleroma/friendships/deny", %{"user_id" => other_user.id}) - - assert relationship = json_response(conn, 200) - assert other_user.id == relationship["id"] - assert relationship["follows_you"] == false - end - end - - describe "GET /api/pleroma/search_user" do - test "it returns users, ordered by similarity", %{conn: conn} do - user = insert(:user, %{name: "eal"}) - user_two = insert(:user, %{name: "eal me"}) - _user_three = insert(:user, %{name: "zzz"}) - - resp = - conn - |> get(twitter_api_search__path(conn, :search_user), query: "eal me") - |> json_response(200) - - assert length(resp) == 2 - assert [user_two.id, user.id] == Enum.map(resp, fn %{"id" => id} -> id end) - end - end - - describe "POST /api/media/upload" do - setup context do - Pleroma.DataCase.ensure_local_uploader(context) - end - - test "it performs the upload and sets `data[actor]` with AP id of uploader user", %{ - conn: conn - } do - user = insert(:user) - - upload_filename = "test/fixtures/image_tmp.jpg" - File.cp!("test/fixtures/image.jpg", upload_filename) - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname(upload_filename), - filename: "image.jpg" - } - - response = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/octet-stream") - |> post("/api/media/upload", %{ - "media" => file - }) - |> json_response(:ok) - - assert response["media_id"] - object = Repo.get(Object, response["media_id"]) - assert object - assert object.data["actor"] == User.ap_id(user) - end - end - - describe "POST /api/media/metadata/create" do - setup do - object = insert(:note) - user = User.get_cached_by_ap_id(object.data["actor"]) - %{object: object, user: user} - end - - test "it returns :forbidden status on attempt to modify someone else's upload", %{ - conn: conn, - object: object - } do - initial_description = object.data["name"] - another_user = insert(:user) - - conn - |> assign(:user, another_user) - |> post("/api/media/metadata/create", %{"media_id" => object.id}) - |> json_response(:forbidden) - - object = Repo.get(Object, object.id) - assert object.data["name"] == initial_description - end - - test "it updates `data[name]` of referenced Object with provided value", %{ - conn: conn, - object: object, - user: user - } do - description = "Informative description of the image. Initial value: #{object.data["name"]}}" - - conn - |> assign(:user, user) - |> post("/api/media/metadata/create", %{ - "media_id" => object.id, - "alt_text" => %{"text" => description} - }) - |> json_response(:no_content) - - object = Repo.get(Object, object.id) - assert object.data["name"] == description - end - end - - describe "POST /api/statuses/user_timeline.json?user_id=:user_id&pinned=true" do - test "it returns a list of pinned statuses", %{conn: conn} do - Pleroma.Config.put([:instance, :max_pinned_statuses], 1) - - user = insert(:user, %{name: "egor"}) - {:ok, %{id: activity_id}} = CommonAPI.post(user, %{"status" => "HI!!!"}) - {:ok, _} = CommonAPI.pin(activity_id, user) - - resp = - conn - |> get("/api/statuses/user_timeline.json", %{user_id: user.id, pinned: true}) - |> json_response(200) - - assert length(resp) == 1 - assert [%{"id" => ^activity_id, "pinned" => true}] = resp - end - end - - describe "POST /api/statuses/pin/:id" do - setup do - Pleroma.Config.put([:instance, :max_pinned_statuses], 1) - [user: insert(:user)] - end - - test "without valid credentials", %{conn: conn} do - note_activity = insert(:note_activity) - conn = post(conn, "/api/statuses/pin/#{note_activity.id}.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "test!"}) - - request_path = "/api/statuses/pin/#{activity.id}.json" - - response = - conn - |> with_credentials(user.nickname, "test") - |> post(request_path) - - user = refresh_record(user) - - assert json_response(response, 200) == - ActivityView.render("activity.json", %{user: user, for: user, activity: activity}) - end - end - - describe "POST /api/statuses/unpin/:id" do - setup do - Pleroma.Config.put([:instance, :max_pinned_statuses], 1) - [user: insert(:user)] - end - - test "without valid credentials", %{conn: conn} do - note_activity = insert(:note_activity) - conn = post(conn, "/api/statuses/unpin/#{note_activity.id}.json") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "test!"}) - {:ok, activity} = CommonAPI.pin(activity.id, user) - - request_path = "/api/statuses/unpin/#{activity.id}.json" - - response = - conn - |> with_credentials(user.nickname, "test") - |> post(request_path) - - user = refresh_record(user) - - assert json_response(response, 200) == - ActivityView.render("activity.json", %{user: user, for: user, activity: activity}) - end - end - - describe "GET /api/oauth_tokens" do - setup do - token = insert(:oauth_token) |> Repo.preload(:user) - - %{token: token} - end - - test "renders list", %{token: token} do - response = - build_conn() - |> assign(:user, token.user) - |> get("/api/oauth_tokens") - - keys = - json_response(response, 200) - |> hd() - |> Map.keys() - - assert keys -- ["id", "app_name", "valid_until"] == [] - end - - test "revoke token", %{token: token} do - response = - build_conn() - |> assign(:user, token.user) - |> delete("/api/oauth_tokens/#{token.id}") - - tokens = Token.get_user_tokens(token.user) - - assert tokens == [] - assert response.status == 201 - end - end -end From 90c2dae9a4d5fd7e7c1f0d0f532ce95fbc4c69f9 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 10:20:34 +0300 Subject: [PATCH 02/14] Remove most of Pleroma.Web.TwitterAPI.TwitterAPI --- lib/pleroma/web/twitter_api/twitter_api.ex | 195 --------- test/notification_test.exs | 87 ++-- test/user_test.exs | 22 +- .../mastodon_api_controller_test.exs | 8 +- test/web/mastodon_api/mastodon_api_test.exs | 7 +- test/web/twitter_api/twitter_api_test.exs | 265 ------------ .../twitter_api/views/activity_view_test.exs | 384 ------------------ .../views/notification_view_test.exs | 112 ----- test/web/twitter_api/views/user_view_test.exs | 323 --------------- 9 files changed, 42 insertions(+), 1361 deletions(-) delete mode 100644 test/web/twitter_api/views/activity_view_test.exs delete mode 100644 test/web/twitter_api/views/notification_view_test.exs delete mode 100644 test/web/twitter_api/views/user_view_test.exs diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 80082ea84..8eda762c7 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -3,133 +3,14 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.TwitterAPI do - alias Pleroma.Activity alias Pleroma.Emails.Mailer alias Pleroma.Emails.UserEmail alias Pleroma.Repo alias Pleroma.User alias Pleroma.UserInviteToken - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.TwitterAPI.UserView - - import Ecto.Query require Pleroma.Constants - def create_status(%User{} = user, %{"status" => _} = data) do - CommonAPI.post(user, data) - end - - def delete(%User{} = user, id) do - with %Activity{data: %{"type" => _type}} <- Activity.get_by_id(id), - {:ok, activity} <- CommonAPI.delete(id, user) do - {:ok, activity} - end - end - - def follow(%User{} = follower, params) do - with {:ok, %User{} = followed} <- get_user(params) do - CommonAPI.follow(follower, followed) - end - end - - def unfollow(%User{} = follower, params) do - with {:ok, %User{} = unfollowed} <- get_user(params), - {:ok, follower} <- CommonAPI.unfollow(follower, unfollowed) do - {:ok, follower, unfollowed} - end - end - - def block(%User{} = blocker, params) do - with {:ok, %User{} = blocked} <- get_user(params), - {:ok, blocker} <- User.block(blocker, blocked), - {:ok, _activity} <- ActivityPub.block(blocker, blocked) do - {:ok, blocker, blocked} - else - err -> err - end - end - - def unblock(%User{} = blocker, params) do - with {:ok, %User{} = blocked} <- get_user(params), - {:ok, blocker} <- User.unblock(blocker, blocked), - {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do - {:ok, blocker, blocked} - else - err -> err - end - end - - def repeat(%User{} = user, ap_id_or_id) do - with {:ok, _announce, %{data: %{"id" => id}}} <- CommonAPI.repeat(ap_id_or_id, user), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do - {:ok, activity} - end - end - - def unrepeat(%User{} = user, ap_id_or_id) do - with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do - {:ok, activity} - end - end - - def pin(%User{} = user, ap_id_or_id) do - CommonAPI.pin(ap_id_or_id, user) - end - - def unpin(%User{} = user, ap_id_or_id) do - CommonAPI.unpin(ap_id_or_id, user) - end - - def fav(%User{} = user, ap_id_or_id) do - with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do - {:ok, activity} - end - end - - def unfav(%User{} = user, ap_id_or_id) do - with {:ok, _unfav, _fav, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do - {:ok, activity} - end - end - - def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do - {:ok, object} = ActivityPub.upload(file, actor: User.ap_id(user)) - - url = List.first(object.data["url"]) - href = url["href"] - type = url["mediaType"] - - case format do - "xml" -> - # Fake this as good as possible... - """ - - - #{object.id} - #{object.id} - #{object.id} - #{href} - #{href} - - - """ - - "json" -> - %{ - media_id: object.id, - media_id_string: "#{object.id}}", - media_url: href, - size: 0 - } - |> Jason.encode!() - end - end - def register_user(params, opts \\ []) do token = params["token"] @@ -236,80 +117,4 @@ def password_reset(nickname_or_email) do {:error, "unknown user"} end end - - def get_user(user \\ nil, params) do - case params do - %{"user_id" => user_id} -> - case User.get_cached_by_nickname_or_id(user_id) do - nil -> - {:error, "No user with such user_id"} - - %User{info: %{deactivated: true}} -> - {:error, "User has been disabled"} - - user -> - {:ok, user} - end - - %{"screen_name" => nickname} -> - case User.get_cached_by_nickname(nickname) do - nil -> {:error, "No user with such screen_name"} - target -> {:ok, target} - end - - _ -> - if user do - {:ok, user} - else - {:error, "You need to specify screen_name or user_id"} - end - end - end - - defp parse_int(string, default) - - defp parse_int(string, default) when is_binary(string) do - with {n, _} <- Integer.parse(string) do - n - else - _e -> default - end - end - - defp parse_int(_, default), do: default - - # TODO: unify the search query with MastoAPI one and do only pagination here - def search(_user, %{"q" => query} = params) do - limit = parse_int(params["rpp"], 20) - page = parse_int(params["page"], 1) - offset = (page - 1) * limit - - q = - from( - [a, o] in Activity.with_preloaded_object(Activity), - where: fragment("?->>'type' = 'Create'", a.data), - where: ^Pleroma.Constants.as_public() in a.recipients, - where: - fragment( - "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", - o.data, - ^query - ), - limit: ^limit, - offset: ^offset, - # this one isn't indexed so psql won't take the wrong index. - order_by: [desc: :inserted_at] - ) - - _activities = Repo.all(q) - end - - def get_external_profile(for_user, uri) do - with {:ok, %User{} = user} <- User.get_or_fetch(uri) do - {:ok, UserView.render("show.json", %{user: user, for: for_user})} - else - _e -> - {:error, "Couldn't find user"} - end - end end diff --git a/test/notification_test.exs b/test/notification_test.exs index 80ea2a085..2a52dad8d 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -12,7 +12,6 @@ defmodule Pleroma.NotificationTest do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.CommonAPI alias Pleroma.Web.Streamer - alias Pleroma.Web.TwitterAPI.TwitterAPI describe "create_notifications" do test "notifies someone when they are directly addressed" do @@ -21,7 +20,7 @@ test "notifies someone when they are directly addressed" do third_user = insert(:user) {:ok, activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{other_user.nickname} and @#{third_user.nickname}" }) @@ -39,7 +38,7 @@ test "it creates a notification for subscribed users" do User.subscribe(subscriber, user) - {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) + {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) {:ok, [notification]} = Notification.create_notifications(status) assert notification.user_id == subscriber.id @@ -184,47 +183,20 @@ test "it doesn't create a notification for user if he is the activity author" do test "it doesn't create a notification for follow-unfollow-follow chains" do user = insert(:user) followed_user = insert(:user) - {:ok, _, _, activity} = TwitterAPI.follow(user, %{"user_id" => followed_user.id}) + {:ok, _, _, activity} = CommonAPI.follow(user, followed_user) Notification.create_notification(activity, followed_user) - TwitterAPI.unfollow(user, %{"user_id" => followed_user.id}) - {:ok, _, _, activity_dupe} = TwitterAPI.follow(user, %{"user_id" => followed_user.id}) + CommonAPI.unfollow(user, followed_user) + {:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user) refute Notification.create_notification(activity_dupe, followed_user) end - test "it doesn't create a notification for like-unlike-like chains" do - user = insert(:user) - liked_user = insert(:user) - {:ok, status} = TwitterAPI.create_status(liked_user, %{"status" => "Yui is best yuru"}) - {:ok, fav_status} = TwitterAPI.fav(user, status.id) - Notification.create_notification(fav_status, liked_user) - TwitterAPI.unfav(user, status.id) - {:ok, dupe} = TwitterAPI.fav(user, status.id) - refute Notification.create_notification(dupe, liked_user) - end - - test "it doesn't create a notification for repeat-unrepeat-repeat chains" do - user = insert(:user) - retweeted_user = insert(:user) - - {:ok, status} = - TwitterAPI.create_status(retweeted_user, %{ - "status" => "Send dupe notifications to the shadow realm" - }) - - {:ok, retweeted_activity} = TwitterAPI.repeat(user, status.id) - Notification.create_notification(retweeted_activity, retweeted_user) - TwitterAPI.unrepeat(user, status.id) - {:ok, dupe} = TwitterAPI.repeat(user, status.id) - refute Notification.create_notification(dupe, retweeted_user) - end - test "it doesn't create duplicate notifications for follow+subscribed users" do user = insert(:user) subscriber = insert(:user) - {:ok, _, _, _} = TwitterAPI.follow(subscriber, %{"user_id" => user.id}) + {:ok, _, _, _} = CommonAPI.follow(subscriber, user) User.subscribe(subscriber, user) - {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) + {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) {:ok, [_notif]} = Notification.create_notifications(status) end @@ -234,8 +206,7 @@ test "it doesn't create subscription notifications if the recipient cannot see t User.subscribe(subscriber, user) - {:ok, status} = - TwitterAPI.create_status(user, %{"status" => "inwisible", "visibility" => "direct"}) + {:ok, status} = CommonAPI.post(user, %{"status" => "inwisible", "visibility" => "direct"}) assert {:ok, []} == Notification.create_notifications(status) end @@ -246,8 +217,7 @@ test "it gets a notification that belongs to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.get(other_user, notification.id) @@ -259,8 +229,7 @@ test "it returns error if the notification doesn't belong to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.get(user, notification.id) @@ -272,8 +241,7 @@ test "it dismisses a notification that belongs to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.dismiss(other_user, notification.id) @@ -285,8 +253,7 @@ test "it returns error if the notification doesn't belong to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.dismiss(user, notification.id) @@ -300,14 +267,14 @@ test "it clears all notifications belonging to the user" do third_user = insert(:user) {:ok, activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !" }) {:ok, _notifs} = Notification.create_notifications(activity) {:ok, activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !" }) @@ -325,12 +292,12 @@ test "it sets all notifications as read up to a specified notification ID" do other_user = insert(:user) {:ok, _activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{other_user.nickname}!" }) {:ok, _activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey again @#{other_user.nickname}!" }) @@ -340,7 +307,7 @@ test "it sets all notifications as read up to a specified notification ID" do assert n2.id > n1.id {:ok, _activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey yet again @#{other_user.nickname}!" }) @@ -677,7 +644,7 @@ test "it returns notifications for muted user without notifications" do muted = insert(:user) {:ok, user} = User.mute(user, muted, false) - {:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) assert length(Notification.for_user(user)) == 1 end @@ -687,7 +654,7 @@ test "it doesn't return notifications for muted user with notifications" do muted = insert(:user) {:ok, user} = User.mute(user, muted) - {:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) assert Notification.for_user(user) == [] end @@ -697,7 +664,7 @@ test "it doesn't return notifications for blocked user" do blocked = insert(:user) {:ok, user} = User.block(user, blocked) - {:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) assert Notification.for_user(user) == [] end @@ -707,7 +674,7 @@ test "it doesn't return notificatitons for blocked domain" do blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com") - {:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) assert Notification.for_user(user) == [] end @@ -716,8 +683,7 @@ test "it doesn't return notifications for muted thread" do user = insert(:user) another_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(another_user, %{"status" => "hey @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) assert Notification.for_user(user) == [] @@ -728,7 +694,7 @@ test "it returns notifications for muted user with notifications and with_muted muted = insert(:user) {:ok, user} = User.mute(user, muted) - {:ok, _activity} = TwitterAPI.create_status(muted, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) assert length(Notification.for_user(user, %{with_muted: true})) == 1 end @@ -738,7 +704,7 @@ test "it returns notifications for blocked user and with_muted parameter" do blocked = insert(:user) {:ok, user} = User.block(user, blocked) - {:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) assert length(Notification.for_user(user, %{with_muted: true})) == 1 end @@ -748,7 +714,7 @@ test "it returns notificatitons for blocked domain and with_muted parameter" do blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com") - {:ok, _activity} = TwitterAPI.create_status(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) assert length(Notification.for_user(user, %{with_muted: true})) == 1 end @@ -757,8 +723,7 @@ test "it returns notifications for muted thread with_muted parameter" do user = insert(:user) another_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(another_user, %{"status" => "hey @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) assert length(Notification.for_user(user, %{with_muted: true})) == 1 diff --git a/test/user_test.exs b/test/user_test.exs index 2cbc1f525..a25b72f4e 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -69,8 +69,8 @@ test "returns all pending follow requests" do locked = insert(:user, %{info: %{locked: true}}) follower = insert(:user) - Pleroma.Web.TwitterAPI.TwitterAPI.follow(follower, %{"user_id" => unlocked.id}) - Pleroma.Web.TwitterAPI.TwitterAPI.follow(follower, %{"user_id" => locked.id}) + CommonAPI.follow(follower, unlocked) + CommonAPI.follow(follower, locked) assert {:ok, []} = User.get_follow_requests(unlocked) assert {:ok, [activity]} = User.get_follow_requests(locked) @@ -83,9 +83,9 @@ test "doesn't return already accepted or duplicate follow requests" do pending_follower = insert(:user) accepted_follower = insert(:user) - Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id}) - Pleroma.Web.TwitterAPI.TwitterAPI.follow(pending_follower, %{"user_id" => locked.id}) - Pleroma.Web.TwitterAPI.TwitterAPI.follow(accepted_follower, %{"user_id" => locked.id}) + CommonAPI.follow(pending_follower, locked) + CommonAPI.follow(pending_follower, locked) + CommonAPI.follow(accepted_follower, locked) User.follow(accepted_follower, locked) assert {:ok, [activity]} = User.get_follow_requests(locked) @@ -1279,11 +1279,9 @@ test "follower count is updated when a follower is blocked" do {:ok, _follower2} = User.follow(follower2, user) {:ok, _follower3} = User.follow(follower3, user) - {:ok, _} = User.block(user, follower) + {:ok, user} = User.block(user, follower) - user_show = Pleroma.Web.TwitterAPI.UserView.render("show.json", %{user: user}) - - assert Map.get(user_show, "followers_count") == 2 + assert User.user_info(user).follower_count == 2 end describe "list_inactive_users_query/1" do @@ -1327,7 +1325,7 @@ test "Only includes users who has no recent activity" do to = Enum.random(users -- [user]) {:ok, _} = - Pleroma.Web.TwitterAPI.TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{to.nickname}" }) end) @@ -1359,12 +1357,12 @@ test "Only includes users with no read notifications" do Enum.each(recipients, fn to -> {:ok, _} = - Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{ + CommonAPI.post(sender, %{ "status" => "hey @#{to.nickname}" }) {:ok, _} = - Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{ + CommonAPI.post(sender, %{ "status" => "hey again @#{to.nickname}" }) end) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 6fcdc19aa..66588c891 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -21,7 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OStatus alias Pleroma.Web.Push - alias Pleroma.Web.TwitterAPI.TwitterAPI import Pleroma.Factory import ExUnit.CaptureLog import Tesla.Mock @@ -1583,12 +1582,9 @@ test "gets an users media", %{conn: conn} do filename: "an_image.jpg" } - media = - TwitterAPI.upload(file, user, "json") - |> Jason.decode!() + {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) - {:ok, image_post} = - CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]}) + {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) conn = conn diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/web/mastodon_api/mastodon_api_test.exs index b4c0427c9..7fcb2bd55 100644 --- a/test/web/mastodon_api/mastodon_api_test.exs +++ b/test/web/mastodon_api/mastodon_api_test.exs @@ -8,8 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do alias Pleroma.Notification alias Pleroma.ScheduledActivity alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.MastodonAPI - alias Pleroma.Web.TwitterAPI.TwitterAPI import Pleroma.Factory @@ -75,8 +75,9 @@ test "returns notifications for user" do User.subscribe(subscriber, user) - {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"}) - {:ok, status1} = TwitterAPI.create_status(user, %{"status" => "Magi"}) + {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) + + {:ok, status1} = CommonAPI.post(user, %{"status" => "Magi"}) {:ok, [notification]} = Notification.create_notifications(status) {:ok, [notification1]} = Notification.create_notifications(status1) res = MastodonAPI.get_notifications(subscriber) diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index cbe83852e..ac9c0c27e 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -4,12 +4,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User alias Pleroma.UserInviteToken - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.TwitterAPI.ActivityView alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.TwitterAPI.UserView @@ -21,253 +18,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do :ok end - test "create a status" do - user = insert(:user) - mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"}) - - object_data = %{ - "type" => "Image", - "url" => [ - %{ - "type" => "Link", - "mediaType" => "image/jpg", - "href" => "http://example.org/image.jpg" - } - ], - "uuid" => 1 - } - - object = Repo.insert!(%Object{data: object_data}) - - input = %{ - "status" => - "Hello again, @shp.\nThis is on another :firefox: line. #2hu #epic #phantasmagoric", - "media_ids" => [object.id] - } - - {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) - object = Object.normalize(activity) - - expected_text = - "Hello again, @shp.<script></script>
This is on another :firefox: line.
image.jpg" - - assert get_in(object.data, ["content"]) == expected_text - assert get_in(object.data, ["type"]) == "Note" - assert get_in(object.data, ["actor"]) == user.ap_id - assert get_in(activity.data, ["actor"]) == user.ap_id - assert Enum.member?(get_in(activity.data, ["cc"]), User.ap_followers(user)) - - assert Enum.member?( - get_in(activity.data, ["to"]), - "https://www.w3.org/ns/activitystreams#Public" - ) - - assert Enum.member?(get_in(activity.data, ["to"]), "shp") - assert activity.local == true - - assert %{"firefox" => "http://localhost:4001/emoji/Firefox.gif"} = object.data["emoji"] - - # hashtags - assert object.data["tag"] == ["2hu", "epic", "phantasmagoric"] - - # Add a context - assert is_binary(get_in(activity.data, ["context"])) - assert is_binary(get_in(object.data, ["context"])) - - assert is_list(object.data["attachment"]) - - assert activity.data["object"] == object.data["id"] - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.info.note_count == 1 - end - - test "create a status that is a reply" do - user = insert(:user) - - input = %{ - "status" => "Hello again." - } - - {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) - object = Object.normalize(activity) - - input = %{ - "status" => "Here's your (you).", - "in_reply_to_status_id" => activity.id - } - - {:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input) - reply_object = Object.normalize(reply) - - assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"]) - - assert get_in(reply_object.data, ["context"]) == get_in(object.data, ["context"]) - - assert get_in(reply_object.data, ["inReplyTo"]) == get_in(activity.data, ["object"]) - assert Activity.get_in_reply_to_activity(reply).id == activity.id - end - - test "Follow another user using user_id" do - user = insert(:user) - followed = insert(:user) - - {:ok, user, followed, _activity} = TwitterAPI.follow(user, %{"user_id" => followed.id}) - assert User.ap_followers(followed) in user.following - - {:ok, _, _, _} = TwitterAPI.follow(user, %{"user_id" => followed.id}) - end - - test "Follow another user using screen_name" do - user = insert(:user) - followed = insert(:user) - - {:ok, user, followed, _activity} = - TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) - - assert User.ap_followers(followed) in user.following - - followed = User.get_cached_by_ap_id(followed.ap_id) - assert followed.info.follower_count == 1 - - {:ok, _, _, _} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) - end - - test "Unfollow another user using user_id" do - unfollowed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(unfollowed)]}) - ActivityPub.follow(user, unfollowed) - - {:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) - assert user.following == [] - - {:error, msg} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) - assert msg == "Not subscribed!" - end - - test "Unfollow another user using screen_name" do - unfollowed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(unfollowed)]}) - - ActivityPub.follow(user, unfollowed) - - {:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) - assert user.following == [] - - {:error, msg} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) - assert msg == "Not subscribed!" - end - - test "Block another user using user_id" do - user = insert(:user) - blocked = insert(:user) - - {:ok, user, blocked} = TwitterAPI.block(user, %{"user_id" => blocked.id}) - assert User.blocks?(user, blocked) - end - - test "Block another user using screen_name" do - user = insert(:user) - blocked = insert(:user) - - {:ok, user, blocked} = TwitterAPI.block(user, %{"screen_name" => blocked.nickname}) - assert User.blocks?(user, blocked) - end - - test "Unblock another user using user_id" do - unblocked = insert(:user) - user = insert(:user) - {:ok, user, _unblocked} = TwitterAPI.block(user, %{"user_id" => unblocked.id}) - - {:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"user_id" => unblocked.id}) - assert user.info.blocks == [] - end - - test "Unblock another user using screen_name" do - unblocked = insert(:user) - user = insert(:user) - {:ok, user, _unblocked} = TwitterAPI.block(user, %{"screen_name" => unblocked.nickname}) - - {:ok, user, _unblocked} = TwitterAPI.unblock(user, %{"screen_name" => unblocked.nickname}) - assert user.info.blocks == [] - end - - test "upload a file" do - user = insert(:user) - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - response = TwitterAPI.upload(file, user) - - assert is_binary(response) - end - - test "it favorites a status, returns the updated activity" do - user = insert(:user) - other_user = insert(:user) - note_activity = insert(:note_activity) - - {:ok, status} = TwitterAPI.fav(user, note_activity.id) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - assert ActivityView.render("activity.json", %{activity: updated_activity})["fave_num"] == 1 - - object = Object.normalize(note_activity) - - assert object.data["like_count"] == 1 - - assert status == updated_activity - - {:ok, _status} = TwitterAPI.fav(other_user, note_activity.id) - - object = Object.normalize(note_activity) - - assert object.data["like_count"] == 2 - - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - assert ActivityView.render("activity.json", %{activity: updated_activity})["fave_num"] == 2 - end - - test "it unfavorites a status, returns the updated activity" do - user = insert(:user) - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - - {:ok, _like_activity, _object} = ActivityPub.like(user, object) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - - assert ActivityView.render("activity.json", activity: updated_activity)["fave_num"] == 1 - - {:ok, activity} = TwitterAPI.unfav(user, note_activity.id) - - assert ActivityView.render("activity.json", activity: activity)["fave_num"] == 0 - end - - test "it retweets a status and returns the retweet" do - user = insert(:user) - note_activity = insert(:note_activity) - - {:ok, status} = TwitterAPI.repeat(user, note_activity.id) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - - assert status == updated_activity - end - - test "it unretweets an already retweeted status" do - user = insert(:user) - note_activity = insert(:note_activity) - - {:ok, _status} = TwitterAPI.repeat(user, note_activity.id) - {:ok, status} = TwitterAPI.unrepeat(user, note_activity.id) - updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - - assert status == updated_activity - end - test "it registers a new user and returns the user." do data = %{ "nickname" => "lain", @@ -701,19 +451,4 @@ test "it assigns an integer conversation_id" do Supervisor.restart_child(Pleroma.Supervisor, Cachex) :ok end - - describe "fetching a user by uri" do - test "fetches a user by uri" do - id = "https://mastodon.social/users/lambadalambda" - user = insert(:user) - {:ok, represented} = TwitterAPI.get_external_profile(user, id) - remote = User.get_cached_by_ap_id(id) - - assert represented["id"] == UserView.render("show.json", %{user: remote, for: user})["id"] - - # Also fetches the feed. - # assert Activity.get_create_by_object_ap_id("tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status") - # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength - end - end end diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs deleted file mode 100644 index 56d861efb..000000000 --- a/test/web/twitter_api/views/activity_view_test.exs +++ /dev/null @@ -1,384 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.TwitterAPI.ActivityView - alias Pleroma.Web.TwitterAPI.UserView - - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - import Mock - - test "returns a temporary ap_id based user for activities missing db users" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) - - Repo.delete(user) - Cachex.clear(:user_cache) - - %{"user" => tw_user} = ActivityView.render("activity.json", activity: activity) - - assert tw_user["screen_name"] == "erroruser@example.com" - assert tw_user["name"] == user.ap_id - assert tw_user["statusnet_profile_url"] == user.ap_id - end - - test "tries to get a user by nickname if fetching by ap_id doesn't work" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) - - {:ok, user} = - user - |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"}) - |> Repo.update() - - Cachex.clear(:user_cache) - - result = ActivityView.render("activity.json", activity: activity) - assert result["user"]["id"] == user.id - end - - test "tells if the message is muted for some reason" do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.mute(user, other_user) - - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) - status = ActivityView.render("activity.json", %{activity: activity}) - - assert status["muted"] == false - - status = ActivityView.render("activity.json", %{activity: activity, for: user}) - - assert status["muted"] == true - end - - test "a create activity with a html status" do - text = """ - #Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg - """ - - {:ok, activity} = CommonAPI.post(insert(:user), %{"status" => text}) - - result = ActivityView.render("activity.json", activity: activity) - - assert result["statusnet_html"] == - "#Bike log - Commute Tuesday
https://pla.bike/posts/20181211/
#cycling #CHScycling #commute
MVIMG_20181211_054020.jpg" - - assert result["text"] == - "#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg" - end - - test "a create activity with a summary containing emoji" do - {:ok, activity} = - CommonAPI.post(insert(:user), %{ - "spoiler_text" => ":firefox: meow", - "status" => "." - }) - - result = ActivityView.render("activity.json", activity: activity) - - expected = ":firefox: meow" - - expected_html = - "\"firefox\" meow" - - assert result["summary"] == expected - assert result["summary_html"] == expected_html - end - - test "a create activity with a summary containing invalid HTML" do - {:ok, activity} = - CommonAPI.post(insert(:user), %{ - "spoiler_text" => "meow", - "status" => "." - }) - - result = ActivityView.render("activity.json", activity: activity) - - expected = "meow" - - assert result["summary"] == expected - assert result["summary_html"] == expected - end - - test "a create activity with a note" do - user = insert(:user) - other_user = insert(:user, %{nickname: "shp"}) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) - object = Object.normalize(activity) - - result = ActivityView.render("activity.json", activity: activity) - - convo_id = Utils.context_to_conversation_id(object.data["context"]) - - expected = %{ - "activity_type" => "post", - "attachments" => [], - "attentions" => [ - UserView.render("show.json", %{user: other_user}) - ], - "created_at" => object.data["published"] |> Utils.date_to_asctime(), - "external_url" => object.data["id"], - "fave_num" => 0, - "favorited" => false, - "id" => activity.id, - "in_reply_to_status_id" => nil, - "in_reply_to_screen_name" => nil, - "in_reply_to_user_id" => nil, - "in_reply_to_profileurl" => nil, - "in_reply_to_ostatus_uri" => nil, - "is_local" => true, - "is_post_verb" => true, - "possibly_sensitive" => false, - "repeat_num" => 0, - "repeated" => false, - "pinned" => false, - "statusnet_conversation_id" => convo_id, - "summary" => "", - "summary_html" => "", - "statusnet_html" => - "Hey @shp!", - "tags" => [], - "text" => "Hey @shp!", - "uri" => object.data["id"], - "user" => UserView.render("show.json", %{user: user}), - "visibility" => "direct", - "card" => nil, - "muted" => false - } - - assert result == expected - end - - test "a list of activities" do - user = insert(:user) - other_user = insert(:user, %{nickname: "shp"}) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) - object = Object.normalize(activity) - - convo_id = Utils.context_to_conversation_id(object.data["context"]) - - mocks = [ - { - Utils, - [:passthrough], - [context_to_conversation_id: fn _ -> false end] - }, - { - User, - [:passthrough], - [get_cached_by_ap_id: fn _ -> nil end] - } - ] - - with_mocks mocks do - [result] = ActivityView.render("index.json", activities: [activity]) - - assert result["statusnet_conversation_id"] == convo_id - assert result["user"] - refute called(Utils.context_to_conversation_id(:_)) - refute called(User.get_cached_by_ap_id(user.ap_id)) - refute called(User.get_cached_by_ap_id(other_user.ap_id)) - end - end - - test "an activity that is a reply" do - user = insert(:user) - other_user = insert(:user, %{nickname: "shp"}) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) - - {:ok, answer} = - CommonAPI.post(other_user, %{"status" => "Hi!", "in_reply_to_status_id" => activity.id}) - - result = ActivityView.render("activity.json", %{activity: answer}) - - assert result["in_reply_to_status_id"] == activity.id - end - - test "a like activity" 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) - - result = ActivityView.render("activity.json", activity: like) - activity = Pleroma.Activity.get_by_ap_id(activity.data["id"]) - - 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" => activity.id, - "is_local" => true, - "is_post_verb" => false, - "favorited_status" => ActivityView.render("activity.json", activity: activity), - "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 "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, - "favorited_status" => nil, - "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 - user = insert(:user) - other_user = insert(:user, %{nickname: "shp"}) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) - {:ok, announce, object} = CommonAPI.repeat(activity.id, other_user) - - convo_id = Utils.context_to_conversation_id(object.data["context"]) - - activity = Activity.get_by_id(activity.id) - - result = ActivityView.render("activity.json", activity: announce) - - expected = %{ - "activity_type" => "repeat", - "created_at" => announce.data["published"] |> Utils.date_to_asctime(), - "external_url" => announce.data["id"], - "id" => announce.id, - "is_local" => true, - "is_post_verb" => false, - "statusnet_html" => "shp repeated a status.", - "text" => "shp repeated a status.", - "uri" => "tag:#{announce.data["id"]}:objectType=note", - "user" => UserView.render("show.json", user: other_user), - "retweeted_status" => ActivityView.render("activity.json", activity: activity), - "statusnet_conversation_id" => convo_id - } - - assert result == expected - end - - test "A follow activity" do - user = insert(:user) - other_user = insert(:user, %{nickname: "shp"}) - - {:ok, follower} = User.follow(user, other_user) - {:ok, follow} = ActivityPub.follow(follower, other_user) - - result = ActivityView.render("activity.json", activity: follow) - - expected = %{ - "activity_type" => "follow", - "attentions" => [], - "created_at" => follow.data["published"] |> Utils.date_to_asctime(), - "external_url" => follow.data["id"], - "id" => follow.id, - "in_reply_to_status_id" => nil, - "is_local" => true, - "is_post_verb" => false, - "statusnet_html" => "#{user.nickname} started following shp", - "text" => "#{user.nickname} started following shp", - "user" => UserView.render("show.json", user: user) - } - - assert result == expected - end - - test "a delete activity" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"}) - {:ok, delete} = CommonAPI.delete(activity.id, user) - - result = ActivityView.render("activity.json", activity: delete) - - expected = %{ - "activity_type" => "delete", - "attentions" => [], - "created_at" => delete.data["published"] |> Utils.date_to_asctime(), - "external_url" => delete.data["id"], - "id" => delete.id, - "in_reply_to_status_id" => nil, - "is_local" => true, - "is_post_verb" => false, - "statusnet_html" => "deleted notice {{tag", - "text" => "deleted notice {{tag", - "uri" => Object.normalize(delete).data["id"], - "user" => UserView.render("show.json", user: user) - } - - assert result == expected - end - - test "a peertube video" do - {:ok, object} = - Pleroma.Object.Fetcher.fetch_object_from_id( - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - ) - - %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) - - result = ActivityView.render("activity.json", activity: activity) - - assert length(result["attachments"]) == 1 - assert result["summary"] == "Friday Night" - end - - test "special characters are not escaped in text field for status created" do - text = "<3 is on the way" - - {:ok, activity} = CommonAPI.post(insert(:user), %{"status" => text}) - - result = ActivityView.render("activity.json", activity: activity) - - assert result["text"] == text - end -end diff --git a/test/web/twitter_api/views/notification_view_test.exs b/test/web/twitter_api/views/notification_view_test.exs deleted file mode 100644 index 6baeeaf63..000000000 --- a/test/web/twitter_api/views/notification_view_test.exs +++ /dev/null @@ -1,112 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.NotificationViewTest do - use Pleroma.DataCase - - alias Pleroma.Notification - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.TwitterAPI.ActivityView - alias Pleroma.Web.TwitterAPI.NotificationView - alias Pleroma.Web.TwitterAPI.TwitterAPI - alias Pleroma.Web.TwitterAPI.UserView - - import Pleroma.Factory - - setup do - user = insert(:user, bio: "Here's some html") - [user: user] - end - - test "A follow notification" do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - follower = insert(:user) - - {:ok, follower} = User.follow(follower, user) - {:ok, activity} = ActivityPub.follow(follower, user) - Cachex.put(:user_cache, "user_info:#{user.id}", User.user_info(Repo.get!(User, user.id))) - [follow_notif] = Notification.for_user(user) - - represented = %{ - "created_at" => follow_notif.inserted_at |> Utils.format_naive_asctime(), - "from_profile" => UserView.render("show.json", %{user: follower, for: user}), - "id" => follow_notif.id, - "is_seen" => 0, - "notice" => ActivityView.render("activity.json", %{activity: activity, for: user}), - "ntype" => "follow" - } - - assert represented == - NotificationView.render("notification.json", %{notification: follow_notif, for: user}) - end - - test "A mention notification" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - TwitterAPI.create_status(other_user, %{"status" => "Päivää, @#{user.nickname}"}) - - [notification] = Notification.for_user(user) - - represented = %{ - "created_at" => notification.inserted_at |> Utils.format_naive_asctime(), - "from_profile" => UserView.render("show.json", %{user: other_user, for: user}), - "id" => notification.id, - "is_seen" => 0, - "notice" => ActivityView.render("activity.json", %{activity: activity, for: user}), - "ntype" => "mention" - } - - assert represented == - NotificationView.render("notification.json", %{notification: notification, for: user}) - end - - test "A retweet notification" do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - repeater = insert(:user) - - {:ok, _activity} = TwitterAPI.repeat(repeater, note_activity.id) - [notification] = Notification.for_user(user) - - represented = %{ - "created_at" => notification.inserted_at |> Utils.format_naive_asctime(), - "from_profile" => UserView.render("show.json", %{user: repeater, for: user}), - "id" => notification.id, - "is_seen" => 0, - "notice" => - ActivityView.render("activity.json", %{activity: notification.activity, for: user}), - "ntype" => "repeat" - } - - assert represented == - NotificationView.render("notification.json", %{notification: notification, for: user}) - end - - test "A like notification" do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - liker = insert(:user) - - {:ok, _activity} = TwitterAPI.fav(liker, note_activity.id) - [notification] = Notification.for_user(user) - - represented = %{ - "created_at" => notification.inserted_at |> Utils.format_naive_asctime(), - "from_profile" => UserView.render("show.json", %{user: liker, for: user}), - "id" => notification.id, - "is_seen" => 0, - "notice" => - ActivityView.render("activity.json", %{activity: notification.activity, for: user}), - "ntype" => "like" - } - - assert represented == - NotificationView.render("notification.json", %{notification: notification, for: user}) - end -end diff --git a/test/web/twitter_api/views/user_view_test.exs b/test/web/twitter_api/views/user_view_test.exs deleted file mode 100644 index 70c5a0b7f..000000000 --- a/test/web/twitter_api/views/user_view_test.exs +++ /dev/null @@ -1,323 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.UserViewTest do - use Pleroma.DataCase - - alias Pleroma.User - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.TwitterAPI.UserView - - import Pleroma.Factory - - setup do - user = insert(:user, bio: "Here's some html") - [user: user] - end - - test "A user with only a nickname", %{user: user} do - user = %{user | name: nil, nickname: "scarlett@catgirl.science"} - represented = UserView.render("show.json", %{user: user}) - assert represented["name"] == user.nickname - assert represented["name_html"] == user.nickname - end - - test "A user with an avatar object", %{user: user} do - image = "image" - user = %{user | avatar: %{"url" => [%{"href" => image}]}} - represented = UserView.render("show.json", %{user: user}) - assert represented["profile_image_url"] == image - end - - test "A user with emoji in username" do - expected = - "\"karjalanpiirakka\" man" - - user = - insert(:user, %{ - info: %{ - source_data: %{ - "tag" => [ - %{ - "type" => "Emoji", - "icon" => %{"url" => "/file.png"}, - "name" => ":karjalanpiirakka:" - } - ] - } - }, - name: ":karjalanpiirakka: man" - }) - - represented = UserView.render("show.json", %{user: user}) - assert represented["name_html"] == expected - end - - test "A user" do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - {:ok, user} = User.update_note_count(user) - follower = insert(:user) - second_follower = insert(:user) - - User.follow(follower, user) - User.follow(second_follower, user) - User.follow(user, follower) - {:ok, user} = User.update_follower_count(user) - Cachex.put(:user_cache, "user_info:#{user.id}", User.user_info(Repo.get!(User, user.id))) - - image = "http://localhost:4001/images/avi.png" - banner = "http://localhost:4001/images/banner.png" - - represented = %{ - "id" => user.id, - "name" => user.name, - "screen_name" => user.nickname, - "name_html" => user.name, - "description" => HtmlSanitizeEx.strip_tags(user.bio |> String.replace("
", "\n")), - "description_html" => HtmlSanitizeEx.basic_html(user.bio), - "created_at" => user.inserted_at |> Utils.format_naive_asctime(), - "favourites_count" => 0, - "statuses_count" => 1, - "friends_count" => 1, - "followers_count" => 2, - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "following" => false, - "follows_you" => false, - "statusnet_blocking" => false, - "statusnet_profile_url" => user.ap_id, - "cover_photo" => banner, - "background_image" => nil, - "is_local" => true, - "locked" => false, - "hide_follows" => false, - "hide_followers" => false, - "fields" => [], - "pleroma" => %{ - "confirmation_pending" => false, - "tags" => [], - "skip_thread_containment" => false - }, - "rights" => %{"admin" => false, "delete_others_notice" => false}, - "role" => "member" - } - - assert represented == UserView.render("show.json", %{user: user}) - end - - test "User exposes settings for themselves and only for themselves", %{user: user} do - as_user = UserView.render("show.json", %{user: user, for: user}) - assert as_user["default_scope"] == user.info.default_scope - assert as_user["no_rich_text"] == user.info.no_rich_text - assert as_user["pleroma"]["notification_settings"] == user.info.notification_settings - as_stranger = UserView.render("show.json", %{user: user}) - refute as_stranger["default_scope"] - refute as_stranger["no_rich_text"] - refute as_stranger["pleroma"]["notification_settings"] - end - - test "A user for a given other follower", %{user: user} do - follower = insert(:user, %{following: [User.ap_followers(user)]}) - {:ok, user} = User.update_follower_count(user) - image = "http://localhost:4001/images/avi.png" - banner = "http://localhost:4001/images/banner.png" - - represented = %{ - "id" => user.id, - "name" => user.name, - "screen_name" => user.nickname, - "name_html" => user.name, - "description" => HtmlSanitizeEx.strip_tags(user.bio |> String.replace("
", "\n")), - "description_html" => HtmlSanitizeEx.basic_html(user.bio), - "created_at" => user.inserted_at |> Utils.format_naive_asctime(), - "favourites_count" => 0, - "statuses_count" => 0, - "friends_count" => 0, - "followers_count" => 1, - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "following" => true, - "follows_you" => false, - "statusnet_blocking" => false, - "statusnet_profile_url" => user.ap_id, - "cover_photo" => banner, - "background_image" => nil, - "is_local" => true, - "locked" => false, - "hide_follows" => false, - "hide_followers" => false, - "fields" => [], - "pleroma" => %{ - "confirmation_pending" => false, - "tags" => [], - "skip_thread_containment" => false - }, - "rights" => %{"admin" => false, "delete_others_notice" => false}, - "role" => "member" - } - - assert represented == UserView.render("show.json", %{user: user, for: follower}) - end - - test "A user that follows you", %{user: user} do - follower = insert(:user) - {:ok, follower} = User.follow(follower, user) - {:ok, user} = User.update_follower_count(user) - image = "http://localhost:4001/images/avi.png" - banner = "http://localhost:4001/images/banner.png" - - represented = %{ - "id" => follower.id, - "name" => follower.name, - "screen_name" => follower.nickname, - "name_html" => follower.name, - "description" => HtmlSanitizeEx.strip_tags(follower.bio |> String.replace("
", "\n")), - "description_html" => HtmlSanitizeEx.basic_html(follower.bio), - "created_at" => follower.inserted_at |> Utils.format_naive_asctime(), - "favourites_count" => 0, - "statuses_count" => 0, - "friends_count" => 1, - "followers_count" => 0, - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "following" => false, - "follows_you" => true, - "statusnet_blocking" => false, - "statusnet_profile_url" => follower.ap_id, - "cover_photo" => banner, - "background_image" => nil, - "is_local" => true, - "locked" => false, - "hide_follows" => false, - "hide_followers" => false, - "fields" => [], - "pleroma" => %{ - "confirmation_pending" => false, - "tags" => [], - "skip_thread_containment" => false - }, - "rights" => %{"admin" => false, "delete_others_notice" => false}, - "role" => "member" - } - - assert represented == UserView.render("show.json", %{user: follower, for: user}) - end - - test "a user that is a moderator" do - user = insert(:user, %{info: %{is_moderator: true}}) - represented = UserView.render("show.json", %{user: user, for: user}) - - assert represented["rights"]["delete_others_notice"] - assert represented["role"] == "moderator" - end - - test "a user that is a admin" do - user = insert(:user, %{info: %{is_admin: true}}) - represented = UserView.render("show.json", %{user: user, for: user}) - - assert represented["rights"]["admin"] - assert represented["role"] == "admin" - end - - test "A moderator with hidden role for another user", %{user: user} do - admin = insert(:user, %{info: %{is_moderator: true, show_role: false}}) - represented = UserView.render("show.json", %{user: admin, for: user}) - - assert represented["role"] == nil - end - - test "An admin with hidden role for another user", %{user: user} do - admin = insert(:user, %{info: %{is_admin: true, show_role: false}}) - represented = UserView.render("show.json", %{user: admin, for: user}) - - assert represented["role"] == nil - end - - test "A regular user for the admin", %{user: user} do - admin = insert(:user, %{info: %{is_admin: true}}) - represented = UserView.render("show.json", %{user: user, for: admin}) - - assert represented["pleroma"]["deactivated"] == false - end - - test "A blocked user for the blocker" do - user = insert(:user) - blocker = insert(:user) - User.block(blocker, user) - image = "http://localhost:4001/images/avi.png" - banner = "http://localhost:4001/images/banner.png" - - represented = %{ - "id" => user.id, - "name" => user.name, - "screen_name" => user.nickname, - "name_html" => user.name, - "description" => HtmlSanitizeEx.strip_tags(user.bio |> String.replace("
", "\n")), - "description_html" => HtmlSanitizeEx.basic_html(user.bio), - "created_at" => user.inserted_at |> Utils.format_naive_asctime(), - "favourites_count" => 0, - "statuses_count" => 0, - "friends_count" => 0, - "followers_count" => 0, - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "following" => false, - "follows_you" => false, - "statusnet_blocking" => true, - "statusnet_profile_url" => user.ap_id, - "cover_photo" => banner, - "background_image" => nil, - "is_local" => true, - "locked" => false, - "hide_follows" => false, - "hide_followers" => false, - "fields" => [], - "pleroma" => %{ - "confirmation_pending" => false, - "tags" => [], - "skip_thread_containment" => false - }, - "rights" => %{"admin" => false, "delete_others_notice" => false}, - "role" => "member" - } - - blocker = User.get_cached_by_id(blocker.id) - assert represented == UserView.render("show.json", %{user: user, for: blocker}) - end - - test "a user with mastodon fields" do - fields = [ - %{ - "name" => "Pronouns", - "value" => "she/her" - }, - %{ - "name" => "Website", - "value" => "https://example.org/" - } - ] - - user = - insert(:user, %{ - info: %{ - source_data: %{ - "attachment" => - Enum.map(fields, fn field -> Map.put(field, "type", "PropertyValue") end) - } - } - }) - - userview = UserView.render("show.json", %{user: user}) - assert userview["fields"] == fields - end -end From 985122cc03380b8e3decd4ac7180ea5b0f7ab30d Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 10:31:15 +0300 Subject: [PATCH 03/14] Remove Activity, User and Notification views from TwitterAPI --- .../web/twitter_api/views/activity_view.ex | 366 ------------------ .../twitter_api/views/notification_view.ex | 71 ---- .../web/twitter_api/views/user_view.ex | 191 --------- test/web/twitter_api/twitter_api_test.exs | 38 +- 4 files changed, 15 insertions(+), 651 deletions(-) delete mode 100644 lib/pleroma/web/twitter_api/views/activity_view.ex delete mode 100644 lib/pleroma/web/twitter_api/views/notification_view.ex delete mode 100644 lib/pleroma/web/twitter_api/views/user_view.ex diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex deleted file mode 100644 index abae63877..000000000 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ /dev/null @@ -1,366 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.ActivityView do - use Pleroma.Web, :view - alias Pleroma.Activity - alias Pleroma.Formatter - alias Pleroma.HTML - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.TwitterAPI.ActivityView - alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter - alias Pleroma.Web.TwitterAPI.UserView - - import Ecto.Query - require Logger - require Pleroma.Constants - - defp query_context_ids([]), do: [] - - defp query_context_ids(contexts) do - query = from(o in Object, where: fragment("(?)->>'id' = ANY(?)", o.data, ^contexts)) - - Repo.all(query) - end - - defp query_users([]), do: [] - - defp query_users(user_ids) do - query = from(user in User, where: user.ap_id in ^user_ids) - - Repo.all(query) - end - - defp collect_context_ids(activities) do - _contexts = - activities - |> Enum.reject(& &1.data["context_id"]) - |> Enum.map(fn %{data: data} -> - data["context"] - end) - |> Enum.filter(& &1) - |> query_context_ids() - |> Enum.reduce(%{}, fn %{data: %{"id" => ap_id}, id: id}, acc -> - Map.put(acc, ap_id, id) - end) - end - - defp collect_users(activities) do - activities - |> Enum.map(fn activity -> - case activity.data do - data = %{"type" => "Follow"} -> - [data["actor"], data["object"]] - - data -> - [data["actor"]] - end ++ activity.recipients - end) - |> List.flatten() - |> Enum.uniq() - |> query_users() - |> Enum.reduce(%{}, fn user, acc -> - Map.put(acc, user.ap_id, user) - end) - end - - defp get_context_id(%{data: %{"context_id" => context_id}}, _) when not is_nil(context_id), - do: context_id - - defp get_context_id(%{data: %{"context" => nil}}, _), do: nil - - defp get_context_id(%{data: %{"context" => context}}, options) do - cond do - id = options[:context_ids][context] -> id - true -> Utils.context_to_conversation_id(context) - end - end - - defp get_context_id(_, _), do: nil - - defp get_user(ap_id, opts) do - cond do - user = opts[:users][ap_id] -> - user - - String.ends_with?(ap_id, "/followers") -> - nil - - ap_id == Pleroma.Constants.as_public() -> - nil - - user = User.get_cached_by_ap_id(ap_id) -> - user - - user = User.get_by_guessed_nickname(ap_id) -> - user - - true -> - User.error_user(ap_id) - end - end - - def render("index.json", opts) do - context_ids = collect_context_ids(opts.activities) - users = collect_users(opts.activities) - - opts = - opts - |> Map.put(:context_ids, context_ids) - |> Map.put(:users, users) - - safe_render_many( - opts.activities, - ActivityView, - "activity.json", - opts - ) - end - - def render("activity.json", %{activity: %{data: %{"type" => "Delete"}} = activity} = opts) do - user = get_user(activity.data["actor"], opts) - created_at = activity.data["published"] |> Utils.date_to_asctime() - - %{ - "id" => activity.id, - "uri" => activity.data["object"], - "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), - "attentions" => [], - "statusnet_html" => "deleted notice {{tag", - "text" => "deleted notice {{tag", - "is_local" => activity.local, - "is_post_verb" => false, - "created_at" => created_at, - "in_reply_to_status_id" => nil, - "external_url" => activity.data["id"], - "activity_type" => "delete" - } - end - - def render("activity.json", %{activity: %{data: %{"type" => "Follow"}} = activity} = opts) do - user = get_user(activity.data["actor"], opts) - created_at = activity.data["published"] || DateTime.to_iso8601(activity.inserted_at) - created_at = created_at |> Utils.date_to_asctime() - - followed = get_user(activity.data["object"], opts) - text = "#{user.nickname} started following #{followed.nickname}" - - %{ - "id" => activity.id, - "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), - "attentions" => [], - "statusnet_html" => text, - "text" => text, - "is_local" => activity.local, - "is_post_verb" => false, - "created_at" => created_at, - "in_reply_to_status_id" => nil, - "external_url" => activity.data["id"], - "activity_type" => "follow" - } - end - - def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do - user = get_user(activity.data["actor"], opts) - created_at = activity.data["published"] |> Utils.date_to_asctime() - announced_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) - - text = "#{user.nickname} repeated a status." - - retweeted_status = render("activity.json", Map.merge(opts, %{activity: announced_activity})) - - %{ - "id" => activity.id, - "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), - "statusnet_html" => text, - "text" => text, - "is_local" => activity.local, - "is_post_verb" => false, - "uri" => "tag:#{activity.data["id"]}:objectType=note", - "created_at" => created_at, - "retweeted_status" => retweeted_status, - "statusnet_conversation_id" => get_context_id(announced_activity, opts), - "external_url" => activity.data["id"], - "activity_type" => "repeat" - } - end - - def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do - user = get_user(activity.data["actor"], opts) - liked_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) - liked_activity_id = if liked_activity, do: liked_activity.id, else: nil - - created_at = - activity.data["published"] - |> Utils.date_to_asctime() - - text = "#{user.nickname} favorited a status." - - favorited_status = - if liked_activity, - do: render("activity.json", Map.merge(opts, %{activity: liked_activity})), - else: nil - - %{ - "id" => activity.id, - "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), - "statusnet_html" => text, - "text" => text, - "is_local" => activity.local, - "is_post_verb" => false, - "uri" => "tag:#{activity.data["id"]}:objectType=Favourite", - "created_at" => created_at, - "favorited_status" => favorited_status, - "in_reply_to_status_id" => liked_activity_id, - "external_url" => activity.data["id"], - "activity_type" => "like" - } - end - - def render( - "activity.json", - %{activity: %{data: %{"type" => "Create", "object" => object_id}} = activity} = opts - ) do - user = get_user(activity.data["actor"], opts) - - object = Object.normalize(object_id) - - created_at = object.data["published"] |> Utils.date_to_asctime() - like_count = object.data["like_count"] || 0 - announcement_count = object.data["announcement_count"] || 0 - favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) - repeated = opts[:for] && opts[:for].ap_id in (object.data["announcements"] || []) - pinned = activity.id in user.info.pinned_activities - - attentions = - [] - |> Utils.maybe_notify_to_recipients(activity) - |> Utils.maybe_notify_mentioned_recipients(activity) - |> Enum.map(fn ap_id -> get_user(ap_id, opts) end) - |> Enum.filter(& &1) - |> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) - - conversation_id = get_context_id(activity, opts) - - tags = object.data["tag"] || [] - possibly_sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw") - - tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags - - {summary, content} = render_content(object.data) - - html = - content - |> HTML.get_cached_scrubbed_html_for_activity( - User.html_filter_policy(opts[:for]), - activity, - "twitterapi:content" - ) - |> Formatter.emojify(object.data["emoji"]) - - text = - if content do - content - |> String.replace(~r//, "\n") - |> HTML.get_cached_stripped_html_for_activity(activity, "twitterapi:content") - else - "" - end - - reply_parent = Activity.get_in_reply_to_activity(activity) - - reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor) - - summary = HTML.strip_tags(summary) - - card = - StatusView.render( - "card.json", - Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - ) - - thread_muted? = - case activity.thread_muted? do - thread_muted? when is_boolean(thread_muted?) -> thread_muted? - nil -> CommonAPI.thread_muted?(user, activity) - end - - %{ - "id" => activity.id, - "uri" => object.data["id"], - "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), - "statusnet_html" => html, - "text" => text, - "is_local" => activity.local, - "is_post_verb" => true, - "created_at" => created_at, - "in_reply_to_status_id" => reply_parent && reply_parent.id, - "in_reply_to_screen_name" => reply_user && reply_user.nickname, - "in_reply_to_profileurl" => User.profile_url(reply_user), - "in_reply_to_ostatus_uri" => reply_user && reply_user.ap_id, - "in_reply_to_user_id" => reply_user && reply_user.id, - "statusnet_conversation_id" => conversation_id, - "attachments" => (object.data["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts), - "attentions" => attentions, - "fave_num" => like_count, - "repeat_num" => announcement_count, - "favorited" => !!favorited, - "repeated" => !!repeated, - "pinned" => pinned, - "external_url" => object.data["external_url"] || object.data["id"], - "tags" => tags, - "activity_type" => "post", - "possibly_sensitive" => possibly_sensitive, - "visibility" => Pleroma.Web.ActivityPub.Visibility.get_visibility(object), - "summary" => summary, - "summary_html" => summary |> Formatter.emojify(object.data["emoji"]), - "card" => card, - "muted" => thread_muted? || User.mutes?(opts[:for], user) - } - end - - def render("activity.json", %{activity: unhandled_activity}) do - Logger.warn("#{__MODULE__} unhandled activity: #{inspect(unhandled_activity)}") - nil - end - - def render_content(%{"type" => "Note"} = object) do - summary = object["summary"] - - content = - if !!summary and summary != "" do - "

#{summary}

#{object["content"]}" - else - object["content"] - end - - {summary, content} - end - - def render_content(%{"type" => object_type} = object) - when object_type in ["Article", "Page", "Video"] do - summary = object["name"] || object["summary"] - - content = - if !!summary and summary != "" and is_bitstring(object["url"]) do - "

#{summary}

#{object["content"]}" - else - object["content"] - end - - {summary, content} - end - - def render_content(object) do - summary = object["summary"] || "Unhandled activity type: #{object["type"]}" - content = "

#{summary}

#{object["content"]}" - - {summary, content} - end -end diff --git a/lib/pleroma/web/twitter_api/views/notification_view.ex b/lib/pleroma/web/twitter_api/views/notification_view.ex deleted file mode 100644 index 085cd5aa3..000000000 --- a/lib/pleroma/web/twitter_api/views/notification_view.ex +++ /dev/null @@ -1,71 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.NotificationView do - use Pleroma.Web, :view - alias Pleroma.Notification - alias Pleroma.User - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.TwitterAPI.ActivityView - alias Pleroma.Web.TwitterAPI.UserView - - require Pleroma.Constants - - defp get_user(ap_id, opts) do - cond do - user = opts[:users][ap_id] -> - user - - String.ends_with?(ap_id, "/followers") -> - nil - - ap_id == Pleroma.Constants.as_public() -> - nil - - true -> - User.get_cached_by_ap_id(ap_id) - end - end - - def render("notification.json", %{notifications: notifications, for: user}) do - render_many( - notifications, - Pleroma.Web.TwitterAPI.NotificationView, - "notification.json", - for: user - ) - end - - def render( - "notification.json", - %{ - notification: %Notification{ - id: id, - seen: seen, - activity: activity, - inserted_at: created_at - }, - for: user - } = opts - ) do - ntype = - case activity.data["type"] do - "Create" -> "mention" - "Like" -> "like" - "Announce" -> "repeat" - "Follow" -> "follow" - end - - from = get_user(activity.data["actor"], opts) - - %{ - "id" => id, - "ntype" => ntype, - "notice" => ActivityView.render("activity.json", %{activity: activity, for: user}), - "from_profile" => UserView.render("show.json", %{user: from, for: user}), - "is_seen" => if(seen, do: 1, else: 0), - "created_at" => created_at |> Utils.format_naive_asctime() - } - end -end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex deleted file mode 100644 index 8a7d2fc72..000000000 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ /dev/null @@ -1,191 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.UserView do - use Pleroma.Web, :view - alias Pleroma.Formatter - alias Pleroma.HTML - alias Pleroma.User - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.MediaProxy - - def render("show.json", %{user: user = %User{}} = assigns) do - render_one(user, Pleroma.Web.TwitterAPI.UserView, "user.json", assigns) - end - - def render("index.json", %{users: users, for: user}) do - users - |> render_many(Pleroma.Web.TwitterAPI.UserView, "user.json", for: user) - |> Enum.filter(&Enum.any?/1) - end - - def render("user.json", %{user: user = %User{}} = assigns) do - if User.visible_for?(user, assigns[:for]), - do: do_render("user.json", assigns), - else: %{} - end - - def render("short.json", %{ - user: %User{ - nickname: nickname, - id: id, - ap_id: ap_id, - name: name - } - }) do - %{ - "fullname" => name, - "id" => id, - "ostatus_uri" => ap_id, - "profile_url" => ap_id, - "screen_name" => nickname - } - end - - defp do_render("user.json", %{user: user = %User{}} = assigns) do - for_user = assigns[:for] - image = User.avatar_url(user) |> MediaProxy.url() - - {following, follows_you, statusnet_blocking} = - if for_user do - { - User.following?(for_user, user), - User.following?(user, for_user), - User.blocks?(for_user, user) - } - else - {false, false, false} - end - - user_info = User.get_cached_user_info(user) - - 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) - - emoji = Enum.dedup(emoji ++ user.info.emoji) - - description_html = - (user.bio || "") - |> HTML.filter_tags(User.html_filter_policy(for_user)) - |> Formatter.emojify(emoji) - - fields = - user.info - |> User.Info.fields() - |> Enum.map(fn %{"name" => name, "value" => value} -> - %{ - "name" => Pleroma.HTML.strip_tags(name), - "value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly) - } - end) - - data = - %{ - "created_at" => user.inserted_at |> Utils.format_naive_asctime(), - "description" => HTML.strip_tags((user.bio || "") |> String.replace("
", "\n")), - "description_html" => description_html, - "favourites_count" => 0, - "followers_count" => user_info[:follower_count], - "following" => following, - "follows_you" => follows_you, - "statusnet_blocking" => statusnet_blocking, - "friends_count" => user_info[:following_count], - "id" => user.id, - "name" => user.name || user.nickname, - "name_html" => - if(user.name, - do: HTML.strip_tags(user.name) |> Formatter.emojify(emoji), - else: user.nickname - ), - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "screen_name" => user.nickname, - "statuses_count" => user_info[:note_count], - "statusnet_profile_url" => user.ap_id, - "cover_photo" => User.banner_url(user) |> MediaProxy.url(), - "background_image" => image_url(user.info.background) |> MediaProxy.url(), - "is_local" => user.local, - "locked" => user.info.locked, - "hide_followers" => user.info.hide_followers, - "hide_follows" => user.info.hide_follows, - "fields" => fields, - - # Pleroma extension - "pleroma" => - %{ - "confirmation_pending" => user_info.confirmation_pending, - "tags" => user.tags, - "skip_thread_containment" => user.info.skip_thread_containment - } - |> maybe_with_activation_status(user, for_user) - |> with_notification_settings(user, for_user) - } - |> maybe_with_user_settings(user, for_user) - |> maybe_with_role(user, for_user) - - if assigns[:token] do - Map.put(data, "token", token_string(assigns[:token])) - else - data - end - end - - defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do - Map.put(data, "notification_settings", user.info.notification_settings) - end - - defp with_notification_settings(data, _, _), do: data - - defp maybe_with_activation_status(data, user, %User{info: %{is_admin: true}}) do - Map.put(data, "deactivated", user.info.deactivated) - end - - defp maybe_with_activation_status(data, _, _), do: data - - defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do - Map.merge(data, %{ - "role" => role(user), - "show_role" => user.info.show_role, - "rights" => %{ - "delete_others_notice" => !!user.info.is_moderator, - "admin" => !!user.info.is_admin - } - }) - end - - defp maybe_with_role(data, %User{info: %{show_role: true}} = user, _user) do - Map.merge(data, %{ - "role" => role(user), - "rights" => %{ - "delete_others_notice" => !!user.info.is_moderator, - "admin" => !!user.info.is_admin - } - }) - end - - defp maybe_with_role(data, _, _), do: data - - defp maybe_with_user_settings(data, %User{info: info, id: id} = _user, %User{id: id}) do - data - |> Kernel.put_in(["default_scope"], info.default_scope) - |> Kernel.put_in(["no_rich_text"], info.no_rich_text) - end - - defp maybe_with_user_settings(data, _, _), do: data - defp role(%User{info: %{:is_admin => true}}), do: "admin" - defp role(%User{info: %{:is_moderator => true}}), do: "moderator" - defp role(_), do: "member" - - defp image_url(%{"url" => [%{"href" => href} | _]}), do: href - defp image_url(_), do: nil - - defp token_string(%Pleroma.Web.OAuth.Token{token: token_str}), do: token_str - defp token_string(token), do: token -end diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index ac9c0c27e..50ed43c15 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -7,9 +7,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do alias Pleroma.Repo alias Pleroma.User alias Pleroma.UserInviteToken - alias Pleroma.Web.TwitterAPI.ActivityView alias Pleroma.Web.TwitterAPI.TwitterAPI - alias Pleroma.Web.TwitterAPI.UserView + alias Pleroma.Web.MastodonAPI.AccountView import Pleroma.Factory @@ -31,8 +30,8 @@ test "it registers a new user and returns the user." do fetched_user = User.get_cached_by_nickname("lain") - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) end test "it registers a new user with empty string in bio and returns the user." do @@ -49,8 +48,8 @@ test "it registers a new user with empty string in bio and returns the user." do fetched_user = User.get_cached_by_nickname("lain") - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) end test "it sends confirmation email if :account_activation_required is specified in instance config" do @@ -147,8 +146,8 @@ test "returns user on success" do assert invite.used == true - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) end test "returns error on invalid token" do @@ -212,8 +211,8 @@ test "returns error on expired token" do {:ok, user} = TwitterAPI.register_user(data) fetched_user = User.get_cached_by_nickname("vinny") - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) end {:ok, data: data, check_fn: check_fn} @@ -287,8 +286,8 @@ test "returns user on success, after him registration fails" do assert invite.used == true - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) data = %{ "nickname" => "GrimReaper", @@ -338,8 +337,8 @@ test "returns user on success" do refute invite.used - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) end test "error after max uses" do @@ -362,8 +361,8 @@ test "error after max uses" do invite = Repo.get_by(UserInviteToken, token: invite.token) assert invite.used == true - assert UserView.render("show.json", %{user: user}) == - UserView.render("show.json", %{user: fetched_user}) + assert AccountView.render("account.json", %{user: user}) == + AccountView.render("account.json", %{user: fetched_user}) data = %{ "nickname" => "GrimReaper", @@ -439,13 +438,6 @@ test "it returns the error on registration problems" do refute User.get_cached_by_nickname("lain") end - test "it assigns an integer conversation_id" do - note_activity = insert(:note_activity) - status = ActivityView.render("activity.json", activity: note_activity) - - assert is_number(status["statusnet_conversation_id"]) - end - setup do Supervisor.terminate_child(Pleroma.Supervisor, Cachex) Supervisor.restart_child(Pleroma.Supervisor, Cachex) From 2e7bb107e0267d0e50aebaa3e6db1312e1557b18 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 10:34:29 +0300 Subject: [PATCH 04/14] Remove Mention of TwitterAPI in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5aad34ccc..846442346 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Pleroma is a microblogging server software that can federate (= exchange message Pleroma is written in Elixir, high-performance and can run on small devices like a Raspberry Pi. -For clients it supports both the [GNU Social API with Qvitter extensions](https://twitter-api.readthedocs.io/en/latest/index.html) and the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/). +For clients it supports the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/) with Pleroma extensions (see "Pleroma's APIs and Mastodon API extensions" section on ). - [Client Applications for Pleroma](https://docs-develop.pleroma.social/clients.html) From 64410497d20869f9b6c1c92a48761157048b0cb9 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 10:41:15 +0300 Subject: [PATCH 05/14] Remove TwitterAPI representers --- .../representers/base_representer.ex | 38 ------------ .../representers/object_representer.ex | 39 ------------ .../representers/object_representer_test.exs | 60 ------------------- 3 files changed, 137 deletions(-) delete mode 100644 lib/pleroma/web/twitter_api/representers/base_representer.ex delete mode 100644 lib/pleroma/web/twitter_api/representers/object_representer.ex delete mode 100644 test/web/twitter_api/representers/object_representer_test.exs diff --git a/lib/pleroma/web/twitter_api/representers/base_representer.ex b/lib/pleroma/web/twitter_api/representers/base_representer.ex deleted file mode 100644 index 3d31e6079..000000000 --- a/lib/pleroma/web/twitter_api/representers/base_representer.ex +++ /dev/null @@ -1,38 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do - defmacro __using__(_opts) do - quote do - def to_json(object) do - to_json(object, %{}) - end - - def to_json(object, options) do - object - |> to_map(options) - |> Jason.encode!() - end - - def enum_to_list(enum, options) do - mapping = fn el -> to_map(el, options) end - Enum.map(enum, mapping) - end - - def to_map(object) do - to_map(object, %{}) - end - - def enum_to_json(enum) do - enum_to_json(enum, %{}) - end - - def enum_to_json(enum, options) do - enum - |> enum_to_list(options) - |> Jason.encode!() - end - end - end -end diff --git a/lib/pleroma/web/twitter_api/representers/object_representer.ex b/lib/pleroma/web/twitter_api/representers/object_representer.ex deleted file mode 100644 index 47130ba06..000000000 --- a/lib/pleroma/web/twitter_api/representers/object_representer.ex +++ /dev/null @@ -1,39 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do - use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter - alias Pleroma.Object - - def to_map(%Object{data: %{"url" => [url | _]}} = object, _opts) do - data = object.data - - %{ - url: url["href"] |> Pleroma.Web.MediaProxy.url(), - mimetype: url["mediaType"] || url["mimeType"], - id: data["uuid"], - oembed: false, - description: data["name"] - } - end - - def to_map(%Object{data: %{"url" => url} = data}, _opts) when is_binary(url) do - %{ - url: url |> Pleroma.Web.MediaProxy.url(), - mimetype: data["mediaType"] || data["mimeType"], - id: data["uuid"], - oembed: false, - description: data["name"] - } - end - - def to_map(%Object{}, _opts) do - %{} - end - - # If we only get the naked data, wrap in an object - def to_map(%{} = data, opts) do - to_map(%Object{data: data}, opts) - end -end diff --git a/test/web/twitter_api/representers/object_representer_test.exs b/test/web/twitter_api/representers/object_representer_test.exs deleted file mode 100644 index c3cf330f1..000000000 --- a/test/web/twitter_api/representers/object_representer_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.Representers.ObjectReprenterTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter - - test "represent an image attachment" do - object = %Object{ - id: 5, - data: %{ - "type" => "Image", - "url" => [ - %{ - "mediaType" => "sometype", - "href" => "someurl" - } - ], - "uuid" => 6 - } - } - - expected_object = %{ - id: 6, - url: "someurl", - mimetype: "sometype", - oembed: false, - description: nil - } - - assert expected_object == ObjectRepresenter.to_map(object) - end - - test "represents mastodon-style attachments" do - object = %Object{ - id: nil, - data: %{ - "mediaType" => "image/png", - "name" => "blabla", - "type" => "Document", - "url" => - "http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png" - } - } - - expected_object = %{ - url: - "http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png", - mimetype: "image/png", - oembed: false, - id: nil, - description: "blabla" - } - - assert expected_object == ObjectRepresenter.to_map(object) - end -end From dbfcba85ec2d3336219c75a32adbcff93a684309 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 10:45:37 +0300 Subject: [PATCH 06/14] Add a changelog entry for twitterapi removal and fix credo issues --- CHANGELOG.md | 1 + test/web/twitter_api/twitter_api_test.exs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fdcb014a..e8ea83005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - RichMedia: add the rich media ttl based on image expiration time. ### Removed +- GNU Social API with Qvitter extensions support - Emoji: Remove longfox emojis. - Remove `Reply-To` header from report emails for admins. - ActivityPub: The `accept_blocks` configuration setting. diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 50ed43c15..0a57e174f 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -7,8 +7,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do alias Pleroma.Repo alias Pleroma.User alias Pleroma.UserInviteToken - alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.TwitterAPI.TwitterAPI import Pleroma.Factory From 9cabc02864ff33b76f424a342732ef8039dfd73d Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 10:57:35 +0300 Subject: [PATCH 07/14] Remove a useless import --- test/web/twitter_api/twitter_api_test.exs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 0a57e174f..c5b18234e 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -10,8 +10,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.TwitterAPI.TwitterAPI - import Pleroma.Factory - setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok From bd3ed3a62299bad5d717aaff0a0bd088ff1c1ef7 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 11:40:04 +0300 Subject: [PATCH 08/14] Add back /api/qvitter/statuses/notifications/read.json --- lib/pleroma/web/router.ex | 6 +++++ .../web/twitter_api/twitter_api_controller.ex | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 53728e298..eb7cbbc96 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -495,6 +495,12 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) + + scope [] do + pipe_through(:oauth_read) + + post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) + end end pipeline :ap_service_actor do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 1c3b11a57..8ca754b51 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do alias Ecto.Changeset alias Pleroma.User + alias Pleroma.Notification alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TokenView @@ -58,4 +59,28 @@ defp json_reply(conn, status, json) do |> put_resp_content_type("application/json") |> send_resp(status, json) end + + def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do + Notification.set_read_up_to(user, latest_id) + + notifications = Notification.for_user(user, params) + + conn + # XXX: This is a hack because pleroma-fe still uses that API. + |> put_view(Pleroma.Web.MastodonAPI.NotificationView) + |> render("index.json", %{notifications: notifications, for: user}) + end + + def notifications_read(%{assigns: %{user: _user}} = conn, _) do + bad_request_reply(conn, "You need to specify latest_id") + end + + defp bad_request_reply(conn, error_message) do + json = error_json(conn, error_message) + json_reply(conn, 400, json) + end + + defp error_json(conn, error_message) do + %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!() + end end From 70eed0594ce4fe2ec668c5ee3ad42c941b29888e Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 31 Aug 2019 13:08:43 +0300 Subject: [PATCH 09/14] credo fixes --- lib/pleroma/web/twitter_api/twitter_api_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 8ca754b51..42234ae09 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -6,8 +6,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller alias Ecto.Changeset - alias Pleroma.User alias Pleroma.Notification + alias Pleroma.User alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TokenView From cc1d1ee4069c47d2e5e91347438b2a6c7bff86cf Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 3 Sep 2019 17:54:21 +0300 Subject: [PATCH 10/14] Mastdon API: Add ability to get a remote account by nickname to `/api/v1/accounts/:id` --- lib/pleroma/plugs/trailing_format_plug.ex | 40 ++++++++ lib/pleroma/user.ex | 8 +- lib/pleroma/web/endpoint.ex | 2 +- .../controllers/mastodon_api_controller.ex | 25 ++++- .../mastodon_api_controller_test.exs | 91 +++++++++++++++---- 5 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 lib/pleroma/plugs/trailing_format_plug.ex diff --git a/lib/pleroma/plugs/trailing_format_plug.ex b/lib/pleroma/plugs/trailing_format_plug.ex new file mode 100644 index 000000000..2473e07fe --- /dev/null +++ b/lib/pleroma/plugs/trailing_format_plug.ex @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.TrailingFormatPlug do + @moduledoc "Calls TrailingFormatPlug for specific paths. Ideally we would just do this in the router, but TrailingFormatPlug needs to be called before Plug.Parsers." + + @behaviour Plug + @paths [ + "/api/statusnet", + "/api/statuses", + "/api/qvitter", + "/api/search", + "/api/account", + "/api/friends", + "/api/mutes", + "/api/media", + "/api/favorites", + "/api/blocks", + "/api/friendships", + "/api/users", + "/users", + "/nodeinfo", + "/api/help", + "/api/externalprofile", + "/notice" + ] + + def init(opts) do + TrailingFormatPlug.init(opts) + end + + for path <- @paths do + def call(%{request_path: unquote(path) <> _} = conn, opts) do + TrailingFormatPlug.call(conn, opts) + end + end + + def call(conn, _opts), do: conn +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 29fd6d2ea..d68015a80 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -569,8 +569,12 @@ def get_cached_by_nickname(nickname) do end) end - def get_cached_by_nickname_or_id(nickname_or_id) do - get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id) + def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do + if is_integer(nickname_or_id) or Pleroma.FlakeId.is_flake_id?(nickname_or_id) do + get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id) + else + unless opts[:restrict_remote_nicknames], do: get_cached_by_nickname(nickname_or_id) + end end def get_by_nickname(nickname) do diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index c123530dc..eb805e853 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -57,7 +57,7 @@ defmodule Pleroma.Web.Endpoint do plug(Phoenix.CodeReloader) end - plug(TrailingFormatPlug) + plug(Pleroma.Plugs.TrailingFormatPlug) plug(Plug.RequestId) plug(Plug.Logger) diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 83e877c0e..c5f281976 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -290,7 +290,7 @@ def verify_app_credentials(%{assigns: %{user: _user, token: token}} = conn, _) d end def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do - with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id), + with %User{} = user <- get_user_by_nickname_or_id(for_user, nickname_or_id), true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do account = AccountView.render("account.json", %{user: user, for: for_user}) json(conn, account) @@ -390,7 +390,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do end def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do - with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"]) do + with %User{} = user <- get_user_by_nickname_or_id(reading_user, params["id"]) do params = params |> Map.put("tag", params["tagged"]) @@ -1697,4 +1697,25 @@ def try_render(conn, _, _) do defp present?(nil), do: false defp present?(false), do: false defp present?(_), do: true + + defp get_user_by_nickname_or_id(for_user, nickname_or_id) do + restrict_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) + + opts = + cond do + restrict_to_local == :all -> + [restrict_remote_nicknames: true] + + restrict_to_local == false -> + [] + + restrict_to_local == :unauthenticated and match?(%User{}, for_user) -> + [] + + true -> + [restrict_remote_nicknames: true] + end + + User.get_cached_by_nickname_or_id(nickname_or_id, opts) + end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 4fd0a5aeb..427ee6f63 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1675,32 +1675,85 @@ test "/api/v1/follow_requests/:id/reject works" do end end - test "account fetching", %{conn: conn} do - user = insert(:user) + describe "account fetching" do + test "works by id" do + user = insert(:user) - conn = - conn - |> get("/api/v1/accounts/#{user.id}") + conn = + build_conn() + |> get("/api/v1/accounts/#{user.id}") - assert %{"id" => id} = json_response(conn, 200) - assert id == to_string(user.id) + assert %{"id" => id} = json_response(conn, 200) + assert id == to_string(user.id) - conn = - build_conn() - |> get("/api/v1/accounts/-1") + conn = + build_conn() + |> get("/api/v1/accounts/-1") - assert %{"error" => "Can't find user"} = json_response(conn, 404) - end + assert %{"error" => "Can't find user"} = json_response(conn, 404) + end - test "account fetching also works nickname", %{conn: conn} do - user = insert(:user) + test "works by nickname" do + user = insert(:user) - conn = - conn - |> get("/api/v1/accounts/#{user.nickname}") + conn = + build_conn() + |> get("/api/v1/accounts/#{user.nickname}") - assert %{"id" => id} = json_response(conn, 200) - assert id == user.id + assert %{"id" => id} = json_response(conn, 200) + assert id == user.id + end + + test "works by nickname for remote users" do + limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) + Pleroma.Config.put([:instance, :limit_to_local_content], false) + user = insert(:user, nickname: "user@example.com", local: false) + + conn = + build_conn() + |> get("/api/v1/accounts/#{user.nickname}") + + Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) + assert %{"id" => id} = json_response(conn, 200) + assert id == user.id + end + + test "respects limit_to_local_content == :all for remote user nicknames" do + limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) + Pleroma.Config.put([:instance, :limit_to_local_content], :all) + + user = insert(:user, nickname: "user@example.com", local: false) + + conn = + build_conn() + |> get("/api/v1/accounts/#{user.nickname}") + + Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) + assert json_response(conn, 404) + end + + test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do + limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) + Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + + user = insert(:user, nickname: "user@example.com", local: false) + reading_user = insert(:user) + + conn = + build_conn() + |> get("/api/v1/accounts/#{user.nickname}") + + assert json_response(conn, 404) + + conn = + build_conn() + |> assign(:user, reading_user) + |> get("/api/v1/accounts/#{user.nickname}") + + Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) + assert %{"id" => id} = json_response(conn, 200) + assert id == user.id + end end test "mascot upload", %{conn: conn} do From b312ca3d528305ebc3b0c72799af535a406ce251 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 5 Sep 2019 11:58:02 +0300 Subject: [PATCH 11/14] Mastodon API Poll view: Fix handling of polls without an end date --- CHANGELOG.md | 1 + .../web/mastodon_api/views/status_view.ex | 31 +++++++++++++------ .../tesla_mock/misskey_poll_no_end_date.json | 1 + test/fixtures/tesla_mock/sjw.json | 1 + test/support/http_request_mock.ex | 12 +++++++ .../mastodon_api/views/status_view_test.exs | 8 +++++ 6 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/tesla_mock/misskey_poll_no_end_date.json create mode 100644 test/fixtures/tesla_mock/sjw.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f4e7a132..fbbaf18f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `federation_incoming_replies_max_depth` option being ignored in certain cases - Federation/MediaProxy not working with instances that have wrong certificate order - Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`) +- Mastodon API: Misskey's endless polls being unable to render - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity - Mastodon API: Notifications endpoint crashing if one notification failed to render - Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 4c3c8c564..e71083b91 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -385,16 +385,27 @@ def render("poll.json", %{object: object} = opts) do end if options do - end_time = - (object.data["closed"] || object.data["endTime"]) - |> NaiveDateTime.from_iso8601!() + {end_time, expired} = + case object.data["closed"] || object.data["endTime"] do + end_time when is_binary(end_time) -> + end_time = + (object.data["closed"] || object.data["endTime"]) + |> NaiveDateTime.from_iso8601!() - expired = - end_time - |> NaiveDateTime.compare(NaiveDateTime.utc_now()) - |> case do - :lt -> true - _ -> false + expired = + end_time + |> NaiveDateTime.compare(NaiveDateTime.utc_now()) + |> case do + :lt -> true + _ -> false + end + + end_time = Utils.to_masto_date(end_time) + + {end_time, expired} + + _ -> + {nil, false} end voted = @@ -421,7 +432,7 @@ def render("poll.json", %{object: object} = opts) do # Mastodon uses separate ids for polls, but an object can't have # more than one poll embedded so object id is fine id: to_string(object.id), - expires_at: Utils.to_masto_date(end_time), + expires_at: end_time, expired: expired, multiple: multiple, votes_count: votes_count, diff --git a/test/fixtures/tesla_mock/misskey_poll_no_end_date.json b/test/fixtures/tesla_mock/misskey_poll_no_end_date.json new file mode 100644 index 000000000..0e08de4de --- /dev/null +++ b/test/fixtures/tesla_mock/misskey_poll_no_end_date.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"Hashtag":"as:Hashtag"}],"id":"https://skippers-bin.com/notes/7x9tmrp97i","type":"Question","attributedTo":"https://skippers-bin.com/users/7v1w1r8ce6","summary":null,"content":"

@march@marchgenso.me How are your notifications now?
リモートで結果を表示

","_misskey_content":"@march@marchgenso.me How are your notifications now?\n[リモートで結果を表示](https://skippers-bin.com/notes/7x9tmrp97i)","published":"2019-09-05T05:35:32.541Z","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://skippers-bin.com/users/7v1w1r8ce6/followers","https://marchgenso.me/users/march"],"inReplyTo":null,"attachment":[],"sensitive":false,"tag":[{"type":"Mention","href":"https://marchgenso.me/users/march","name":"@march@marchgenso.me"}],"_misskey_fallback_content":"

@march@marchgenso.me How are your notifications now?
リモートで結果を表示
----------------------------------------
0: Working
1: Broken af
----------------------------------------
番号を返信して投票

","endTime":null,"oneOf":[{"type":"Note","name":"Working","replies":{"type":"Collection","totalItems":0}},{"type":"Note","name":"Broken af","replies":{"type":"Collection","totalItems":1}}]} \ No newline at end of file diff --git a/test/fixtures/tesla_mock/sjw.json b/test/fixtures/tesla_mock/sjw.json new file mode 100644 index 000000000..ff64478d3 --- /dev/null +++ b/test/fixtures/tesla_mock/sjw.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"Hashtag":"as:Hashtag"}],"type":"Person","id":"https://skippers-bin.com/users/7v1w1r8ce6","inbox":"https://skippers-bin.com/users/7v1w1r8ce6/inbox","outbox":"https://skippers-bin.com/users/7v1w1r8ce6/outbox","followers":"https://skippers-bin.com/users/7v1w1r8ce6/followers","following":"https://skippers-bin.com/users/7v1w1r8ce6/following","featured":"https://skippers-bin.com/users/7v1w1r8ce6/collections/featured","sharedInbox":"https://skippers-bin.com/inbox","endpoints":{"sharedInbox":"https://skippers-bin.com/inbox"},"url":"https://skippers-bin.com/@sjw","preferredUsername":"sjw","name":"It's ya boi sjw :verified:","summary":"

Admin of skippers-bin.com and neckbeard.xyz
For the most part I'm just a normal user. I mostly post animu, lewds, may-mays, and shitposts.

Not an alt of
@sjw@neckbeard.xyz but another main.

Email/XMPP: neckbeard@rape.lol
PGP: d016 b622 75ba bcbc 5b3a fced a7d9 4824 0eb3 9c4e

","icon":{"type":"Image","url":"https://skippers-bin.com/files/webpublic-21b17f5b-3a83-4f50-8d4f-eda92066aa26","sensitive":false},"image":{"type":"Image","url":"https://skippers-bin.com/files/webpublic-1cd7f961-421e-4c31-aa03-74fb82584308","sensitive":false},"tag":[{"id":"https://skippers-bin.com/emojis/verified","type":"Emoji","name":":verified:","updated":"2019-07-12T02:16:12.088Z","icon":{"type":"Image","mediaType":"image/png","url":"https://skippers-bin.com/files/webpublic-dd10b435-6dad-4602-938b-f69ec0a19f2c"}}],"manuallyApprovesFollowers":false,"publicKey":{"id":"https://skippers-bin.com/users/7v1w1r8ce6/publickey","type":"Key","owner":"https://skippers-bin.com/users/7v1w1r8ce6","publicKeyPem":"-----BEGIN RSA PUBLIC KEY-----\nMIICCgKCAgEAvmp71/A6Oxe1UW/44HK0juAJhrjv9gYhaoslaS9K1FB+BHfIjaE9\n9+W2SKRLnVNYNFSN4JJrSGhX5RUjAsf4tcdRDVcmHl7tp2sgOAZeZz5geULm2sJQ\nwElnGk34jT/xCfX+w/O+7DuX31sU7ZK0B2P7ulNGDQXhrzVO0RMx7HhNcsFcusno\n3kmPyyPT1l+PbM2UNWms599/3yicKtuOzMgzxNeXvuHYtAO19txyPiOeYckQOMmT\nwEVIxypgCgNQ0MNtPLPKQTwOgVbvnN7MN+h3esKeKDcPcGQySkbkjZPaVnA6xCQf\nj58c19wqdCfAS4Effo5/bxVmhLpe0l9HYpV7IMasv2LhFntmSmAxBQzhdz0oTYb1\naNqiyfZdClnzutOiKcrFppADo4rZH9Z1WlPHapahrKbF0GRPN8DjSUsoBxfY9wZs\ntlL056hT4o+EFHYrRGo7KP6X/6aQ9sSsmpE08aVpVuXdwuaoaDlW1KrJ0oOk4lZw\nUNXvjEaN3c+VQAw2CNvkAqLuwrjnw7MdcxEGodEXb6s8VvoSOaiDqT7cexSaZe0R\nliCe/3dqFXpX1UrgRiryI4yc1BrEJIGTanchmP2aUJ2R2pccFsREp23C3vMN3M5b\nHw7fvKbUQHyf6lhRoLCOSCz1xaPutaMJmpwLuJo4wPCHGg9QFBYsqxcCAwEAAQ==\n-----END RSA PUBLIC KEY-----\n"},"isCat":true} diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 05eebbe9b..231e7c498 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -992,6 +992,18 @@ def get("http://example.com/rel_me/null", _, _, _) do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_null.html")}} end + def get("https://skippers-bin.com/notes/7x9tmrp97i", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/misskey_poll_no_end_date.json") + }} + end + + def get("https://skippers-bin.com/users/7v1w1r8ce6", _, _, _) do + {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/sjw.json")}} + end + def get(url, query, body, headers) do {:error, "Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{ diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 90451cbdc..fcdd7fbcb 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -551,6 +551,14 @@ test "detects vote status" do assert Enum.at(result[:options], 1)[:votes_count] == 1 assert Enum.at(result[:options], 2)[:votes_count] == 1 end + + test "does not crash on polls with no end date" do + object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i") + result = StatusView.render("poll.json", %{object: object}) + + assert result[:expires_at] == nil + assert result[:expired] == false + end end test "embeds a relationship in the account" do From 26fe6f70c9cd6a37e72f4795a1a9a316ef5d95fb Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 5 Sep 2019 15:33:49 +0300 Subject: [PATCH 12/14] Move checking for restrict_local to User.get_cached_by_id_or_nickname --- lib/pleroma/user.ex | 18 ++++++++++--- .../controllers/mastodon_api_controller.ex | 25 ++----------------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d68015a80..3aa245f2a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -570,10 +570,20 @@ def get_cached_by_nickname(nickname) do end def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do - if is_integer(nickname_or_id) or Pleroma.FlakeId.is_flake_id?(nickname_or_id) do - get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id) - else - unless opts[:restrict_remote_nicknames], do: get_cached_by_nickname(nickname_or_id) + restrict_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) + + cond do + is_integer(nickname_or_id) or Pleroma.FlakeId.is_flake_id?(nickname_or_id) -> + get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id) + + restrict_to_local == false -> + get_cached_by_nickname(nickname_or_id) + + restrict_to_local == :unauthenticated and match?(%User{}, opts[:for]) -> + get_cached_by_nickname(nickname_or_id) + + true -> + nil end end diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index c5f281976..8dfad7a54 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -290,7 +290,7 @@ def verify_app_credentials(%{assigns: %{user: _user, token: token}} = conn, _) d end def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do - with %User{} = user <- get_user_by_nickname_or_id(for_user, nickname_or_id), + with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do account = AccountView.render("account.json", %{user: user, for: for_user}) json(conn, account) @@ -390,7 +390,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do end def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do - with %User{} = user <- get_user_by_nickname_or_id(reading_user, params["id"]) do + with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do params = params |> Map.put("tag", params["tagged"]) @@ -1697,25 +1697,4 @@ def try_render(conn, _, _) do defp present?(nil), do: false defp present?(false), do: false defp present?(_), do: true - - defp get_user_by_nickname_or_id(for_user, nickname_or_id) do - restrict_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) - - opts = - cond do - restrict_to_local == :all -> - [restrict_remote_nicknames: true] - - restrict_to_local == false -> - [] - - restrict_to_local == :unauthenticated and match?(%User{}, for_user) -> - [] - - true -> - [restrict_remote_nicknames: true] - end - - User.get_cached_by_nickname_or_id(nickname_or_id, opts) - end end From 3523bdcf262dddc7bdf14d759538097f8838cddb Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 5 Sep 2019 22:21:20 +0300 Subject: [PATCH 13/14] Call TrailingFormatPlug for /api/pleroma/emoji Apparently Pleroma-FE still calls it with trailing '.json' --- lib/pleroma/plugs/trailing_format_plug.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/plugs/trailing_format_plug.ex b/lib/pleroma/plugs/trailing_format_plug.ex index 2473e07fe..ce366b218 100644 --- a/lib/pleroma/plugs/trailing_format_plug.ex +++ b/lib/pleroma/plugs/trailing_format_plug.ex @@ -23,7 +23,8 @@ defmodule Pleroma.Plugs.TrailingFormatPlug do "/nodeinfo", "/api/help", "/api/externalprofile", - "/notice" + "/notice", + "/api/pleroma/emoji" ] def init(opts) do From 16e6be340dc56aa03a1a9eed77843962ce97d5ca Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 6 Sep 2019 11:31:44 +0300 Subject: [PATCH 14/14] Update frontend bundle to e75ac9dd --- priv/static/index.html | 2 +- priv/static/static/config.json | 1 - ...a6198.css => app.cb3673e4b661fd9526ea.css} | 35 +++++++++++------- .../css/app.cb3673e4b661fd9526ea.css.map | 1 + .../css/app.db80066bde2c96ea6198.css.map | 1 - priv/static/static/font/LICENSE.txt | 0 priv/static/static/font/README.txt | 0 priv/static/static/font/config.json | 26 ++++++++++--- .../static/static/font/css/fontello-codes.css | 3 +- .../static/font/css/fontello-embedded.css | 15 ++++---- .../static/font/css/fontello-ie7-codes.css | 3 +- priv/static/static/font/css/fontello-ie7.css | 3 +- priv/static/static/font/css/fontello.css | 17 +++++---- priv/static/static/font/demo.html | 21 ++++++----- priv/static/static/font/font/fontello.eot | Bin 19060 -> 19452 bytes priv/static/static/font/font/fontello.svg | 4 +- priv/static/static/font/font/fontello.ttf | Bin 18892 -> 19284 bytes priv/static/static/font/font/fontello.woff | Bin 11452 -> 11776 bytes priv/static/static/font/font/fontello.woff2 | Bin 9724 -> 9980 bytes .../static/js/app.670c36c0acc42fadb4fe.js | Bin 856921 -> 0 bytes .../static/js/app.670c36c0acc42fadb4fe.js.map | Bin 1429874 -> 0 bytes .../static/js/app.8098503330c7dd14a415.js | Bin 0 -> 961729 bytes .../static/js/app.8098503330c7dd14a415.js.map | Bin 0 -> 1499246 bytes .../js/vendors~app.4b7be53256fba5c365c9.js | Bin 430333 -> 0 bytes .../vendors~app.4b7be53256fba5c365c9.js.map | Bin 1994198 -> 0 bytes .../js/vendors~app.4cedffe4993b111c7421.js | Bin 0 -> 465520 bytes .../vendors~app.4cedffe4993b111c7421.js.map | Bin 0 -> 2162926 bytes priv/static/sw-pleroma.js | Bin 31068 -> 31068 bytes 28 files changed, 82 insertions(+), 50 deletions(-) rename priv/static/static/css/{app.db80066bde2c96ea6198.css => app.cb3673e4b661fd9526ea.css} (84%) create mode 100644 priv/static/static/css/app.cb3673e4b661fd9526ea.css.map delete mode 100644 priv/static/static/css/app.db80066bde2c96ea6198.css.map mode change 100644 => 100755 priv/static/static/font/LICENSE.txt mode change 100644 => 100755 priv/static/static/font/README.txt mode change 100644 => 100755 priv/static/static/font/config.json mode change 100644 => 100755 priv/static/static/font/demo.html delete mode 100644 priv/static/static/js/app.670c36c0acc42fadb4fe.js delete mode 100644 priv/static/static/js/app.670c36c0acc42fadb4fe.js.map create mode 100644 priv/static/static/js/app.8098503330c7dd14a415.js create mode 100644 priv/static/static/js/app.8098503330c7dd14a415.js.map delete mode 100644 priv/static/static/js/vendors~app.4b7be53256fba5c365c9.js delete mode 100644 priv/static/static/js/vendors~app.4b7be53256fba5c365c9.js.map create mode 100644 priv/static/static/js/vendors~app.4cedffe4993b111c7421.js create mode 100644 priv/static/static/js/vendors~app.4cedffe4993b111c7421.js.map diff --git a/priv/static/index.html b/priv/static/index.html index e58c4380b..f681f4def 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +Pleroma
\ No newline at end of file diff --git a/priv/static/static/config.json b/priv/static/static/config.json index 5cdb33a0a..c82678699 100644 --- a/priv/static/static/config.json +++ b/priv/static/static/config.json @@ -6,7 +6,6 @@ "logoMargin": ".1em", "redirectRootNoLogin": "/main/all", "redirectRootLogin": "/main/friends", - "chatDisabled": false, "showInstanceSpecificPanel": false, "collapseMessageWithSubject": false, "scopeCopy": true, diff --git a/priv/static/static/css/app.db80066bde2c96ea6198.css b/priv/static/static/css/app.cb3673e4b661fd9526ea.css similarity index 84% rename from priv/static/static/css/app.db80066bde2c96ea6198.css rename to priv/static/static/css/app.cb3673e4b661fd9526ea.css index b87bc5901..e083f12c8 100644 --- a/priv/static/static/css/app.db80066bde2c96ea6198.css +++ b/priv/static/static/css/app.cb3673e4b661fd9526ea.css @@ -1,17 +1,8 @@ -.with-load-more-footer { - padding: 10px; - text-align: center; - border-top: 1px solid; - border-top-color: #222; - border-top-color: var(--border, #222); -} -.with-load-more-footer .error { - font-size: 14px; -} .tab-switcher .contents .hidden { display: none; } .tab-switcher .tabs { + display: -ms-flexbox; display: flex; position: relative; width: 100%; @@ -23,7 +14,8 @@ .tab-switcher .tabs { .tab-switcher .tabs::after, .tab-switcher .tabs::before { display: block; content: ""; - flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; border-bottom: 1px solid; border-bottom-color: #222; border-bottom-color: var(--border, #222); @@ -31,8 +23,10 @@ .tab-switcher .tabs::after, .tab-switcher .tabs::before { .tab-switcher .tabs .tab-wrapper { height: 28px; position: relative; + display: -ms-flexbox; display: flex; - flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; } .tab-switcher .tabs .tab-wrapper .tab { width: 100%; @@ -55,6 +49,11 @@ .tab-switcher .tabs .tab-wrapper .tab.active { background: transparent; z-index: 5; } +.tab-switcher .tabs .tab-wrapper .tab img { + max-height: 26px; + vertical-align: top; + margin-top: -5px; +} .tab-switcher .tabs .tab-wrapper:not(.active)::after { content: ""; position: absolute; @@ -66,6 +65,16 @@ .tab-switcher .tabs .tab-wrapper:not(.active)::after { border-bottom-color: #222; border-bottom-color: var(--border, #222); } +.with-load-more-footer { + padding: 10px; + text-align: center; + border-top: 1px solid; + border-top-color: #222; + border-top-color: var(--border, #222); +} +.with-load-more-footer .error { + font-size: 14px; +} .with-subscription-loading { padding: 10px; text-align: center; @@ -74,4 +83,4 @@ .with-subscription-loading .error { font-size: 14px; } -/*# sourceMappingURL=app.db80066bde2c96ea6198.css.map*/ \ No newline at end of file +/*# sourceMappingURL=app.cb3673e4b661fd9526ea.css.map*/ \ No newline at end of file diff --git a/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map b/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map new file mode 100644 index 000000000..8cecb0901 --- /dev/null +++ b/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;AClEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACTA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.cb3673e4b661fd9526ea.css","sourcesContent":[".tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher .tabs .tab-wrapper {\n height: 28px;\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tabs .tab-wrapper .tab {\n width: 100%;\n min-width: 1px;\n position: relative;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 1em;\n padding-bottom: 99px;\n margin-bottom: -93px;\n white-space: nowrap;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tabs .tab-wrapper .tab.active {\n background: transparent;\n z-index: 5;\n}\n.tab-switcher .tabs .tab-wrapper .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 7;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}",".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}",".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/css/app.db80066bde2c96ea6198.css.map b/priv/static/static/css/app.db80066bde2c96ea6198.css.map deleted file mode 100644 index 86f0dd18f..000000000 --- a/priv/static/static/css/app.db80066bde2c96ea6198.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACzDA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.db80066bde2c96ea6198.css","sourcesContent":[".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}",".tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .tabs {\n display: flex;\n position: relative;\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n content: \"\";\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher .tabs .tab-wrapper {\n height: 28px;\n position: relative;\n display: flex;\n flex: 0 0 auto;\n}\n.tab-switcher .tabs .tab-wrapper .tab {\n width: 100%;\n min-width: 1px;\n position: relative;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 1em;\n padding-bottom: 99px;\n margin-bottom: -93px;\n white-space: nowrap;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tabs .tab-wrapper .tab.active {\n background: transparent;\n z-index: 5;\n}\n.tab-switcher .tabs .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 7;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}",".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/font/LICENSE.txt b/priv/static/static/font/LICENSE.txt old mode 100644 new mode 100755 diff --git a/priv/static/static/font/README.txt b/priv/static/static/font/README.txt old mode 100644 new mode 100755 diff --git a/priv/static/static/font/config.json b/priv/static/static/font/config.json old mode 100644 new mode 100755 index baa2c763a..72a48a74f --- a/priv/static/static/font/config.json +++ b/priv/static/static/font/config.json @@ -150,12 +150,6 @@ "code": 61669, "src": "fontawesome" }, - { - "uid": "cd21cbfb28ad4d903cede582157f65dc", - "css": "bell", - "code": 59408, - "src": "fontawesome" - }, { "uid": "ccc2329632396dc096bb638d4b46fb98", "css": "mail-alt", @@ -277,6 +271,26 @@ "search": [ "ellipsis" ] + }, + { + "uid": "0bef873af785ead27781fdf98b3ae740", + "css": "bell-ringing-o", + "code": 59408, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M497.8 0C468.3 0 444.4 23.9 444.4 53.3 444.4 61.1 446.1 68.3 448.9 75 301.7 96.7 213.3 213.3 213.3 320 213.3 588.3 117.8 712.8 35.6 782.2 35.6 821.1 67.8 853.3 106.7 853.3H355.6C355.6 931.7 419.4 995.6 497.8 995.6S640 931.7 640 853.3H888.9C927.8 853.3 960 821.1 960 782.2 877.8 712.8 782.2 588.3 782.2 320 782.2 213.3 693.9 96.7 546.7 75 549.4 68.3 551.1 61.1 551.1 53.3 551.1 23.9 527.2 0 497.8 0ZM189.4 44.8C108.4 118.6 70.5 215.1 71.1 320.2L142.2 319.8C141.7 231.2 170.4 158.3 237.3 97.4L189.4 44.8ZM806.2 44.8L758.3 97.4C825.2 158.3 853.9 231.2 853.3 319.8L924.4 320.2C925.1 215.1 887.2 118.6 806.2 44.8ZM408.9 844.4C413.9 844.4 417.8 848.3 417.8 853.3 417.8 897.2 453.9 933.3 497.8 933.3 502.8 933.3 506.7 937.2 506.7 942.2S502.8 951.1 497.8 951.1C443.9 951.1 400 907.2 400 853.3 400 848.3 403.9 844.4 408.9 844.4Z", + "width": 1000 + }, + "search": [ + "bell-ringing-o" + ] + }, + { + "uid": "0b2b66e526028a6972d51a6f10281b4b", + "css": "zoom-in", + "code": 59420, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/priv/static/static/font/css/fontello-codes.css b/priv/static/static/font/css/fontello-codes.css index 5f84df349..2083f618a 100755 --- a/priv/static/static/font/css/fontello-codes.css +++ b/priv/static/static/font/css/fontello-codes.css @@ -15,7 +15,7 @@ .icon-video:before { content: '\e80c'; } /* '' */ .icon-right-open:before { content: '\e80d'; } /* '' */ .icon-left-open:before { content: '\e80e'; } /* '' */ .icon-up-open:before { content: '\e80f'; } /* '' */ -.icon-bell:before { content: '\e810'; } /* '' */ +.icon-bell-ringing-o:before { content: '\e810'; } /* '' */ .icon-lock:before { content: '\e811'; } /* '' */ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ @@ -27,6 +27,7 @@ .icon-pencil:before { content: '\e818'; } /* '' */ .icon-pin:before { content: '\e819'; } /* '' */ .icon-wrench:before { content: '\e81a'; } /* '' */ .icon-chart-bar:before { content: '\e81b'; } /* '' */ +.icon-zoom-in:before { content: '\e81c'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/priv/static/static/font/css/fontello-embedded.css b/priv/static/static/font/css/fontello-embedded.css index b4079ea06..ad4246e6e 100755 --- a/priv/static/static/font/css/fontello-embedded.css +++ b/priv/static/static/font/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?3632997'); - src: url('../font/fontello.eot?3632997#iefix') format('embedded-opentype'), - url('../font/fontello.svg?3632997#fontello') format('svg'); + src: url('../font/fontello.eot?49712213'); + src: url('../font/fontello.eot?49712213#iefix') format('embedded-opentype'), + url('../font/fontello.svg?49712213#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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') format('truetype'); + src: url('data:application/octet-stream;base64,d09GRgABAAAAAC4AAA8AAAAAS1QAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY+L1N4Y21hcAAAAdgAAAFvAAAEUAeNlGtjdnQgAAADSAAAABMAAAAgBv/+9GZwZ20AAANcAAAFkAAAC3CKkZBZZ2FzcAAACOwAAAAIAAAACAAAABBnbHlmAAAI9AAAIH8AADLWU0P5MWhlYWQAACl0AAAAMgAAADYWS6h0aGhlYQAAKagAAAAgAAAAJAfJBAlobXR4AAApyAAAAGAAAAC4pX3/4WxvY2EAACooAAAAXgAAAF4sEh0AbWF4cAAAKogAAAAgAAAAIAGDDaZuYW1lAAAqqAAAAXcAAALNzJ0fIXBvc3QAACwgAAABYgAAAf2XlBi5cHJlcAAALYQAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZJ7JOIGBlYGBqYppDwMDQw+EZnzAYMjIBBRlYGVmwAoC0lxTGBxeMHwyYY78X8gQxZzOMA8ozAiSAwD3dAwvAHic3dRLThtBFIXh38bhFZKAeYWEkAQIT3uCPEZCYhWIIeuBdbELJpbOsMrMDad87xSYp1uf1W613NW6fxv4BMzZwHrQPaTjIzp7PtuZnZ9jeXa+x8Tf9/njo652daFReSiP5amMy3OZ1n69rFf1ut7V8WT08gJids39e9d8sHV8v5vZfvvG3q7peo09P8k8Cyyy5PV+ZoUvfOUbq6zRZ50NNtlim+/s8IOf7PKLPX77af76Hgcc8o8jjjnhlDPO/dxD//T8hyv8/7eV9tEd5Ldhm2tofSh5Bii1npRaU0qtNSXPCiVPDSXPDyVPEqXWoJKni1JbnZInjpJnj5IrQMk9oOQyUHIjKLkWlNwNSi4IJbeEkqtCyX2h5NJQcnMouT6U3CFKLtJvTHCbaBRcKeU+uFfKQ3C5lMfghilPwTVTxsFdU56DC6dMg1un9oOrp14G90+9Cn4TqNfB7wT1LvjtoI5D+9+YjALDV9E5p/wAeJxjYEADEhDInP4/CYQBEw4D9wB4nK1WaXfTRhQdeUmchCwlCy1qYcTEabBGJmzBgAlBsmMgXZytlaCLFDvpvvGJ3+Bf82Tac+g3flrvGy8kkLTncJqTo3fnzdXM22USWpLYC+uRlJsvxdTWJo3sPAnphk3LUXwoO3shZYrJ3wVREK2W2rcdh0REIlC1rrBEEPseWZpkfOhRRsu2pFdNyi096S5b40G9Vd9+GjrKsTuhpGYzdGg9siVVGFWiSKY9UtKmZaj6K0krvL/CzFfNUMKITiJpvBnG0EjeG2e0ymg1tuMoimyy3ChSJJrhQRR5lNUS5+SKCQzKB82Q8sqnEeXD/Iis2KOcVrBLttP8vi95p3c5P7Ffb1G25EAfyI7s4Ox0JV+EW1th3LST7ShUEXbXd0Js2exU/2aP8ppGA7crMr3QjGCpfIUQKz+hzP4hWS2cT/mSR6NaspETQetlTuxLPoHW44gpcc0YWdDd0QkR1P2SMwz2mD4e/PHeKZYLEwJ4HMt6RyWcCBMpYXM0SdowcmAlZYsqqfWumDjldVrEW8J+7drRl85o41B3YjxbDx1bOVHJ8WhSp5lMndpJzaMpDaKUdCZ4zK8DKD+iSV5tYzWJlUfTOGbGhEQiAi3cS1NBLDuxpCkEzaMZvbkbprl2LVqkyQP13KP39OZWuLnTU9oO9LNGf1anYjrYC9PpaeQv8Wna5SJF6frpGX5M4kHWAjKRLTbDlIMHb/0O0svXlhyF1wbY7u3zK6h91kTwpAH7G9AeT9UpCUyFmFWIVkBirWtZlsnVrBapyNR3Q5pWvqzTBIpyHBfHvoxx/V8zM5aYEr7fidOzIy49c+1LCNMcfJt1PZrXqcVyAXFmeU6nWZbv6zTH8gOd5lme1+kIS1unoyw/1GmB5Uc6HWN5QQuadN/BkIsw5AIOkDCEpQNDWF6CISwVDGG5CENYFmEIyyUYwvJjGMJyGYawvKxl1dRTSePamVgGbEJgYo4eucxF5WoquVRCu2hUakOeEm6VVBTPqn9loF488oY5sBZIl8iaXzHOlY9G5fjWFS1vGjtXwLHqbx+O9jnxUtaLhT8F/9XWVCW9Ys3Dk6vwG4aebCeqNql4dE2Xz1U9uv5fVFRYC/QbSIVYKMqybHBnIoSPOp2GaqCVQ8xszDy063XLmp/D/TcxQhZQ/fg3FBoL3INOWUlZ7eCs1dfbstw7g3I4EyxJMTfz+lb4IiOz0n6RWcqej3wecAWMSmXYagOtFbzZJzEPmd4kzwRxW1E2SNrYzgSJDRzzgHnznQQmYeqqDeRO4YYN+AVhbsF5J1yieqMsh+5F7PMopPxbp+JE9qhojMCz2Rthr+9Cym9xDCQ0+aV+DFQVoakYNRXQNFJuqAZfxtm6bULGDvQjKnbDsqziw8cW95WSbRmEfKSI1aOjn9Zeok6q3H5mFJfvnb4FwSA1MX9733RxkMq7WskyR20DU7calVPXmkPjVYfq5lH1vePsEzlrmm66Jx56X9Oq28HFXCyw9m0O0lImF9T1YYUNosvFpVDqZTRJ77gHGBYY0O9Qio3/q/rYfJ4rVYXRcSTfTtS30edgDPwP2H9H9QPQ92Pocg0uz/eaE59u9OFsma6iF+un6Dcwa625WboG3NB0A+IhR62OuMoNfKcGcXqkuRzpIeBj3RXiAcAmgMXgE921jOZTAKP5jDk+wOfMYdBkDoMt5jDYZs4awA5zGOwyh8Eecxh8wZx1gC+ZwyBkDoOIOQyeMCcAeMocBl8xh8HXzGHwDXPuA3zLHAYxcxgkzGGwr+nWMMwtXtBdoLZBVaADU09Y3MPiUFNlyP6OF4b9vUHM/sEgpv6o6faQ+hMvDPVng5j6i0FM/VXTnSH1N14Y6u8GMfUPg5j6TL8Yy2UGv4x8lwoHlF1sPufvifcP28VAuQABAAH//wAPeJzFew2QXNWV3j333vf/+v/1656f7unp3/nTaNS/0kgatX5HoJE0kgYxIyQxCEmABmmAxcACwxJLS0HMIqKlCLFrMcpiqhIbh5VsTOIYXF5hb0RSBeu1THmzVTF2uYSdsC6H3c1qUSvnvO4ZjfhZJ1uVymjmdb/37r3v3nPP+c53znliwNiVv+V/wf+AZVmy3pFuC2qScRgVwBmfBbx92Ol0HKnE+3NOANT0UtDokC+vgQIdqsUuqNHBxdsxl/9FYCw4EHzxRTyMBekzePU8EHjxxcD9Ln35ylcCn2wYGKQGTOKcXhOnRYXpLMR6WZ1tqq+v4HMNxnFWo8xQjVkdVE2dZZrQZrEDlxMKCJwuF2yaSckn8RIfW70qU8qki7kV8bCpJPpz5byfJ6Fam/+MOmqmO50vVMrVWCkJK6FYrZWKrlD7AW9pGbqFh+YqXX7eSTo83h7/AycV5m5nfFPK/eitWBJS7gd2NXMyXfV94KZeNeInncDJgAMnY5HQJTNpXgpn/S4Pp8Ky3Z7/8sRZN5Vy8QBdPT1dSdjpXsIerv/SAHYxL4UY/tDevINyGGVdLFFvDwdMKRTaHLawNwknJpRYP6DsI1HHD97u5CvlWqRAx5y3M4orTgfOD9lR+x8u2a4NQ2/5uyD+iJWy5yCegl/bgTcb79tWELQTJ7SwKXWIvRmwo0pPIxZr9OATF+Zh4G4U6tnONsfvM3RNVQTY104ol4254aBQnH6oLQXUCK0WizRnl0l/xuz4Q//2V0fu+O9f7f3hDxs4z5j56fPsfSn9ox+lX/rV7CycaU658zMmjD805ytyiB9n3Ww9W1dfkwapklrjFDRQjxqgSk2VMzrquQZcmyatkxOoOmxSATwZW1d3u3PxbjfaE/F0x1ELqCpLYRBKoUx6EFpKQWoS7aZv8/aRL1dXQqW7+a3WXXS7IAnREOoVP2/ql99XVI7WBTO43/pZXNwZw/XPwFpDmZJwSD9jp6yzOl5pvE5XTJ3Hpddhxu+GNRu4kGDDdrfDvGDbF8xOBy6odyrv+cwLPt8Fs8O9oM0oPhObKVwXjTMuygIFcuW8uMhfw/1rZyNsA7uR3VifKHdwJnepaFI713Pg4+t6C2hUKshRpkhlFkWI5gRHGaj4O8NUgb8zTIhji0TFSFJjWyKDbVknoSnt/bnaINTKNVVzoZzX0mrUcYtVNK8SWpYTVTmKKJP2dn+Q8KM2AqVirIa3UUqu5kZQnBE35uAm+SGDd2v5Qi2JuALV/qHlkH70hv1wJGhtOhh0gxuGrOD5lb9c2amY2gajbfyxomXt/uhfFotdiin8VtYCIzp53R/JS5ZbmPivD/fe/2cb1+7LVA6krLu2Z46sXj+89sTTcBuq/cGNVjBoDW0Ifk7CHY09dxSNgmpqfdkHtob6wsefM6uGqjoqKI3L2x7tgHjb/kgku2T6yPXmiTsO1tdkD1QjqG9Xrly5G23EQczqZhN1swvNwY+QxEe3vNI9Pll3SWogEZ2ACQ5iGrHMx6/rqCcQs/hdV+8KARMMQEwyAWJs6luxjBMJK0pbP5QHQXXcEQCH1AzlOMhHZJKjtBB3j596+xT+QnJg2Hnj4EPjp26v81V3PvXiU3eugo1vROGJ207xZ88/pz7Z+GKiL/rGxpEjT//rp44Ny3WHn9360ME3oi2beU3sEWFcwxG2sb7u0NTYWsnkSpMDK/d0BCXOqKkcqCVMzhJEzeKMYRaXJFBj+OG9N+3acd3m/r50KhLWFBcnnU/7AXUgh4CKm6+5MdfBvS3QCnCXEWkREQr5AiIDHj2NqHlWRqCMNlbLz6tJF57gPwRp0hVUmmKsNZjmmRhfufOBnXz3vbuhU9duN61Ij6oExn2atrWt3dBk8GHdDnbEtqtBdZMrFb3HDOiHNR1M5XbdH8s12+pb4+2GLkIPo6UFOmPblYC22ZHSaDY24dDKiYn7JiYeoPvBZLSjqPrV6Dgoq3z6WGfQ1G4z7FWKWk8qftUuBjo7AmBrXtu29tQSzdac8UVNrZWKsr6z1bQ9iFDq7QFjYoafZwX0e4hbDkIFAqzKFa4qR5kiuIJWKAUTkh0lC1WBT9OJmEDbJIsUbMzNtOX6cgVN6UDccgOAIkI3VgmVPelFY96lQiataiHHjZWKSQ4OomI6vxoydEDcKqH43Ri4cAixAXT9zPr9+9ef0U2A5mmuDNXsN1SO+KFajXesTveS30X/5XZasNSq8LDiN7jYvx4eX7/f1C1DReGiMjQewI6S6zDgtxpvm07gtOu/gEh4Gh2jgRfm/d4H4lU+xBzWVnd9gOYwilJgqGfER2Jh8nmI4OkCNClIzGiBsfhq4wB63MYBy9qHn9ADPVanvdeCZxu3Whb8kZU091pW4128bO21OvFZVxpXHhKvidvYMtZV76Rne+bHJlHngY0B6+tly2AZebRYuoDgBtUYAZJGEsxX8RS/qjG3Rl9RV/EkyVtXPzy8YbPcDb8e3z+wyW4fb+R7plNJdQDG4uX2xjcG4rYdd+EnxdSqarURXicPPnY9/JpuBXf+/uZN392PHdvtTQPT1NFMxQ/2wbb2chw7tutcUsf7gv5iIzz22AFZhw/ig9SR5CcRi16TSz07DiB3WMpuqO9MOAg6AVyT32cbkrPOKBIkSZ6b2NNRhl5RgCoInhD1iUQpigfyyiRZ/FgoONifz7TFgl2hrkgkrHusw08uLgkQ7a7UYpDrbioU+rxqIVTOx0KI5ugza6GmH4RDI3tG8Jev+uiDM3sgAcmPjqNN2aqYQxMxd5RzHx3PVqGcE3O5Mo8vGeHrdq+Tw41Ll2bOTkHiNDrPPdRQ5y/pZvjyHk8F+Uv0wSzCX2/NzRWvYdvZzQhOv8dOsi+zf8fO1duernNDf/zR6ZRU5APLEXTHhxBimWwBdI1FwzbXjag+HQEjCFIx5HTIx9E+OXnT6QAIE+WHBNPSUBudSeY4PgcRfOT/rqfjwMTCCOCMTdXzX//qS3/8/JeefeapJx878cjD9/3OsZnDB/fftHti25ZKpZLHf5WSixwkVkGfilabAMclrooQmUf89M6Rt3rnhdZ9tOoq4CYgv1VxI9wSbgp8rP/8uRZtngtsr7Xax7B9rDU+3afxa63x6TzWOl/cvxZq8un5Db/gBDYTKOABPvUrX+n6G7u8S/BywLn85tVbIuT6Rz1KjMcfXdPsx4vufNZx8zXKlL362J9fncYvFvVp3AJJutF4D4/8X4wG8H5wFL9f/sLVvvBtSHg3Gj+jPv/p04f6+dXOt10O58rlHP/A01HCtR/we8UWxLVY3TE8XGPzsNYZ5ugvjRaFrBnz0Iaoxo8goHVa+xDBehrvtqDteRPuatximvvwDvQSzlEDajiPoT/gz80/C659VizmPYu7HmUlFK21AJQ/1fgx9DZHJRTFxyTNfSb/k8a7jR97X034svd4bxr0HPQ4r/KtTaxW4NpwIOZ4WJ0j776wtNaqxFf3IhzjuO+21vY8reR56569+IxefJpJ93ECZmtRFBd+TnxXTLE+Vmar2Tb2rXqoWuaSJfwcncroGDqnTVteaUdr7lWQoaBxw1Gk/C0Cq+EvEn7G7M1M0yhE9BjYlley2CH/WR2YxrjG93r9GFo7kmScySw2BzlLbVGkzVCUc22iNbDGx6am6jFg12/esH54xdDSjjY3Gg6yPugzKDYiYhwjjhslqkvW4yShOAIVtKpKnvgxHgpInPMKjh0tldG95LQkaKVqIU8uHA26iLEHefVyFX7jppTR5aXh4b6tfYm/z6/dtjb/94m+rf3DK8q1UdnV+ObKZGLUjS/jK4aKgzAKiWSjJpWl3YTxPcOKmOpyTdsqZO+ofnPuprWJvsK6fH5doS+x9qa5b1bvyBYsv+52Lalu3rBjQ3X5knJ5ybEN4xtHL8cV2b1U0ZXhHqnMcxd+HDFYQwzuqxeQrDBkC4wfRVFj2ADgeRSYRKoLY5lIrhoJqhg0RLpxoX6IKS2Hgc4D43FcWNHVorjOs5BA1gXwdsq9/L4Xb4ee+c/P8jB+/dqdKyf4+OrTjdddvB6FdRhR33n4mWcO35lk4spl5LNTOB8bvgN/x+/f8ooxPrl2FfsO+zZ7Dd3Cs+xxpiJfYugkcJb47Sfsh8iqptgOtg4DpRJLsTZmkgbA8/AcPAtPwhfgQfgcHIJbEc5/yv4b6oSKAeQu2Ao92F9nKnwIfwnvwFvwXXgdlkMJrwFdZ6OoaSY+f33r6Y+jGpHYvkMRAX77fz8HjY3imgGfBWxTx/8/QUxNeTtRr2DoowmuHWWaKjSyOF2o+gzTQegwg4h1DDESSe0EfjAxqUiOtHesKcb6sAT0rYo4hNamcLJTVWmOoTTHUK6OoSjNMZTduHbl+o5/4pOnpta2eQzxXbgA/x6+BTfCbvYD9ib7JvsG+xP2dfa77D6UkcoIKgD/THwcWnsxSVSJwjUgKk52jlFONZanAGcNqPmKo5XzamVQEj5SlsTpAyetpjW09gyyytIgR+qJlxGiVUQBiqQo9lHT+IWQIq/RXzGvjUCGBi24FDqh/ZTccqHoNVBj1BgfUMBhcdRCns4RdZDL4qNUV8OYyyX3joFYrRwrqFqRhorVYthZczWcAXZVtSR3aq7mBV9aIa+6JRqnCydUU7sEhqMqjVfBVsiJC4O8QpEbcuISzruYlF3CLeKo2LmW9hIjSKarFRwFD7T6fDVWrOJycVmOGs1UyQnidS2t+UUep0DnBZoXEo4yrsOt4kg4YbeW5Cidas1FVBgBjCkrg5Tp86RRxBZpnA2GkS4da241PwLRWjVDcyQBFysoEIHRJrqoKsaf9BsAXFkU5TWIuxaAfDVPcq+qUYRtDAS8KADROeaoLrx87/fvuef7F//smPrgf4QI1zHwlyIUjSC95boqcMukNBVVgo6AKITEHxVUJI2KVLEl6DYonVJwjK3wYVwzsAm6NOxocqn4hHD8EaljrAdcMThEDFVyRTWFLlH5hWrgaMg6FYHhoQS/ZgVkUOCoUgedPnBggXQ/rAjbxsdzu61DqIoSUYQlfRY+SJW6NOSOoqQwU0DcxDkokuZJ8SdwU9PCUjMkPpD78Zz7MXzgAV3g0AJ9IbpsHEGxNS50YWiuqiq6HpQOjoODC7+QGGjrIZPjDygcz7iwBcaBJCo0RAufw3VHYIBJrhwtGyhLAjIuDHSyIHzcT+KQeEfFOaCcpNR0RbMlnmAQrHgTsSUPY3dOwSc3dRSVqmqKYZt3/M442ODD/lGCDRK0YqPN4w/QzE3cIY6ixkY4EWkFgBsmiPC953557l7v0Pgr0Dmlx3ShWNgMh8BYRPPkCly1FRXlii5OeBfwO9dJrIArx73WhK6ZmlRUxSbVwKXZBgpFwSWIEBd+na4LA7dVqOCXJg6p4LJMqWkaGIqu6SgkQbJEdTCF8NNtRWIYYeoBLgjM/CgAqeI/nMSS7ZJ2XaoBE+eA8ZvfcCwOajtHLytVjGaFCKKMpa7oEqy4T7Fx1dLW/dIPpuVgrK6gyHEvwsKU0qCcpekJmAf1MOkvzsNE/kJbifIOKgHCYm7hovFUxv2GXzEo5YqiRqGjmSg8gDoClMoUGD1KrqMg/dw0FcppWoZCqoF7gGuWaBAoAhVwediR9h0PDV/0BlozJRHJDlDU3BQYYiFFEhhqURvSJxpH6dRDht+wuQxqXl7rK+KkyCIix1i63oV0mIcU4iZIVYHPLvDiDjft0VVHLaQRJgrEQJCpapSOQtYKb3/9oR0bNuyEyQcn4flUd+N7zs7lMJLa/87Dr0BP4Z/vXD05CX+T2p9qfK824eAN9B1X/gY5yP9Ezhpg3ehHD9btDtxvbni8aLTJPRMMZYdKeZRcMIWJHhGVe9Fv2QpSzjRD4c5SnnD2agvcZkpUyElqikGkEVteQG9BfCq3KC6kOK0QKRfoAjLJWDOSE+BS3oy4Vr6GcFnEUMzU7tRM74CmqmkPoiM1bO023dbha07USIc/eimcNqIOvGyk8+k9R3TT1PEA9o+RBEsVEeQKulyVBz/6IJMJhTEEymREOOQ4rXgEhRFGLpZhvfU8Q930xH6Xgqthgqo3wsu5CDaWKeUyJW8hVIUpZFqlmFol0yzVeLkpSmTFiByKcMq9mHJnkPhd9LjhxWRsBr/Qyet09X2PGb7fukpll4spxj1ueLPHVR3WX++ZF6wk+4cJSaA3Sdoxxlg4ZFvYTgspSrQ/F0pT8WjBo6Nzh5d3PTnOJx7nMO8CPvwPj1T49M4nX3xyJwx9roUg957zcvG43J/jc1XUiBXIrTaDVo/WR1YG0VA0JstUxBpdi5ayqclzhpiGIKvJo4wINDuEE5VMyGkdT1QF1JsZIcEE6grNV+HEjyyKgFrtNfg/6BD/Jz2ovmxxFwyfjv7WPlMYFrmMbVi/etWywd58ssONoCRUxyDJ1gpI96PkeFXiL5FWea7STCfg7uGNgpfR0FoZitUeH3BFDDIV0AqtQiT8un5DvQJRwzhnhPEvu399Y4jyl/B2JmkIrUM3fXZjyMsfwdu5spLV47XTjSdO89nS6VJwIHhD8NzaG9Z2VeHU/BCN1480B1i3H+E5onYiupZzrTE2aTiCDidfaDzxAgyWT5cDgRuCA6065laB66PomGXZY3VcBFcSrl9Dd9UmiFWionk5qDBuQeEaQxeUYAd2K3koCTvxA+SNJOgtHWhBn2jJZj/ZcKoeZqw7FY8FA4buCVpDQZdagi5n0hpgJFoqYkxe8GToBKAlxbeKx0ub4WZbkY13pA9ZxVKRvNgYuii2Ovsu7nNWused0vHSqlF0fbLx5xKPMCjvvthY+j58MRHd9/7eaPS462Hv3V5MaLFBduurGuEetFJuHUzTVYRhjU0b5Iw85zCNvNsnEPWyVFS469omZJr0ibZJxAT1qe5fMhAphZx0KRr1CnWU1GpCX7GqEHwIL/tdEIQe5WoplCZe2E3RcyGE8G6bQRN/4X7X/9cJMFUI8P6fBhyYrCX6+GAHHEr09SVqk3D/JQI8OnzH70LjN+giAxBwAhuPQbJvuA8Glg9A471jZN645jnxgpd/dXDVpfpQHn1gTyGMDpx2VIyijeCeIVGY9XIlrUIKHO7MLIl7xZ75EnVuIWmShFyzcEJVxSKxfySnMaK3lGhrFqbhcON4dNhdGY3Cw+4E/Ctfx+e3337q1O2pTW2G8cd38r4t3QFzoRj9t43jjrMadxIerk38lZvbsh9Ovf00d4JqWNs/t4q3LXG8fDzVD1+T9+N6BKKgj4VYlB2u38oUU5kNgCnMWWRDupj1I3fVZy0vUW8gMUJ9m/Wha5DqTvxQ5W6G5OT6cNjv15ElIKxGw1En4g/5Q8GA7tN9tmUaUpPo8UmlQ0HUVAhlQt5ftDtU8r7l6OzQSTj0FD//D0+M8rdOemeN91BAycvn+KrLc5vFno8+gA8bu+DlC5fn+HEPchfqcxk2wDay0/X2rjjyjkgY4VH4kD2w9aBBEQxNaWnnEE5XIB+jKouGGngUwzlVmuq0QtS06YU1ZuiaMc10nD+qbO0TXXRDv+vT+hmGlysyJpmhGajFwdWrKiWHquR5J5OxSI/nfV9hse9bCYt9X+wfLQmiR5+z1axmw5yXZp8z9Sx+wZPN88XCvlWfWiz8UrMT5dqbneZI9+fMtt9aRCS/erf4FfKdAhtnr9T9WRejHz62rkyRQkuyeebRQnEUfROSOzlD73VMqs3MXLPioPiU+cxc5hOtJVUk9l7tRGXU3o+3UpuFDP6JSgYl5np7gI1uXLa0Z7x33AnbJitAQfcScxTRaY7bBRQSUr2RxKypVHIcAapLYiBYyEM66lUvqZaNeEnRoh8onF0DaJmIp9S1jJfhg+N3H12/EWcgJyJKpbTrxlu3P1UeNrj9d5ZjymEeNtZu2LMXSt7N3beOb95YWalz63+17pr1DXv2Hf783cfWeWOIqfrIzLF/pmMIGD6wa8fSZSPLVxgRURSGG/yZbqmrNuV7GrJ5K5X85D3q/Xld5x4do5r1AfFL3KsutpZdV6egA7F5GcCGpuQjV98BgGNiPgEaEJQimm0xZzg8VfcB+hgnwrqgS84LcRlKgFIEMZdC5CRQXbdKATwJzxOy6jbvo6SW4VnBy2NUqVEefnPjjokNu+88ctuR7eu6u9Wcvz1YCgmTZyCXf3r/TQ0lHqAAOsuz+c03PXT/787dQo1nsHFKyemqPyymEskVG6NOMrV93e5dZ3f0dgQhJALqnj+d2vd0Ptf4IChV3TvbfFM2HW/bsahttNsfZgu12IueLq9hc/VIDxLdELqd2iAGYt0Yb8gWgc8ilGMkdLU+ixKSnisjb7CXqaqtouQGGMb8s/9Y20U13Km6OdyRq1RzJSrjwrVcyEX/pn6MCHmwEAl5bwEteI0CeotSN7HkBQ70BLH8xv0L7AcNXDcvuJ3WgcYzSlDWMca984Dl+iGBXnDXmQXa47Vb4DxnEBHgJ1QI4TZ2VNW64vc6drruwrsElHuNsx5WrC8NY0DOvNJAU6MQ//mnvO9QqTltJe9th1A5X8AFduFaKO2FFKVpWJFWknheEvy8E2h8GI+ExxsXLGsF1RD6dpgBVY+e3L/+8vs0fR5bvx92gY3rWRKkZklzBc6+b9wSJlQuX8TFTa/lcfpgTf+Nhz38rVYtsVYv94BUdNaMGBUMXhRJ+0U5iulFJWNy42NUrsuXvDAs2kpnV5ozFdHmO0Te62at88zi8ykn+NFfe4UkEfJqSJ95NrOo3gTBhQoVOOCnupPfK0HN2/hr4n1+Hvnncrak3kfv0Anch+YLG83g95r5I2xlh0uS3t1YqN+SnSY5Bo5kxPgPr6sB8HNyTYSJlFUcpHDZU7WL5dxH57NVaOs6P5bKb+jgnet6um7+Vipe7f3zcsVOJ33cToaSvrT6h9PhzCoY7BdVbP5fGhubOvntDveJWry9E9o7Yxsedt8YGE+cyhSMMLojM6x3ikPr/LGd2f7hckvP7hYXcX0xtpodqNtlAre8RRmelreJoQOA+VdvrmGYySbDvHqb6CVbCPym6iFgK4ez6URHOMhiEFM9cEM6Sf4BEQzJF+nkCB/0wkF0BoRrnqfOV/NebneEr6HkQXkEUhgnfnjP9++F8euGAr72GzbGU/k0nvMHvgePPvaLxwt9x/6wIyt0P0dwEbb0OZoT1AKTB+GxX0DwF4/x49tOjI3c09tZKQ1mV0WFsu3Ecye2Nd675cVpeUtel7YBHIOSgOJ39c7OSF/x1ATemn5xXkbnxB7E7zJ7vm61+ziVuDibF1GBAfIWdJ4UjauCzWAnlfEZAq7NqDCUZJA+Oe+Rs5/VnBOlXYC7XBPu6P0gakhBiuc3pOQTrUG5JHdsdqXDmQy9MNTRr5SJ7pTTVHIu5hy1n6hNnuhPZUTW0JDyFRSmG3W0JMScpIDLRvcQhId7DPhBX+LSxH0TlxJ9nYND2TA/cYeSGkgpRz4PbnpoaEof6jaM3mH4N4m+lRMTK/sS8eLE/se3TpwKmhZqdzpqmcFTE9sem95ZXoxfGVZia+sj9BZKFxCH8TgcknhaPaX3qEKIdA+xg5IgRDTUSSR86lilkim5mWxGVzr7m6X2hfp5Zr6oPl85p8LfpyHb2aaRn/HM/Uzz5KzffeoaaFtLpn/Wa3K2iQNnCQTOOrDm4+AG3rouigrrpzwMxSUwH40ApWQ4zIck7HAmFlkdIzCmyIM3391E86+UBxXPIS1UjqlMl4qh5Y+CKRUtZCIyOunlI7t31+aclNH4mWVBwuqM8zl4ak/y4r4vy3BQmjYyLpHvWr6nPpQMqyf9rgVJKi0nTSdw8qdbmjkjjB334B4cbmpeCo0VSRw7yikoVFizVoN0kdyH8JJluU9vIrANeVcigQJJYD0CLNXVEXciQb+hsgxkNLJuJHsfL0Im0d6br6lRYBmL8q1emmlxJdIJYFDllSi/lowtLkWeve0Z/vQdtBmkcmfnfUsYPmz5lnS9S6GoACGYPhGEJflJhsqDP54bibRigGbqq+gqCzHBtefN17YwGGxmupopsUTzo3mpyzvBw3xqbHHLT/nOWCtP5tVQWTvL1zML5A88I6ZaLocxYG1xTWU22B4BXFzwqpYLubwaDTkx8uHX1EnGw+HG26Fs2Ajr12Tay2afecaKOo0vOpZUvHj6NTHnxdNxdgPbVt9yPehaVwcVKlFky0I4DznKNF2bZbrQZ1VYSIkQxNy1CNgVL2uwdCCaXZOvRMIGBlq1MtV3MNb27C8zv/MoWEdzo67metUvukPkteDdH+HoBfEwIkrFpFRjuFqyEOx00k2gHSacPU7wBc8iXwi4/JkuAzACN1wllb1+c253sW9jBG86HcOd+bDpVxWpBqOBtr64o6uc27pNyfsv9dfpHWFvPBhofMkbDQ55vn4o0xbuS3dluqIjhX4I+wPx+Xv1zLKwmXbibjzt2uH2eCrsiw64jrT9ar0Vy9/tcdogc1gONXAF+8t6rNTLNR0ZLU9EfTaGV2JUgkIZSPIOS1RbWBI0rgFV8hWNKzM4kKawGQM0TZ80QdftzRK12MfmfcTAZ3eihscW9dTQaou/pTk2xPYT1F7bjX117Xp0HKlQiLFapbhsSX9PIZvuSna0hZyQEwnj6gI1n5dAcIgjtyw6ggTFyyjMX6C/UjGWi2ZaYbWy8A2ecP2tN32+IH3w5ae8l5XoFH//h082Rt409dO6Cfc3P/lLjQm803ijuU8JuGg3HoDHG3bzZSE/rMW/r9kvzzVjaTq2ckWvyQdEBHV7gG1n99fvHchxU0t1+YXgxQiXuhhloCEqa6Y26wdm+kzmO8osH/dZ/ChyIuazTN+0Chz9kc7FNNOl1CeYrstJg0pbaJxbt1y/eeOGtWuqpWVLe3uy6c6OWDQcNA2ESR30gEdr8iOQ5KpSIjB3rv4HCO+N0IWsO5lFzHvbLNoMR8ojSowys0UvUo6hX4vCE1OP8AdffUA9AX96zsshnLPVGd1800tCoLBm8EvjUF/iZH5FI75+p7TDyfxwt2UNTBycGLCs64bmEn1w6JFXHuUPf+PB6z7Ztzlo443EAPx+57b1yeXrqsvT7dxM449Z7Uuw/w05LoKNAHicY2BkYGAA4o3CQWzx/DZfGbiZXwBFGG7Gb0qG0f+//k9iqWBOB3I5GJhAogBKJQyTAAB4nGNgZGBgjvxfyMDAUvb/6//PLBUMQBEUoAcAo0YG2XicTYzBDcAgDAMjYAEmYR4W6QCdhH836SR0gD6pG5Og9nGyYkcXu0jMSvtIRZN9A2L1nveBJxWAhFOEzF4Ju2bFsF/c08mbe/Wd7s29XbeLafuk+7265c1/tzled5Ez0gAAAAAASgDOARIBbAHyAqQDBgPIBEoEgATqBWQGtgbsByAHVggmCG4McgywDTQNfA24Dq4PMA+qEBIQdBEoEf4SjhMsE4oT8BRgFPIVjBX4Fk4WuBcQF1IX+BjAGWsAAAABAAAALgH4AAsAAAAAAAIALAA8AHMAAACqC3AAAAAAeJx1kMtOwkAUhv+RiwqJGk3cOisDMZZL4gISEhIMbHRDDFtTSmlLSodMBxJew3fwYXwJn8WfdjAGYpvpfOebM2dOB8A1viGQP08cOQucMcr5BKfoWS7QP1sukl8sl1DFm+Uy/bvlCh4QWK7iBh+sIIrnjBb4tCxwJS4tn+BC3Fku0D9aLpJ7lku4Fa+Wy/Se5QomIrVcxb34GqjVVkdBaGRtUJftZqsjp1upqKLEjaW7NqHSqezLuUqMH8fK8dRyz2M/WMeu3of7eeLrNFKJbDnNvRr5ia9d48921dNN0DZmLudaLeXQZsiVVgvfM05ozKrbaPw9DwMorLCFRsSrCmEgUaOtc26jiRY6pCkzJDPzrAgJXMQ0LtbcEWYrKeM+x5xRQuszIyY78PhdHvkxKeD+mFX00ephPCHtzogyL9mXw+4Os0akJMt0Mzv77T3Fhqe1aQ137brUWVcSw4MakvexW1vQePROdiuGtosG33/+7wfseIRVAHicbU/ZktQwDEzvJM7BzHLfN7vceHmAH3IcTWLWsY0PhuHrcTLFGyqV1JZasro4K07WFf+3K5xhgxIVGGo0aNHhBrbY4Rw3cQu3cQd3cQ/38QAP8QiP8QRP8QzP8QIv8Qqv8QYXuMRbvMN7fMBHfMJnfAHHFb4WTAojSbPktBVDGaLw3RI4zS4ea0/xQBRrOhK3+z0LJLycNtKOTNvRptgO9mC4dWSYiFHIqXZKxuSp+qUGsp1X4xTXfqtpf0J1cms+70lr7pUZs3Nbaiuvq1HbnqrepzC1eSOZqKwpnU6BieFHCrGkQUWWx6XSG6cMO/iMp1ZOwkfeC1//sXbmylQhd7+t8Xujlbnm9Dtu/wEudCxnMqmZhdLLayftnAvxJL1Zj8vl7fI3Dz+T8DRUnpw+7pZLVwkLoclE5YIKmSmOXCovNQ27OKW5DzxrzZyuV8bKpIUPbQrk+bK0KP4C7N6NkwAAeJxj8N7BcCIoYiMjY1/kBsadHAwcDMkFGxlYnTYxMDJogRibuZgYOSAsPgYwi81pF9MBoDQnkM3utIvBAcJmZnDZqMLYERixwaEjYiNzistGNRBvF0cDAyOLQ0dySARISSQQbOZhYuTR2sH4v3UDS+9GJgYXAAx2I/QAAA==') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @font-face { @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?3632997#fontello') format('svg'); + src: url('../font/fontello.svg?49712213#fontello') format('svg'); } } */ @@ -68,7 +68,7 @@ .icon-video:before { content: '\e80c'; } /* '' */ .icon-right-open:before { content: '\e80d'; } /* '' */ .icon-left-open:before { content: '\e80e'; } /* '' */ .icon-up-open:before { content: '\e80f'; } /* '' */ -.icon-bell:before { content: '\e810'; } /* '' */ +.icon-bell-ringing-o:before { content: '\e810'; } /* '' */ .icon-lock:before { content: '\e811'; } /* '' */ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ @@ -80,6 +80,7 @@ .icon-pencil:before { content: '\e818'; } /* '' */ .icon-pin:before { content: '\e819'; } /* '' */ .icon-wrench:before { content: '\e81a'; } /* '' */ .icon-chart-bar:before { content: '\e81b'; } /* '' */ +.icon-zoom-in:before { content: '\e81c'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/priv/static/static/font/css/fontello-ie7-codes.css b/priv/static/static/font/css/fontello-ie7-codes.css index 3fe390d82..cbc410004 100755 --- a/priv/static/static/font/css/fontello-ie7-codes.css +++ b/priv/static/static/font/css/fontello-ie7-codes.css @@ -15,7 +15,7 @@ .icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML .icon-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-left-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-up-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-bell { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bell-ringing-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -27,6 +27,7 @@ .icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTM .icon-pin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-wrench { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-chart-bar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-zoom-in { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/priv/static/static/font/css/fontello-ie7.css b/priv/static/static/font/css/fontello-ie7.css index 77c23c0e2..1ef174bf8 100755 --- a/priv/static/static/font/css/fontello-ie7.css +++ b/priv/static/static/font/css/fontello-ie7.css @@ -26,7 +26,7 @@ .icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML .icon-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-left-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-up-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-bell { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bell-ringing-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-lock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } @@ -38,6 +38,7 @@ .icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTM .icon-pin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-wrench { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-chart-bar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-zoom-in { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/priv/static/static/font/css/fontello.css b/priv/static/static/font/css/fontello.css index 93def62db..84fd6802c 100755 --- a/priv/static/static/font/css/fontello.css +++ b/priv/static/static/font/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?3304725'); - src: url('../font/fontello.eot?3304725#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?3304725') format('woff2'), - url('../font/fontello.woff?3304725') format('woff'), - url('../font/fontello.ttf?3304725') format('truetype'), - url('../font/fontello.svg?3304725#fontello') format('svg'); + src: url('../font/fontello.eot?4060331'); + src: url('../font/fontello.eot?4060331#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?4060331') format('woff2'), + url('../font/fontello.woff?4060331') format('woff'), + url('../font/fontello.ttf?4060331') format('truetype'), + url('../font/fontello.svg?4060331#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @font-face { @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?3304725#fontello') format('svg'); + src: url('../font/fontello.svg?4060331#fontello') format('svg'); } } */ @@ -71,7 +71,7 @@ .icon-video:before { content: '\e80c'; } /* '' */ .icon-right-open:before { content: '\e80d'; } /* '' */ .icon-left-open:before { content: '\e80e'; } /* '' */ .icon-up-open:before { content: '\e80f'; } /* '' */ -.icon-bell:before { content: '\e810'; } /* '' */ +.icon-bell-ringing-o:before { content: '\e810'; } /* '' */ .icon-lock:before { content: '\e811'; } /* '' */ .icon-globe:before { content: '\e812'; } /* '' */ .icon-brush:before { content: '\e813'; } /* '' */ @@ -83,6 +83,7 @@ .icon-pencil:before { content: '\e818'; } /* '' */ .icon-pin:before { content: '\e819'; } /* '' */ .icon-wrench:before { content: '\e81a'; } /* '' */ .icon-chart-bar:before { content: '\e81b'; } /* '' */ +.icon-zoom-in:before { content: '\e81c'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/priv/static/static/font/demo.html b/priv/static/static/font/demo.html old mode 100644 new mode 100755 index a1e14322c..225e4ec5b --- a/priv/static/static/font/demo.html +++ b/priv/static/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?14310629'); - src: url('./font/fontello.eot?14310629#iefix') format('embedded-opentype'), - url('./font/fontello.woff?14310629') format('woff'), - url('./font/fontello.ttf?14310629') format('truetype'), - url('./font/fontello.svg?14310629#fontello') format('svg'); + src: url('./font/fontello.eot?25455785'); + src: url('./font/fontello.eot?25455785#iefix') format('embedded-opentype'), + url('./font/fontello.woff?25455785') format('woff'), + url('./font/fontello.ttf?25455785') format('truetype'), + url('./font/fontello.svg?25455785#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -322,7 +322,7 @@ body {
icon-up-open0xe80f
-
icon-bell0xe810
+
icon-bell-ringing-o0xe810
icon-lock0xe811
icon-globe0xe812
icon-brush0xe813
@@ -340,27 +340,30 @@ body {
icon-chart-bar0xe81b
+
icon-zoom-in0xe81c
icon-spin30xe832
icon-spin40xe834
icon-link-ext0xf08e
-
icon-link-ext-alt0xf08f
+
icon-link-ext-alt0xf08f
icon-menu0xf0c9
icon-mail-alt0xf0e0
icon-comment-empty0xf0e5
-
icon-bell-alt0xf0f3
+
icon-bell-alt0xf0f3
icon-plus-squared0xf0fe
icon-reply0xf112
icon-lock-open-alt0xf13e
-
icon-ellipsis0xf141
+
icon-ellipsis0xf141
icon-play-circled0xf144
icon-thumbs-up-alt0xf164
icon-binoculars0xf1e5
+
+
icon-user-plus0xf234
diff --git a/priv/static/static/font/font/fontello.eot b/priv/static/static/font/font/fontello.eot index 6f9cb4a29dff60d98242a41f57538003a9d8e2d7..d08692e84134f5c16e188e97c841e691f820e15a 100755 GIT binary patch delta 1452 zcmZ`(ZAe>J7=GXTb#7u3V{&t2Kk}ibiAGax5)+NFOR;7taTRXas zEU>XZrL(IMIv8UuY@-ZT>_p~Z(86F)=%Bv_gZU!QkNrJbh$N8$kB zKAW4)T)FvuPHF-wuzdaAo_K2QG zW@vyt5Oe_TGWnjF%>3og^QGU&Zz6v!oju(<*<^8$-Ts)mS2L5BbNHU|3xHS``R=*N z%+$kATs7pM0pNbjWiQU(SgQUOppz27=eY}0xhKiS?*RH97H{JB4IV(!0Xl)-wVH_a zr1XFQoAjU-++=a<`-@k(S;t8z-{b#%7cKml7@%0v%6XncGZ@N|xpLy=E3opeFpP!$ z0P6&xIjjdYfRmWgD+62r8w(of)39+(1JIN<9%!JXVB=Q}P)Gbk19*u4)Bp#FcQk+( z;17$2KJbxoSOe4(CpAC=G1agP@Do%0%78{<`al^#bJ?UM%b@uzNne=?0zl~!f1e*E zQ5ZU55WY!NMZ%l_4ikq0ml{CCm-?6Y4vi!lL=>pX0%mwbi3)WNBu4F=i~xonV2m39 zjx%z-`}&tXB=G*902pwL8<(*_Lg?L>@KFc>vh+R**>ZHuQeY_yjN{meehhY2LR-wOP#IN54)~8nITgDRzmO(55hnDQX8QP7EH#)=)6g>h*bB!_@gA zL?Xqug~HKDA$ov0k%b~YF9ubcqK1O1BGuK>yAj+}-D0XO6pQ(f`yJcfU8>$t;q%N!R`X*?hu?S9 z>pkl8JCbAh)yS;JS7DOfsw)ub>3X><(iRAZ19M$N-Kpo*qEOQ;%3`cu5LbRzKQ$Nl zQmJ&Fzr`!GUz(sBZYB1*sySY;T960cC~*kKkYNyYOicAJ*HGXu{kXZw)C{ zn^Nah>gp;)M(l>ov864F2a7Xpm-w5dQt_9zsiaq)Gh8q%8^gx8 zjccYml`WNDnl0w}s`{!O%YwDZdd0e-OemYStF|q5P+hZ|?XTFks_*ZiBX#%uIk@sp xdj(HHuf$Js*j#)Z&sUmWn@Xnx7iQFx4BKX#Lb-7KdGCBG5nX>vM84pQ4wlEF1IwAxpbL9Vnk0R zncSitEUX9dp~9MP`4AOBP}D;dMMMOJLJ!mIshIudEupA;`0zd7bMNizNMG6?@z#I^kW^U3IlvE&sDnNmgW_-r34)H@-%}9*a ze-G6n?2UE{dY0@=l%&TUJtOX7gKL2q0$HRU4XgrU=QWT`N;gVCyCL>O19Xen2Msz0 zVxKgSOZrs0@JMKd9+{}etXB`l!L}6Ixxh9zP8xSKdtFIP$G~(!Y9T{=qNzH8wLui+ zX2`O>0h~+CkR*fTvo}uWP{jR5gdhG-W^g%_N1>wsg*YTG?OWbY^x3^@{{B{mD9B>l z@e`0if^<5E*gON&>Ztyl_e=G-((vM$S|^W-YFf(nmfO}AC7&Ce+?drG-t - + @@ -62,6 +62,8 @@ + + diff --git a/priv/static/static/font/font/fontello.ttf b/priv/static/static/font/font/fontello.ttf index 8a771e529acaa1311334bc7cb01b0c4bcb0e7856..6f5a81d7670e06dbc3cac29ea406c81af010f651 100755 GIT binary patch delta 1431 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 ZtJ$`2I4O$UR041oq4Awo5(*I?&v z2Pov?>n`iaG;qKte z&1UOD!8*ew!ryBf3%+TWG%CdvUfjDWsWTQ%>6lG#{9RSr+=KRcg;uyY?Hg%F+PM zX=lj=%S9kTa)B7M4iPM)LAoIYf5PE$g89eGMmpa|93CPhjI?((d3!A;%Hs-qw}9rZr}N00%4{~E330qzu7 znJNh=Q6eTbV-Nm)%gzA8|Kr+cK}evOo4y5_DbPYO%ajx|A6DoJ9j8vFY~^wZrM8>P zms{g@5DJA$0yski49*)E)0{GV(CTge``tY=XU?v)0uz%S=owYlGOqzud)Gj*BWFM% zeBdC)=m#lq_A39BdET6_(#BHDf)XXaL|`b;r)9{w*jM}YCJ1btaDj1?bTww`Za?xECXu1?-$|mWrEW^|5sr`0MPV|9LlhR}`CEBuPG7LCQz_mzN`s};I z+N#BvNVmcyi~@-o?t5Us+R}?CY6(JR2fimHXj*lOTqfN;0{UkG%|I~fASFG1mrb8# zJ_fXXt3m-#ZEaOEWd6I)QOLibuL1l%KmYU>hzf|T%w=2jEk1XE>VcPX*y=fu36}u- z`;&oGA(e#Q^Qp^|wMH5aXt6&`Q2%yN{{bAusO%q4sq%2fJbsJuc=dex1?$Cv7wQ+f z7q%DUf4OG_;B(r(QduS$_d1<}?A0~1*WUh*r&gVM4H`9R)}ockVsp4WzCfra(l;

+W&s!!ds zxSpilf6#f`MS6XTY>t%ge5LX%LbUs@IGH`Pf%6`C1zfSU9?)qH4G7|R)j*(;_-?JyYf;zJc=HkoY?J^b(CV_CLBny(N3J_b(NeYY>6V`5p*eK;klQG66c- zLGl%-pC*o2H?|B-(5Z9yk2=z*P}w}E=4f7vH>IGLhrsE2nDdkdwI}66S`QYQg_@w6 zALka9G-8qo5hE86AK|Q70zs`?>;97Hqz35+1idEEVvEV&>jPLHy)&QYeAS_iN*a2; z7Bn|>(C8AF+##iVT0)|oLyu9LWMsM!20c3HA9Z$N>C4H?Dj#_#3YO2n?pdt#yOt*M zJwN-wP1JTAcTbOo^UWlCS_>7r7w5dYbOBSKYiTiQqM^1H zLt3@BH-X=fg8`wFd~qgP+@#HEAj>wb$mR`N0ob+%5;*PAgXiKlu2q0sokhA5w2r>i zL=!Lu*43UfsHYUd~Zn zrmqHkc5H(-VDEJ;;QfN^hogIEi*@1MxrP<=Mw@et16TBys9}xv-kXL7O$UQCx8nwt zxP?iz$_ALoCb!GE!G?6FIB@o^6bXIfG2r%G!xn%C)lP2CkIMORnTzzM?8j!<)v0aY z0+oPai;s%jksrsO9ezyn|HSK#24&4?Nj#E)ARS07&Ns_D`~Rocrsh~eL2Q}Wo`byKU2!V-(jTMME{BJ^D7_Ck+{TA>jKQ)SSMgT;#mtvJ{!gLWJoUKXGe2bapA zD-Ld6ROpU_M`h3x2QM#L^v1!bGU$tgpBJn|6fp>?5(1PlGq0?+ipHY;F}`&5q=r0n z6L0_uFd`Zgrc1+eX|YA}4fe?f4wr$`W#DodxF=h9TozuJh0kS|`Bw+Yvq2F*`7J8k zRxv^R@HbylUhu)q#r-_>EI?bN>JZ zS|UhiP()8o>A`5`A+rx)XJrt*1S>dD1hS*sFULdO&gyq79!W%-V{nn(P^GS!>e_l9U@bOU)#Y$|oX;N4o)b5~nbvxvC9F!LW#Vl?f(L zpdba8k7Z}C+gae?`W`P0r|)a*Cb0^^P<|;~Cj*HNyZsnF$UeZj<6r zPI=)XAwm!&3sa^zHVD{N1UB945pUtmJi-fn2ht;=7L3E&F>G_3s?uJWr(*0QNvQ74 zjog*@JL2b3@H($v&baL>r-<7kF0-a>M_HP`x0J^g|P>(jSWYW zH{XB>aD~iu4~PVL2a*qrHG7xJ&7yp+ndZOI@ACfzCA@JVTLh^AfKFobw)b=+H+M5c zrEgLRjY#w@36JLbv2FMl;3gIOQw0}Mu zw-~^}Ip=olt$Z=;;J{6g7(uMi~G8us;$I#rtUu@9jjNT>wt6jL!C+9VM{6v;v&BAO}GD~78LRjVD8 zN+IR06QW4ZgE*DM(Ar5u_0w)JY_$zS?JtK=`qTbanqy4~maW-X1L~h8NOz0t5CC ziS!6ybmvld@S(2OO>{iJ9ZzzbxtMBEL@m~d21Bcs&}94l61AI4b@QKF9ew>qa6S8$ zCvI(Ly0;q9Yb35433< z0ainA$4GI=Y<3hIIL0Lfm(KMN&3QTBh6klUE%=wg%lGkQ*G^HSuB?}tr)tEp>9!~W zLs+`EuI98oxz?xmq`iPypnWa zPp{2RGul4PLbG_QxLjdKwd`bdm3cP&svQ;H!=i*YpGC$vLEiMO@t4 z-nN@Tnr|$Xxm#y-@KrCn9?1J2w*aEkTE)col=gIXf#x88}Tr5D_WJGXE3O-hKJh z+5ju)SvoWc@HyuGRPQ;5+I&a|l28!vu_G}SWGQYyT}gJhGe4{07-n}CUF%`Ct9BVc zfst(W!&XgOVh567kH1}xsN)MvErNa15F-i`|DJl?39=98+i=*9)c>DxZ?^ukJiJ`~ zvyJ^ivso+_Xz*KMh%lUZ=9|wtVy~hP@`W}!@n{m#gP282nbR&1q%G3%H}CUQBN!@#qSTeO1tPaMu&4mh)`UqUkx) zN4^;7O0H%PI^w9v5*<1*Z(^q>_yZ!k;!ad(<&K7}K6J$kW~jNhLEqNP@o>)lmC}uc zY2r$T$SKVD_XKC^yty^dIf==Bb7Bg=NhfzU@Zp1t@2cN;Mp8WUx$nflt@#{ZpR1}1 zlPw=wE^yr52s)qh-K}4I1ODPhIHrz;U1*--M!d!wXPi9xNKiIZI`mj@>gbF||6N(f zrrd4M43F!5FeGAtiFQ|TDQ1=k2my_DOyS+oGkiL%-#xsEM-19W)L#0fcDe0$zXtd4 z*I{RRh8sdix_w0Bm9(^6?&qoqM9eHP$c0nwbW)>OH7oUHOqs7XXsyc9lH)8Z_VFA{ zDlF9dcsTh)YPFF*o`KZ}7+?DEXnd(V*&Ee{w|b+SjH1@MsKp0o9U?b;?Cg{VjVF7< z!$)ODfsOu9YR1~+;Jw_~4>|0^#Bw6<$Fk-bDYahTwCGAzh1rSov;_rk4rgp=E9vO? z57@EmyN=HPP&&5YMR;Gl{K}O=U6ZLJp`ujYdCo^&qD!uqp zd1|~4vl%o>IbE@ZmDf;&<#+3Ca)^oGw92X;ZBCB1Egst*nv3R5qwuJ86+IFerAV8r0$q1|wwAUbN|-Mjbg?c#Td)G7YS?5snLaLH4= zud=elh=X2jEY=zfdiW}t^X!??m=n~m*Y^kIiON^RG?$5aOd?Y`#3a1yC{d8ZqGY<_w@D(f3X*OBe|mvG>L9rY zFmAwPzeQdHMl_T0`}u;O<%S=>D0voB#D5-?+gCFTb`-0IkbZH|lZv6QaLgm+(-9bs z$$pECd|Mm#!!m*Ku316()PX;_Tw8}3u-0cNo{-2S4l#jDOnjhQWvlk>Kgm0poqdXT zDq;q0`KgOh+_A5V$KCcNO3s+!;=6#RX_mo186pvKC)mM_|FSA8OBt?+G;3{Uoq{l& zaol%`7ZfV8a;7=ni;}!+MRBFk=i{1ZW-eCYF8o7;M6Hsfsw5J1#D^y#^jVpaF+U$2 zTPiC~@=l;}CKk>)iW_)Zvq4AJ`Nqm#@#d}ov&a?@zFl2;^N}I# zLG}mVRIj*6dDw8XznUdv)Cs@<1t>x_;x3?{QDqkRNB{?_qmEJ8wpOuH3P3VQ04(bcOwimEhsO*FkqR?} zgIwJf79ea}s$grVyWk4C3{7E&nKwq6LH#CKQg}Fn5{rzY$L>oRf^eu(MgwaEU^OBR zVhIzNawY|K`y!%nh|%}i-(a&w6Rr`Qm0mv#E7+v0I?^lBG$$%mrs5)LS_!zu1qKxA zWa0z_p|QO|Ai!0S&yg3lO2ySu)~^W21lgnhG#MlKezRB!!H_@;D?9grGDjq(ubdrT z7q)R(wwbD`tau@f+X1p;{YgkTCX9lSZb~Hs?O?GGb^u6W$5Hn&#BLGDhG-zee~lOv zvkH9TK`y&GN>JTNr{PE}L!@VbFa$vYFtS?M8cspLc>|2QkxU^FB{cY_S4VNVFhZ}4 zi?e~1n!>MQ5q~C=3aS-?AXwigW3!=1c93=${FDiOUaQ~8Hm9K!aAR*r>B}mAqXqN@ zZ@~&dDiEe-2pABcf%>Xo(rmU+s8WZb1F#XyPLLS`fo>29;YOZ#N()(!9H34+{xjHo z_V0~=SsS4ZH*W&W2^krU85#RtdvdXaglIESosi(k6~Y@qAe-pcb5ifwM zEkYZqHCh^NnsPC}gmxaqf^V{T z2i%{jcJ3|t-~s1XE|-3#kU8JXGxB_5i`lpZJfiRIGcz#1Ml$oh?yGj(oc-)(qRL`1 zg+dk4{vB;?H*dBvV_T#yH?7(r08McXFev)DMObvYZpRJZL5U2J5)t`UD8xyqPF7bJ z-G1;uZSA3hE9Z(19u!T_nYLIw$oqTNh7H0`k`1{OUtfxB-dq{g*Y7@35ngfs{(lA^ zJe1$*!sXf7_a9_u$(5GObtpddshy;`7%tjnjB3B8lY`HAe$-AAi1BC9f5&8Wea4Cy zKJE${J6MEw$97-i$9QKK4u=yT?4mc#&Dwb4j8>^{YDy=FJwI`71utBmlM0k}bPRSl zOTaKa+u6$c^l*;Du4L|6I0wdDGMkO>^r00l6sR7Wi>A3sS8WJ%s%PBw$pLnV|EIO3B5`?wrd+oISw~BbinOp< zNP#>}YD$}z9$9GImJqY2jn(ru>Tv9?#JE4W!L2H#R&SMLMMytl0l6uYAykJOPSmpw zzuTL;J|8^3OgsYD#sZ|y?q9|(#>+xeQ`PD)GAxbK zBR2i7%Ok@50YWQqL%dXy?R4fyGR2e3z-KZ=21R5AZ-@e$F&Hv48%4E(dTw)IzNs5` zxi>Tod3LK!KQmLJ1VoQKJ^d!sMHACp=%z!ohgN>OhsSV?aGx$Hu-o+vg~viC?e>C# zQ#_K6V+zMWnlC2%%{OpPsL?oc<|+Qkwv)V*GiRENT^O}0rClD__1(DIoILeUQmXje zZg>-W&h%kqbC3X*Ojme*KS=q}1mI@~F`zQ|EW|8W|F3?DOcgCA;r`9z18s|<-&wUG z2O*w1sOes=_CnJe?v*(T-%TwdwN;5orY<9GmnEPr& z)$JwKEx@7$#>(UwAOE_s&mz((SHH9f>t1HCv##a(lC^k2>yh!iwul`o^0!Nl%V64% zfgm%6ExaQCm!d@ZaoNL+sKO3_z~GFAtRr9c|D`l9fd{B#dChcHc>Uu9?!qUP1W`L0 z%E@cuF5a0>h;o>pfr~9I9i>=`4|mG_BETo53|rp;5UByJg1q_WSNjK4j~TJvUKbzh z?V(4kr#A>7^lj6LZ3|dBj_L*L9^@qLhdUCrWz23Gg$+qUW7*cwwLg)SW9Tb8_yPXUr8Bop7S?<@Tdw6(Xzzrrb2 zRMou8nWuir>t`Id&B=m8G*>P9Q~Hl>Hmkz{{=H%Cd9bdD(%aH-Bl#BEPqD|6w{D|- zrrpy`y=}Qr`)#`0x6tki`@ZoC)NDQ7PB;v zpp~XHV=YUTQCf~1@=Zx2Aw$nyTXLgtz&G5nEeXf^oO|Oe z7<+DM&tN>CZXyDItdj8P1)&~3uL^~w&{x7^BoN|&Wjto5*xh%`_PZ651MBgQJr7f7 zAjc^+*Um&@Mx#~_7h}^)?tuez^avBJCv`eFzy~1Hn~+!9j2onF8Eu{Z@&$qG46t-04G$W=yyBjA-z-{M!M4+JJYD7G0v ztOJ)+a5Y}dr*a<;973BDoORR2h0|Q(@=ay((mW`s&0@>}01_TSU5K*6W8G%V>zswY zF)~NCZ7JhzefYZyYU;=3FdF{hE-^j43Pw21e^1=Ga zN_(*_H!Izex=7*FaIh6Jj`&6Wy{K6WBq<&XqHw5LfK^vzckyS1Uh0DJ<3VED7UR(QfA?r{fP3unQ}&>|sz zV~^do+M+Sui17`bawWdH-Vq0fl>YlrhHCw^^I|W}9f!I4Wp7O*R8^-+Ay8-Jjtl_`9^Tlgr?1bGI59+ zn#+7g!nX3L{(z++x&XNu=EFF`<7omG2vt!4GGx1E?$=28+AUGG-DS;5f|tRNWpv`JQ&#bR8!Y5*d*VCT}U#ZiMaJJO$L!=z=?RGMDZs5l(qm+ zi_Xg~fW-=JC}}8$9jHJ%2Dcm_xr+wxT+S|0iYlb&LB6=MC1@N=S}PE3>s^};O}UVD z)6PsR#%zlti=MU#o{2gjt51{|<7lV(x)MaP=cNcLlx;0u)>~WFcr0Z~oL2KZM>iTC z#0HAiXHmQ|;oB!Z@ULUY7(5V;Au9p{&fONV@tQf(OOQ3I257^S1)0w6dZ4pLQQ+7{ zrztoW&y{zrBhkcZy0NQWNPDrB`9&kbR74|HfP+w62Tl&;f+?qJve$QRW6d)o0t1Pw zO(ObA)*Mx~!y6;_zD-Qc zL+KTdO%rhAcDDU%5V18rU(*FnjG|fnxXN@{$y2d76ZH38EwmCKv zZP%h3OgeQF4UKHkP1>%nsGwC@ue#TbT76qbuf7f(dANP^+SN;EyJoeh$~5UmzH1p; z6OPI{kK<|}00KW~Gyk85-S$uQv7-RsPyhbQ=a8N^o@M@F{wknC0Ji+!vljgQ1iT7d zgPFLZ55tyrhVLZ5Qm*rTx5un3AOF?UVS?G~vHA{BZAYW}Xq?a#9_eE+GmjpKCtymy z;rIJgAB4rjPX<$7!$_+>+dxZxQ1`=v^_ zUg>;9#q~ZRal6k@aUXK`+83B$<146m&e!5ncy;&*j)l*`pf>>C@RQC*EO71%IM@0A8nU%Z@XN-6=zxOnh$HTny@1h?cAhFP zq!Dlem7=0)*q9eN?tu#~T95oqNhH@Omg;flR3kmIw=LinU;7ssb@UKzEat*NvLraxQ$F55ycj}_t^F$dznzM6X)E3wcRd4};R0Fxh z_ZbLF(19krb(nk=%Zr2p5y(wKY6?@Bx(R`l4|F6*@X7{V@GYU17f99AfucANJd0q9 z*g*{h&qb)>+oT;PT%jy+TwNqnMk{ngW+4$09Fc}lC$z1?+?G`h^N~BDx|2pYp**)B zWH^Ei!iMC*f8IhqGB!=`nSorpr$JhC4asv-7meH5L;D0N~j~fCy G6&MMca|N6L literal 9724 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

    625As^gh5%tk)*$D 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